diff --git a/third_party/dawn/.gitattributes b/third_party/dawn/.gitattributes new file mode 100644 index 00000000000..d31b156d208 --- /dev/null +++ b/third_party/dawn/.gitattributes @@ -0,0 +1,4 @@ +* text=auto +*.sh eol=lf +*.gn eol=lf +*.gni eol=lf diff --git a/third_party/dawn/.gitignore b/third_party/dawn/.gitignore index 1dc05c780cb..b0f60f1ad7d 100644 --- a/third_party/dawn/.gitignore +++ b/third_party/dawn/.gitignore @@ -1,6 +1,7 @@ *.pyc # Directories added by gclient sync and the GN build +.cipd .gclient .gclient_entries build @@ -14,12 +15,17 @@ third_party/glm/ third_party/glslang/ third_party/googletest/ third_party/jinja2/ +third_party/jsoncpp/ third_party/llvm-build third_party/markupsafe/ third_party/shaderc/ +third_party/swiftshader/ third_party/spirv-cross/ third_party/spirv-headers/ -third_party/stb/ +third_party/tint/ +third_party/vulkan-headers/ +third_party/vulkan-loader/ +third_party/vulkan-validation-layers/ tools out @@ -45,8 +51,6 @@ tramp *.DS_Store .AppleDouble .LSOverride -# Icon must end with two \r -Icon ._* .DocumentRevisions-V100 .fseventsd diff --git a/third_party/dawn/.gn b/third_party/dawn/.gn index 038d50ee8ee..c2127661ede 100644 --- a/third_party/dawn/.gn +++ b/third_party/dawn/.gn @@ -16,6 +16,13 @@ buildconfig = "//build/config/BUILDCONFIG.gn" default_args = { clang_use_chrome_plugins = false + + # Override the mac version so standalone Dawn compiles with at least 10.11 + # which allows us to not skip the -Wunguarded-availability warning and get + # proper warnings for use of APIs that are 10.12 and above (even if + # Chromium is still on 10.10). + mac_deployment_target = "10.11.0" + mac_min_system_version = "10.11.0" } check_targets = [ diff --git a/third_party/dawn/BUILD.gn b/third_party/dawn/BUILD.gn index df93f904b4a..c4152d6c4fe 100644 --- a/third_party/dawn/BUILD.gn +++ b/third_party/dawn/BUILD.gn @@ -12,805 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("//build_overrides/build.gni") -import("generator/dawn_generator.gni") -import("scripts/dawn_component.gni") -import("scripts/dawn_features.gni") import("scripts/dawn_overrides_with_defaults.gni") -import("//testing/test.gni") - -############################################################################### -# libdawn_native -############################################################################### - -config("libdawn_native_internal") { - configs = [ "${dawn_root}/src/common:dawn_internal" ] - - # Suppress warnings that Metal isn't in the deployment target of Chrome - if (is_mac) { - cflags_objcc = [ "-Wno-unguarded-availability" ] - } -} - -dawn_json_generator("libdawn_native_utils_gen") { - target = "dawn_native_utils" - outputs = [ - "dawn_native/ProcTable.cpp", - "dawn_native/dawn_structs_autogen.h", - "dawn_native/dawn_structs_autogen.cpp", - "dawn_native/ValidationUtils_autogen.h", - "dawn_native/ValidationUtils_autogen.cpp", - ] -} - -if (dawn_enable_opengl) { - dawn_generator("libdawn_native_opengl_loader_gen") { - script = "generator/opengl_loader_generator.py" - args = [ - "--gl-xml", - rebase_path("third_party/gl.xml", root_build_dir), - ] - outputs = [ - "dawn_native/opengl/OpenGLFunctionsBase_autogen.cpp", - "dawn_native/opengl/OpenGLFunctionsBase_autogen.h", - ] - } -} - -# Public libdawn_native headers so they can be publically visible for -# dependencies of libdawn_native -source_set("libdawn_native_headers") { - public_deps = [ - "${dawn_root}/src/dawn:dawn_headers", - ] - all_dependent_configs = [ "${dawn_root}/src/common:dawn_public_include_dirs" ] - sources = [ - "src/include/dawn_native/DawnNative.h", - "src/include/dawn_native/dawn_native_export.h", - - # Include all backend's public headers so that dependencies can include - # them even when the backends are disabled. - "src/include/dawn_native/D3D12Backend.h", - "src/include/dawn_native/MetalBackend.h", - "src/include/dawn_native/NullBackend.h", - "src/include/dawn_native/OpenGLBackend.h", - "src/include/dawn_native/VulkanBackend.h", - ] -} - -# The meat of the compilation for libdawn_native so that we can cheaply have -# shared_library / static_library versions of it. It compiles all the files -# except those that define exported symbols. -source_set("libdawn_native_sources") { - deps = [ - ":libdawn_native_headers", - ":libdawn_native_utils_gen", - "${dawn_root}/src/common", - "${dawn_spirv_tools_dir}:spvtools_val", - "third_party:spirv_cross", - ] - - configs += [ ":libdawn_native_internal" ] - libs = [] - - sources = get_target_outputs(":libdawn_native_utils_gen") - sources += [ - "src/dawn_native/Adapter.cpp", - "src/dawn_native/Adapter.h", - "src/dawn_native/BackendConnection.cpp", - "src/dawn_native/BackendConnection.h", - "src/dawn_native/BindGroup.cpp", - "src/dawn_native/BindGroup.h", - "src/dawn_native/BindGroupLayout.cpp", - "src/dawn_native/BindGroupLayout.h", - "src/dawn_native/Buffer.cpp", - "src/dawn_native/Buffer.h", - "src/dawn_native/CommandAllocator.cpp", - "src/dawn_native/CommandAllocator.h", - "src/dawn_native/CommandBuffer.cpp", - "src/dawn_native/CommandBuffer.h", - "src/dawn_native/CommandBufferStateTracker.cpp", - "src/dawn_native/CommandBufferStateTracker.h", - "src/dawn_native/CommandEncoder.cpp", - "src/dawn_native/CommandEncoder.h", - "src/dawn_native/Commands.cpp", - "src/dawn_native/Commands.h", - "src/dawn_native/ComputePassEncoder.cpp", - "src/dawn_native/ComputePassEncoder.h", - "src/dawn_native/ComputePipeline.cpp", - "src/dawn_native/ComputePipeline.h", - "src/dawn_native/Device.cpp", - "src/dawn_native/Device.h", - "src/dawn_native/DynamicUploader.cpp", - "src/dawn_native/DynamicUploader.h", - "src/dawn_native/Error.cpp", - "src/dawn_native/Error.h", - "src/dawn_native/ErrorData.cpp", - "src/dawn_native/ErrorData.h", - "src/dawn_native/Fence.cpp", - "src/dawn_native/Fence.h", - "src/dawn_native/FenceSignalTracker.cpp", - "src/dawn_native/FenceSignalTracker.h", - "src/dawn_native/Forward.h", - "src/dawn_native/Instance.cpp", - "src/dawn_native/Instance.h", - "src/dawn_native/ObjectBase.cpp", - "src/dawn_native/ObjectBase.h", - "src/dawn_native/PassResourceUsage.h", - "src/dawn_native/PerStage.cpp", - "src/dawn_native/PerStage.h", - "src/dawn_native/Pipeline.cpp", - "src/dawn_native/Pipeline.h", - "src/dawn_native/PipelineLayout.cpp", - "src/dawn_native/PipelineLayout.h", - "src/dawn_native/ProgrammablePassEncoder.cpp", - "src/dawn_native/ProgrammablePassEncoder.h", - "src/dawn_native/Queue.cpp", - "src/dawn_native/Queue.h", - "src/dawn_native/RefCounted.cpp", - "src/dawn_native/RefCounted.h", - "src/dawn_native/RenderPassEncoder.cpp", - "src/dawn_native/RenderPassEncoder.h", - "src/dawn_native/RenderPipeline.cpp", - "src/dawn_native/RenderPipeline.h", - "src/dawn_native/RingBuffer.cpp", - "src/dawn_native/RingBuffer.h", - "src/dawn_native/Sampler.cpp", - "src/dawn_native/Sampler.h", - "src/dawn_native/ShaderModule.cpp", - "src/dawn_native/ShaderModule.h", - "src/dawn_native/StagingBuffer.cpp", - "src/dawn_native/StagingBuffer.h", - "src/dawn_native/SwapChain.cpp", - "src/dawn_native/SwapChain.h", - "src/dawn_native/Texture.cpp", - "src/dawn_native/Texture.h", - "src/dawn_native/ToBackend.h", - "src/dawn_native/Toggles.h", - "src/dawn_native/dawn_platform.h", - ] - - if (dawn_enable_d3d12) { - libs += [ "dxguid.lib" ] - sources += [ - "src/dawn_native/d3d12/AdapterD3D12.cpp", - "src/dawn_native/d3d12/AdapterD3D12.h", - "src/dawn_native/d3d12/BackendD3D12.cpp", - "src/dawn_native/d3d12/BackendD3D12.h", - "src/dawn_native/d3d12/BindGroupD3D12.cpp", - "src/dawn_native/d3d12/BindGroupD3D12.h", - "src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp", - "src/dawn_native/d3d12/BindGroupLayoutD3D12.h", - "src/dawn_native/d3d12/BufferD3D12.cpp", - "src/dawn_native/d3d12/BufferD3D12.h", - "src/dawn_native/d3d12/CommandAllocatorManager.cpp", - "src/dawn_native/d3d12/CommandAllocatorManager.h", - "src/dawn_native/d3d12/CommandBufferD3D12.cpp", - "src/dawn_native/d3d12/CommandBufferD3D12.h", - "src/dawn_native/d3d12/ComputePipelineD3D12.cpp", - "src/dawn_native/d3d12/ComputePipelineD3D12.h", - "src/dawn_native/d3d12/D3D12Info.cpp", - "src/dawn_native/d3d12/D3D12Info.h", - "src/dawn_native/d3d12/DescriptorHeapAllocator.cpp", - "src/dawn_native/d3d12/DescriptorHeapAllocator.h", - "src/dawn_native/d3d12/DeviceD3D12.cpp", - "src/dawn_native/d3d12/DeviceD3D12.h", - "src/dawn_native/d3d12/Forward.h", - "src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp", - "src/dawn_native/d3d12/NativeSwapChainImplD3D12.h", - "src/dawn_native/d3d12/PipelineLayoutD3D12.cpp", - "src/dawn_native/d3d12/PipelineLayoutD3D12.h", - "src/dawn_native/d3d12/PlatformFunctions.cpp", - "src/dawn_native/d3d12/PlatformFunctions.h", - "src/dawn_native/d3d12/QueueD3D12.cpp", - "src/dawn_native/d3d12/QueueD3D12.h", - "src/dawn_native/d3d12/RenderPipelineD3D12.cpp", - "src/dawn_native/d3d12/RenderPipelineD3D12.h", - "src/dawn_native/d3d12/ResourceAllocator.cpp", - "src/dawn_native/d3d12/ResourceAllocator.h", - "src/dawn_native/d3d12/SamplerD3D12.cpp", - "src/dawn_native/d3d12/SamplerD3D12.h", - "src/dawn_native/d3d12/ShaderModuleD3D12.cpp", - "src/dawn_native/d3d12/ShaderModuleD3D12.h", - "src/dawn_native/d3d12/StagingBufferD3D12.cpp", - "src/dawn_native/d3d12/StagingBufferD3D12.h", - "src/dawn_native/d3d12/SwapChainD3D12.cpp", - "src/dawn_native/d3d12/SwapChainD3D12.h", - "src/dawn_native/d3d12/TextureCopySplitter.cpp", - "src/dawn_native/d3d12/TextureCopySplitter.h", - "src/dawn_native/d3d12/TextureD3D12.cpp", - "src/dawn_native/d3d12/TextureD3D12.h", - "src/dawn_native/d3d12/UtilsD3D12.cpp", - "src/dawn_native/d3d12/UtilsD3D12.h", - "src/dawn_native/d3d12/d3d12_platform.h", - ] - } - - if (dawn_enable_metal) { - libs += [ - "Metal.framework", - "Cocoa.framework", - "IOKit.framework", - "IOSurface.framework", - ] - sources += [ - "src/dawn_native/metal/BackendMTL.h", - "src/dawn_native/metal/BackendMTL.mm", - "src/dawn_native/metal/BufferMTL.h", - "src/dawn_native/metal/BufferMTL.mm", - "src/dawn_native/metal/CommandBufferMTL.h", - "src/dawn_native/metal/CommandBufferMTL.mm", - "src/dawn_native/metal/ComputePipelineMTL.h", - "src/dawn_native/metal/ComputePipelineMTL.mm", - "src/dawn_native/metal/DeviceMTL.h", - "src/dawn_native/metal/DeviceMTL.mm", - "src/dawn_native/metal/Forward.h", - "src/dawn_native/metal/PipelineLayoutMTL.h", - "src/dawn_native/metal/PipelineLayoutMTL.mm", - "src/dawn_native/metal/QueueMTL.h", - "src/dawn_native/metal/QueueMTL.mm", - "src/dawn_native/metal/RenderPipelineMTL.h", - "src/dawn_native/metal/RenderPipelineMTL.mm", - "src/dawn_native/metal/SamplerMTL.h", - "src/dawn_native/metal/SamplerMTL.mm", - "src/dawn_native/metal/ShaderModuleMTL.h", - "src/dawn_native/metal/ShaderModuleMTL.mm", - "src/dawn_native/metal/StagingBufferMTL.h", - "src/dawn_native/metal/StagingBufferMTL.mm", - "src/dawn_native/metal/SwapChainMTL.h", - "src/dawn_native/metal/SwapChainMTL.mm", - "src/dawn_native/metal/TextureMTL.h", - "src/dawn_native/metal/TextureMTL.mm", - "src/dawn_native/metal/UtilsMetal.h", - "src/dawn_native/metal/UtilsMetal.mm", - ] - } - - if (dawn_enable_null) { - sources += [ - "src/dawn_native/null/DeviceNull.cpp", - "src/dawn_native/null/DeviceNull.h", - ] - } - - if (dawn_enable_opengl) { - deps += [ - ":libdawn_native_opengl_loader_gen", - "third_party:glad", - ] - sources += get_target_outputs(":libdawn_native_opengl_loader_gen") - sources += [ - "src/dawn_native/opengl/BackendGL.cpp", - "src/dawn_native/opengl/BackendGL.h", - "src/dawn_native/opengl/BufferGL.cpp", - "src/dawn_native/opengl/BufferGL.h", - "src/dawn_native/opengl/CommandBufferGL.cpp", - "src/dawn_native/opengl/CommandBufferGL.h", - "src/dawn_native/opengl/ComputePipelineGL.cpp", - "src/dawn_native/opengl/ComputePipelineGL.h", - "src/dawn_native/opengl/DeviceGL.cpp", - "src/dawn_native/opengl/DeviceGL.h", - "src/dawn_native/opengl/Forward.h", - "src/dawn_native/opengl/OpenGLFunctions.cpp", - "src/dawn_native/opengl/OpenGLFunctions.h", - "src/dawn_native/opengl/PersistentPipelineStateGL.cpp", - "src/dawn_native/opengl/PersistentPipelineStateGL.h", - "src/dawn_native/opengl/PipelineGL.cpp", - "src/dawn_native/opengl/PipelineGL.h", - "src/dawn_native/opengl/PipelineLayoutGL.cpp", - "src/dawn_native/opengl/PipelineLayoutGL.h", - "src/dawn_native/opengl/QueueGL.cpp", - "src/dawn_native/opengl/QueueGL.h", - "src/dawn_native/opengl/RenderPipelineGL.cpp", - "src/dawn_native/opengl/RenderPipelineGL.h", - "src/dawn_native/opengl/SamplerGL.cpp", - "src/dawn_native/opengl/SamplerGL.h", - "src/dawn_native/opengl/ShaderModuleGL.cpp", - "src/dawn_native/opengl/ShaderModuleGL.h", - "src/dawn_native/opengl/SwapChainGL.cpp", - "src/dawn_native/opengl/SwapChainGL.h", - "src/dawn_native/opengl/TextureGL.cpp", - "src/dawn_native/opengl/TextureGL.h", - "src/dawn_native/opengl/UtilsGL.cpp", - "src/dawn_native/opengl/UtilsGL.h", - ] - } - - if (dawn_enable_vulkan) { - deps += [ "third_party:vulkan_headers" ] - sources += [ - "src/dawn_native/vulkan/AdapterVk.cpp", - "src/dawn_native/vulkan/AdapterVk.h", - "src/dawn_native/vulkan/BackendVk.cpp", - "src/dawn_native/vulkan/BackendVk.h", - "src/dawn_native/vulkan/BindGroupLayoutVk.cpp", - "src/dawn_native/vulkan/BindGroupLayoutVk.h", - "src/dawn_native/vulkan/BindGroupVk.cpp", - "src/dawn_native/vulkan/BindGroupVk.h", - "src/dawn_native/vulkan/BufferVk.cpp", - "src/dawn_native/vulkan/BufferVk.h", - "src/dawn_native/vulkan/CommandBufferVk.cpp", - "src/dawn_native/vulkan/CommandBufferVk.h", - "src/dawn_native/vulkan/ComputePipelineVk.cpp", - "src/dawn_native/vulkan/ComputePipelineVk.h", - "src/dawn_native/vulkan/DeviceVk.cpp", - "src/dawn_native/vulkan/DeviceVk.h", - "src/dawn_native/vulkan/FencedDeleter.cpp", - "src/dawn_native/vulkan/FencedDeleter.h", - "src/dawn_native/vulkan/Forward.h", - "src/dawn_native/vulkan/MemoryAllocator.cpp", - "src/dawn_native/vulkan/MemoryAllocator.h", - "src/dawn_native/vulkan/NativeSwapChainImplVk.cpp", - "src/dawn_native/vulkan/NativeSwapChainImplVk.h", - "src/dawn_native/vulkan/PipelineLayoutVk.cpp", - "src/dawn_native/vulkan/PipelineLayoutVk.h", - "src/dawn_native/vulkan/QueueVk.cpp", - "src/dawn_native/vulkan/QueueVk.h", - "src/dawn_native/vulkan/RenderPassCache.cpp", - "src/dawn_native/vulkan/RenderPassCache.h", - "src/dawn_native/vulkan/RenderPipelineVk.cpp", - "src/dawn_native/vulkan/RenderPipelineVk.h", - "src/dawn_native/vulkan/SamplerVk.cpp", - "src/dawn_native/vulkan/SamplerVk.h", - "src/dawn_native/vulkan/ShaderModuleVk.cpp", - "src/dawn_native/vulkan/ShaderModuleVk.h", - "src/dawn_native/vulkan/StagingBufferVk.cpp", - "src/dawn_native/vulkan/StagingBufferVk.h", - "src/dawn_native/vulkan/SwapChainVk.cpp", - "src/dawn_native/vulkan/SwapChainVk.h", - "src/dawn_native/vulkan/TextureVk.cpp", - "src/dawn_native/vulkan/TextureVk.h", - "src/dawn_native/vulkan/UtilsVulkan.cpp", - "src/dawn_native/vulkan/UtilsVulkan.h", - "src/dawn_native/vulkan/VulkanError.cpp", - "src/dawn_native/vulkan/VulkanError.h", - "src/dawn_native/vulkan/VulkanFunctions.cpp", - "src/dawn_native/vulkan/VulkanFunctions.h", - "src/dawn_native/vulkan/VulkanInfo.cpp", - "src/dawn_native/vulkan/VulkanInfo.h", - ] - } -} - -# The static and shared libraries for libdawn_native. Most of the files are -# already compiled in libdawn_native_sources, but we still need to compile -# files defining exported symbols. -dawn_component("libdawn_native") { - DEFINE_PREFIX = "DAWN_NATIVE" - - #Make headers publically visible - public_deps = [ - ":libdawn_native_headers", - ] - - deps = [ - ":libdawn_native_sources", - "${dawn_root}/src/common", - ] - sources = [ - "src/dawn_native/DawnNative.cpp", - ] - configs = [ ":libdawn_native_internal" ] - - if (dawn_enable_d3d12) { - sources += [ "src/dawn_native/d3d12/D3D12Backend.cpp" ] - } - if (dawn_enable_metal) { - sources += [ "src/dawn_native/metal/MetalBackend.mm" ] - } - if (dawn_enable_null) { - sources += [ "src/dawn_native/null/NullBackend.cpp" ] - } - if (dawn_enable_opengl) { - sources += [ "src/dawn_native/opengl/OpenGLBackend.cpp" ] - deps += [ "third_party:glad" ] - } - if (dawn_enable_vulkan) { - sources += [ "src/dawn_native/vulkan/VulkanBackend.cpp" ] - deps += [ "third_party:vulkan_headers" ] - } -} - -############################################################################### -# libdawn_wire -############################################################################### - -dawn_json_generator("libdawn_wire_gen") { - target = "dawn_wire" - outputs = [ - "dawn_wire/WireCmd_autogen.h", - "dawn_wire/WireCmd_autogen.cpp", - "dawn_wire/client/ApiObjects_autogen.h", - "dawn_wire/client/ApiProcs_autogen.cpp", - "dawn_wire/client/ApiProcs_autogen.h", - "dawn_wire/client/ClientBase_autogen.h", - "dawn_wire/client/ClientHandlers_autogen.cpp", - "dawn_wire/client/ClientPrototypes_autogen.inc", - "dawn_wire/server/ServerBase_autogen.h", - "dawn_wire/server/ServerDoers_autogen.cpp", - "dawn_wire/server/ServerHandlers_autogen.cpp", - "dawn_wire/server/ServerPrototypes_autogen.inc", - ] -} - -dawn_component("libdawn_wire") { - DEFINE_PREFIX = "DAWN_WIRE" - - deps = [ - ":libdawn_wire_gen", - "${dawn_root}/src/common", - "${dawn_root}/src/dawn_wire:libdawn_wire_headers", - ] - - configs = [ "${dawn_root}/src/common:dawn_internal" ] - sources = get_target_outputs(":libdawn_wire_gen") - sources += [ - "src/dawn_wire/WireClient.cpp", - "src/dawn_wire/WireDeserializeAllocator.cpp", - "src/dawn_wire/WireDeserializeAllocator.h", - "src/dawn_wire/WireServer.cpp", - "src/dawn_wire/client/ApiObjects.h", - "src/dawn_wire/client/ApiProcs.cpp", - "src/dawn_wire/client/Buffer.cpp", - "src/dawn_wire/client/Buffer.h", - "src/dawn_wire/client/Client.cpp", - "src/dawn_wire/client/Client.h", - "src/dawn_wire/client/ClientDoers.cpp", - "src/dawn_wire/client/Device.cpp", - "src/dawn_wire/client/Device.h", - "src/dawn_wire/client/Fence.cpp", - "src/dawn_wire/client/Fence.h", - "src/dawn_wire/client/ObjectAllocator.h", - "src/dawn_wire/server/ObjectStorage.h", - "src/dawn_wire/server/Server.cpp", - "src/dawn_wire/server/Server.h", - "src/dawn_wire/server/ServerBuffer.cpp", - "src/dawn_wire/server/ServerDevice.cpp", - "src/dawn_wire/server/ServerFence.cpp", - "src/dawn_wire/server/ServerQueue.cpp", - ] - - # Make headers publically visible - public_deps = [ - "${dawn_root}/src/dawn_wire:libdawn_wire_headers", - ] -} - -############################################################################### -# Utils for tests and samples -############################################################################### - -static_library("dawn_utils") { - configs += [ "${dawn_root}/src/common:dawn_internal" ] - - sources = [ - "src/utils/BackendBinding.cpp", - "src/utils/BackendBinding.h", - "src/utils/ComboRenderPipelineDescriptor.cpp", - "src/utils/ComboRenderPipelineDescriptor.h", - "src/utils/DawnHelpers.cpp", - "src/utils/DawnHelpers.h", - "src/utils/SystemUtils.cpp", - "src/utils/SystemUtils.h", - "src/utils/TerribleCommandBuffer.cpp", - "src/utils/TerribleCommandBuffer.h", - ] - - public_deps = [ - "${dawn_root}/src/dawn:dawn_headers", - ] - - deps = [ - ":libdawn_native", - ":libdawn_wire", - "${dawn_root}/src/common", - "${dawn_shaderc_dir}:libshaderc", - "third_party:glfw", - ] - libs = [] - - if (dawn_enable_d3d12) { - sources += [ "src/utils/D3D12Binding.cpp" ] - } - - if (dawn_enable_metal) { - sources += [ "src/utils/MetalBinding.mm" ] - libs += [ - "Metal.framework", - "QuartzCore.framework", - ] - - # Suppress warnings that Metal isn't in the deployment target of Chrome - if (is_mac) { - cflags_objcc = [ "-Wno-unguarded-availability" ] - } - } - - if (dawn_enable_null) { - sources += [ "src/utils/NullBinding.cpp" ] - } - - if (dawn_enable_opengl) { - sources += [ "src/utils/OpenGLBinding.cpp" ] - deps += [ "third_party:glad" ] - } - - if (dawn_enable_vulkan) { - sources += [ "src/utils/VulkanBinding.cpp" ] - deps += [ "third_party:vulkan_headers" ] - } -} - -############################################################################### -# Dawn test targets -############################################################################### - -dawn_json_generator("mock_dawn_gen") { - target = "mock_dawn" - outputs = [ - "mock/mock_dawn.h", - "mock/mock_dawn.cpp", - ] -} - -test("dawn_unittests") { - configs += [ "${dawn_root}/src/common:dawn_internal" ] - - deps = [ - ":dawn_utils", - ":libdawn_native", - ":libdawn_native_sources", - ":libdawn_wire", - ":mock_dawn_gen", - "${dawn_root}/src/common", - "${dawn_root}/src/dawn:libdawn", - "third_party:gmock_and_gtest", - ] - - # Add internal Dawn Native headers and config for internal unittests. - deps += [ ":libdawn_native_headers" ] - configs += [ ":libdawn_native_internal" ] - - sources = get_target_outputs(":mock_dawn_gen") - sources += [ - "src/tests/unittests/BitSetIteratorTests.cpp", - "src/tests/unittests/CommandAllocatorTests.cpp", - "src/tests/unittests/EnumClassBitmasksTests.cpp", - "src/tests/unittests/ErrorTests.cpp", - "src/tests/unittests/MathTests.cpp", - "src/tests/unittests/ObjectBaseTests.cpp", - "src/tests/unittests/PerStageTests.cpp", - "src/tests/unittests/RefCountedTests.cpp", - "src/tests/unittests/ResultTests.cpp", - "src/tests/unittests/RingBufferTests.cpp", - "src/tests/unittests/SerialMapTests.cpp", - "src/tests/unittests/SerialQueueTests.cpp", - "src/tests/unittests/ToBackendTests.cpp", - "src/tests/unittests/validation/BindGroupValidationTests.cpp", - "src/tests/unittests/validation/BufferValidationTests.cpp", - "src/tests/unittests/validation/CommandBufferValidationTests.cpp", - "src/tests/unittests/validation/ComputeIndirectValidationTests.cpp", - "src/tests/unittests/validation/ComputeValidationTests.cpp", - "src/tests/unittests/validation/CopyCommandsValidationTests.cpp", - "src/tests/unittests/validation/DebugMarkerValidationTests.cpp", - "src/tests/unittests/validation/DrawIndirectValidationTests.cpp", - "src/tests/unittests/validation/DynamicStateCommandValidationTests.cpp", - "src/tests/unittests/validation/FenceValidationTests.cpp", - "src/tests/unittests/validation/QueueSubmitValidationTests.cpp", - "src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp", - "src/tests/unittests/validation/RenderPassValidationTests.cpp", - "src/tests/unittests/validation/RenderPipelineValidationTests.cpp", - "src/tests/unittests/validation/SamplerValidationTests.cpp", - "src/tests/unittests/validation/ShaderModuleValidationTests.cpp", - "src/tests/unittests/validation/TextureValidationTests.cpp", - "src/tests/unittests/validation/TextureViewValidationTests.cpp", - "src/tests/unittests/validation/ToggleValidationTests.cpp", - "src/tests/unittests/validation/ValidationTest.cpp", - "src/tests/unittests/validation/ValidationTest.h", - "src/tests/unittests/validation/VertexBufferValidationTests.cpp", - "src/tests/unittests/validation/VertexInputValidationTests.cpp", - "src/tests/unittests/wire/WireArgumentTests.cpp", - "src/tests/unittests/wire/WireBasicTests.cpp", - "src/tests/unittests/wire/WireBufferMappingTests.cpp", - "src/tests/unittests/wire/WireErrorCallbackTests.cpp", - "src/tests/unittests/wire/WireFenceTests.cpp", - "src/tests/unittests/wire/WireInjectTextureTests.cpp", - "src/tests/unittests/wire/WireOptionalTests.cpp", - "src/tests/unittests/wire/WireTest.cpp", - "src/tests/unittests/wire/WireTest.h", - ] - - if (dawn_enable_d3d12) { - sources += [ "src/tests/unittests/d3d12/CopySplitTests.cpp" ] - } - - # When building inside Chromium, use their gtest main function because it is - # needed to run in swarming correctly. - if (build_with_chromium) { - sources += [ "//gpu/dawn_unittests_main.cc" ] - } else { - sources += [ "src/tests/UnittestsMain.cpp" ] - } -} - -test("dawn_end2end_tests") { - configs += [ "${dawn_root}/src/common:dawn_internal" ] - - deps = [ - ":dawn_utils", - ":libdawn_native", - ":libdawn_wire", - "${dawn_root}/src/common", - "${dawn_root}/src/dawn:libdawn", - "third_party:glfw", - "third_party:gmock_and_gtest", - ] - - sources = [ - "src/tests/DawnTest.cpp", - "src/tests/DawnTest.h", - "src/tests/end2end/BasicTests.cpp", - "src/tests/end2end/BindGroupTests.cpp", - "src/tests/end2end/BufferTests.cpp", - "src/tests/end2end/ClipSpaceTests.cpp", - "src/tests/end2end/ColorStateTests.cpp", - "src/tests/end2end/ComputeCopyStorageBufferTests.cpp", - "src/tests/end2end/ComputeIndirectTests.cpp", - "src/tests/end2end/ComputeSharedMemoryTests.cpp", - "src/tests/end2end/CopyTests.cpp", - "src/tests/end2end/DebugMarkerTests.cpp", - "src/tests/end2end/DepthStencilStateTests.cpp", - "src/tests/end2end/DestroyTests.cpp", - "src/tests/end2end/DrawIndexedIndirectTests.cpp", - "src/tests/end2end/DrawIndexedTests.cpp", - "src/tests/end2end/DrawIndirectTests.cpp", - "src/tests/end2end/DrawTests.cpp", - "src/tests/end2end/DynamicBufferOffsetTests.cpp", - "src/tests/end2end/FenceTests.cpp", - "src/tests/end2end/IndexFormatTests.cpp", - "src/tests/end2end/MultisampledRenderingTests.cpp", - "src/tests/end2end/NonzeroTextureCreationTests.cpp", - "src/tests/end2end/ObjectCachingTests.cpp", - "src/tests/end2end/PrimitiveTopologyTests.cpp", - "src/tests/end2end/RenderPassLoadOpTests.cpp", - "src/tests/end2end/RenderPassTests.cpp", - "src/tests/end2end/SamplerTests.cpp", - "src/tests/end2end/ScissorTests.cpp", - "src/tests/end2end/TextureViewTests.cpp", - "src/tests/end2end/TextureZeroInitTests.cpp", - "src/tests/end2end/VertexFormatTests.cpp", - "src/tests/end2end/VertexInputTests.cpp", - "src/tests/end2end/ViewportOrientationTests.cpp", - ] - - libs = [] - - if (dawn_enable_metal) { - sources += [ "src/tests/end2end/IOSurfaceWrappingTests.cpp" ] - - libs += [ "IOSurface.framework" ] - } - - # When building inside Chromium, use their gtest main function because it is - # needed to run in swarming correctly. - if (build_with_chromium) { - sources += [ "//gpu/dawn_end2end_tests_main.cc" ] - } else { - sources += [ "src/tests/End2EndTestsMain.cpp" ] - } -} - -# Temporary groups to make a 5-way patch to fix crbug.com/913171 -group("dawn_unittests_temp_group") { - testonly = true - deps = [ - ":dawn_unittests", - ] -} - -group("dawn_end2end_tests_temp_group") { +group("all") { testonly = true deps = [ - ":dawn_end2end_tests", + "src/fuzzers:dawn_fuzzers", + "src/tests:dawn_tests", ] -} - -############################################################################### -# Dawn samples, only in standalone builds -############################################################################### - -if (dawn_standalone) { - # Static library to contain code and dependencies common to all samples - static_library("dawn_sample_utils") { - sources = [ - "examples/SampleUtils.cpp", - "examples/SampleUtils.h", - ] - - # Export all of these as public deps so that `gn check` allows includes - public_deps = [ - ":dawn_utils", - ":libdawn_native", - ":libdawn_wire", - "${dawn_root}/src/common", - "${dawn_root}/src/dawn:libdawn", - "third_party:glfw", - ] - public_configs = [ "${dawn_root}/src/common:dawn_internal" ] + if (dawn_standalone) { + deps += [ "examples:dawn_samples" ] } - - # Template for samples to avoid listing dawn_sample_utils as a dep every time - template("dawn_sample") { - executable(target_name) { - deps = [ - ":dawn_sample_utils", - ] - forward_variables_from(invoker, "*", [ "deps" ]) - - if (defined(invoker.deps)) { - deps += invoker.deps - } - } - } - - dawn_sample("CppHelloTriangle") { - sources = [ - "examples/CppHelloTriangle.cpp", - ] - } - - dawn_sample("CHelloTriangle") { - sources = [ - "examples/CHelloTriangle.cpp", - ] - } - - dawn_sample("ComputeBoids") { - sources = [ - "examples/ComputeBoids.cpp", - ] - deps = [ - "third_party:glm", - ] - } - - dawn_sample("Animometer") { - sources = [ - "examples/Animometer.cpp", - ] - } - - dawn_sample("CubeReflection") { - sources = [ - "examples/CubeReflection.cpp", - ] - deps = [ - "third_party:glm", - ] - } - - group("dawn_samples") { - deps = [ - ":Animometer", - ":CHelloTriangle", - ":ComputeBoids", - ":CppHelloTriangle", - ":CubeReflection", - ] - } -} - -############################################################################### -# Fuzzers -############################################################################### - -group("dawn_fuzzers") { - testonly = true - deps = [ - "src/fuzzers:dawn_spirv_cross_glsl_fast_fuzzer", - "src/fuzzers:dawn_spirv_cross_hlsl_fast_fuzzer", - "src/fuzzers:dawn_spirv_cross_msl_fast_fuzzer", - "src/fuzzers:dawn_wire_server_and_frontend_fuzzer", - ] } diff --git a/third_party/dawn/CMakeLists.txt b/third_party/dawn/CMakeLists.txt new file mode 100644 index 00000000000..8394d031a36 --- /dev/null +++ b/third_party/dawn/CMakeLists.txt @@ -0,0 +1,150 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +cmake_minimum_required(VERSION 3.10) + +# When upgrading to CMake 3.11 we can remove DAWN_DUMMY_FILE because source-less add_library +# becomes available. +# When upgrading to CMake 3.12 we should add CONFIGURE_DEPENDS to DawnGenerator to rerun CMake in +# case any of the generator files changes. We should also remove the CACHE "" FORCE stuff to +# override options in third_party dependencies. + +project( + Dawn + DESCRIPTION "Dawn, a WebGPU implementation" + HOMEPAGE_URL "https://dawn.googlesource.com/dawn" + LANGUAGES C CXX +) + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +if(NOT CMAKE_BUILD_TYPE) + message(WARNING "CMAKE_BUILD_TYPE not set, forcing it to Debug") + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING + "Build type (Debug, Release, RelWithDebInfo, MinSizeRel)" FORCE) +endif() + +set(DAWN_BUILD_GEN_DIR "${Dawn_BINARY_DIR}/gen") +set(DAWN_GENERATOR_DIR "${Dawn_SOURCE_DIR}/generator") +set(DAWN_SRC_DIR "${Dawn_SOURCE_DIR}/src") +set(DAWN_INCLUDE_DIR "${DAWN_SRC_DIR}/include") +set(DAWN_TEMPLATE_DIR "${DAWN_GENERATOR_DIR}/templates") +set(DAWN_THIRD_PARTY_DIR "${Dawn_SOURCE_DIR}/third_party") + +set(DAWN_DUMMY_FILE "${DAWN_SRC_DIR}/Dummy.cpp") + +################################################################################ +# Configuration options +################################################################################ + +# Default values for the backend-enabling options +set(ENABLE_D3D12 OFF) +set(ENABLE_METAL OFF) +set(ENABLE_OPENGL OFF) +set(ENABLE_VULKAN OFF) +set(USE_X11 OFF) +if (WIN32) + set(ENABLE_D3D12 ON) + set(ENABLE_VULKAN ON) +elseif(APPLE) + set(ENABLE_METAL ON) +elseif(UNIX) + set(ENABLE_OPENGL ON) + set(ENABLE_VULKAN ON) + set(USE_X11 ON) +endif() + +option(DAWN_ENABLE_D3D12 "Enable compilation of the D3D12 backend" ${ENABLE_D3D12}) +option(DAWN_ENABLE_METAL "Enable compilation of the Metal backend" ${ENABLE_METAL}) +option(DAWN_ENABLE_NULL "Enable compilation of the Null backend" ON) +option(DAWN_ENABLE_OPENGL "Enable compilation of the OpenGL backend" ${ENABLE_OPENGL}) +option(DAWN_ENABLE_VULKAN "Enable compilation of the Vulkan backend" ${ENABLE_VULKAN}) +option(DAWN_ALWAYS_ASSERT "Enable assertions on all build types" OFF) +option(DAWN_USE_X11 "Enable support for X11 surface" ${USE_X11}) + +option(DAWN_BUILD_EXAMPLES "Enables building Dawn's exmaples" ON) + +set(DAWN_GLFW_DIR "${DAWN_THIRD_PARTY_DIR}/glfw" CACHE STRING "Directory in which to find GLFW") +set(DAWN_GLM_DIR "${DAWN_THIRD_PARTY_DIR}/glm" CACHE STRING "Directory in which to find GLM") +set(DAWN_GLSLANG_DIR "${DAWN_THIRD_PARTY_DIR}/glslang" CACHE STRING "Directory in which to find GLSLang") +set(DAWN_JINJA2_DIR "${DAWN_THIRD_PARTY_DIR}/jinja2" CACHE STRING "Directory in which to find Jinja2") +set(DAWN_SHADERC_DIR "${DAWN_THIRD_PARTY_DIR}/shaderc" CACHE STRING "Directory in which to find shaderc") +set(DAWN_SPIRV_CROSS_DIR "${DAWN_THIRD_PARTY_DIR}/spirv-cross" CACHE STRING "Directory in which to find SPIRV-Cross") +set(DAWN_SPIRV_HEADERS_DIR "${DAWN_THIRD_PARTY_DIR}/spirv-headers" CACHE STRING "Directory in which to find SPIRV-Headers") +set(DAWN_SPIRV_TOOLS_DIR "${DAWN_THIRD_PARTY_DIR}/SPIRV-Tools" CACHE STRING "Directory in which to find SPIRV-Tools") + +################################################################################ +# Dawn's public and internal "configs" +################################################################################ + +# The public config contains only the include paths for the Dawn headers. +add_library(dawn_public_config INTERFACE) +target_include_directories(dawn_public_config INTERFACE + "${DAWN_SRC_DIR}/include" + "${DAWN_BUILD_GEN_DIR}/src/include" +) + +# The internal config conatins additional path but includes the dawn_public_config include paths +add_library(dawn_internal_config INTERFACE) +target_include_directories(dawn_internal_config INTERFACE + "${DAWN_SRC_DIR}" + "${DAWN_BUILD_GEN_DIR}/src" +) +target_link_libraries(dawn_internal_config INTERFACE dawn_public_config) + +# Compile definitions for the internal config +if (DAWN_ALWAYS_ASSERT OR $) + target_compile_definitions(dawn_internal_config INTERFACE "DAWN_ENABLE_ASSERTS") +endif() +if (DAWN_ENABLE_D3D12) + target_compile_definitions(dawn_internal_config INTERFACE "DAWN_ENABLE_BACKEND_D3D12") +endif() +if (DAWN_ENABLE_METAL) + target_compile_definitions(dawn_internal_config INTERFACE "DAWN_ENABLE_BACKEND_METAL") +endif() +if (DAWN_ENABLE_NULL) + target_compile_definitions(dawn_internal_config INTERFACE "DAWN_ENABLE_BACKEND_NULL") +endif() +if (DAWN_ENABLE_OPENGL) + target_compile_definitions(dawn_internal_config INTERFACE "DAWN_ENABLE_BACKEND_OPENGL") +endif() +if (DAWN_ENABLE_VULKAN) + target_compile_definitions(dawn_internal_config INTERFACE "DAWN_ENABLE_BACKEND_VULKAN") +endif() +if (DAWN_USE_X11) + target_compile_definitions(dawn_internal_config INTERFACE "DAWN_USE_X11") +endif() +if (WIN32) + target_compile_definitions(dawn_internal_config INTERFACE "NOMINMAX" "WIN32_LEAN_AND_MEAN") +endif() + + +set(CMAKE_CXX_STANDARD "14") + +################################################################################ +# Run on all subdirectories +################################################################################ + +add_subdirectory(third_party) +add_subdirectory(src/common) +add_subdirectory(generator) +add_subdirectory(src/dawn) +add_subdirectory(src/dawn_platform) +add_subdirectory(src/dawn_native) +add_subdirectory(src/dawn_wire) + +if (DAWN_BUILD_EXAMPLES) + add_subdirectory(src/utils) + add_subdirectory(examples) +endif() diff --git a/third_party/dawn/CONTRIBUTING.md b/third_party/dawn/CONTRIBUTING.md index c500c152805..4917a8571a7 100644 --- a/third_party/dawn/CONTRIBUTING.md +++ b/third_party/dawn/CONTRIBUTING.md @@ -19,3 +19,6 @@ again. All submissions, including submissions by project members, require review. We use [Dawn's Gerrit](https://dawn-review.googlesource.com) for this purpose. + +Dawn doesn't have a formal coding style yet, except what's defined by our clang format style. +Overall try to use the same style and convention as code around your change. diff --git a/third_party/dawn/DEPS b/third_party/dawn/DEPS index 679e9abc9db..ab49a29ec89 100644 --- a/third_party/dawn/DEPS +++ b/third_party/dawn/DEPS @@ -1,40 +1,56 @@ use_relative_paths = True -use_relative_hooks = True + +gclient_gn_args_file = 'build/config/gclient_args.gni' +gclient_gn_args = [ + 'mac_xcode_version', +] vars = { 'chromium_git': 'https://chromium.googlesource.com', 'dawn_git': 'https://dawn.googlesource.com', 'github_git': 'https://github.com', + 'swiftshader_git': 'https://swiftshader.googlesource.com', 'dawn_standalone': True, + + # This can be overridden, e.g. with custom_vars, to download a nonstandard + # Xcode version in build/mac_toolchain.py instead of downloading the + # prebuilt pinned revision. + 'mac_xcode_version': 'default', } deps = { # Dependencies required to use GN/Clang in standalone 'build': { - 'url': '{chromium_git}/chromium/src/build@e439f6082423106f1fe2afa7e22f8fd4c00691df', + 'url': '{chromium_git}/chromium/src/build@b8f14c09b76ae3bd6edabe45105527a97e1e16bd', 'condition': 'dawn_standalone', }, 'buildtools': { - 'url': '{chromium_git}/chromium/buildtools@24ebce4578745db15274e180da1938ebc1358243', + 'url': '{chromium_git}/chromium/src/buildtools@eb3987ec709b39469423100c1e77f0446890e059', 'condition': 'dawn_standalone', }, 'tools/clang': { - 'url': '{chromium_git}/chromium/src/tools/clang@1d879cee563167a2b18baffb096cf9e29f2f9376', + 'url': '{chromium_git}/chromium/src/tools/clang@d027d75e8dd91140115a4cc9c7c3598c44bbf634', 'condition': 'dawn_standalone', }, - 'third_party/binutils': { - 'url': '{chromium_git}/chromium/src/third_party/binutils@2be73f7fbf783d7a0b288e174a5773b67c7656bc', - 'condition': 'dawn_standalone', + 'tools/clang/dsymutil': { + 'packages': [ + { + 'package': 'chromium/llvm-build-tools/dsymutil', + 'version': 'M56jPzDv1620Rnm__jTMYS62Zi8rxHVq7yw0qeBFEgkC', + } + ], + 'condition': 'checkout_mac or checkout_ios', + 'dep_type': 'cipd', }, # Testing, GTest and GMock 'testing': { - 'url': '{chromium_git}/chromium/src/testing@b07830f6905ce9e33034ad14820bc0a58b6e9e41', + 'url': '{chromium_git}/chromium/src/testing@e5ced5141379ee8ae28b4f93d3c02df039d2b052', 'condition': 'dawn_standalone', }, 'third_party/googletest': { - 'url': '{chromium_git}/external/github.com/google/googletest@5ec7f0c4a113e2f18ac2c6cc7df51ad6afc24081', + 'url': '{chromium_git}/external/github.com/google/googletest@a09ea700d32bab83325aff9ff34d0582e50e3997', 'condition': 'dawn_standalone', }, @@ -50,124 +66,68 @@ deps = { # SPIRV-Cross 'third_party/spirv-cross': { - 'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@fce83b7e8b0f6599efd4481992b2eb30f69f21de', + 'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Cross@6575e451f5bffded6e308988362224dd076b0f2b', 'condition': 'dawn_standalone', }, # SPIRV compiler dependencies: SPIRV-Tools, SPIRV-headers, glslang and shaderc 'third_party/SPIRV-Tools': { - 'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@0125b28ed4214d1860696f22d230dbfc965c6c2c', + 'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Tools@c10d6cebbcb7b8bc815c01142b3ebbeca02d0072', 'condition': 'dawn_standalone', }, 'third_party/spirv-headers': { - 'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@c4f8f65792d4bf2657ca751904c511bbcf2ac77b', + 'url': '{chromium_git}/external/github.com/KhronosGroup/SPIRV-Headers@7f2ae1193ad821fc55c30cf3e7f8fc1536eeec1f', 'condition': 'dawn_standalone', }, 'third_party/glslang': { - 'url': '{chromium_git}/external/github.com/KhronosGroup/glslang@f88e5824d2cfca5edc58c7c2101ec9a4ec36afac', + 'url': '{chromium_git}/external/github.com/KhronosGroup/glslang@9eef54b2513ca6b40b47b07d24f453848b65c0df', 'condition': 'dawn_standalone', }, 'third_party/shaderc': { - 'url': '{chromium_git}/external/github.com/google/shaderc@3c76b8aa36fad139c871859d75056b222d31489c', + 'url': '{chromium_git}/external/github.com/google/shaderc@6af6e625573fb20bd64fbd36af31ef1420751ddd', + 'condition': 'dawn_standalone', + }, + + # WGSL support + 'third_party/tint': { + 'url': '{dawn_git}/tint@18c85f52e482845079a26a22c9dfc633bf7a11a7', 'condition': 'dawn_standalone', }, # GLFW for tests and samples 'third_party/glfw': { - 'url': '{chromium_git}/external/github.com/glfw/glfw@2de2589f910b1a85905f425be4d32f33cec092df', + 'url': '{chromium_git}/external/github.com/glfw/glfw@d973acc123826666ecc9e6fd475682e3d84c54a6', 'condition': 'dawn_standalone', }, # Dependencies for samples: GLM 'third_party/glm': { - 'url': '{github_git}/g-truc/glm.git@06f084063fd6d9aa2ef6904517650700ae47b63d', + 'url': '{github_git}/g-truc/glm.git@bf71a834948186f4097caa076cd2663c69a10e1e', 'condition': 'dawn_standalone', }, - # Our own pre-compiled Linux clang-format 7.0 for presubmit - 'third_party/clang-format': { - 'url': '{dawn_git}/clang-format@2451c56cd368676cdb230fd5ad11731ab859f1a3', - 'condition': 'dawn_standalone and checkout_linux', - }, -} - -hooks = [ - # Pull clang-format binaries using checked-in hashes. - { - 'name': 'clang_format_win', - 'pattern': '.', - 'condition': 'host_os == "win" and dawn_standalone', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=win32', - '--no_auth', - '--bucket', 'chromium-clang-format', - '-s', 'buildtools/win/clang-format.exe.sha1', - ], + # Khronos Vulkan headers, validation layers and loader. + 'third_party/vulkan-headers': { + 'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Headers@4c079bf40c2587220dbf157d825d3185c9adc896', + 'condition': 'dawn_standalone', }, - { - 'name': 'clang_format_mac', - 'pattern': '.', - 'condition': 'host_os == "mac" and dawn_standalone', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=darwin', - '--no_auth', - '--bucket', 'chromium-clang-format', - '-s', 'buildtools/mac/clang-format.sha1', - ], + 'third_party/vulkan-validation-layers': { + 'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-ValidationLayers@e8b96e86fe2edfaee274b98fbbe1bd65579b0904', + 'condition': 'dawn_standalone', }, - { - 'name': 'clang_format_linux', - 'pattern': '.', - 'condition': 'host_os == "linux" and dawn_standalone', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=linux*', - '--no_auth', - '--bucket', 'chromium-clang-format', - '-s', 'buildtools/linux64/clang-format.sha1', - ], + 'third_party/vulkan-loader': { + 'url': '{chromium_git}/external/github.com/KhronosGroup/Vulkan-Loader@006586926adece57adea3e006140b5df19826371', + 'condition': 'dawn_standalone', }, - # Pull GN binaries using checked-in hashes. - { - 'name': 'gn_win', - 'pattern': '.', - 'condition': 'host_os == "win" and dawn_standalone', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=win32', - '--no_auth', - '--bucket', 'chromium-gn', - '-s', 'buildtools/win/gn.exe.sha1', - ], - }, - { - 'name': 'gn_mac', - 'pattern': '.', - 'condition': 'host_os == "mac" and dawn_standalone', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=darwin', - '--no_auth', - '--bucket', 'chromium-gn', - '-s', 'buildtools/mac/gn.sha1', - ], - }, - { - 'name': 'gn_linux64', - 'pattern': '.', - 'condition': 'host_os == "linux" and dawn_standalone', - 'action': [ 'download_from_google_storage', - '--no_resume', - '--platform=linux*', - '--no_auth', - '--bucket', 'chromium-gn', - '-s', 'buildtools/linux64/gn.sha1', - ], + 'third_party/swiftshader': { + 'url': '{swiftshader_git}/SwiftShader@e8dd233c7a85f3c689caf06c226a7f8405a480d3', + 'condition': 'dawn_standalone', }, +} + +hooks = [ # Pull the compilers and system libraries for hermetic builds { 'name': 'sysroot_x86', @@ -209,20 +169,11 @@ hooks = [ '-s', 'build/toolchain/win/rc/win/rc.exe.sha1', ], }, - # Pull binutils for linux hermetic builds - { - 'name': 'binutils', - 'pattern': 'src/third_party/binutils', - 'condition': 'host_os == "linux"', - 'action': [ - 'python', - 'third_party/binutils/download.py', - ], - }, # Update build/util/LASTCHANGE. { 'name': 'lastchange', 'pattern': '.', + 'condition': 'dawn_standalone', 'action': ['python', 'build/util/lastchange.py', '-o', 'build/util/LASTCHANGE'], }, diff --git a/third_party/dawn/OWNERS b/third_party/dawn/OWNERS index 06bf5663c5d..cde87a48d89 100644 --- a/third_party/dawn/OWNERS +++ b/third_party/dawn/OWNERS @@ -1,4 +1,5 @@ cwallez@chromium.org kainino@chromium.org +enga@chromium.org # COMPONENT: Internals>GPU>Dawn diff --git a/third_party/dawn/PRESUBMIT.py b/third_party/dawn/PRESUBMIT.py index ae10063e7d5..4ad052cb994 100644 --- a/third_party/dawn/PRESUBMIT.py +++ b/third_party/dawn/PRESUBMIT.py @@ -16,54 +16,21 @@ import platform import subprocess -def _DoClangFormat(input_api, output_api): - # Our binary clang-format is a linux binary compiled for x64 - if platform.system() != 'Linux' or platform.architecture()[0] != '64bit': - return [output_api.PresubmitNotifyResult('Skipping clang-format')] - - # We need to know which commit to diff against. It doesn't seem to be exposed anywhere - # except in that private member of presubmit_support.Change. This is likely to break - # but hopefully we have an updated clang-format in CPID/GS before it does. - upstream_commit = input_api.change._upstream - if upstream_commit == None: - return [] - - lint_cmd = [ - 'scripts/lint_clang_format.sh', - 'third_party/clang-format/clang-format', - upstream_commit - ] - - # Make clang-format use our linux x64 sysroot because it is compiled with a version of - # stdlibc++ that's incompatible with the old libraries present on the bots. - env = { - 'LD_LIBRARY_PATH': os.path.join( - os.getcwd(), - 'build', - 'linux', - 'debian_sid_amd64-sysroot', - 'usr', - 'lib', - 'x86_64-linux-gnu' - ) - } - - # Call the linting script and forward the output as a notification or as an error - try: - output = subprocess.check_output(lint_cmd, env=env); - return [output_api.PresubmitNotifyResult(output)] - except subprocess.CalledProcessError as e: - return [output_api.PresubmitError(e.output)] def _DoCommonChecks(input_api, output_api): results = [] - results.extend(input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api)) - results.extend(input_api.canned_checks.CheckGNFormatted(input_api, output_api)) - results.extend(_DoClangFormat(input_api, output_api)) + results.extend( + input_api.canned_checks.CheckChangedLUCIConfigs(input_api, output_api)) + results.extend( + input_api.canned_checks.CheckPatchFormatted(input_api, + output_api, + check_python=True)) return results + def CheckChangeOnUpload(input_api, output_api): return _DoCommonChecks(input_api, output_api) + def CheckChangeOnCommit(input_api, output_api): return _DoCommonChecks(input_api, output_api) diff --git a/third_party/dawn/README.md b/third_party/dawn/README.md index d262b403f63..a6800ee1bdc 100644 --- a/third_party/dawn/README.md +++ b/third_party/dawn/README.md @@ -1,78 +1,48 @@ # Dawn, a WebGPU implementation -Dawn (formerly NXT) is an open-source and cross-platform implementation of the work-in-progress WebGPU standard. -It exposes a C/C++ API that maps almost one-to-one to the WebGPU IDL and can be managed as part of a larger system such as a Web browser. +Dawn is an open-source and cross-platform implementation of the work-in-progress [WebGPU](https://webgpu.dev) standard. +More precisely it implements [`webgpu.h`](https://github.com/webgpu-native/webgpu-headers/blob/master/webgpu.h) that is a one-to-one mapping with the WebGPU IDL. +Dawn is meant to be integrated as part of a larger system and is the underlying implementation of WebGPU in Chromium. Dawn provides several WebGPU building blocks: - **WebGPU C/C++ headers** that applications and other building blocks use. + - The `webgpu.h` version that Dawn implements. + - A C++ wrapper for the `webgpu.h`. - **A "native" implementation of WebGPU** using platforms' GPU APIs: - **D3D12** on Windows 10 - - **Metal** on OSX (and eventually iOS) - - **Vulkan** on Windows, Linux (eventually ChromeOS and Android too) + - **Metal** on macOS and iOS + - **Vulkan** on Windows, Linux, ChromeOS, Android and Fuchsia - OpenGL as best effort where available - **A client-server implementation of WebGPU** for applications that are in a sandbox without access to native drivers -## Directory structure +Helpful links: -- `dawn.json`: description of the API used to drive code generators. -- `examples`: examples showing how Dawn is used. -- `generator`: code generator for files produces from `dawn.json` - - `templates`: Jinja2 templates for the generator -- `scripts`: scripts to support things like continuous testing, build files, etc. -- `src`: - - `common`: helper code shared between core Dawn libraries and tests/samples - - `dawn_native`: native implementation of WebGPU, one subfolder per backend - - `dawn_wire`: client-server implementation of WebGPU - - `include`: public headers for Dawn - - `tests`: internal Dawn tests - - `end2end`: WebGPU tests performing GPU operations - - `unittests`: unittests and by extension tests not using the GPU - - `validation`: WebGPU validation tests not using the GPU (frontend tests) - - `utils`: helper code to use Dawn used by tests and samples -- `third_party`: directory where dependencies live as well as their buildfiles. + - [Dawn's bug tracker](https://bugs.chromium.org/p/dawn/issues/entry) if you find issues with Dawn. + - [Dawn's mailing list](https://groups.google.com/forum/#!members/dawn-graphics) for other discussions related to Dawn. + - [Dawn's source code](https://dawn.googlesource.com/dawn) + - [Dawm's Matrix chatroom](https://matrix.to/#/#webgpu-dawn:matrix.org) for live discussion around contributing or using Dawn. + - [WebGPU's Matrix chatroom](https://matrix.to/#/#WebGPU:matrix.org) -## Building Dawn +## Documentation table of content -Dawn uses the Chromium build system and dependency management so you need to [install depot_tools] and add it to the PATH. +Developer documentation: -[install depot_tools]: http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up + - [Dawn overview](docs/overview.md) + - [Building Dawn](docs/buiding.md) + - [Contributing to Dawn](CONTRIBUTING.md) + - [Testing Dawn](docs/testing.md) + - [Debugging Dawn](docs/debugging.md) + - [Dawn's infrastructure](docs/infra.md) -On Linux you need to have the `pkg-config` command: -```sh -# Install pkg-config on Ubuntu -sudo apt-get install pkg-config -``` +User documentation: (TODO, figure out what overlaps with webgpu.h docs) -Then get the source as follows: +## Status -```sh -# Clone the repo as "dawn" -git clone https://dawn.googlesource.com/dawn dawn && cd dawn - -# Bootstrap the gclient configuration -cp scripts/standalone.gclient .gclient - -# Fetch external dependencies and toolchains with gclient -gclient sync -``` - -Then generate build files using `gn args out/Debug` or `gn args out/Release`. -A text editor will appear asking build options, the most common option is `is_debug=true/false`; otherwise `gn args out/Release --list` shows all the possible options. - -Then use `ninja -C out/Release` to build dawn and for example `./out/Release/dawn_end2end_tests` to run the tests. - -## Contributing - -Please read and follow [CONTRIBUTING.md](/CONTRIBUTING.md). -Dawn doesn't have a formal coding style yet, except what's defined by our clang format style. -Overall try to use the same style and convention as code around your change. - -If you find issues with Dawn, please feel free to report them on the [bug tracker](https://bugs.chromium.org/p/dawn/issues/entry). -For other discussions, please post to [Dawn's mailing list](https://groups.google.com/forum/#!members/dawn-graphics). +(TODO) ## License -Please see [LICENSE](/LICENSE). +Apache 2.0 Public License, please see [LICENSE](/LICENSE). ## Disclaimer diff --git a/third_party/dawn/build_overrides/build.gni b/third_party/dawn/build_overrides/build.gni index 1acd4a16562..9bb87130c41 100644 --- a/third_party/dawn/build_overrides/build.gni +++ b/third_party/dawn/build_overrides/build.gni @@ -12,16 +12,35 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Tell Dawn and dependencies to not do Chromium-specific things -build_with_chromium = false +declare_args() { + # Tell Dawn and dependencies to not do Chromium-specific things + build_with_chromium = false -# Use Chromium's binutils to have "hermetic" builds on bots -linux_use_bundled_binutils_override = true + # Use Chromium's binutils to have "hermetic" builds on bots + linux_use_bundled_binutils_override = true -# In standalone Dawn builds, don't try to use the hermetic install of Xcode -# that Chromium uses -use_system_xcode = true + # In standalone Dawn builds, don't try to use the hermetic install of Xcode + # that Chromium uses + use_system_xcode = "" -# Android 32-bit non-component, non-clang builds cannot have symbol_level=2 -# due to 4GiB file size limit, see https://crbug.com/648948. -ignore_elf32_limitations = false + # Android 32-bit non-component, non-clang builds cannot have symbol_level=2 + # due to 4GiB file size limit, see https://crbug.com/648948. + ignore_elf32_limitations = false +} + +# Detect whether we can use the hermetic XCode like in Chromium and do so if +# possible. +if (host_os == "mac" && use_system_xcode == "") { + _result = exec_script("//build/mac/should_use_hermetic_xcode.py", + [ target_os ], + "value") + + assert(_result != 2, + "Do not allow building targets with the default" + + "hermetic toolchain if the minimum OS version is not met.") + assert(_result != 3, + "iOS does not support building with a hermetic toolchain. " + + "Please install Xcode.") + + use_system_xcode = _result != 1 +} diff --git a/third_party/dawn/build_overrides/dawn.gni b/third_party/dawn/build_overrides/dawn.gni index 4e186b202c0..d84165fc707 100644 --- a/third_party/dawn/build_overrides/dawn.gni +++ b/third_party/dawn/build_overrides/dawn.gni @@ -20,13 +20,21 @@ # MUST be unset in other projects (will default to false). dawn_standalone = true +# True if Dawn can access build/, testing/ and other Chrome folders. +dawn_has_build = true + # Defaults for these are set again in dawn_overrides_with_defaults.gni so that # users of Dawn don't have to set dirs if they happen to use the same as Dawn. # The paths to Dawn's dependencies dawn_jinja2_dir = "//third_party/jinja2" dawn_glfw_dir = "//third_party/glfw" +dawn_glm_dir = "//third_party/glm" dawn_googletest_dir = "//third_party/googletest" dawn_shaderc_dir = "//third_party/shaderc" dawn_spirv_tools_dir = "//third_party/SPIRV-Tools" dawn_spirv_cross_dir = "//third_party/spirv-cross" +dawn_swiftshader_dir = "//third_party/swiftshader" +dawn_tint_dir = "//third_party/tint" +dawn_vulkan_loader_dir = "//third_party/vulkan-loader" +dawn_vulkan_validation_layers_dir = "//third_party/vulkan-validation-layers" diff --git a/third_party/dawn/build_overrides/shaderc.gni b/third_party/dawn/build_overrides/shaderc.gni index 1cb6bd12f13..331fecb1e2c 100644 --- a/third_party/dawn/build_overrides/shaderc.gni +++ b/third_party/dawn/build_overrides/shaderc.gni @@ -14,4 +14,5 @@ shaderc_glslang_dir = "//third_party/glslang" shaderc_spirv_tools_dir = "//third_party/SPIRV-Tools" -shaderc_spirv_cross_dir = "//third_party" +shaderc_spirv_cross_dir = "//third_party/spirv-cross" +shaderc_spirv_headers_dir = "//third_party/spirv-headers" diff --git a/third_party/dawn/build_overrides/swiftshader.gni b/third_party/dawn/build_overrides/swiftshader.gni new file mode 100644 index 00000000000..4153f72e4f3 --- /dev/null +++ b/third_party/dawn/build_overrides/swiftshader.gni @@ -0,0 +1,22 @@ +# Copyright 2019 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# We are building SwiftShader inside Dawn +swiftshader_standalone = false + +# Path to SwiftShader +swiftshader_dir = "//third_party/swiftshader" + +# Paths to SwiftShader dependencies in Dawn +swiftshader_spirv_tools_dir = "//third_party/SPIRV-Tools" diff --git a/third_party/dawn/build_overrides/tint.gni b/third_party/dawn/build_overrides/tint.gni new file mode 100644 index 00000000000..352d4b2f701 --- /dev/null +++ b/third_party/dawn/build_overrides/tint.gni @@ -0,0 +1,24 @@ +# Copyright 2020 The Dawn Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +tint_root_dir = "//third_party/tint" +tint_spirv_tools_dir = "//third_party/SPIRV-Tools" +tint_googletest_dir = "//third_party/googletest" +tint_spirv_headers_dir = "//third_party/spirv-headers" + +# Only need the WGSL->SPIR-V transformation +tint_build_spv_reader = false +tint_build_spv_writer = true +tint_build_wgsl_reader = true +tint_build_wgsl_writer = false diff --git a/third_party/dawn/build_overrides/vulkan_headers.gni b/third_party/dawn/build_overrides/vulkan_headers.gni new file mode 100644 index 00000000000..4c0047a0d92 --- /dev/null +++ b/third_party/dawn/build_overrides/vulkan_headers.gni @@ -0,0 +1,17 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Fake the vulkan_use_x11 when inside Dawn's repository +import("../scripts/dawn_features.gni") +vulkan_use_x11 = dawn_use_x11 diff --git a/third_party/dawn/build_overrides/vulkan_loader.gni b/third_party/dawn/build_overrides/vulkan_loader.gni new file mode 100644 index 00000000000..c38074ffa13 --- /dev/null +++ b/third_party/dawn/build_overrides/vulkan_loader.gni @@ -0,0 +1,18 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +vulkan_headers_dir = "//third_party/vulkan-headers" + +vulkan_gen_subdir = "vulkan_loader" +vulkan_loader_shared = true diff --git a/third_party/dawn/build_overrides/vulkan_validation_layers.gni b/third_party/dawn/build_overrides/vulkan_validation_layers.gni new file mode 100644 index 00000000000..491afbcdfaf --- /dev/null +++ b/third_party/dawn/build_overrides/vulkan_validation_layers.gni @@ -0,0 +1,28 @@ +# Copyright 2019 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# These are variables that are overridable by projects that include Dawn. +# The values in this file are the defaults for when we are building from +# Dawn's repository. +vulkan_headers_dir = "//third_party/vulkan-headers" +vvl_spirv_tools_dir = "//third_party/SPIRV-Tools" +vvl_glslang_dir = "//third_party/glslang" + +# Subdirectories for generated files +vulkan_data_subdir = "vulkandata" +vulkan_gen_subdir = "" + +# Fake the use_x11 when inside Dawn's repository +import("../scripts/dawn_features.gni") +use_x11 = dawn_use_x11 diff --git a/third_party/dawn/dawn.json b/third_party/dawn/dawn.json index a0fa36bd01c..109eeae8651 100644 --- a/third_party/dawn/dawn.json +++ b/third_party/dawn/dawn.json @@ -14,24 +14,58 @@ "See the License for the specific language governing permissions and", "limitations under the License." ], + "adapter properties": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "device ID", "type": "uint32_t"}, + {"name": "vendor ID", "type": "uint32_t"}, + {"name": "name", "type": "char", "annotation": "const*"}, + {"name": "adapter type", "type": "adapter type"}, + {"name": "backend type", "type": "backend type"} + ] + }, + "adapter type": { + "category": "enum", + "javascript": false, + "values": [ + {"value": 0, "name": "discrete GPU"}, + {"value": 1, "name": "integrated GPU"}, + {"value": 2, "name": "CPU"}, + {"value": 3, "name": "unknown"} + ] + }, "address mode": { "category": "enum", "values": [ {"value": 0, "name": "repeat"}, - {"value": 1, "name": "mirrored repeat"}, + {"value": 1, "name": "mirror repeat"}, {"value": 2, "name": "clamp to edge"} ] }, + "backend type": { + "category": "enum", + "javascript": false, + "values": [ + {"value": 0, "name": "null"}, + {"value": 1, "name": "D3D11"}, + {"value": 2, "name": "D3D12"}, + {"value": 3, "name": "metal"}, + {"value": 4, "name": "vulkan"}, + {"value": 5, "name": "openGL"}, + {"value": 6, "name": "openGLES"} + ] + }, "bind group": { "category": "object" }, - "bind group binding": { + "bind group entry": { "category": "structure", "extensible": false, "members": [ {"name": "binding", "type": "uint32_t"}, {"name": "buffer", "type": "buffer", "optional": true}, - {"name": "offset", "type": "uint64_t"}, + {"name": "offset", "type": "uint64_t", "default": "0"}, {"name": "size", "type": "uint64_t"}, {"name": "sampler", "type": "sampler", "optional": true}, {"name": "texture view", "type": "texture view", "optional": true} @@ -41,49 +75,60 @@ "category": "structure", "extensible": true, "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, {"name": "layout", "type": "bind group layout"}, - {"name": "binding count", "type": "uint32_t"}, - {"name": "bindings", "type": "bind group binding", "annotation": "const*", "length": "binding count"} + {"name": "entry count", "type": "uint32_t"}, + {"name": "entries", "type": "bind group entry", "annotation": "const*", "length": "entry count"} ] }, "bind group layout": { "category": "object" }, - "bind group layout binding": { + "bind group layout entry": { "category": "structure", "extensible": false, "members": [ {"name": "binding", "type": "uint32_t"}, - {"name": "visibility", "type": "shader stage bit"}, - {"name": "type", "type": "binding type"} + {"name": "visibility", "type": "shader stage"}, + {"name": "type", "type": "binding type"}, + {"name": "has dynamic offset", "type": "bool", "default": "false"}, + {"name": "min buffer binding size", "type": "uint64_t", "default": "0"}, + {"name": "multisampled", "type": "bool", "default": "false"}, + {"name": "view dimension", "type": "texture view dimension", "default": "undefined"}, + {"name": "texture component type", "type": "texture component type", "default": "float"}, + {"name": "storage texture format", "type": "texture format", "default": "undefined"} ] }, "bind group layout descriptor": { "category": "structure", "extensible": true, "members": [ - {"name": "binding count", "type": "uint32_t"}, - {"name": "bindings", "type": "bind group layout binding", "annotation": "const*", "length": "binding count"} + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "entry count", "type": "uint32_t"}, + {"name": "entries", "type": "bind group layout entry", "annotation": "const*", "length": "entry count"} ] }, "binding type": { "category": "enum", "values": [ {"value": 0, "name": "uniform buffer"}, - {"value": 1, "name": "sampler"}, - {"value": 2, "name": "sampled texture"}, - {"value": 3, "name": "storage buffer"}, - {"value": 4, "name": "dynamic uniform buffer"}, - {"value": 5, "name": "dynamic storage buffer"} + {"value": 1, "name": "storage buffer"}, + {"value": 2, "name": "readonly storage buffer"}, + {"value": 3, "name": "sampler"}, + {"value": 4, "name": "comparison sampler"}, + {"value": 5, "name": "sampled texture"}, + {"value": 6, "name": "readonly storage texture"}, + {"value": 7, "name": "writeonly storage texture"}, + {"value": 8, "name": "storage texture"} ] }, "blend descriptor": { "category": "structure", "extensible": false, "members": [ - {"name": "operation", "type": "blend operation"}, - {"name": "src factor", "type": "blend factor"}, - {"name": "dst factor", "type": "blend factor"} + {"name": "operation", "type": "blend operation", "default": "add"}, + {"name": "src factor", "type": "blend factor", "default": "one"}, + {"name": "dst factor", "type": "blend factor", "default": "zero"} ] }, "blend factor": { @@ -121,7 +166,7 @@ {"name": "format", "type": "texture format"}, {"name": "alpha blend", "type": "blend descriptor"}, {"name": "color blend", "type": "blend descriptor"}, - {"name": "write mask", "type": "color write mask"} + {"name": "write mask", "type": "color write mask", "default": "all"} ] }, "bool": { @@ -152,6 +197,32 @@ {"name": "userdata", "type": "void", "annotation": "*"} ] }, + { + "name": "map async", + "args": [ + {"name": "mode", "type": "map mode"}, + {"name": "offset", "type": "size_t"}, + {"name": "size", "type": "size_t"}, + {"name": "callback", "type": "buffer map callback"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] + }, + { + "name": "get mapped range", + "returns": "void *", + "args": [ + {"name": "offset", "type": "size_t", "default": 0}, + {"name": "size", "type": "size_t", "default": 0} + ] + }, + { + "name": "get const mapped range", + "returns": "void const *", + "args": [ + {"name": "offset", "type": "size_t", "default": 0}, + {"name": "size", "type": "size_t", "default": 0} + ] + }, { "name": "unmap" }, @@ -164,25 +235,47 @@ "category": "structure", "extensible": true, "members": [ + {"name": "layout", "type": "texture data layout"}, {"name": "buffer", "type": "buffer"}, - {"name": "offset", "type": "uint64_t"}, - {"name": "row pitch", "type": "uint32_t"}, - {"name": "image height", "type": "uint32_t"} + {"name": "offset", "type": "uint64_t", "default": 0}, + {"name": "bytes per row", "type": "uint32_t"}, + {"name": "rows per image", "type": "uint32_t", "default": 0} ] }, "buffer descriptor": { "category": "structure", "extensible": true, "members": [ - {"name": "usage", "type": "buffer usage bit"}, - {"name": "size", "type": "uint64_t"} + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "usage", "type": "buffer usage"}, + {"name": "size", "type": "uint64_t"}, + {"name": "mapped at creation", "type": "bool", "default": "false"} + ] + }, + "buffer map callback": { + "category": "callback", + "args": [ + {"name": "status", "type": "buffer map async status"}, + {"name": "userdata", "type": "void", "annotation": "*"} ] }, "buffer map read callback": { - "category": "natively defined" + "category": "callback", + "args": [ + {"name": "status", "type": "buffer map async status"}, + {"name": "data", "type": "void", "annotation": "const*"}, + {"name": "data length", "type": "uint64_t"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] }, "buffer map write callback": { - "category": "natively defined" + "category": "callback", + "args": [ + {"name": "status", "type": "buffer map async status"}, + {"name": "data", "type": "void", "annotation": "*"}, + {"name": "data length", "type": "uint64_t"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] }, "buffer map async status": { "category": "enum", @@ -190,22 +283,23 @@ {"value": 0, "name": "success"}, {"value": 1, "name": "error"}, {"value": 2, "name": "unknown"}, - {"value": 3, "name": "context lost"} + {"value": 3, "name": "device lost"} ] }, - "buffer usage bit": { + "buffer usage": { "category": "bitmask", "values": [ {"value": 0, "name": "none"}, {"value": 1, "name": "map read"}, {"value": 2, "name": "map write"}, - {"value": 4, "name": "transfer src"}, - {"value": 8, "name": "transfer dst"}, + {"value": 4, "name": "copy src"}, + {"value": 8, "name": "copy dst"}, {"value": 16, "name": "index"}, {"value": 32, "name": "vertex"}, {"value": 64, "name": "uniform"}, {"value": 128, "name": "storage"}, - {"value": 256, "name": "indirect"} + {"value": 256, "name": "indirect"}, + {"value": 512, "name": "query resolve"} ] }, "char": { @@ -242,23 +336,36 @@ "command buffer": { "category": "object" }, + "command buffer descriptor": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true} + ] + }, "command encoder": { "category": "object", "methods": [ { "name": "finish", - "returns": "command buffer" + "returns": "command buffer", + "args": [ + {"name": "descriptor", "type": "command buffer descriptor", "annotation": "const*", "optional": true} + ] }, { "name": "begin compute pass", - "returns": "compute pass encoder" + "returns": "compute pass encoder", + "args": [ + {"name": "descriptor", "type": "compute pass descriptor", "annotation": "const*", "optional": true} + ] }, { "name": "begin render pass", + "returns": "render pass encoder", "args": [ - {"name": "info", "type": "render pass descriptor", "annotation": "const*"} - ], - "returns": "render pass encoder" + {"name": "descriptor", "type": "render pass descriptor", "annotation": "const*"} + ] }, { "name": "copy buffer to buffer", @@ -296,20 +403,68 @@ {"name": "destination", "type": "texture copy view", "annotation": "const*"}, {"name": "copy size", "type": "extent 3D", "annotation": "const*"} ] + }, + { + "name": "insert debug marker", + "args": [ + {"name": "marker label", "type": "char", "annotation": "const*", "length": "strlen"} + ] + }, + { + "name": "pop debug group", + "args": [] + }, + { + "name": "push debug group", + "args": [ + {"name": "group label", "type": "char", "annotation": "const*", "length": "strlen"} + ] + }, + { + "name": "resolve query set", + "args": [ + {"name": "query set", "type": "query set"}, + {"name": "first query", "type": "uint32_t"}, + {"name": "query count", "type": "uint32_t"}, + {"name": "destination", "type": "buffer"}, + {"name": "destination offset", "type": "uint64_t"} + ] + }, + { + "name": "write timestamp", + "args": [ + {"name": "query set", "type": "query set"}, + {"name": "query index", "type": "uint32_t"} + ] } ] }, + "command encoder descriptor": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true} + ] + }, "compare function": { "category": "enum", "values": [ - {"value": 0, "name": "never"}, - {"value": 1, "name": "less"}, - {"value": 2, "name": "less equal"}, - {"value": 3, "name": "greater"}, - {"value": 4, "name": "greater equal"}, - {"value": 5, "name": "equal"}, - {"value": 6, "name": "not equal"}, - {"value": 7, "name": "always"} + {"value": 0, "name": "undefined", "jsrepr": "undefined"}, + {"value": 1, "name": "never"}, + {"value": 2, "name": "less"}, + {"value": 3, "name": "less equal"}, + {"value": 4, "name": "greater"}, + {"value": 5, "name": "greater equal"}, + {"value": 6, "name": "equal"}, + {"value": 7, "name": "not equal"}, + {"value": 8, "name": "always"} + ] + }, + "compute pass descriptor": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true} ] }, "compute pass encoder": { @@ -318,7 +473,7 @@ { "name": "insert debug marker", "args": [ - {"name": "group label", "type": "char", "annotation": "const*", "length": "strlen"} + {"name": "marker label", "type": "char", "annotation": "const*", "length": "strlen"} ] }, { @@ -342,16 +497,23 @@ "args": [ {"name": "group index", "type": "uint32_t"}, {"name": "group", "type": "bind group"}, - {"name": "dynamic offset count", "type": "uint32_t"}, - {"name": "dynamic offsets", "type": "uint64_t", "annotation": "const*", "length": "dynamic offset count"} + {"name": "dynamic offset count", "type": "uint32_t", "default": "0"}, + {"name": "dynamic offsets", "type": "uint32_t", "annotation": "const*", "length": "dynamic offset count", "optional": true} + ] + }, + { + "name": "write timestamp", + "args": [ + {"name": "query set", "type": "query set"}, + {"name": "query index", "type": "uint32_t"} ] }, { "name": "dispatch", "args": [ {"name": "x", "type": "uint32_t"}, - {"name": "y", "type": "uint32_t"}, - {"name": "z", "type": "uint32_t"} + {"name": "y", "type": "uint32_t", "default": "1"}, + {"name": "z", "type": "uint32_t", "default": "1"} ] }, { @@ -362,20 +524,29 @@ ] }, { - "name": "end pass", - "TODO": "This returns the top-level encoder in the WebGPU IDL" + "name": "end pass" } ] }, "compute pipeline": { - "category": "object" + "category": "object", + "methods": [ + { + "name": "get bind group layout", + "returns": "bind group layout", + "args": [ + {"name": "group index", "type": "uint32_t"} + ] + } + ] }, "compute pipeline descriptor": { "category": "structure", "extensible": true, "members": [ - {"name": "layout", "type": "pipeline layout"}, - {"name": "compute stage", "type": "pipeline stage descriptor", "annotation": "const*"} + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "layout", "type": "pipeline layout", "optional": true}, + {"name": "compute stage", "type": "programmable stage descriptor"} ] }, "cull mode": { @@ -410,6 +581,11 @@ {"name": "descriptor", "type": "buffer descriptor", "annotation": "const*"} ] }, + { + "name": "create error buffer", + "returns": "buffer", + "TODO": "enga@: Make this part of a dawn_wire extension" + }, { "name": "create buffer mapped", "returns": "create buffer mapped result", @@ -419,7 +595,10 @@ }, { "name": "create command encoder", - "returns": "command encoder" + "returns": "command encoder", + "args": [ + {"name": "descriptor", "type": "command encoder descriptor", "annotation": "const*", "optional": true} + ] }, { "name": "create compute pipeline", @@ -429,22 +608,32 @@ ] }, { - "name": "create render pipeline", - "returns": "render pipeline", + "name": "create pipeline layout", + "returns": "pipeline layout", "args": [ - {"name": "descriptor", "type": "render pipeline descriptor", "annotation": "const*"} + {"name": "descriptor", "type": "pipeline layout descriptor", "annotation": "const*"} ] }, { - "name": "create pipeline layout", - "returns": "pipeline layout", + "name": "create query set", + "returns": "query set", "args": [ - {"name": "descriptor", "type": "pipeline layout descriptor", "annotation": "const*"} + {"name": "descriptor", "type": "query set descriptor", "annotation": "const*"} ] }, { - "name": "create queue", - "returns": "queue" + "name": "create render bundle encoder", + "returns": "render bundle encoder", + "args": [ + {"name": "descriptor", "type": "render bundle encoder descriptor", "annotation": "const*"} + ] + }, + { + "name": "create render pipeline", + "returns": "render pipeline", + "args": [ + {"name": "descriptor", "type": "render pipeline descriptor", "annotation": "const*"} + ] }, { "name": "create sampler", @@ -464,6 +653,7 @@ "name": "create swap chain", "returns": "swap chain", "args": [ + {"name": "surface", "type": "surface", "optional": "true"}, {"name": "descriptor", "type": "swap chain descriptor", "annotation": "const*"} ] }, @@ -474,33 +664,109 @@ {"name": "descriptor", "type": "texture descriptor", "annotation": "const*"} ] }, + { + "name": "get default queue", + "returns": "queue" + }, + { + "name": "inject error", + "args": [ + {"name": "type", "type": "error type"}, + {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"} + ], + "TODO": "enga@: Make this a Dawn extension" + }, + { + "name": "lose for testing" + }, { "name": "tick" }, { - "name": "set error callback", + "name": "set uncaptured error callback", "args": [ - {"name": "callback", "type": "device error callback"}, + {"name": "callback", "type": "error callback"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] + }, + { + "name": "set device lost callback", + "args": [ + {"name": "callback", "type": "device lost callback"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] + }, + { + "name": "push error scope", + "args": [ + {"name": "filter", "type": "error filter"} + ] + }, + { + "name": "pop error scope", + "returns": "bool", + "args": [ + {"name": "callback", "type": "error callback"}, {"name": "userdata", "type": "void", "annotation": "*"} ] } ] }, + "device lost callback": { + "category": "callback", + "args": [ + {"name": "message", "type": "char", "annotation": "const*"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] + }, + "device properties": { + "category": "structure", + "extensible": false, + "members": [ + {"name": "texture compression BC", "type": "bool", "default": "false"}, + {"name": "shader float16", "type": "bool", "default": "false"}, + {"name": "pipeline statistics query", "type": "bool", "default": "false"}, + {"name": "timestamp query", "type": "bool", "default": "false"} + ] + }, "depth stencil state descriptor": { "category": "structure", "extensible": true, "members": [ {"name": "format", "type": "texture format"}, - {"name": "depth write enabled", "type": "bool"}, - {"name": "depth compare", "type": "compare function"}, + {"name": "depth write enabled", "type": "bool", "default": "false"}, + {"name": "depth compare", "type": "compare function", "default": "always"}, {"name": "stencil front", "type": "stencil state face descriptor"}, {"name": "stencil back", "type": "stencil state face descriptor"}, - {"name": "stencil read mask", "type": "uint32_t"}, - {"name": "stencil write mask", "type": "uint32_t"} + {"name": "stencil read mask", "type": "uint32_t", "default": "0xFFFFFFFF"}, + {"name": "stencil write mask", "type": "uint32_t", "default": "0xFFFFFFFF"} + ] + }, + "error callback": { + "category": "callback", + "args": [ + {"name": "type", "type": "error type"}, + {"name": "message", "type": "char", "annotation": "const*"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] + }, + "error filter": { + "category": "enum", + "values": [ + {"value": 0, "name": "none"}, + {"value": 1, "name": "validation"}, + {"value": 2, "name": "out of memory"} ] }, - "device error callback": { - "category": "natively defined" + "error type": { + "category": "enum", + "values": [ + {"value": 0, "name": "no error"}, + {"value": 1, "name": "validation"}, + {"value": 2, "name": "out of memory"}, + {"value": 3, "name": "unknown"}, + {"value": 4, "name": "device lost"} + ] }, "extent 3D": { "category": "structure", @@ -510,15 +776,6 @@ {"name": "depth", "type": "uint32_t"} ] }, - "face": { - "category": "bitmask", - "values": [ - {"value": 0, "name": "none"}, - {"value": 1, "name": "back"}, - {"value": 2, "name": "front"}, - {"value": 3, "name": "both"} - ] - }, "fence": { "category": "object", "methods": [ @@ -537,7 +794,11 @@ ] }, "fence on completion callback": { - "category": "natively defined" + "category": "callback", + "args": [ + {"name": "status", "type": "fence completion status"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] }, "fence completion status": { "category": "enum", @@ -545,14 +806,15 @@ {"value": 0, "name": "success"}, {"value": 1, "name": "error"}, {"value": 2, "name": "unknown"}, - {"value": 3, "name": "context lost"} + {"value": 3, "name": "device lost"} ] }, "fence descriptor": { "category": "structure", "extensible": true, "members": [ - {"name": "initial value", "type": "uint64_t"} + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "initial value", "type": "uint64_t", "default": "0"} ] }, "filter mode": { @@ -579,32 +841,49 @@ {"value": 1, "name": "uint32"} ] }, + "instance": { + "category": "object", + "methods": [ + { + "name": "create surface", + "returns": "surface", + "args": [ + {"name": "descriptor", "type": "surface descriptor", "annotation": "const*"} + ] + } + ] + }, + "instance descriptor": { + "category": "structure", + "extensible": true, + "members": [] + }, "vertex attribute descriptor": { "category": "structure", "extensible": false, "members": [ - {"name": "shader location", "type": "uint32_t"}, + {"name": "format", "type": "vertex format"}, {"name": "offset", "type": "uint64_t"}, - {"name": "format", "type": "vertex format"} + {"name": "shader location", "type": "uint32_t"} ] }, - "vertex buffer descriptor": { + "vertex buffer layout descriptor": { "category": "structure", "extensible": false, "members": [ - {"name": "stride", "type": "uint64_t"}, - {"name": "step mode", "type": "input step mode"}, + {"name": "array stride", "type": "uint64_t"}, + {"name": "step mode", "type": "input step mode", "default": "vertex"}, {"name": "attribute count", "type": "uint32_t"}, {"name": "attributes", "type": "vertex attribute descriptor", "annotation": "const*", "length": "attribute count"} ] }, - "vertex input descriptor": { + "vertex state descriptor": { "category": "structure", "extensible": true, "members": [ - {"name": "index format", "type": "index format"}, - {"name": "buffer count", "type": "uint32_t"}, - {"name": "buffers", "type": "vertex buffer descriptor", "annotation": "const*", "length": "buffer count"} + {"name": "index format", "type": "index format", "default": "uint32"}, + {"name": "vertex buffer count", "type": "uint32_t", "default": 0}, + {"name": "vertex buffers", "type": "vertex buffer layout descriptor", "annotation": "const*", "length": "vertex buffer count"} ] }, "input step mode": { @@ -621,18 +900,27 @@ {"value": 1, "name": "load"} ] }, + "map mode": { + "category": "bitmask", + "values": [ + {"value": 0, "name": "none"}, + {"value": 1, "name": "read"}, + {"value": 2, "name": "write"} + ] + }, "store op": { "category": "enum", "values": [ - {"value": 0, "name": "store"} + {"value": 0, "name": "store"}, + {"value": 1, "name": "clear"} ] }, "origin 3D": { "category": "structure", "members": [ - {"name": "x", "type": "uint32_t"}, - {"name": "y", "type": "uint32_t"}, - {"name": "z", "type": "uint32_t"} + {"name": "x", "type": "uint32_t", "default": "0"}, + {"name": "y", "type": "uint32_t", "default": "0"}, + {"name": "z", "type": "uint32_t", "default": "0"} ] }, "pipeline layout": { @@ -642,11 +930,30 @@ "category": "structure", "extensible": true, "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, {"name": "bind group layout count", "type": "uint32_t"}, {"name": "bind group layouts", "type": "bind group layout", "annotation": "const*", "length": "bind group layout count"} ] }, - "pipeline stage descriptor": { + "pipeline statistic name": { + "category": "enum", + "values": [ + {"value": 0, "name": "vertex shader invocations"}, + {"value": 1, "name": "clipper invocations"}, + {"value": 2, "name": "clipper primitives out"}, + {"value": 3, "name": "fragment shader invocations"}, + {"value": 4, "name": "compute shader invocations"} + ] + }, + "present mode": { + "category": "enum", + "values": [ + {"value": 0, "name": "immediate"}, + {"value": 1, "name": "mailbox"}, + {"value": 2, "name": "fifo"} + ] + }, + "programmable stage descriptor": { "category": "structure", "extensible": true, "members": [ @@ -664,6 +971,33 @@ {"value": 4, "name": "triangle strip"} ] }, + "query set": { + "category": "object", + "methods": [ + { + "name": "destroy" + } + ] + }, + "query set descriptor": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "type", "type": "query type"}, + {"name": "count", "type": "uint32_t"}, + {"name": "pipeline statistics", "type": "pipeline statistic name", "annotation": "const*", "length": "pipeline statistics count"}, + {"name": "pipeline statistics count", "type": "uint32_t", "default": "0"} + ] + }, + "query type": { + "category": "enum", + "values": [ + {"value": 0, "name": "occlusion"}, + {"value": 1, "name": "pipeline statistics"}, + {"value": 2, "name": "timestamp"} + ] + }, "queue": { "category": "object", "methods": [ @@ -685,7 +1019,26 @@ "name": "create fence", "returns": "fence", "args": [ - {"name": "descriptor", "type": "fence descriptor", "annotation": "const*"} + {"name": "descriptor", "type": "fence descriptor", "annotation": "const*", "optional": true} + ] + }, + { + "name": "write buffer", + "args": [ + {"name": "buffer", "type": "buffer"}, + {"name": "buffer offset", "type": "uint64_t"}, + {"name": "data", "type": "void", "annotation": "const*", "length": "size"}, + {"name": "size", "type": "size_t"} + ] + }, + { + "name": "write texture", + "args": [ + {"name": "destination", "type": "texture copy view", "annotation": "const*"}, + {"name": "data", "type": "void", "annotation": "const*", "length": "data size"}, + {"name": "data size", "type": "size_t"}, + {"name": "data layout", "type": "texture data layout", "annotation": "const*"}, + {"name": "write size", "type": "extent 3D", "annotation": "const*"} ] } ] @@ -695,11 +1048,129 @@ "category": "structure", "extensible": true, "members": [ - {"name": "front face", "type": "front face"}, - {"name": "cull mode", "type": "cull mode"}, - {"name": "depth bias", "type": "int32_t"}, - {"name": "depth bias slope scale", "type": "float"}, - {"name": "depth bias clamp", "type": "float"} + {"name": "front face", "type": "front face", "default": "CCW"}, + {"name": "cull mode", "type": "cull mode", "default": "none"}, + {"name": "depth bias", "type": "int32_t", "default": "0"}, + {"name": "depth bias slope scale", "type": "float", "default": "0.0f"}, + {"name": "depth bias clamp", "type": "float", "default": "0.0f"} + ] + }, + + "render bundle": { + "category": "object" + }, + + "render bundle encoder": { + "category": "object", + "methods": [ + { + "name": "set pipeline", + "args": [ + {"name": "pipeline", "type": "render pipeline"} + ] + }, + { + "name": "set bind group", + "args": [ + {"name": "group index", "type": "uint32_t"}, + {"name": "group", "type": "bind group"}, + {"name": "dynamic offset count", "type": "uint32_t", "default": "0"}, + {"name": "dynamic offsets", "type": "uint32_t", "annotation": "const*", "length": "dynamic offset count", "optional": true} + ] + }, + { + "name": "draw", + "args": [ + {"name": "vertex count", "type": "uint32_t"}, + {"name": "instance count", "type": "uint32_t", "default": "1"}, + {"name": "first vertex", "type": "uint32_t", "default": "0"}, + {"name": "first instance", "type": "uint32_t", "default": "0"} + ] + }, + { + "name": "draw indexed", + "args": [ + {"name": "index count", "type": "uint32_t"}, + {"name": "instance count", "type": "uint32_t", "default": "1"}, + {"name": "first index", "type": "uint32_t", "default": "0"}, + {"name": "base vertex", "type": "int32_t", "default": "0"}, + {"name": "first instance", "type": "uint32_t", "default": "0"} + ] + }, + { + "name": "draw indirect", + "args": [ + {"name": "indirect buffer", "type": "buffer"}, + {"name": "indirect offset", "type": "uint64_t"} + ] + }, + { + "name": "draw indexed indirect", + "args": [ + {"name": "indirect buffer", "type": "buffer"}, + {"name": "indirect offset", "type": "uint64_t"} + ] + }, + { + "name": "insert debug marker", + "args": [ + {"name": "marker label", "type": "char", "annotation": "const*", "length": "strlen"} + ] + }, + { + "name": "pop debug group", + "args": [] + }, + { + "name": "push debug group", + "args": [ + {"name": "group label", "type": "char", "annotation": "const*", "length": "strlen"} + ] + }, + { + "name": "set vertex buffer", + "args": [ + {"name": "slot", "type": "uint32_t"}, + {"name": "buffer", "type": "buffer"}, + {"name": "offset", "type": "uint64_t", "default": "0"}, + {"name": "size", "type": "uint64_t", "default": "0"} + ] + }, + { + "name": "set index buffer", + "args": [ + {"name": "buffer", "type": "buffer"}, + {"name": "offset", "type": "uint64_t", "default": "0"}, + {"name": "size", "type": "uint64_t", "default": "0"} + ] + }, + { + "name": "finish", + "returns": "render bundle", + "args": [ + {"name": "descriptor", "type": "render bundle descriptor", "annotation": "const*", "optional": true} + ] + } + ] + }, + + "render bundle descriptor": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true} + ] + }, + + "render bundle encoder descriptor": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "color formats count", "type": "uint32_t"}, + {"name": "color formats", "type": "texture format", "annotation": "const*", "length": "color formats count"}, + {"name": "depth stencil format", "type": "texture format", "default": "undefined"}, + {"name": "sample count", "type": "uint32_t", "default": "1"} ] }, @@ -721,18 +1192,23 @@ {"name": "depth load op", "type": "load op"}, {"name": "depth store op", "type": "store op"}, {"name": "clear depth", "type": "float"}, + {"name": "depth read only", "type": "bool", "default": "false"}, {"name": "stencil load op", "type": "load op"}, {"name": "stencil store op", "type": "store op"}, - {"name": "clear stencil", "type": "uint32_t"} + {"name": "clear stencil", "type": "uint32_t", "default": "0"}, + {"name": "stencil read only", "type": "bool", "default": "false"} ] }, "render pass descriptor": { "category": "structure", + "extensible": true, "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, {"name": "color attachment count", "type": "uint32_t"}, - {"name": "color attachments", "type": "render pass color attachment descriptor", "annotation": "const*const*", "length": "color attachment count"}, - {"name": "depth stencil attachment", "type": "render pass depth stencil attachment descriptor", "annotation": "const*", "optional": true} + {"name": "color attachments", "type": "render pass color attachment descriptor", "annotation": "const*", "length": "color attachment count"}, + {"name": "depth stencil attachment", "type": "render pass depth stencil attachment descriptor", "annotation": "const*", "optional": true}, + {"name": "occlusion query set", "type": "query set", "optional": true} ] }, "render pass encoder": { @@ -749,27 +1225,27 @@ "args": [ {"name": "group index", "type": "uint32_t"}, {"name": "group", "type": "bind group"}, - {"name": "dynamic offset count", "type": "uint32_t"}, - {"name": "dynamic offsets", "type": "uint64_t", "annotation": "const*", "length": "dynamic offset count"} + {"name": "dynamic offset count", "type": "uint32_t", "default": "0"}, + {"name": "dynamic offsets", "type": "uint32_t", "annotation": "const*", "length": "dynamic offset count", "optional": true} ] }, { "name": "draw", "args": [ {"name": "vertex count", "type": "uint32_t"}, - {"name": "instance count", "type": "uint32_t"}, - {"name": "first vertex", "type": "uint32_t"}, - {"name": "first instance", "type": "uint32_t"} + {"name": "instance count", "type": "uint32_t", "default": "1"}, + {"name": "first vertex", "type": "uint32_t", "default": "0"}, + {"name": "first instance", "type": "uint32_t", "default": "0"} ] }, { "name": "draw indexed", "args": [ {"name": "index count", "type": "uint32_t"}, - {"name": "instance count", "type": "uint32_t"}, - {"name": "first index", "type": "uint32_t"}, - {"name": "base vertex", "type": "int32_t"}, - {"name": "first instance", "type": "uint32_t"} + {"name": "instance count", "type": "uint32_t", "default": "1"}, + {"name": "first index", "type": "uint32_t", "default": "0"}, + {"name": "base vertex", "type": "int32_t", "default": "0"}, + {"name": "first instance", "type": "uint32_t", "default": "0"} ] }, { @@ -786,10 +1262,17 @@ {"name": "indirect offset", "type": "uint64_t"} ] }, + { + "name": "execute bundles", + "args": [ + {"name": "bundles count", "type": "uint32_t"}, + {"name": "bundles", "type": "render bundle", "annotation": "const*", "length": "bundles count"} + ] + }, { "name": "insert debug marker", "args": [ - {"name": "group label", "type": "char", "annotation": "const*", "length": "strlen"} + {"name": "marker label", "type": "char", "annotation": "const*", "length": "strlen"} ] }, { @@ -814,6 +1297,17 @@ {"name": "color", "type": "color", "annotation": "const*"} ] }, + { + "name": "set viewport", + "args": [ + {"name": "x", "type": "float"}, + {"name": "y", "type": "float"}, + {"name": "width", "type": "float"}, + {"name": "height", "type": "float"}, + {"name": "min depth", "type": "float"}, + {"name": "max depth", "type": "float"} + ] + }, { "name": "set scissor rect", "args": [ @@ -824,44 +1318,70 @@ ] }, { - "name": "set vertex buffers", + "name": "set vertex buffer", "args": [ - {"name": "start slot", "type": "uint32_t"}, - {"name": "count", "type": "uint32_t"}, - {"name": "buffers", "type": "buffer", "annotation": "const*", "length": "count"}, - {"name": "offsets", "type": "uint64_t", "annotation": "const*", "length": "count"} + {"name": "slot", "type": "uint32_t"}, + {"name": "buffer", "type": "buffer"}, + {"name": "offset", "type": "uint64_t", "default": "0"}, + {"name": "size", "type": "uint64_t", "default": "0"} ] }, { "name": "set index buffer", "args": [ {"name": "buffer", "type": "buffer"}, - {"name": "offset", "type": "uint64_t"} + {"name": "offset", "type": "uint64_t", "default": "0"}, + {"name": "size", "type": "uint64_t", "default": "0"} ] }, { - "name": "end pass", - "TODO": "This returns the top-level encoder in the WebGPU IDL" + "name": "write timestamp", + "args": [ + {"name": "query set", "type": "query set"}, + {"name": "query index", "type": "uint32_t"} + ] + }, + { + "name": "end pass" } ] }, "render pipeline": { - "category": "object" + "category": "object", + "methods": [ + { + "name": "get bind group layout", + "returns": "bind group layout", + "args": [ + {"name": "group index", "type": "uint32_t"} + ] + } + ] }, "render pipeline descriptor": { "category": "structure", "extensible": true, "members": [ - {"name": "layout", "type": "pipeline layout"}, - {"name": "vertex stage", "type": "pipeline stage descriptor", "annotation": "const*"}, - {"name": "fragment stage", "type": "pipeline stage descriptor", "annotation": "const*"}, - {"name": "vertex input", "type": "vertex input descriptor", "annotation": "const*"}, + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "layout", "type": "pipeline layout", "optional": true}, + {"name": "vertex stage", "type": "programmable stage descriptor"}, + {"name": "fragment stage", "type": "programmable stage descriptor", "annotation": "const*", "optional": true}, + {"name": "vertex state", "type": "vertex state descriptor", "annotation": "const*", "optional": true}, {"name": "primitive topology", "type": "primitive topology"}, - {"name": "rasterization state", "type": "rasterization state descriptor", "annotation": "const*"}, - {"name": "sample count", "type": "uint32_t"}, + {"name": "rasterization state", "type": "rasterization state descriptor", "annotation": "const*", "optional": true}, + {"name": "sample count", "type": "uint32_t", "default": "1"}, {"name": "depth stencil state", "type": "depth stencil state descriptor", "annotation": "const*", "optional": true}, {"name": "color state count", "type": "uint32_t"}, - {"name": "color states", "type": "color state descriptor", "annotation": "const*const*", "length": "color state count"} + {"name": "color states", "type": "color state descriptor", "annotation": "const*", "length": "color state count"}, + {"name": "sample mask", "type": "uint32_t", "default": "0xFFFFFFFF"}, + {"name": "alpha to coverage enabled", "type": "bool", "default": "false"} + ] + }, + "render pipeline descriptor dummy extension": { + "category": "structure", + "chained": true, + "members": [ + {"name": "dummy stage", "type": "programmable stage descriptor"} ] }, "sampler": { @@ -871,15 +1391,23 @@ "category": "structure", "extensible": true, "members": [ - {"name": "address mode u", "type": "address mode"}, - {"name": "address mode v", "type": "address mode"}, - {"name": "address mode w", "type": "address mode"}, - {"name": "mag filter", "type": "filter mode"}, - {"name": "min filter", "type": "filter mode"}, - {"name": "mipmap filter", "type": "filter mode"}, - {"name": "lod min clamp", "type": "float"}, - {"name": "lod max clamp", "type": "float"}, - {"name": "compare function", "type": "compare function"} + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "address mode u", "type": "address mode", "default": "clamp to edge"}, + {"name": "address mode v", "type": "address mode", "default": "clamp to edge"}, + {"name": "address mode w", "type": "address mode", "default": "clamp to edge"}, + {"name": "mag filter", "type": "filter mode", "default": "nearest"}, + {"name": "min filter", "type": "filter mode", "default": "nearest"}, + {"name": "mipmap filter", "type": "filter mode", "default": "nearest"}, + {"name": "lod min clamp", "type": "float", "default": "0.0f"}, + {"name": "lod max clamp", "type": "float", "default": "1000.0f"}, + {"name": "compare", "type": "compare function", "default": "undefined"} + ] + }, + "sampler descriptor dummy anisotropic filtering": { + "category": "structure", + "chained": true, + "members": [ + {"name": "max anisotropy", "type": "float"} ] }, "shader module": { @@ -888,20 +1416,26 @@ "shader module descriptor": { "category": "structure", "extensible": true, + "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true} + ] + }, + "shader module SPIRV descriptor": { + "category": "structure", + "chained": true, "members": [ {"name": "code size", "type": "uint32_t"}, {"name": "code", "type": "uint32_t", "annotation": "const*", "length": "code size"} ] }, - "shader stage": { - "category": "enum", - "values": [ - {"value": 0, "name": "vertex"}, - {"value": 1, "name": "fragment"}, - {"value": 2, "name": "compute"} + "shader module WGSL descriptor": { + "category": "structure", + "chained": true, + "members": [ + {"name": "source", "type": "char", "annotation": "const*", "length": "strlen"} ] }, - "shader stage bit": { + "shader stage": { "category": "bitmask", "values": [ {"value": 0, "name": "none"}, @@ -927,10 +1461,53 @@ "category": "structure", "extensible": false, "members": [ - {"name": "compare", "type": "compare function"}, - {"name": "fail op", "type": "stencil operation"}, - {"name": "depth fail op", "type": "stencil operation"}, - {"name": "pass op", "type": "stencil operation"} + {"name": "compare", "type": "compare function", "default": "always"}, + {"name": "fail op", "type": "stencil operation", "default": "keep"}, + {"name": "depth fail op", "type": "stencil operation", "default": "keep"}, + {"name": "pass op", "type": "stencil operation", "default": "keep"} + ] + }, + "surface": { + "category": "object" + }, + "surface descriptor": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true} + ] + }, + "surface descriptor from canvas HTML selector": { + "category": "structure", + "chained": true, + "members": [ + {"name": "selector", "type": "char", "annotation": "const*", "length": "strlen"} + ] + }, + "surface descriptor from metal layer": { + "category": "structure", + "chained": true, + "javascript": false, + "members": [ + {"name": "layer", "type": "void", "annotation": "*"} + ] + }, + "surface descriptor from windows HWND": { + "category": "structure", + "chained": true, + "javascript": false, + "members": [ + {"name": "hinstance", "type": "void", "annotation": "*"}, + {"name": "hwnd", "type": "void", "annotation": "*"} + ] + }, + "surface descriptor from xlib": { + "category": "structure", + "chained": true, + "javascript": false, + "members": [ + {"name": "display", "type": "void", "annotation": "*"}, + {"name": "window", "type": "uint32_t"} ] }, "swap chain": { @@ -940,42 +1517,51 @@ "name": "configure", "args": [ {"name": "format", "type": "texture format"}, - {"name": "allowed usage", "type": "texture usage bit"}, + {"name": "allowed usage", "type": "texture usage"}, {"name": "width", "type": "uint32_t"}, {"name": "height", "type": "uint32_t"} ] }, - { - "name": "get next texture", - "returns": "texture" - }, - { - "name": "present", - "args": [ - {"name": "texture", "type": "texture"} - ] - } + {"name": "get current texture view", "returns": "texture view"}, + {"name": "present"} ] }, "swap chain descriptor": { "category": "structure", "extensible": true, "members": [ - {"name": "implementation", "type": "uint64_t"} + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "usage", "type": "texture usage"}, + {"name": "format", "type": "texture format"}, + {"name": "width", "type": "uint32_t"}, + {"name": "height", "type": "uint32_t"}, + {"name": "present mode", "type": "present mode"}, + {"name": "implementation", "type": "uint64_t", "default": 0} + ] + }, + "s type": { + "category": "enum", + "javascript": false, + "values": [ + {"value": 0, "name": "invalid", "valid": false}, + {"value": 1, "name": "surface descriptor from metal layer"}, + {"value": 2, "name": "surface descriptor from windows HWND"}, + {"value": 3, "name": "surface descriptor from xlib"}, + {"value": 4, "name": "surface descriptor from canvas HTML selector"}, + {"value": 5, "name": "shader module SPIRV descriptor"}, + {"value": 6, "name": "shader module WGSL descriptor"}, + {"value": 7, "name": "sampler descriptor dummy anisotropic filtering"}, + {"value": 8, "name": "render pipeline descriptor dummy extension"} ] }, "texture": { "category": "object", "methods": [ - { - "name": "create default view", - "returns": "texture view" - }, { "name": "create view", "returns": "texture view", "args": [ - {"name": "descriptor", "type": "texture view descriptor", "annotation": "const*"} + {"name": "descriptor", "type": "texture view descriptor", "annotation": "const*", "optional": true} ] }, { @@ -984,11 +1570,19 @@ ] }, "texture aspect": { - "category": "bitmask", + "category": "enum", "values": [ - {"value": 1, "name": "color"}, - {"value": 2, "name": "depth"}, - {"value": 4, "name": "stencil"} + {"value": 0, "name": "all"}, + {"value": 1, "name": "stencil only"}, + {"value": 2, "name": "depth only"} + ] + }, + "texture component type": { + "category": "enum", + "values": [ + {"value": 0, "name": "float"}, + {"value": 1, "name": "sint"}, + {"value": 2, "name": "uint"} ] }, "texture copy view": { @@ -996,66 +1590,112 @@ "extensible": true, "members": [ {"name": "texture", "type": "texture"}, - {"name": "level", "type": "uint32_t"}, - {"name": "slice", "type": "uint32_t"}, + {"name": "mip level", "type": "uint32_t", "default": "0"}, + {"name": "array layer", "type": "uint32_t", "default": "0"}, {"name": "origin", "type": "origin 3D"} ] }, + "texture data layout": { + "category": "structure", + "extensible": true, + "members": [ + {"name": "offset", "type": "uint64_t", "default": 0}, + {"name": "bytes per row", "type": "uint32_t"}, + {"name": "rows per image", "type": "uint32_t", "default": 0} + ] + }, "texture descriptor": { "category": "structure", "extensible": true, "members": [ - {"name": "usage", "type": "texture usage bit"}, - {"name": "dimension", "type": "texture dimension"}, + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "usage", "type": "texture usage"}, + {"name": "dimension", "type": "texture dimension", "default": "2D"}, {"name": "size", "type": "extent 3D"}, - {"name": "array layer count", "type": "uint32_t"}, + {"name": "array layer count", "type": "uint32_t", "default": "1"}, {"name": "format", "type": "texture format"}, - {"name": "mip level count", "type": "uint32_t"}, - {"name": "sample count", "type": "uint32_t"} + {"name": "mip level count", "type": "uint32_t", "default": 1}, + {"name": "sample count", "type": "uint32_t", "default": 1} ] }, "texture dimension": { "category": "enum", "values": [ - {"value": 0, "name": "2D"} + {"value": 0, "name": "1D"}, + {"value": 1, "name": "2D"}, + {"value": 2, "name": "3D"} ] }, "texture format": { "category": "enum", "values": [ - {"value": 0, "name": "r8 g8 b8 a8 unorm"}, - {"value": 1, "name": "r8 g8 unorm"}, - {"value": 2, "name": "r8 unorm"}, - {"value": 3, "name": "r8 g8 b8 a8 uint"}, - {"value": 4, "name": "r8 g8 uint"}, - {"value": 5, "name": "r8 uint"}, - {"value": 6, "name": "b8 g8 r8 a8 unorm"}, - {"value": 7, "name": "d32 float s8 uint"}, - {"value": 8, "name": "BC1 RGBA unorm"}, - {"value": 9, "name": "BC1 RGBA unorm srgb"}, - {"value": 10, "name": "BC2 RGBA unorm"}, - {"value": 11, "name": "BC2 RGBA unorm srgb"}, - {"value": 12, "name": "BC3 RGBA unorm"}, - {"value": 13, "name": "BC3 RGBA unorm srgb"}, - {"value": 14, "name": "BC4 R unorm"}, - {"value": 15, "name": "BC4 R snorm"}, - {"value": 16, "name": "BC5 RG unorm"}, - {"value": 17, "name": "BC5 RG snorm"}, - {"value": 18, "name": "BC6H RGB ufloat"}, - {"value": 19, "name": "BC6H RGB sfloat"}, - {"value": 20, "name": "BC7 RGBA unorm"}, - {"value": 21, "name": "BC7 RGBA unorm srgb"} - ], - "TODO": [ - "jiawei.shao@intel.com: support BC formats as extension" - ] - }, - "texture usage bit": { + {"value": 0, "name": "undefined", "valid": false, "jsrepr": "undefined"}, + {"value": 1, "name": "R8 unorm"}, + {"value": 2, "name": "R8 snorm"}, + {"value": 3, "name": "R8 uint"}, + {"value": 4, "name": "R8 sint"}, + + {"value": 5, "name": "R16 uint"}, + {"value": 6, "name": "R16 sint"}, + {"value": 7, "name": "R16 float"}, + {"value": 8, "name": "RG8 unorm"}, + {"value": 9, "name": "RG8 snorm"}, + {"value": 10, "name": "RG8 uint"}, + {"value": 11, "name": "RG8 sint"}, + + {"value": 12, "name": "R32 float"}, + {"value": 13, "name": "R32 uint"}, + {"value": 14, "name": "R32 sint"}, + {"value": 15, "name": "RG16 uint"}, + {"value": 16, "name": "RG16 sint"}, + {"value": 17, "name": "RG16 float"}, + {"value": 18, "name": "RGBA8 unorm"}, + {"value": 19, "name": "RGBA8 unorm srgb"}, + {"value": 20, "name": "RGBA8 snorm"}, + {"value": 21, "name": "RGBA8 uint"}, + {"value": 22, "name": "RGBA8 sint"}, + {"value": 23, "name": "BGRA8 unorm"}, + {"value": 24, "name": "BGRA8 unorm srgb"}, + {"value": 25, "name": "RGB10 A2 unorm"}, + {"value": 26, "name": "RG11 B10 float"}, + + {"value": 27, "name": "RG32 float"}, + {"value": 28, "name": "RG32 uint"}, + {"value": 29, "name": "RG32 sint"}, + {"value": 30, "name": "RGBA16 uint"}, + {"value": 31, "name": "RGBA16 sint"}, + {"value": 32, "name": "RGBA16 float"}, + + {"value": 33, "name": "RGBA32 float"}, + {"value": 34, "name": "RGBA32 uint"}, + {"value": 35, "name": "RGBA32 sint"}, + + {"value": 36, "name": "depth32 float"}, + {"value": 37, "name": "depth24 plus"}, + {"value": 38, "name": "depth24 plus stencil8"}, + + {"value": 39, "name": "BC1 RGBA unorm"}, + {"value": 40, "name": "BC1 RGBA unorm srgb"}, + {"value": 41, "name": "BC2 RGBA unorm"}, + {"value": 42, "name": "BC2 RGBA unorm srgb"}, + {"value": 43, "name": "BC3 RGBA unorm"}, + {"value": 44, "name": "BC3 RGBA unorm srgb"}, + {"value": 45, "name": "BC4 R unorm"}, + {"value": 46, "name": "BC4 R snorm"}, + {"value": 47, "name": "BC5 RG unorm"}, + {"value": 48, "name": "BC5 RG snorm"}, + {"value": 49, "name": "BC6H RGB ufloat"}, + {"value": 50, "name": "BC6H RGB sfloat"}, + {"value": 51, "name": "BC7 RGBA unorm"}, + {"value": 52, "name": "BC7 RGBA unorm srgb"} + ] + }, + "texture usage": { "category": "bitmask", "values": [ {"value": 0, "name": "none"}, - {"value": 1, "name": "transfer src"}, - {"value": 2, "name": "transfer dst"}, + {"value": 1, "name": "copy src"}, + {"value": 2, "name": "copy dst"}, {"value": 4, "name": "sampled"}, {"value": 8, "name": "storage"}, {"value": 16, "name": "output attachment"}, @@ -1066,12 +1706,14 @@ "category": "structure", "extensible": true, "members": [ - {"name": "format", "type": "texture format"}, - {"name": "dimension", "type": "texture view dimension"}, - {"name": "base mip level", "type": "uint32_t"}, - {"name": "mip level count", "type": "uint32_t"}, - {"name": "base array layer", "type": "uint32_t"}, - {"name": "array layer count", "type": "uint32_t"} + {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true}, + {"name": "format", "type": "texture format", "default": "undefined"}, + {"name": "dimension", "type": "texture view dimension", "default": "undefined"}, + {"name": "base mip level", "type": "uint32_t", "default": "0"}, + {"name": "mip level count", "type": "uint32_t", "default": "0"}, + {"name": "base array layer", "type": "uint32_t", "default": "0"}, + {"name": "array layer count", "type": "uint32_t", "default": "0"}, + {"name": "aspect", "type": "texture aspect", "default": "all"} ], "TODO": [ "jiawei.shao@intel.com: Allow choosing the aspect (depth vs. stencil)" @@ -1083,10 +1725,13 @@ "texture view dimension": { "category": "enum", "values": [ - {"value": 0, "name": "2D"}, - {"value": 1, "name": "2D array"}, - {"value": 2, "name": "cube"}, - {"value": 3, "name": "cube array"} + {"value": 0, "name": "undefined", "valid": false, "jsrepr": "undefined"}, + {"value": 1, "name": "1D"}, + {"value": 2, "name": "2D"}, + {"value": 3, "name": "2D array"}, + {"value": 4, "name": "cube"}, + {"value": 5, "name": "cube array"}, + {"value": 6, "name": "3D"} ], "TODO": [ "jiawei.shao@intel.com: support 1D and 3D texture views" @@ -1142,12 +1787,21 @@ "void": { "category": "native" }, + "void *": { + "category": "native" + }, + "void const *": { + "category": "native" + }, "uint32_t": { "category": "native" }, "int32_t": { "category": "native" }, + "size_t": { + "category": "native" + }, "uint64_t": { "category": "native" }, diff --git a/third_party/dawn/dawn_wire.json b/third_party/dawn/dawn_wire.json index b935b540617..c618b93e45d 100644 --- a/third_party/dawn/dawn_wire.json +++ b/third_party/dawn/dawn_wire.json @@ -14,11 +14,18 @@ "See the License for the specific language governing permissions and", "limitations under the License." ], + "_todos": [ + "Remove usage of size_t because it is not network transparent" + ], "commands": { "buffer map async": [ { "name": "buffer id", "type": "ObjectId" }, { "name": "request serial", "type": "uint32_t" }, - { "name": "is write", "type": "bool" } + { "name": "mode", "type": "map mode" }, + { "name": "offset", "type": "size_t"}, + { "name": "size", "type": "size_t"}, + { "name": "handle create info length", "type": "uint64_t" }, + { "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true} ], "buffer set sub data internal": [ {"name": "buffer id", "type": "ObjectId" }, @@ -28,34 +35,58 @@ ], "buffer update mapped data": [ { "name": "buffer id", "type": "ObjectId" }, - { "name": "data length", "type": "uint32_t" }, - { "name": "data", "type": "uint8_t", "annotation": "const*", "length": "data length" } + { "name": "write flush info length", "type": "uint64_t" }, + { "name": "write flush info", "type": "uint8_t", "annotation": "const*", "length": "write flush info length", "skip_serialize": true} ], "device create buffer mapped": [ { "name": "device", "type": "device" }, { "name": "descriptor", "type": "buffer descriptor", "annotation": "const*" }, - { "name": "result", "type": "ObjectHandle", "handle_type": "buffer" } + { "name": "result", "type": "ObjectHandle", "handle_type": "buffer" }, + { "name": "handle create info length", "type": "uint64_t" }, + { "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true} + ], + "device pop error scope": [ + { "name": "device", "type": "device" }, + { "name": "request serial", "type": "uint64_t" } ], "destroy object": [ { "name": "object type", "type": "ObjectType" }, { "name": "object id", "type": "ObjectId" } + ], + "queue write buffer internal": [ + {"name": "queue id", "type": "ObjectId" }, + {"name": "buffer id", "type": "ObjectId" }, + {"name": "buffer offset", "type": "uint64_t"}, + {"name": "data", "type": "uint8_t", "annotation": "const*", "length": "size"}, + {"name": "size", "type": "size_t"} + ], + "queue write texture internal": [ + {"name": "queue id", "type": "ObjectId" }, + {"name": "destination", "type": "texture copy view", "annotation": "const*"}, + {"name": "data", "type": "uint8_t", "annotation": "const*", "length": "data size"}, + {"name": "data size", "type": "size_t"}, + {"name": "data layout", "type": "texture data layout", "annotation": "const*"}, + {"name": "writeSize", "type": "extent 3D", "annotation": "const*"} ] }, "return commands": { - "buffer map read async callback": [ + "buffer map async callback": [ { "name": "buffer", "type": "ObjectHandle", "handle_type": "buffer" }, { "name": "request serial", "type": "uint32_t" }, { "name": "status", "type": "uint32_t" }, - { "name": "data length", "type": "uint64_t" }, - { "name": "data", "type": "uint8_t", "annotation": "const*", "length": "data length" } + { "name": "read initial data info length", "type": "uint64_t" }, + { "name": "read initial data info", "type": "uint8_t", "annotation": "const*", "length": "read initial data info length", "skip_serialize": true } ], - "buffer map write async callback": [ - { "name": "buffer", "type": "ObjectHandle", "handle_type": "buffer" }, - { "name": "request serial", "type": "uint32_t" }, - { "name": "status", "type": "uint32_t" }, - { "name": "data length", "type": "uint64_t" } + "device uncaptured error callback": [ + { "name": "type", "type": "error type"}, + { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" } + ], + "device lost callback" : [ + { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" } ], - "device error callback": [ + "device pop error scope callback": [ + { "name": "request serial", "type": "uint64_t" }, + { "name": "type", "type": "error type" }, { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" } ], "fence update completed value": [ @@ -65,26 +96,46 @@ }, "special items": { "client_side_structures": [ - "CreateBufferMappedResult" + "CreateBufferMappedResult", + "SurfaceDescriptorFromMetalLayer", + "SurfaceDescriptorFromWindowsHWND", + "SurfaceDescriptorFromXlib" ], "client_side_commands": [ + "BufferMapAsync", + "BufferMapReadAsync", + "BufferMapWriteAsync", "BufferSetSubData", - "FenceGetCompletedValue" + "BufferGetConstMappedRange", + "BufferGetMappedRange", + "DevicePopErrorScope", + "DeviceSetDeviceLostCallback", + "DeviceSetUncapturedErrorCallback", + "FenceGetCompletedValue", + "FenceOnCompletion", + "QueueWriteBuffer", + "QueueWriteTexture" ], "client_handwritten_commands": [ - "BufferSetSubData", + "BufferDestroy", "BufferUnmap", + "DeviceCreateBuffer", "DeviceCreateBufferMapped", + "DeviceCreateErrorBuffer", + "DeviceGetDefaultQueue", + "DeviceInjectError", + "DevicePushErrorScope", "QueueCreateFence", - "FenceGetCompletedValue", "QueueSignal" ], "client_special_objects": [ "Buffer", "Device", - "Fence" + "Fence", + "Queue" ], "server_custom_pre_handler_commands": [ + "BufferDestroy", "BufferUnmap" ], "server_handwritten_commands": [ diff --git a/third_party/dawn/docs/buiding.md b/third_party/dawn/docs/buiding.md new file mode 100644 index 00000000000..f1ab8a28efe --- /dev/null +++ b/third_party/dawn/docs/buiding.md @@ -0,0 +1,30 @@ +# Building Dawn + +Dawn uses the Chromium build system and dependency management so you need to [install depot_tools] and add it to the PATH. + +[install depot_tools]: http://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html#_setting_up + +On Linux you need to have the `pkg-config` command: +```sh +# Install pkg-config on Ubuntu +sudo apt-get install pkg-config +``` + +Then get the source as follows: + +```sh +# Clone the repo as "dawn" +git clone https://dawn.googlesource.com/dawn dawn && cd dawn + +# Bootstrap the gclient configuration +cp scripts/standalone.gclient .gclient + +# Fetch external dependencies and toolchains with gclient +gclient sync +``` + +Then generate build files using `gn args out/Debug` or `gn args out/Release`. +A text editor will appear asking build options, the most common option is `is_debug=true/false`; otherwise `gn args out/Release --list` shows all the possible options. + +Then use `ninja -C out/Release` to build dawn and for example `./out/Release/dawn_end2end_tests` to run the tests. + diff --git a/third_party/dawn/docs/debug_markers.md b/third_party/dawn/docs/debug_markers.md new file mode 100644 index 00000000000..8b2404a4233 --- /dev/null +++ b/third_party/dawn/docs/debug_markers.md @@ -0,0 +1,50 @@ +# Debug Markers + +Dawn provides debug tooling integration for each backend. + +Debugging markers are exposed through this API: +``` +partial GPUProgrammablePassEncoder { + void pushDebugGroup(const char * markerLabel); + void popDebugGroup(); + void insertDebugMarker(const char * markerLabel); +}; +``` + +These APIs will result in silent no-ops if they are used without setting up +the execution environment properly. Each backend has a specific process +for setting up this environment. + +## D3D12 + +Debug markers on D3D12 are implemented with the [PIX Event Runtime](https://blogs.msdn.microsoft.com/pix/winpixeventruntime/). + +To enable marker functionality, you must: +1. Click the download link on https://www.nuget.org/packages/WinPixEventRuntime +2. Rename the .nupkg file to a .zip extension, then extract its contents. +3. Copy `bin\WinPixEventRuntime.dll` into the same directory as `libdawn_native.dll`. +4. Launch your application. + +You may now call the debug marker APIs mentioned above and see them from your GPU debugging tool. When using your tool, it is supported to both launch your application with the debugger attached, or attach the debugger while your application is running. + +D3D12 debug markers have been tested with [Microsoft PIX](https://devblogs.microsoft.com/pix/) and [Intel Graphics Frame Analyzer](https://software.intel.com/en-us/gpa/graphics-frame-analyzer). + +Unfortunately, PIX's UI does does not lend itself to capturing single frame applications like tests. You must enable capture from within your application. To do this in Dawn tests, pass the --begin-capture-on-startup flag to dawn_end2end_tests.exe. + +## Vulkan + +Debug markers on Vulkan are implemented with [VK_EXT_debug_marker](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#VK_EXT_debug_marker). + +To enable marker functionality, you must launch your application from your debugging tool. Attaching to an already running application is not supported. + +Vulkan markers have been tested with [RenderDoc](https://renderdoc.org/). + +## Metal + +Debug markers on Metal are used with the XCode debugger. + +To enable marker functionality, you must launch your application from XCode and use [GPU Frame Capture](https://developer.apple.com/documentation/metal/tools_profiling_and_debugging/metal_gpu_capture). + +## OpenGL + +Debug markers on OpenGL are not implemented and will result in a silent no-op. This is due to low adoption of the GL_EXT_debug_marker extension in Linux device drivers. diff --git a/third_party/dawn/docs/debugging.md b/third_party/dawn/docs/debugging.md new file mode 100644 index 00000000000..740d0b284c2 --- /dev/null +++ b/third_party/dawn/docs/debugging.md @@ -0,0 +1,3 @@ +# Debugging Dawn + +(TODO) diff --git a/third_party/dawn/docs/device_facilities.md b/third_party/dawn/docs/device_facilities.md new file mode 100644 index 00000000000..ae753233b3f --- /dev/null +++ b/third_party/dawn/docs/device_facilities.md @@ -0,0 +1,106 @@ +# Devices + +In Dawn the `Device` is a "god object" that contains a lot of facilities useful for the whole object graph that descends from it. +There a number of facilities common to all backends that live in the frontend and backend-specific facilities. +Example of frontend facilities are the management of content-less object caches, or the toggle management. +Example of backend facilities are GPU memory allocators or the backing API function pointer table. + +## Frontend facilities + +### Error Handling + +Dawn (dawn_native) uses the [Error.h](../src/dawn_native/Error.h) error handling to robustly handle errors. +With `DAWN_TRY` errors bubble up all the way to, and are "consumed" by the entry-point that was called by the application. +Error consumption uses `Device::ConsumeError` that expose them via the WebGPU "error scopes" and can also influence the device lifecycle by notifying of a device loss, or triggering a device loss.. + +See [Error.h](../src/dawn_native/Error.h) for more information about using errors. + +### Device Lifecycle + +The device lifecycle is a bit more complicated than other objects in Dawn for multiple reasons: + + - The device initialization creates facilities in both the backend and the frontend, which can fail. + When a device fails to initialize, it should still be possible to destroy it without crashing. + - Execution of commands on the GPU must be finished before the device can be destroyed (because there's noone to "DeleteWhenUnused" the device). + - On creation a device might want to run some GPU commands (like initializing zero-buffers), which must be completed before it is destroyed. + - A device can become "disconnected" when a TDR or hot-unplug happens. + In this case, destruction of the device doesn't need to wait on GPU commands to finish because they just disappeared. + +There is a state machine `State` defined in [Device.h](../src/dawn_native/Device.h) that controls all of the above. +The most common state is `Alive` when there are potentially GPU commands executing. + +Initialization of a device looks like the following: + + - `DeviceBase::DeviceBase` is called and does mostly nothing except setting `State` to `BeingCreated` (and initial toggles). + - `backend::Device::Initialize` creates things like the underlying device and other stuff that doesn't run GPU commands. + - It then calls `DeviceBase::Initialize` that enables the `DeviceBase` facilities and sets the `State` to `Alive`. + - Optionally, `backend::Device::Initialize` can now enqueue GPU commands for its initialization. + - The device is ready to be used by the application! + +While it is `Alive` the device can notify it has been disconnected by the backend, in which case it jumps directly to the `Disconnected` state. +Internal errors, or a call to `LoseForTesting` can also disconnect the device, but in the underlying API commands are still running, so the frontend will finish all commands (with `WaitForIdleForDesctruction`) and prevent any new commands to be enqueued (by setting state to `BeingDisconnected`). +After this the device is set in the `Disconnected` state. +If an `Alive` device is destroyed, then a similar flow to `LoseForTesting happens`. + +All this ensures that during destruction or forceful disconnect of the device, it properly gets to the `Disconnected` state with no commands executing on the GPU. +After disconnecting, frontend will call `backend::Device::ShutDownImpl` so that it can properly free driver objects. + +### Toggles + +Toggles are booleans that control code paths inside of Dawn, like lazy-clearing resources or using D3D12 render passes. +They aren't just booleans close to the code path they control, because embedders of Dawn like Chromium want to be able to surface what toggles are used by a device (like in about:gpu). + +Toogles are to be used for any optional code path in Dawn, including: + + - Workarounds for driver bugs. + - Disabling select parts of the validation or robustness. + - Enabling limitations that help with testing. + - Using more advanced or optional backend API features. + +Toggles can be queried using `DeviceBase::IsToggleEnabled`: +``` +bool useRenderPass = device->IsToggleEnabled(Toggle::UseD3D12RenderPass); +``` + +Toggles are defined in a table in [Toggles.cpp](../src/dawn_native/Toggles.cpp) that also includes their name and description. +The name can be used to force enabling of a toggle or, at the contrary, force the disabling of a toogle. +This is particularly useful in tests so that the two sides of a code path can be tested (for example using D3D12 render passes and not). + +Here's an example of a test that is run in the D3D12 backend both with the D3D12 render passes forcibly disabled, and in the default configuration. +``` +DAWN_INSTANTIATE_TEST(RenderPassTest, + D3D12Backend(), + D3D12Backend({}, {"use_d3d12_render_pass"})); +// The {} is the list of force enabled toggles, {"..."} the force disabled ones. +``` + +The initialization order of toggles looks as follows: + + - The toggles overrides from the device descriptor are applied. + - The frontend device default toggles are applied (unless already overriden). + - The backend device default toggles are applied (unless already overriden) using `DeviceBase::SetToggle` + - The backend device can ignore overriden toggles if it can't support them by using `DeviceBase::ForceSetToggle` + +Forcing toggles should only be done when there is no "safe" option for the toggle. +This is to avoid crashes during testing when the tests try to use both sides of a toggle. +For toggles that are safe to enable, like workarounds, the tests can run against the base configuration and with the toggle enabled. +For toggles that are safe to disable, like using more advanced backing API features, the tests can run against the base configuation and with the toggle disabled. + +### Immutable object caches + +A number of WebGPU objects are immutable once created, and can be expensive to create, like pipelines. +`DeviceBase` contains caches for these objects so that they are free to create the second time. +This is also useful to be able to compare objects by pointers like `BindGroupLayouts` since two BGLs would be equal iff they are the same object. + +### Format Tables + +The frontend has a `Format` structure that represent all the information that are known about a particular WebGPU format for this Device based on the enabled extensions. +Formats are precomputed at device initialization and can be queried from a WebGPU format either assuming the format is a valid enum, or in a safe manner that doesn't do this assumption. +A reference to these formats can be stored persistently as they have the same lifetime as the `Device`. + +Formats also have an "index" so that backends can create parallel tables for internal informations about formats, like what they translate to in the backing API. + +### Object factory + +Like WebGPU's device object, `DeviceBase` is an factory with methods to create all kinds of other WebGPU objects. +WebGPU has some objects that aren't created from the device, like the texture view, but in Dawn these creations also go through `DeviceBase` so that there is a single factory for each backend. diff --git a/third_party/dawn/docs/fuzzing.md b/third_party/dawn/docs/fuzzing.md new file mode 100644 index 00000000000..8c7b7baffa7 --- /dev/null +++ b/third_party/dawn/docs/fuzzing.md @@ -0,0 +1,26 @@ +# Fuzzing Dawn + +## `dawn_wire_server_and_frontend_fuzzer` + +The `dawn_wire_server_and_frontend_fuzzer` sets up Dawn using the Null backend, and passes inputs to the wire server. This fuzzes the `dawn_wire` deserialization, as well as Dawn's frontend validation. + +## `dawn_wire_server_and_vulkan_backend_fuzzer` + +The `dawn_wire_server_and_vulkan_backend_fuzzer` is like `dawn_wire_server_and_frontend_fuzzer` but it runs using a Vulkan CPU backend such as Swiftshader. This fuzzer supports error injection by using the first bytes of the fuzzing input as a Vulkan call index for which to mock a failure. + +## Updating the Seed Corpus + +Using a seed corpus significantly improves the efficiency of fuzzing. Dawn's fuzzers use interesting testcases discovered in previous fuzzing runs to seed future runs. Fuzzing can be further improved by using Dawn tests as a example of API usage which allows the fuzzer to quickly discover and use new API entrypoints and usage patterns. + +The script [update_fuzzer_seed_corpus.sh](../scripts/update_fuzzer_seed_corpus.sh) can be used to capture a trace while running Dawn tests, and upload it to the existing fuzzer seed corpus. It does the following steps: +1. Builds the provided test and fuzzer targets. +2. Runs the provided test target with `--use-wire --wire-trace-dir=tmp_dir1 [additional_test_args]` to dump traces of the tests. +3. Generates one variant of each trace for every possible error index, by running the fuzzer target with `--injected-error-testcase-dir=tmp_dir2 ...`. +4. Minimizes all testcases by running the fuzzer target with `-merge=1 tmp_dir3 tmp_dir1 tmp_dir2`. + +To run the script: +1. You must be in a Chromium checkout using the GN arg `use_libfuzzer=true` +2. Run `./third_party/dawn/scripts/update_fuzzer_seed_corpus.sh [additional_test_args]`. + + Example: `./third_party/dawn/scripts/update_fuzzer_seed_corpus.sh out/fuzz dawn_wire_server_and_vulkan_backend_fuzzer dawn_end2end_tests --gtest_filter=*Vulkan` +3. The script will print instructions for testing, and then uploading new inputs. Please, only upload inputs after testing the fuzzer with new inputs, and verifying there is a meaningful change in coverage. Uploading requires [gcloud](https://g3doc.corp.google.com/cloud/sdk/g3doc/index.md?cl=head) to be logged in with @google.com credentials: `gcloud auth login`. diff --git a/third_party/dawn/docs/infra.md b/third_party/dawn/docs/infra.md new file mode 100644 index 00000000000..605d9cad187 --- /dev/null +++ b/third_party/dawn/docs/infra.md @@ -0,0 +1,92 @@ +# Dawn's Continuous Testing Infrastructure + +Dawn uses Chromium's continuous integration (CI) infrastructure to continually run tests on changes to Dawn and provide a way for developers to run tests against their changes before submitting. CI bots continually build and run tests for every new change, and Try bots build and run developers' pending changes before submission. Dawn uses two different build recipes. There is a Dawn build recipe which checks out Dawn standalone, compiles, and runs the `dawn_unittests`. And, there is the Chromium build recipe which checks out Dawn inside a Chromium checkout. Inside a Chromium checkout, there is more infrastructure available for triggering `dawn_end2end_tests` that run on real GPU hardware, and we are able to run Chromium integration tests as well as tests for WebGPU. + + - [Dawn CI Builders](https://ci.chromium.org/p/dawn/g/ci/builders) + - [Dawn Try Builders](https://ci.chromium.org/p/dawn/g/try/builders) + - [chromium.dawn Waterfall](https://ci.chromium.org/p/chromium/g/chromium.dawn/console) + +For additional information on GPU testing in Chromium, please see [[chromium/src]//docs/gpu/gpu_testing_bot_details.md](https://chromium.googlesource.com/chromium/src.git/+/master/docs/gpu/gpu_testing_bot_details.md). + +## Dawn CI/Try Builders +Dawn builders are specified in [[dawn]//infra/config/global/cr-buildbucket.cfg](../infra/config/global/cr-buildbucket.cfg). This file contains a few mixins such as `clang`, `no_clang`, `x64`, `x86`, `debug`, `release` which are used to specify the bot dimensions and build properties (builder_mixins.recipe.properties). At the time of writing, we have the following builders: + - [dawn/try/presubmit](https://ci.chromium.org/p/dawn/builders/try/presubmit) + - [dawn/try/linux-clang-dbg-x64](https://ci.chromium.org/p/dawn/builders/try/linux-clang-dbg-x64) + - [dawn/try/linux-clang-dbg-x86](https://ci.chromium.org/p/dawn/builders/try/linux-clang-dbg-x86) + - [dawn/try/linux-clang-rel-x64](https://ci.chromium.org/p/dawn/builders/try/linux-clang-rel-x64) + - [dawn/try/mac-dbg](https://ci.chromium.org/p/dawn/builders/try/mac-dbg) + - [dawn/try/mac-rel](https://ci.chromium.org/p/dawn/builders/try/mac-rel) + - [dawn/try/win-clang-dbg-x86](https://ci.chromium.org/p/dawn/builders/try/win-clang-dbg-x86) + - [dawn/try/win-clang-rel-x64](https://ci.chromium.org/p/dawn/builders/try/win-clang-rel-x64) + - [dawn/try/win-msvc-dbg-x86](https://ci.chromium.org/p/dawn/builders/try/win-msvc-dbg-x86) + - [dawn/try/win-msvc-rel-x64](https://ci.chromium.org/p/dawn/builders/try/win-msvc-rel-x64) + +There are additional `chromium/try` builders, but those are described later in this document. + +These bots are defined in both buckets luci.dawn.ci and luci.dawn.try, though their ACL permissions differ. luci.dawn.ci bots will be scheduled regularly based on [[dawn]//infra/config/global/luci-scheduler.cfg](../infra/config/global/luci-scheduler.cfg). luci.dawn.try bots will be triggered on the CQ based on [[dawn]//infra/config/global/commit-queue.cfg](../infra/config/global/commit-queue.cfg). + +One particular note is `buckets.swarming.builder_defaults.recipe.name: "dawn"` which specifies these use the [`dawn.py`](https://source.chromium.org/search/?q=file:recipes/dawn.py) build recipe. + +Build status for both CI and Try builders can be seen at this [console](https://ci.chromium.org/p/dawn) which is generated from [[dawn]//infra/config/global/luci-milo.cfg](../infra/config/global/luci-milo.cfg). + +## Dawn Build Recipe +The [`dawn.py`](https://cs.chromium.org/search/?q=file:recipes/dawn.py) build recipe is simple and intended only for testing compilation and unit tests. It does the following: + 1. Checks out Dawn standalone and dependencies + 2. Builds based on the `builder_mixins.recipe.properties` coming from the builder config in [[dawn]//infra/config/global/cr-buildbucket.cfg](../infra/config/global/cr-buildbucket.cfg). + 3. Runs the `dawn_unittests` on that same bot. + +## Dawn Chromium-Based CI Waterfall Bots +The [`chromium.dawn`](https://ci.chromium.org/p/chromium/g/chromium.dawn/console) waterfall consists of the bots specified in the `chromium.dawn` section of [[chromium/src]//testing/buildbot/waterfalls.pyl](https://source.chromium.org/search/?q=file:waterfalls.pyl%20chromium.dawn). Bots named "Builder" are responsible for building top-of-tree Dawn, whereas bots named "DEPS Builder" are responsible for building Chromium's DEPS version of Dawn. + +The other bots, such as "Dawn Linux x64 DEPS Release (Intel HD 630)" receive the build products from the Builders and are responsible for running tests. The Tester configuration may specify `mixins` from [[chromium/src]//testing/buildbot/mixins.pyl](https://source.chromium.org/search/?q=file:buildbot/mixins.pyl) which help specify bot test dimensions like OS version and GPU vendor. The Tester configuration also specifies `test_suites` from [[chromium/src]//testing/buildbot/test_suites.pyl](https://source.chromium.org/search/?q=file:buildbot/test_suites.pyl%20dawn_end2end_tests) which declare the tests are arguments passed to tests that should be run on the bot. + +The Builder and Tester bots are additionally configured at [[chromium/tools/build]//scripts/slave/recipe_modules/chromium_tests/chromium_dawn.py](https://source.chromium.org/search?q=file:chromium_dawn.py) which defines the bot specs for the builders and testers. Some things to note: + - The Tester bots set `parent_buildername` to be their respective Builder bot. + - The non DEPS bots use the `dawn_top_of_tree` config. + - The bots apply the `mb` config which references [[chromium]//tools/mb/mb_config.pyl](https://source.chromium.org/search?q=file:mb_config.pyl%20%22Dawn%20Linux%20x64%20Builder%22) and [[chromium]//tools/mb/mb_config_buckets.pyl](https://source.chromium.org/search?q=file:mb_config_buckets.pyl%20%22Dawn%20Linux%20x64%20Builder%22). Various mixins there specify build dimensions like debug, release, gn args, x86, x64, etc. + +Finally, builds on these waterfall bots are automatically scheduled based on the configuration in [[chromium/src]//infra/config/buckets/ci.star](https://source.chromium.org/search?q=file:ci.star%20%22Dawn%20Linux%20x64%20Builder%22). Note that the Tester bots are `triggered_by` the Builder bots. + +## Dawn Chromium-Based Tryjobs +[[dawn]//infra/config/global/commit-queue.cfg](../infra/config/global/commit-queue.cfg) declares additional tryjob builders which are defined in the Chromium workspace. The reason for this separation is that jobs sent to these bots rely on the Chromium infrastructure for doing builds and triggering jobs on bots with GPU hardware in swarming. + +At the time of writing, the bots for Dawn CLs are: + - [chromium/try/linux-dawn-rel](https://ci.chromium.org/p/chromium/builders/try/linux-dawn-rel) + - [chromium/try/mac-dawn-rel](https://ci.chromium.org/p/chromium/builders/try/mac-dawn-rel) + - [chromium/try/win-dawn-rel](https://ci.chromium.org/p/chromium/builders/try/win-dawn-rel) + +And for Chromium CLs: + - [chromium/try/dawn-linux-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-linux-x64-deps-rel) + - [chromium/try/dawn-mac-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-mac-x64-deps-rel) + - [chromium/try/dawn-win10-x86-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-win10-x86-deps-rel) + - [chromium/try/dawn-win10-x64-deps-rel](https://ci.chromium.org/p/chromium/builders/try/dawn-win10-x64-deps-rel) + + The configuration for these bots is generated from [[chromium]//infra/config/buckets/try.star](https://source.chromium.org/search/?q=file:try.star%20linux-dawn-rel) which uses the [`chromium_dawn_builder`](https://source.chromium.org/search/?q=%22def%20chromium_dawn_builder%22) function which sets the `mastername` to `tryserver.chromium.dawn`. + +[[chromium/tools/build]//scripts/slave/recipe_modules/chromium_tests/trybots.py](https://source.chromium.org/search/?q=file:trybots.py%20tryserver.chromium.dawn) specifies `tryserver.chromium.dawn` bots as mirroring bots from the `chromium.dawn` waterfall. Example: +``` +'dawn-linux-x64-deps-rel': { + 'bot_ids': [ + { + 'mastername': 'chromium.dawn', + 'buildername': 'Dawn Linux x64 DEPS Builder', + 'tester': 'Dawn Linux x64 DEPS Release (Intel HD 630)', + }, + { + 'mastername': 'chromium.dawn', + 'buildername': 'Dawn Linux x64 DEPS Builder', + 'tester': 'Dawn Linux x64 DEPS Release (NVIDIA)', + }, + ], +}, +``` + +Using the [[chromium/tools/build]//scripts/slave/recipes/chromium_trybot.py](https://source.chromium.org/search/?q=file:chromium_trybot.py) recipe, these trybots will cherry-pick a CL and run the same tests as the CI waterfall bots. The trybots also pick up some build mixins from [[chromium]//tools/mb/mb_config.pyl](https://source.chromium.org/search?q=file:mb_config.pyl%20dawn-linux-x64-deps-rel). + +## Bot Allocation + +Bots are physically allocated based on the configuration in [[chromium/infradata/config]//configs/chromium-swarm/starlark/bots/dawn.star](https://chrome-internal.googlesource.com/infradata/config/+/refs/heads/master/configs/chromium-swarm/starlark/bots/dawn.star) (Google only). + +`dawn/try` bots are using builderless configurations which means they use builderless GCEs shared with Chromium bots and don't need explicit allocation. + +`chromium/try` bots are still explicitly allocated with a number of GCE instances and lifetime of the build cache. All of the GCE bots should eventually be migrated to builderless (crbug.com/dawn/328). Mac bots such as `dawn-mac-x64-deps-rel`, `mac-dawn-rel`, `Dawn Mac x64 Builder`, and `Dawn Mac x64 DEPS Builder` point to specific ranges of machines that have been reserved by the infrastructure team. diff --git a/third_party/dawn/docs/overview.md b/third_party/dawn/docs/overview.md new file mode 100644 index 00000000000..e63afeb9971 --- /dev/null +++ b/third_party/dawn/docs/overview.md @@ -0,0 +1,55 @@ +# Dawn repository overview + +This repository contains the implementation of Dawn, which is itself composed of two main libraries (dawn_native and dawn_wire), along with support libraries, tests, and samples. Dawn makes heavy use of code-generation based on the `dawn.json` file that describes the native WebGPU API. It is used to generate the API headers, C++ wrapper, parts of the client-server implementation, and more! + +## Directory structure + +- [`dawn.json`](../dawn.json): contains a description of the native WebGPU in JSON form. It is the data model that's used by the code generators. +- [`dawn_wire.json`](../dawn_wire.json): contains additional information used to generate `dawn_wire` files, such as commands in addition to regular WebGPU commands. +- [`examples`](../examples): a small collection of samples using the native WebGPU API. They were mostly used when bringing up Dawn for the first time, and to test the `WGPUSwapChain` object. +- [`generator`](../generator): directory containg the code generators and their templates. Generators are based on Jinja2 and parse data-models from JSON files. + - [`dawn_json_generator.py`](../generator/dawn_json_generator.py): the main code generator that outputs the WebGPU headers, C++ wrapper, client-server implementation, etc. + - [`templates`](../generator/templates): Jinja2 templates for the generator, with subdirectories for groups of templates that are all used in the same library. +- [`infra`](../infra): configuration file for the commit-queue infrastructure. +- [`scripts`](../scripts): contains a grab-bag of files that are used for building Dawn, in testing, etc. +- [`src`](../src): + - [`common`](../src/common): helper code that is allowed to be used by Dawn's core libraries, `dawn_native` and `dawn_wire`. Also allowed for use in all other Dawn targets. + - [`dawn_native`](../src/dawn_native): code for the implementation of WebGPU on top of graphics APIs. Files in this folder are the "frontend" while subdirectories are "backends". + - ``: code for the implementation of the backend on a specific graphics API, for example `d3d12`, `metal` or `vulkan`. + - [`dawn_platform`](../src/dawn_platform): definition of interfaces for dependency injection in `dawn_native` or `dawn_wire`. + - [`dawn_wire`](../src/dawn_wire): code for an implementation of WebGPU as a client-server architecture. + - [`fuzzers`](../src/fuzzers): various fuzzers for Dawn that are running in [Clusterfuzz](https://google.github.io/clusterfuzz/). + - [`include`](../src/include): public headers with subdirectories for each library. Note that some headers are auto-generated and not present directly in the directory. + - [`tests`](../src/tests): + - [`end2end`](../src/tests/end2end): tests for the execution of the WebGPU API and require a GPU to run. + - [`perf_tests`](../src/tests/perf_tests): benchmarks for various aspects of Dawn. + - [`unittests`](../src/tests/unittests): code unittests of internal classes, but also by extension WebGPU API tests that don't require a GPU to run. + - [`validation`](../src/tests/unittests/validation): WebGPU validation tests not using the GPU (frontend tests) + - [`white_box`](../src/tests/white_box): tests using the GPU that need to access the internals of `dawn_native` or `dawn_wire`. + - [`utils`](../src/utils): helper code to use Dawn used by tests and samples but disallowed for `dawn_native` and `dawn_wire`. +- [`third_party`](../third_party): directory where dependencies live as well as their buildfiles. + +## Dawn Native (`dawn_native`) + +The largest library in Dawn is `dawn_native` which implements the WebGPU API by translating to native graphics APIs such as D3D12, Metal or Vulkan. It is composed of a frontend that does all the state-tracking and validation, and backends that do the actual translation to the native graphics APIs. + +`dawn_native` hosts the [SPVC](https://github.com/google/shaderc/tree/master/spvc) shader translator that validates SPIR-V for WebGPU and converts it to an equivalent shader for use in the native graphics API (HLSL for D3D12, MSL for Metal or Vulkan SPIR-V for Vulkan). + +## Dawn Wire (`dawn_wire`) + +A second library that implements both a client that takes WebGPU commands and serializes them into a buffer, and a server that deserializes commands from a buffer, validates they are well-formed and calls the relevant WebGPU commands. Some server to client communication also happens so the API's callbacks work properly. + +Note that `dawn_wire` is meant to do as little state-tracking as possible so that the client can be lean and defer most of the heavy processing to the server side where the server calls into `dawn_native`. + +## Dawn Proc (`dawn_proc`) + +Normally libraries implementing `webgpu.h` should implement function like `wgpuDeviceCreateBuffer` but instead `dawn_native` and `dawn_wire` implement the `dawnProcTable` which is a structure containing all the WebGPU functions Dawn implements. Then a `dawn_proc` library contains a static version of this `dawnProcTable` and for example forwards `wgpuDeviceCreateBuffer` to the `procTable.deviceCreateBuffer` function pointer. This is useful in two ways: + + - It allows deciding at runtime whether to use `dawn_native` and `dawn_wire`, which is useful to test boths paths with the same binary in our infrastructure. + - It avoids applications that know they will only use Dawn to query all entrypoints at once instead of using `wgpuGetProcAddress` repeatedly. + +## Code generation + +When the WebGPU API evolves a lot of places in Dawn have to be updated, so to reduce efforts, Dawn relies heavily on code generation. The code generators are based on [Jinja2](https://jinja.palletsprojects.com/) and separate the model from the view like in Web development. The model is some JSON file, usually [`dawn.json`](../dawn.json) and the views are the Jinja2 templates in [`generator/templates`](../generator/templates). The generated files are not checked into the repository but instead are generated during the build. + +Most of the code generation is done in [`dawn_json_generator.py`](../generator/dawn_json_generator.py) but other generators exist so common functionality to build code generators has been extracted into[`generator_lib.py`](../generator/generator_lib.py). diff --git a/third_party/dawn/docs/testing.md b/third_party/dawn/docs/testing.md new file mode 100644 index 00000000000..7b6f4b64092 --- /dev/null +++ b/third_party/dawn/docs/testing.md @@ -0,0 +1,69 @@ +# Testing Dawn + +(TODO) + +## Dawn Perf Tests + +For benchmarking with `dawn_perf_tests`, it's best to build inside a Chromium checkout using the following GN args: +``` +is_official_build = true # Enables highest optimization level, using LTO on some platforms +use_dawn = true # Required to build Dawn +use_cfi_icall=false # Required because Dawn dynamically loads function pointers, and we don't sanitize them yet. +``` + +A Chromium checkout is required for the highest optimization flags. It is possible to build and run `dawn_perf_tests` from a standalone Dawn checkout as well, only using GN arg `is_debug=false`. For more information on building, please see [building.md](./building.md). + +### Terminology + + - Iteration: The unit of work being measured. It could be a frame, a draw call, a data upload, a computation, etc. `dawn_perf_tests` metrics are reported as time per iteration. + - Step: A group of Iterations run together. The number of `iterationsPerStep` is provided to the constructor of `DawnPerfTestBase`. + - Trial: A group of Steps run consecutively. `kNumTrials` are run for each test. A Step in a Trial is run repetitively for approximately `kCalibrationRunTimeSeconds`. Metrics are accumlated per-trial and reported as the total time divided by `numSteps * iterationsPerStep`. `maxStepsInFlight` is passed to the `DawnPerfTestsBase` constructor to limit the number of Steps pipelined. + +(See [`//src/tests/perf_tests/DawnPerfTest.h`](https://cs.chromium.org/chromium/src/third_party/dawn/src/tests/perf_tests/DawnPerfTest.h) for the values of the constants). + +### Metrics + +`dawn_perf_tests` measures the following metrics: + - `wall_time`: The time per iteration, including time waiting for the GPU between Steps in a Trial. + - `cpu_time`: The time per iteration, not including time waiting for the GPU between Steps in a Trial. + - `validation_time`: The time for CommandBuffer / RenderBundle validation. + - `recording_time`: The time to convert Dawn commands to native commands. + +Metrics are reported according to the format specified at +[[chromium]//build/scripts/slave/performance_log_processor.py](https://cs.chromium.org/chromium/build/scripts/slave/performance_log_processor.py) + +### Dumping Trace Files + +The test harness supports a `--trace-file=path/to/trace.json` argument where Dawn trace events can be dumped. The traces can be viewed in Chrome's `about://tracing` viewer. + +### Test Runner + +[`//scripts/perf_test_runner.py`](https://cs.chromium.org/chromium/src/third_party/dawn/scripts/perf_test_runner.py) may be run to continuously run a test and report mean times and variances. + +Currently the script looks in the `out/Release` build directory and measures the `wall_time` metric (hardcoded into the script). These should eventually become arguments. + +Example usage: + +``` +scripts/perf_test_runner.py DrawCallPerf.Run/Vulkan__e_skip_validation +``` + +### Tests + +**BufferUploadPerf** + +Tests repetitively uploading data to the GPU using either `WriteBuffer` or `CreateBufferMapped`. + +**DrawCallPerf** + +DrawCallPerf tests drawing a simple triangle with many ways of encoding commands, +binding, and uploading data to the GPU. The rationale for this is the following: + - Static/Multiple/Dynamic vertex buffers: Tests switching buffer bindings. This has + a state tracking cost as well as a GPU driver cost. + - Static/Multiple/Dynamic bind groups: Same rationale as vertex buffers + - Static/Dynamic pipelines: In addition to a change to GPU state, changing the pipeline + layout incurs additional state tracking costs in Dawn. + - With/Without render bundles: All of the above can have lower validation costs if + precomputed in a render bundle. + - Static/Dynamic data: Updating data for each draw is a common use case. It also tests + the efficiency of resource transitions. diff --git a/third_party/dawn/examples/Animometer.cpp b/third_party/dawn/examples/Animometer.cpp index abec0a8429b..5c197c4cf55 100644 --- a/third_party/dawn/examples/Animometer.cpp +++ b/third_party/dawn/examples/Animometer.cpp @@ -15,19 +15,19 @@ #include "SampleUtils.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" #include "utils/SystemUtils.h" +#include "utils/WGPUHelpers.h" -#include #include +#include #include -dawn::Device device; -dawn::Queue queue; -dawn::SwapChain swapchain; -dawn::RenderPipeline pipeline; -dawn::BindGroup bindGroup; -dawn::Buffer ubo; +wgpu::Device device; +wgpu::Queue queue; +wgpu::SwapChain swapchain; +wgpu::RenderPipeline pipeline; +wgpu::BindGroup bindGroup; +wgpu::Buffer ubo; float RandomFloat(float min, float max) { float zeroOne = rand() / float(RAND_MAX); @@ -50,12 +50,13 @@ static std::vector shaderData; void init() { device = CreateCppDawnDevice(); - queue = device.CreateQueue(); + queue = device.GetDefaultQueue(); swapchain = GetSwapChain(device); - swapchain.Configure(GetPreferredSwapChainTextureFormat(), - dawn::TextureUsageBit::OutputAttachment, 640, 480); + swapchain.Configure(GetPreferredSwapChainTextureFormat(), wgpu::TextureUsage::OutputAttachment, + 640, 480); - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(std140, set = 0, binding = 0) uniform Constants { @@ -102,7 +103,8 @@ void init() { gl_Position = vec4(xpos, ypos, 0.0, 1.0); })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; layout(location = 0) in vec4 v_color; @@ -110,14 +112,14 @@ void init() { fragColor = v_color; })"); - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, {{0, dawn::ShaderStageBit::Vertex, dawn::BindingType::DynamicUniformBuffer}}); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer, true}}); utils::ComboRenderPipelineDescriptor descriptor(device); descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat(); + descriptor.cColorStates[0].format = GetPreferredSwapChainTextureFormat(); pipeline = device.CreateRenderPipeline(&descriptor); @@ -131,43 +133,42 @@ void init() { data.scalarOffset = RandomFloat(0.0f, 10.0f); } - dawn::BufferDescriptor bufferDesc; + wgpu::BufferDescriptor bufferDesc; bufferDesc.size = kNumTriangles * sizeof(ShaderData); - bufferDesc.usage = dawn::BufferUsageBit::TransferDst | dawn::BufferUsageBit::Uniform; + bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform; ubo = device.CreateBuffer(&bufferDesc); - bindGroup = - utils::MakeBindGroup(device, bgl, {{0, ubo, 0, kNumTriangles * sizeof(ShaderData)}}); + bindGroup = utils::MakeBindGroup(device, bgl, {{0, ubo, 0, sizeof(ShaderData)}}); } void frame() { - dawn::Texture backbuffer = swapchain.GetNextTexture(); + wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView(); static int f = 0; f++; for (auto& data : shaderData) { data.time = f / 60.0f; } - ubo.SetSubData(0, kNumTriangles * sizeof(ShaderData), shaderData.data()); + queue.WriteBuffer(ubo, 0, shaderData.data(), kNumTriangles * sizeof(ShaderData)); - utils::ComboRenderPassDescriptor renderPass({backbuffer.CreateDefaultView()}); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + utils::ComboRenderPassDescriptor renderPass({backbufferView}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline); for (size_t i = 0; i < kNumTriangles; i++) { - uint64_t offset = i * sizeof(ShaderData); + uint32_t offset = i * sizeof(ShaderData); pass.SetBindGroup(0, bindGroup, 1, &offset); - pass.Draw(3, 1, 0, 0); + pass.Draw(3); } pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - swapchain.Present(backbuffer); + swapchain.Present(); DoFlush(); fprintf(stderr, "frame %i\n", f); } diff --git a/third_party/dawn/examples/BUILD.gn b/third_party/dawn/examples/BUILD.gn new file mode 100644 index 00000000000..96ae72fbe9c --- /dev/null +++ b/third_party/dawn/examples/BUILD.gn @@ -0,0 +1,85 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../scripts/dawn_overrides_with_defaults.gni") + +group("dawn_samples") { + deps = [ + ":Animometer", + ":CHelloTriangle", + ":ComputeBoids", + ":CppHelloTriangle", + ":CubeReflection", + ":ManualSwapChainTest", + ] +} + +# Static library to contain code and dependencies common to all samples +static_library("dawn_sample_utils") { + sources = [ + "SampleUtils.cpp", + "SampleUtils.h", + ] + + # Export all of these as public deps so that `gn check` allows includes + public_deps = [ + "${dawn_root}/src/common", + "${dawn_root}/src/dawn:dawn_proc", + "${dawn_root}/src/dawn:dawncpp", + "${dawn_root}/src/dawn_native", + "${dawn_root}/src/dawn_wire", + "${dawn_root}/src/utils:dawn_bindings", + "${dawn_root}/src/utils:dawn_glfw", + "${dawn_root}/src/utils:dawn_utils", + ] + public_configs = [ "${dawn_root}/src/common:dawn_internal" ] +} + +# Template for samples to avoid listing dawn_sample_utils as a dep every time +template("dawn_sample") { + executable(target_name) { + deps = [ ":dawn_sample_utils" ] + forward_variables_from(invoker, "*", [ "deps" ]) + + if (defined(invoker.deps)) { + deps += invoker.deps + } + } +} + +dawn_sample("CppHelloTriangle") { + sources = [ "CppHelloTriangle.cpp" ] +} + +dawn_sample("CHelloTriangle") { + sources = [ "CHelloTriangle.cpp" ] +} + +dawn_sample("ComputeBoids") { + sources = [ "ComputeBoids.cpp" ] + deps = [ "${dawn_root}/third_party/gn/glm" ] +} + +dawn_sample("Animometer") { + sources = [ "Animometer.cpp" ] +} + +dawn_sample("CubeReflection") { + sources = [ "CubeReflection.cpp" ] + deps = [ "${dawn_root}/third_party/gn/glm" ] +} + +dawn_sample("ManualSwapChainTest") { + sources = [ "ManualSwapChainTest.cpp" ] +} diff --git a/third_party/dawn/examples/CHelloTriangle.cpp b/third_party/dawn/examples/CHelloTriangle.cpp index 8628711f902..22d820295c4 100644 --- a/third_party/dawn/examples/CHelloTriangle.cpp +++ b/third_party/dawn/examples/CHelloTriangle.cpp @@ -14,29 +14,27 @@ #include "SampleUtils.h" -#include "utils/DawnHelpers.h" #include "utils/SystemUtils.h" +#include "utils/WGPUHelpers.h" -DawnDevice device; -DawnQueue queue; -DawnSwapChain swapchain; -DawnRenderPipeline pipeline; +WGPUDevice device; +WGPUQueue queue; +WGPUSwapChain swapchain; +WGPURenderPipeline pipeline; -DawnTextureFormat swapChainFormat; +WGPUTextureFormat swapChainFormat; void init() { device = CreateCppDawnDevice().Release(); - queue = dawnDeviceCreateQueue(device); + queue = wgpuDeviceGetDefaultQueue(device); { - DawnSwapChainDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUSwapChainDescriptor descriptor = {}; descriptor.implementation = GetSwapChainImplementation(); - swapchain = dawnDeviceCreateSwapChain(device, &descriptor); + swapchain = wgpuDeviceCreateSwapChain(device, nullptr, &descriptor); } - swapChainFormat = static_cast(GetPreferredSwapChainTextureFormat()); - dawnSwapChainConfigure(swapchain, swapChainFormat, DAWN_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT, 640, - 480); + swapChainFormat = static_cast(GetPreferredSwapChainTextureFormat()); + wgpuSwapChainConfigure(swapchain, swapChainFormat, WGPUTextureUsage_OutputAttachment, 640, 480); const char* vs = "#version 450\n" @@ -44,119 +42,109 @@ void init() { "void main() {\n" " gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);\n" "}\n"; - DawnShaderModule vsModule = utils::CreateShaderModule(dawn::Device(device), dawn::ShaderStage::Vertex, vs).Release(); + WGPUShaderModule vsModule = + utils::CreateShaderModule(wgpu::Device(device), utils::SingleShaderStage::Vertex, vs) + .Release(); const char* fs = "#version 450\n" - "layout(location = 0) out vec4 fragColor;" + "layout(location = 0) out vec4 fragColor;\n" "void main() {\n" " fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" "}\n"; - DawnShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, fs).Release(); + WGPUShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fs).Release(); { - DawnRenderPipelineDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPURenderPipelineDescriptor descriptor = {}; - DawnPipelineStageDescriptor vertexStage; - vertexStage.nextInChain = nullptr; - vertexStage.module = vsModule; - vertexStage.entryPoint = "main"; - descriptor.vertexStage = &vertexStage; + descriptor.vertexStage.module = vsModule; + descriptor.vertexStage.entryPoint = "main"; - DawnPipelineStageDescriptor fragmentStage; - fragmentStage.nextInChain = nullptr; + WGPUProgrammableStageDescriptor fragmentStage = {}; fragmentStage.module = fsModule; fragmentStage.entryPoint = "main"; descriptor.fragmentStage = &fragmentStage; descriptor.sampleCount = 1; - DawnBlendDescriptor blendDescriptor; - blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD; - blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE; - blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE; - DawnColorStateDescriptor colorStateDescriptor; - colorStateDescriptor.nextInChain = nullptr; + WGPUBlendDescriptor blendDescriptor = {}; + blendDescriptor.operation = WGPUBlendOperation_Add; + blendDescriptor.srcFactor = WGPUBlendFactor_One; + blendDescriptor.dstFactor = WGPUBlendFactor_One; + WGPUColorStateDescriptor colorStateDescriptor = {}; colorStateDescriptor.format = swapChainFormat; colorStateDescriptor.alphaBlend = blendDescriptor; colorStateDescriptor.colorBlend = blendDescriptor; - colorStateDescriptor.writeMask = DAWN_COLOR_WRITE_MASK_ALL; + colorStateDescriptor.writeMask = WGPUColorWriteMask_All; descriptor.colorStateCount = 1; - DawnColorStateDescriptor* colorStatesPtr[] = {&colorStateDescriptor}; - descriptor.colorStates = colorStatesPtr; + descriptor.colorStates = &colorStateDescriptor; - DawnPipelineLayoutDescriptor pl; - pl.nextInChain = nullptr; + WGPUPipelineLayoutDescriptor pl = {}; pl.bindGroupLayoutCount = 0; pl.bindGroupLayouts = nullptr; - descriptor.layout = dawnDeviceCreatePipelineLayout(device, &pl); - - DawnVertexInputDescriptor vertexInput; - vertexInput.nextInChain = nullptr; - vertexInput.indexFormat = DAWN_INDEX_FORMAT_UINT32; - vertexInput.bufferCount = 0; - vertexInput.buffers = nullptr; - descriptor.vertexInput = &vertexInput; - - DawnRasterizationStateDescriptor rasterizationState; - rasterizationState.nextInChain = nullptr; - rasterizationState.frontFace = DAWN_FRONT_FACE_CCW; - rasterizationState.cullMode = DAWN_CULL_MODE_NONE; + descriptor.layout = wgpuDeviceCreatePipelineLayout(device, &pl); + + WGPUVertexStateDescriptor vertexState = {}; + vertexState.indexFormat = WGPUIndexFormat_Uint32; + vertexState.vertexBufferCount = 0; + vertexState.vertexBuffers = nullptr; + descriptor.vertexState = &vertexState; + + WGPURasterizationStateDescriptor rasterizationState = {}; + rasterizationState.frontFace = WGPUFrontFace_CCW; + rasterizationState.cullMode = WGPUCullMode_None; rasterizationState.depthBias = 0; rasterizationState.depthBiasSlopeScale = 0.0; rasterizationState.depthBiasClamp = 0.0; descriptor.rasterizationState = &rasterizationState; - descriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + descriptor.primitiveTopology = WGPUPrimitiveTopology_TriangleList; + descriptor.sampleMask = 0xFFFFFFFF; + descriptor.alphaToCoverageEnabled = false; descriptor.depthStencilState = nullptr; - pipeline = dawnDeviceCreateRenderPipeline(device, &descriptor); + pipeline = wgpuDeviceCreateRenderPipeline(device, &descriptor); } - dawnShaderModuleRelease(vsModule); - dawnShaderModuleRelease(fsModule); + wgpuShaderModuleRelease(vsModule); + wgpuShaderModuleRelease(fsModule); } void frame() { - DawnTexture backbuffer = dawnSwapChainGetNextTexture(swapchain); - DawnTextureView backbufferView; - { - backbufferView = dawnTextureCreateDefaultView(backbuffer); - } - DawnRenderPassDescriptor renderpassInfo; - DawnRenderPassColorAttachmentDescriptor colorAttachment; - DawnRenderPassColorAttachmentDescriptor* colorAttachments = {&colorAttachment}; + WGPUTextureView backbufferView = wgpuSwapChainGetCurrentTextureView(swapchain); + WGPURenderPassDescriptor renderpassInfo = {}; + WGPURenderPassColorAttachmentDescriptor colorAttachment = {}; { colorAttachment.attachment = backbufferView; colorAttachment.resolveTarget = nullptr; - colorAttachment.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; - colorAttachment.loadOp = DAWN_LOAD_OP_CLEAR; - colorAttachment.storeOp = DAWN_STORE_OP_STORE; + colorAttachment.clearColor = {0.0f, 0.0f, 0.0f, 0.0f}; + colorAttachment.loadOp = WGPULoadOp_Clear; + colorAttachment.storeOp = WGPUStoreOp_Store; renderpassInfo.colorAttachmentCount = 1; - renderpassInfo.colorAttachments = &colorAttachments; + renderpassInfo.colorAttachments = &colorAttachment; renderpassInfo.depthStencilAttachment = nullptr; } - DawnCommandBuffer commands; + WGPUCommandBuffer commands; { - DawnCommandEncoder encoder = dawnDeviceCreateCommandEncoder(device); + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr); - DawnRenderPassEncoder pass = dawnCommandEncoderBeginRenderPass(encoder, &renderpassInfo); - dawnRenderPassEncoderSetPipeline(pass, pipeline); - dawnRenderPassEncoderDraw(pass, 3, 1, 0, 0); - dawnRenderPassEncoderEndPass(pass); - dawnRenderPassEncoderRelease(pass); + WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &renderpassInfo); + wgpuRenderPassEncoderSetPipeline(pass, pipeline); + wgpuRenderPassEncoderDraw(pass, 3, 1, 0, 0); + wgpuRenderPassEncoderEndPass(pass); + wgpuRenderPassEncoderRelease(pass); - commands = dawnCommandEncoderFinish(encoder); - dawnCommandEncoderRelease(encoder); + commands = wgpuCommandEncoderFinish(encoder, nullptr); + wgpuCommandEncoderRelease(encoder); } - dawnQueueSubmit(queue, 1, &commands); - dawnCommandBufferRelease(commands); - dawnSwapChainPresent(swapchain, backbuffer); - dawnTextureViewRelease(backbufferView); + wgpuQueueSubmit(queue, 1, &commands); + wgpuCommandBufferRelease(commands); + wgpuSwapChainPresent(swapchain); + wgpuTextureViewRelease(backbufferView); DoFlush(); } diff --git a/third_party/dawn/examples/CMakeLists.txt b/third_party/dawn/examples/CMakeLists.txt new file mode 100644 index 00000000000..896b87ed03d --- /dev/null +++ b/third_party/dawn/examples/CMakeLists.txt @@ -0,0 +1,44 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_library(dawn_sample_utils STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawn_sample_utils PRIVATE + "SampleUtils.cpp" + "SampleUtils.h" +) +target_link_libraries(dawn_sample_utils PUBLIC + dawn_internal_config + dawncpp + dawn_proc + dawn_common + dawn_native + dawn_wire + dawn_utils + glfw +) + +add_executable(CppHelloTriangle "CppHelloTriangle.cpp") +target_link_libraries(CppHelloTriangle dawn_sample_utils) + +add_executable(CHelloTriangle "CHelloTriangle.cpp") +target_link_libraries(CHelloTriangle dawn_sample_utils) + +add_executable(ComputeBoids "ComputeBoids.cpp") +target_link_libraries(ComputeBoids dawn_sample_utils glm) + +add_executable(Animometer "Animometer.cpp") +target_link_libraries(Animometer dawn_sample_utils) + +add_executable(CubeReflection "CubeReflection.cpp") +target_link_libraries(CubeReflection dawn_sample_utils glm) diff --git a/third_party/dawn/examples/ComputeBoids.cpp b/third_party/dawn/examples/ComputeBoids.cpp index c345b614676..f958ab4002d 100644 --- a/third_party/dawn/examples/ComputeBoids.cpp +++ b/third_party/dawn/examples/ComputeBoids.cpp @@ -15,8 +15,8 @@ #include "SampleUtils.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" #include "utils/SystemUtils.h" +#include "utils/WGPUHelpers.h" #include #include @@ -24,19 +24,19 @@ #include -dawn::Device device; -dawn::Queue queue; -dawn::SwapChain swapchain; -dawn::TextureView depthStencilView; +wgpu::Device device; +wgpu::Queue queue; +wgpu::SwapChain swapchain; +wgpu::TextureView depthStencilView; -dawn::Buffer modelBuffer; -std::array particleBuffers; +wgpu::Buffer modelBuffer; +std::array particleBuffers; -dawn::RenderPipeline renderPipeline; +wgpu::RenderPipeline renderPipeline; -dawn::Buffer updateParams; -dawn::ComputePipeline updatePipeline; -std::array updateBGs; +wgpu::Buffer updateParams; +wgpu::ComputePipeline updatePipeline; +std::array updateBGs; size_t pingpong = 0; @@ -64,36 +64,39 @@ void initBuffers() { {0.01, -0.02}, {0.00, 0.02}, }; - modelBuffer = utils::CreateBufferFromData(device, model, sizeof(model), dawn::BufferUsageBit::Vertex); + modelBuffer = + utils::CreateBufferFromData(device, model, sizeof(model), wgpu::BufferUsage::Vertex); - SimParams params = { 0.04f, 0.1f, 0.025f, 0.025f, 0.02f, 0.05f, 0.005f, kNumParticles }; - updateParams = utils::CreateBufferFromData(device, ¶ms, sizeof(params), dawn::BufferUsageBit::Uniform); + SimParams params = {0.04f, 0.1f, 0.025f, 0.025f, 0.02f, 0.05f, 0.005f, kNumParticles}; + updateParams = + utils::CreateBufferFromData(device, ¶ms, sizeof(params), wgpu::BufferUsage::Uniform); std::vector initialParticles(kNumParticles); { std::mt19937 generator; std::uniform_real_distribution dist(-1.0f, 1.0f); - for (auto& p : initialParticles) - { + for (auto& p : initialParticles) { p.pos = glm::vec2(dist(generator), dist(generator)); p.vel = glm::vec2(dist(generator), dist(generator)) * 0.1f; } } for (size_t i = 0; i < 2; i++) { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = sizeof(Particle) * kNumParticles; - descriptor.usage = dawn::BufferUsageBit::TransferDst | dawn::BufferUsageBit::Vertex | dawn::BufferUsageBit::Storage; + descriptor.usage = + wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Storage; particleBuffers[i] = device.CreateBuffer(&descriptor); - particleBuffers[i].SetSubData(0, - sizeof(Particle) * kNumParticles, - reinterpret_cast(initialParticles.data())); + queue.WriteBuffer(particleBuffers[i], 0, + reinterpret_cast(initialParticles.data()), + sizeof(Particle) * kNumParticles); } } void initRender() { - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(location = 0) in vec2 a_particlePos; layout(location = 1) in vec2 a_particleVel; @@ -106,7 +109,8 @@ void initRender() { } )"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -117,32 +121,33 @@ void initRender() { depthStencilView = CreateDefaultDepthStencilView(device); utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.cVertexInput.bufferCount = 2; - descriptor.cVertexInput.cBuffers[0].stride = sizeof(Particle); - descriptor.cVertexInput.cBuffers[0].stepMode = dawn::InputStepMode::Instance; - descriptor.cVertexInput.cBuffers[0].attributeCount = 2; - descriptor.cVertexInput.cAttributes[0].offset = offsetof(Particle, pos); - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float2; - descriptor.cVertexInput.cAttributes[1].shaderLocation = 1; - descriptor.cVertexInput.cAttributes[1].offset = offsetof(Particle, vel); - descriptor.cVertexInput.cAttributes[1].format = dawn::VertexFormat::Float2; - descriptor.cVertexInput.cBuffers[1].stride = sizeof(glm::vec2); - descriptor.cVertexInput.cBuffers[1].attributeCount = 1; - descriptor.cVertexInput.cBuffers[1].attributes = &descriptor.cVertexInput.cAttributes[2]; - descriptor.cVertexInput.cAttributes[2].shaderLocation = 2; - descriptor.cVertexInput.cAttributes[2].format = dawn::VertexFormat::Float2; + descriptor.cVertexState.vertexBufferCount = 2; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = sizeof(Particle); + descriptor.cVertexState.cVertexBuffers[0].stepMode = wgpu::InputStepMode::Instance; + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 2; + descriptor.cVertexState.cAttributes[0].offset = offsetof(Particle, pos); + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float2; + descriptor.cVertexState.cAttributes[1].shaderLocation = 1; + descriptor.cVertexState.cAttributes[1].offset = offsetof(Particle, vel); + descriptor.cVertexState.cAttributes[1].format = wgpu::VertexFormat::Float2; + descriptor.cVertexState.cVertexBuffers[1].arrayStride = sizeof(glm::vec2); + descriptor.cVertexState.cVertexBuffers[1].attributeCount = 1; + descriptor.cVertexState.cVertexBuffers[1].attributes = &descriptor.cVertexState.cAttributes[2]; + descriptor.cVertexState.cAttributes[2].shaderLocation = 2; + descriptor.cVertexState.cAttributes[2].format = wgpu::VertexFormat::Float2; descriptor.depthStencilState = &descriptor.cDepthStencilState; - descriptor.cDepthStencilState.format = dawn::TextureFormat::D32FloatS8Uint; - descriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat(); + descriptor.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; + descriptor.cColorStates[0].format = GetPreferredSwapChainTextureFormat(); renderPipeline = device.CreateRenderPipeline(&descriptor); } void initSim() { - dawn::ShaderModule module = utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"( + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( #version 450 struct Particle { @@ -233,53 +238,49 @@ void initSim() { auto bgl = utils::MakeBindGroupLayout( device, { - {0, dawn::ShaderStageBit::Compute, dawn::BindingType::UniformBuffer}, - {1, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer}, - {2, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer}, + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}, + {2, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}, }); - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); - dawn::ComputePipelineDescriptor csDesc; + wgpu::ComputePipelineDescriptor csDesc; csDesc.layout = pl; - - dawn::PipelineStageDescriptor computeStage; - computeStage.module = module; - computeStage.entryPoint = "main"; - csDesc.computeStage = &computeStage; - + csDesc.computeStage.module = module; + csDesc.computeStage.entryPoint = "main"; updatePipeline = device.CreateComputePipeline(&csDesc); for (uint32_t i = 0; i < 2; ++i) { - updateBGs[i] = utils::MakeBindGroup(device, bgl, { - {0, updateParams, 0, sizeof(SimParams)}, - {1, particleBuffers[i], 0, kNumParticles * sizeof(Particle)}, - {2, particleBuffers[(i + 1) % 2], 0, kNumParticles * sizeof(Particle)}, - }); + updateBGs[i] = utils::MakeBindGroup( + device, bgl, + { + {0, updateParams, 0, sizeof(SimParams)}, + {1, particleBuffers[i], 0, kNumParticles * sizeof(Particle)}, + {2, particleBuffers[(i + 1) % 2], 0, kNumParticles * sizeof(Particle)}, + }); } } -dawn::CommandBuffer createCommandBuffer(const dawn::Texture backbuffer, size_t i) { - static const uint64_t zeroOffsets[1] = {0}; +wgpu::CommandBuffer createCommandBuffer(const wgpu::TextureView backbufferView, size_t i) { auto& bufferDst = particleBuffers[(i + 1) % 2]; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(updatePipeline); - pass.SetBindGroup(0, updateBGs[i], 0, nullptr); - pass.Dispatch(kNumParticles, 1, 1); + pass.SetBindGroup(0, updateBGs[i]); + pass.Dispatch(kNumParticles); pass.EndPass(); } { - utils::ComboRenderPassDescriptor renderPass({backbuffer.CreateDefaultView()}, - depthStencilView); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + utils::ComboRenderPassDescriptor renderPass({backbufferView}, depthStencilView); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(renderPipeline); - pass.SetVertexBuffers(0, 1, &bufferDst, zeroOffsets); - pass.SetVertexBuffers(1, 1, &modelBuffer, zeroOffsets); - pass.Draw(3, kNumParticles, 0, 0); + pass.SetVertexBuffer(0, bufferDst); + pass.SetVertexBuffer(1, modelBuffer); + pass.Draw(3, kNumParticles); pass.EndPass(); } @@ -289,10 +290,10 @@ dawn::CommandBuffer createCommandBuffer(const dawn::Texture backbuffer, size_t i void init() { device = CreateCppDawnDevice(); - queue = device.CreateQueue(); + queue = device.GetDefaultQueue(); swapchain = GetSwapChain(device); - swapchain.Configure(GetPreferredSwapChainTextureFormat(), - dawn::TextureUsageBit::OutputAttachment, 640, 480); + swapchain.Configure(GetPreferredSwapChainTextureFormat(), wgpu::TextureUsage::OutputAttachment, + 640, 480); initBuffers(); initRender(); @@ -300,11 +301,11 @@ void init() { } void frame() { - dawn::Texture backbuffer = swapchain.GetNextTexture(); + wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView(); - dawn::CommandBuffer commandBuffer = createCommandBuffer(backbuffer, pingpong); + wgpu::CommandBuffer commandBuffer = createCommandBuffer(backbufferView, pingpong); queue.Submit(1, &commandBuffer); - swapchain.Present(backbuffer); + swapchain.Present(); DoFlush(); pingpong = (pingpong + 1) % 2; diff --git a/third_party/dawn/examples/CppHelloTriangle.cpp b/third_party/dawn/examples/CppHelloTriangle.cpp index f69e813f1c4..378afa8db5e 100644 --- a/third_party/dawn/examples/CppHelloTriangle.cpp +++ b/third_party/dawn/examples/CppHelloTriangle.cpp @@ -15,53 +15,54 @@ #include "SampleUtils.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" #include "utils/SystemUtils.h" +#include "utils/WGPUHelpers.h" #include -dawn::Device device; +wgpu::Device device; -dawn::Buffer indexBuffer; -dawn::Buffer vertexBuffer; +wgpu::Buffer indexBuffer; +wgpu::Buffer vertexBuffer; -dawn::Texture texture; -dawn::Sampler sampler; +wgpu::Texture texture; +wgpu::Sampler sampler; -dawn::Queue queue; -dawn::SwapChain swapchain; -dawn::TextureView depthStencilView; -dawn::RenderPipeline pipeline; -dawn::BindGroup bindGroup; +wgpu::Queue queue; +wgpu::SwapChain swapchain; +wgpu::TextureView depthStencilView; +wgpu::RenderPipeline pipeline; +wgpu::BindGroup bindGroup; void initBuffers() { static const uint32_t indexData[3] = { - 0, 1, 2, + 0, + 1, + 2, }; - indexBuffer = utils::CreateBufferFromData(device, indexData, sizeof(indexData), dawn::BufferUsageBit::Index); + indexBuffer = + utils::CreateBufferFromData(device, indexData, sizeof(indexData), wgpu::BufferUsage::Index); static const float vertexData[12] = { - 0.0f, 0.5f, 0.0f, 1.0f, - -0.5f, -0.5f, 0.0f, 1.0f, - 0.5f, -0.5f, 0.0f, 1.0f, + 0.0f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, 0.0f, 1.0f, }; - vertexBuffer = utils::CreateBufferFromData(device, vertexData, sizeof(vertexData), dawn::BufferUsageBit::Vertex); + vertexBuffer = utils::CreateBufferFromData(device, vertexData, sizeof(vertexData), + wgpu::BufferUsage::Vertex); } void initTextures() { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = 1024; descriptor.size.height = 1024; descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; texture = device.CreateTexture(&descriptor); - dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); sampler = device.CreateSampler(&samplerDesc); // Initialize the texture with arbitrary data until we can load images @@ -70,38 +71,41 @@ void initTextures() { data[i] = static_cast(i % 253); } - dawn::Buffer stagingBuffer = utils::CreateBufferFromData(device, data.data(), static_cast(data.size()), dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); - dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); - dawn::Extent3D copySize = {1024, 1024, 1}; + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, 4 * 1024, 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {1024, 1024, 1}; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - dawn::CommandBuffer copy = encoder.Finish(); + wgpu::CommandBuffer copy = encoder.Finish(); queue.Submit(1, ©); } void init() { device = CreateCppDawnDevice(); - queue = device.CreateQueue(); + queue = device.GetDefaultQueue(); swapchain = GetSwapChain(device); - swapchain.Configure(GetPreferredSwapChainTextureFormat(), - dawn::TextureUsageBit::OutputAttachment, 640, 480); + swapchain.Configure(GetPreferredSwapChainTextureFormat(), wgpu::TextureUsage::OutputAttachment, + 640, 480); initBuffers(); initTextures(); - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(location = 0) in vec4 pos; void main() { gl_Position = pos; - })" - ); + })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform sampler mySampler; layout(set = 0, binding = 1) uniform texture2D myTexture; @@ -113,61 +117,61 @@ void init() { auto bgl = utils::MakeBindGroupLayout( device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}, - {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}, + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}, + {1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}, }); - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); depthStencilView = CreateDefaultDepthStencilView(device); utils::ComboRenderPipelineDescriptor descriptor(device); descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; descriptor.depthStencilState = &descriptor.cDepthStencilState; - descriptor.cDepthStencilState.format = dawn::TextureFormat::D32FloatS8Uint; - descriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat(); + descriptor.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; + descriptor.cColorStates[0].format = GetPreferredSwapChainTextureFormat(); pipeline = device.CreateRenderPipeline(&descriptor); - dawn::TextureView view = texture.CreateDefaultView(); + wgpu::TextureView view = texture.CreateView(); - bindGroup = utils::MakeBindGroup(device, bgl, { - {0, sampler}, - {1, view} - }); + bindGroup = utils::MakeBindGroup(device, bgl, {{0, sampler}, {1, view}}); } -struct {uint32_t a; float b;} s; +struct { + uint32_t a; + float b; +} s; void frame() { s.a = (s.a + 1) % 256; s.b += 0.02f; - if (s.b >= 1.0f) {s.b = 0.0f;} + if (s.b >= 1.0f) { + s.b = 0.0f; + } - dawn::Texture backbuffer = swapchain.GetNextTexture(); - utils::ComboRenderPassDescriptor renderPass({backbuffer.CreateDefaultView()}, - depthStencilView); + wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView(); + utils::ComboRenderPassDescriptor renderPass({backbufferView}, depthStencilView); - static const uint64_t vertexBufferOffsets[1] = {0}; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets); - pass.SetIndexBuffer(indexBuffer, 0); - pass.DrawIndexed(3, 1, 0, 0, 0); + pass.SetBindGroup(0, bindGroup); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer); + pass.DrawIndexed(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - swapchain.Present(backbuffer); + swapchain.Present(); DoFlush(); } diff --git a/third_party/dawn/examples/CubeReflection.cpp b/third_party/dawn/examples/CubeReflection.cpp index b3bc51476e8..4ff18e003c3 100644 --- a/third_party/dawn/examples/CubeReflection.cpp +++ b/third_party/dawn/examples/CubeReflection.cpp @@ -15,95 +15,75 @@ #include "SampleUtils.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" #include "utils/SystemUtils.h" +#include "utils/WGPUHelpers.h" -#include #include #include #include +#include -dawn::Device device; +wgpu::Device device; -dawn::Buffer indexBuffer; -dawn::Buffer vertexBuffer; -dawn::Buffer planeBuffer; -dawn::Buffer cameraBuffer; -dawn::Buffer transformBuffer[2]; +wgpu::Buffer indexBuffer; +wgpu::Buffer vertexBuffer; +wgpu::Buffer planeBuffer; +wgpu::Buffer cameraBuffer; +wgpu::Buffer transformBuffer[2]; -dawn::BindGroup cameraBindGroup; -dawn::BindGroup bindGroup[2]; -dawn::BindGroup cubeTransformBindGroup[2]; +wgpu::BindGroup cameraBindGroup; +wgpu::BindGroup bindGroup[2]; +wgpu::BindGroup cubeTransformBindGroup[2]; -dawn::Queue queue; -dawn::SwapChain swapchain; -dawn::TextureView depthStencilView; -dawn::RenderPipeline pipeline; -dawn::RenderPipeline planePipeline; -dawn::RenderPipeline reflectionPipeline; +wgpu::Queue queue; +wgpu::SwapChain swapchain; +wgpu::TextureView depthStencilView; +wgpu::RenderPipeline pipeline; +wgpu::RenderPipeline planePipeline; +wgpu::RenderPipeline reflectionPipeline; void initBuffers() { - static const uint32_t indexData[6*6] = { - 0, 1, 2, - 0, 2, 3, + static const uint32_t indexData[6 * 6] = {0, 1, 2, 0, 2, 3, - 4, 5, 6, - 4, 6, 7, + 4, 5, 6, 4, 6, 7, - 8, 9, 10, - 8, 10, 11, + 8, 9, 10, 8, 10, 11, - 12, 13, 14, - 12, 14, 15, + 12, 13, 14, 12, 14, 15, - 16, 17, 18, - 16, 18, 19, + 16, 17, 18, 16, 18, 19, - 20, 21, 22, - 20, 22, 23 - }; - indexBuffer = utils::CreateBufferFromData(device, indexData, sizeof(indexData), dawn::BufferUsageBit::Index); + 20, 21, 22, 20, 22, 23}; + indexBuffer = + utils::CreateBufferFromData(device, indexData, sizeof(indexData), wgpu::BufferUsage::Index); static const float vertexData[6 * 4 * 6] = { - -1.0, -1.0, 1.0, 1.0, 0.0, 0.0, - 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, - 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, - -1.0, 1.0, 1.0, 1.0, 0.0, 0.0, - - -1.0, -1.0, -1.0, 1.0, 1.0, 0.0, - -1.0, 1.0, -1.0, 1.0, 1.0, 0.0, - 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, - 1.0, -1.0, -1.0, 1.0, 1.0, 0.0, - - -1.0, 1.0, -1.0, 1.0, 0.0, 1.0, - -1.0, 1.0, 1.0, 1.0, 0.0, 1.0, - 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, - 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, - - -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, - 1.0, -1.0, -1.0, 0.0, 1.0, 0.0, - 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, - -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, - - 1.0, -1.0, -1.0, 0.0, 1.0, 1.0, - 1.0, 1.0, -1.0, 0.0, 1.0, 1.0, - 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, - 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, - - -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, - -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, - -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, - -1.0, 1.0, -1.0, 1.0, 1.0, 1.0 - }; - vertexBuffer = utils::CreateBufferFromData(device, vertexData, sizeof(vertexData), dawn::BufferUsageBit::Vertex); + -1.0, -1.0, 1.0, 1.0, 0.0, 0.0, 1.0, -1.0, 1.0, 1.0, 0.0, 0.0, + 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, -1.0, 1.0, 1.0, 1.0, 0.0, 0.0, + + -1.0, -1.0, -1.0, 1.0, 1.0, 0.0, -1.0, 1.0, -1.0, 1.0, 1.0, 0.0, + 1.0, 1.0, -1.0, 1.0, 1.0, 0.0, 1.0, -1.0, -1.0, 1.0, 1.0, 0.0, + + -1.0, 1.0, -1.0, 1.0, 0.0, 1.0, -1.0, 1.0, 1.0, 1.0, 0.0, 1.0, + 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, + + -1.0, -1.0, -1.0, 0.0, 1.0, 0.0, 1.0, -1.0, -1.0, 0.0, 1.0, 0.0, + 1.0, -1.0, 1.0, 0.0, 1.0, 0.0, -1.0, -1.0, 1.0, 0.0, 1.0, 0.0, + + 1.0, -1.0, -1.0, 0.0, 1.0, 1.0, 1.0, 1.0, -1.0, 0.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, -1.0, 1.0, 0.0, 1.0, 1.0, + + -1.0, -1.0, -1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, + -1.0, 1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, -1.0, 1.0, 1.0, 1.0}; + vertexBuffer = utils::CreateBufferFromData(device, vertexData, sizeof(vertexData), + wgpu::BufferUsage::Vertex); static const float planeData[6 * 4] = { - -2.0, -1.0, -2.0, 0.5, 0.5, 0.5, - 2.0, -1.0, -2.0, 0.5, 0.5, 0.5, - 2.0, -1.0, 2.0, 0.5, 0.5, 0.5, - -2.0, -1.0, 2.0, 0.5, 0.5, 0.5, + -2.0, -1.0, -2.0, 0.5, 0.5, 0.5, 2.0, -1.0, -2.0, 0.5, 0.5, 0.5, + 2.0, -1.0, 2.0, 0.5, 0.5, 0.5, -2.0, -1.0, 2.0, 0.5, 0.5, 0.5, }; - planeBuffer = utils::CreateBufferFromData(device, planeData, sizeof(planeData), dawn::BufferUsageBit::Vertex); + planeBuffer = utils::CreateBufferFromData(device, planeData, sizeof(planeData), + wgpu::BufferUsage::Vertex); } struct CameraData { @@ -114,14 +94,15 @@ struct CameraData { void init() { device = CreateCppDawnDevice(); - queue = device.CreateQueue(); + queue = device.GetDefaultQueue(); swapchain = GetSwapChain(device); - swapchain.Configure(GetPreferredSwapChainTextureFormat(), - dawn::TextureUsageBit::OutputAttachment, 640, 480); + swapchain.Configure(GetPreferredSwapChainTextureFormat(), wgpu::TextureUsage::OutputAttachment, + 640, 480); initBuffers(); - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(set = 0, binding = 0) uniform cameraData { mat4 view; @@ -136,10 +117,10 @@ void init() { void main() { f_col = col; gl_Position = camera.proj * camera.view * modelMatrix * vec4(pos, 1.0); - })" - ); + })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 2) in vec3 f_col; layout(location = 0) out vec4 fragColor; @@ -147,8 +128,8 @@ void init() { fragColor = vec4(f_col, 1.0); })"); - dawn::ShaderModule fsReflectionModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsReflectionModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 2) in vec3 f_col; layout(location = 0) out vec4 fragColor; @@ -156,139 +137,140 @@ void init() { fragColor = vec4(mix(f_col, vec3(0.5, 0.5, 0.5), 0.5), 1.0); })"); - utils::ComboVertexInputDescriptor vertexInput; - vertexInput.cBuffers[0].attributeCount = 2; - vertexInput.cAttributes[0].format = dawn::VertexFormat::Float3; - vertexInput.cAttributes[1].shaderLocation = 1; - vertexInput.cAttributes[1].offset = 3 * sizeof(float); - vertexInput.cAttributes[1].format = dawn::VertexFormat::Float3; + utils::ComboVertexStateDescriptor vertexState; + vertexState.cVertexBuffers[0].attributeCount = 2; + vertexState.cAttributes[0].format = wgpu::VertexFormat::Float3; + vertexState.cAttributes[1].shaderLocation = 1; + vertexState.cAttributes[1].offset = 3 * sizeof(float); + vertexState.cAttributes[1].format = wgpu::VertexFormat::Float3; - vertexInput.bufferCount = 1; - vertexInput.cBuffers[0].stride = 6 * sizeof(float); + vertexState.vertexBufferCount = 1; + vertexState.cVertexBuffers[0].arrayStride = 6 * sizeof(float); auto bgl = utils::MakeBindGroupLayout( device, { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - {1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + {1, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, }); - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); - dawn::BufferDescriptor cameraBufDesc; + wgpu::BufferDescriptor cameraBufDesc; cameraBufDesc.size = sizeof(CameraData); - cameraBufDesc.usage = dawn::BufferUsageBit::TransferDst | dawn::BufferUsageBit::Uniform; + cameraBufDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform; cameraBuffer = device.CreateBuffer(&cameraBufDesc); glm::mat4 transform(1.0); - transformBuffer[0] = utils::CreateBufferFromData(device, &transform, sizeof(glm::mat4), dawn::BufferUsageBit::Uniform); + transformBuffer[0] = utils::CreateBufferFromData(device, &transform, sizeof(glm::mat4), + wgpu::BufferUsage::Uniform); transform = glm::translate(transform, glm::vec3(0.f, -2.f, 0.f)); - transformBuffer[1] = utils::CreateBufferFromData(device, &transform, sizeof(glm::mat4), dawn::BufferUsageBit::Uniform); + transformBuffer[1] = utils::CreateBufferFromData(device, &transform, sizeof(glm::mat4), + wgpu::BufferUsage::Uniform); - bindGroup[0] = utils::MakeBindGroup(device, bgl, { - {0, cameraBuffer, 0, sizeof(CameraData)}, - {1, transformBuffer[0], 0, sizeof(glm::mat4)} - }); + bindGroup[0] = utils::MakeBindGroup( + device, bgl, + {{0, cameraBuffer, 0, sizeof(CameraData)}, {1, transformBuffer[0], 0, sizeof(glm::mat4)}}); - bindGroup[1] = utils::MakeBindGroup(device, bgl, { - {0, cameraBuffer, 0, sizeof(CameraData)}, - {1, transformBuffer[1], 0, sizeof(glm::mat4)} - }); + bindGroup[1] = utils::MakeBindGroup( + device, bgl, + {{0, cameraBuffer, 0, sizeof(CameraData)}, {1, transformBuffer[1], 0, sizeof(glm::mat4)}}); depthStencilView = CreateDefaultDepthStencilView(device); utils::ComboRenderPipelineDescriptor descriptor(device); descriptor.layout = pl; - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.vertexInput = &vertexInput; + descriptor.vertexState = &vertexState; descriptor.depthStencilState = &descriptor.cDepthStencilState; - descriptor.cDepthStencilState.format = dawn::TextureFormat::D32FloatS8Uint; - descriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat(); + descriptor.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; + descriptor.cColorStates[0].format = GetPreferredSwapChainTextureFormat(); descriptor.cDepthStencilState.depthWriteEnabled = true; - descriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::Less; + descriptor.cDepthStencilState.depthCompare = wgpu::CompareFunction::Less; pipeline = device.CreateRenderPipeline(&descriptor); utils::ComboRenderPipelineDescriptor pDescriptor(device); pDescriptor.layout = pl; - pDescriptor.cVertexStage.module = vsModule; + pDescriptor.vertexStage.module = vsModule; pDescriptor.cFragmentStage.module = fsModule; - pDescriptor.vertexInput = &vertexInput; + pDescriptor.vertexState = &vertexState; pDescriptor.depthStencilState = &pDescriptor.cDepthStencilState; - pDescriptor.cDepthStencilState.format = dawn::TextureFormat::D32FloatS8Uint; - pDescriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat(); - pDescriptor.cDepthStencilState.stencilFront.passOp = dawn::StencilOperation::Replace; - pDescriptor.cDepthStencilState.stencilBack.passOp = dawn::StencilOperation::Replace; - pDescriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::Less; + pDescriptor.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; + pDescriptor.cColorStates[0].format = GetPreferredSwapChainTextureFormat(); + pDescriptor.cDepthStencilState.stencilFront.passOp = wgpu::StencilOperation::Replace; + pDescriptor.cDepthStencilState.stencilBack.passOp = wgpu::StencilOperation::Replace; + pDescriptor.cDepthStencilState.depthCompare = wgpu::CompareFunction::Less; planePipeline = device.CreateRenderPipeline(&pDescriptor); utils::ComboRenderPipelineDescriptor rfDescriptor(device); rfDescriptor.layout = pl; - rfDescriptor.cVertexStage.module = vsModule; + rfDescriptor.vertexStage.module = vsModule; rfDescriptor.cFragmentStage.module = fsReflectionModule; - rfDescriptor.vertexInput = &vertexInput; + rfDescriptor.vertexState = &vertexState; rfDescriptor.depthStencilState = &rfDescriptor.cDepthStencilState; - rfDescriptor.cDepthStencilState.format = dawn::TextureFormat::D32FloatS8Uint; - rfDescriptor.cColorStates[0]->format = GetPreferredSwapChainTextureFormat(); - rfDescriptor.cDepthStencilState.stencilFront.compare = dawn::CompareFunction::Equal; - rfDescriptor.cDepthStencilState.stencilBack.compare = dawn::CompareFunction::Equal; - rfDescriptor.cDepthStencilState.stencilFront.passOp = dawn::StencilOperation::Replace; - rfDescriptor.cDepthStencilState.stencilBack.passOp = dawn::StencilOperation::Replace; + rfDescriptor.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; + rfDescriptor.cColorStates[0].format = GetPreferredSwapChainTextureFormat(); + rfDescriptor.cDepthStencilState.stencilFront.compare = wgpu::CompareFunction::Equal; + rfDescriptor.cDepthStencilState.stencilBack.compare = wgpu::CompareFunction::Equal; + rfDescriptor.cDepthStencilState.stencilFront.passOp = wgpu::StencilOperation::Replace; + rfDescriptor.cDepthStencilState.stencilBack.passOp = wgpu::StencilOperation::Replace; rfDescriptor.cDepthStencilState.depthWriteEnabled = true; - rfDescriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::Less; + rfDescriptor.cDepthStencilState.depthCompare = wgpu::CompareFunction::Less; reflectionPipeline = device.CreateRenderPipeline(&rfDescriptor); cameraData.proj = glm::perspective(glm::radians(45.0f), 1.f, 1.0f, 100.0f); } -struct {uint32_t a; float b;} s; +struct { + uint32_t a; + float b; +} s; void frame() { s.a = (s.a + 1) % 256; s.b += 0.01f; - if (s.b >= 1.0f) {s.b = 0.0f;} - static const uint64_t vertexBufferOffsets[1] = {0}; + if (s.b >= 1.0f) { + s.b = 0.0f; + } - cameraData.view = glm::lookAt( - glm::vec3(8.f * std::sin(glm::radians(s.b * 360.f)), 2.f, 8.f * std::cos(glm::radians(s.b * 360.f))), - glm::vec3(0.0f, 0.0f, 0.0f), - glm::vec3(0.0f, -1.0f, 0.0f) - ); + cameraData.view = glm::lookAt(glm::vec3(8.f * std::sin(glm::radians(s.b * 360.f)), 2.f, + 8.f * std::cos(glm::radians(s.b * 360.f))), + glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f)); - cameraBuffer.SetSubData(0, sizeof(CameraData), &cameraData); + queue.WriteBuffer(cameraBuffer, 0, &cameraData, sizeof(CameraData)); - dawn::Texture backbuffer = swapchain.GetNextTexture(); - utils::ComboRenderPassDescriptor renderPass({backbuffer.CreateDefaultView()}, - depthStencilView); + wgpu::TextureView backbufferView = swapchain.GetCurrentTextureView(); + utils::ComboRenderPassDescriptor renderPass({backbufferView}, depthStencilView); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup[0], 0, nullptr); - pass.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets); - pass.SetIndexBuffer(indexBuffer, 0); - pass.DrawIndexed(36, 1, 0, 0, 0); + pass.SetBindGroup(0, bindGroup[0]); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer); + pass.DrawIndexed(36); pass.SetStencilReference(0x1); pass.SetPipeline(planePipeline); - pass.SetBindGroup(0, bindGroup[0], 0, nullptr); - pass.SetVertexBuffers(0, 1, &planeBuffer, vertexBufferOffsets); - pass.DrawIndexed(6, 1, 0, 0, 0); + pass.SetBindGroup(0, bindGroup[0]); + pass.SetVertexBuffer(0, planeBuffer); + pass.DrawIndexed(6); pass.SetPipeline(reflectionPipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, vertexBufferOffsets); - pass.SetBindGroup(0, bindGroup[1], 0, nullptr); - pass.DrawIndexed(36, 1, 0, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetBindGroup(0, bindGroup[1]); + pass.DrawIndexed(36); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - swapchain.Present(backbuffer); + swapchain.Present(); DoFlush(); } diff --git a/third_party/dawn/examples/ManualSwapChainTest.cpp b/third_party/dawn/examples/ManualSwapChainTest.cpp new file mode 100644 index 00000000000..9e3990d820b --- /dev/null +++ b/third_party/dawn/examples/ManualSwapChainTest.cpp @@ -0,0 +1,363 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This is an example to manually test swapchain code. Controls are the following, scoped to the +// currently focused window: +// - W: creates a new window. +// - L: Latches the current swapchain, to check what happens when the window changes but not the +// swapchain. +// - R: switches the rendering mode, between "The Red Triangle" and color-cycling clears that's +// (WARNING) likely seizure inducing. +// - D: cycles the divisor for the swapchain size. +// - P: switches present modes. +// +// Closing all the windows exits the example. ^C also works. +// +// Things to test manually: +// +// - Basic tests (with the triangle render mode): +// - Check the triangle is red on a black background and with the pointy side up. +// - Cycle render modes a bunch and check that the triangle background is always solid black. +// - Check that rendering triangles to multiple windows works. +// +// - Present mode single-window tests (with cycling color render mode): +// - Check that Fifo cycles at about 1 cycle per second and has no tearing. +// - Check that Mailbox cycles faster than Fifo and has no tearing. +// - Check that Immediate cycles faster than Fifo, it is allowed to have tearing. (dragging +// between two monitors can help see tearing) +// +// - Present mode multi-window tests, it should have the same results as single-window tests when +// all windows are in the same present mode. In mixed present modes only Immediate windows are +// allowed to tear. +// +// - Resizing tests (with the triangle render mode): +// - Check that cycling divisors on the triangle produces lower and lower resolution triangles. +// - Check latching the swapchain config and resizing the window a bunch (smaller, bigger, and +// diagonal aspect ratio). +// +// - Config change tests: +// - Check that cycling between present modes works. +// - TODO can't be tested yet: check cycling the same window over multiple devices. +// - TODO can't be tested yet: check cycling the same window over multiple formats. + +#include "common/Assert.h" +#include "common/Log.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/GLFWUtils.h" +#include "utils/WGPUHelpers.h" + +#include +#include +#include +#include "GLFW/glfw3.h" + +#include +#include + +struct WindowData { + GLFWwindow* window = nullptr; + uint64_t serial = 0; + + float clearCycle = 1.0f; + bool latched = false; + bool renderTriangle = true; + uint32_t divisor = 1; + + wgpu::Surface surface = nullptr; + wgpu::SwapChain swapchain = nullptr; + + wgpu::SwapChainDescriptor currentDesc; + wgpu::SwapChainDescriptor targetDesc; +}; + +static std::unordered_map> windows; +static uint64_t windowSerial = 0; + +static std::unique_ptr instance; +static wgpu::Device device; +static wgpu::Queue queue; +static wgpu::RenderPipeline trianglePipeline; + +bool IsSameDescriptor(const wgpu::SwapChainDescriptor& a, const wgpu::SwapChainDescriptor& b) { + return a.usage == b.usage && a.format == b.format && a.width == b.width && + a.height == b.height && a.presentMode == b.presentMode; +} + +void OnKeyPress(GLFWwindow* window, int key, int, int action, int); + +void SyncFromWindow(WindowData* data) { + int width; + int height; + glfwGetFramebufferSize(data->window, &width, &height); + + data->targetDesc.width = std::max(1u, width / data->divisor); + data->targetDesc.height = std::max(1u, height / data->divisor); +} + +void AddWindow() { + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + GLFWwindow* window = glfwCreateWindow(400, 400, "", nullptr, nullptr); + glfwSetKeyCallback(window, OnKeyPress); + + wgpu::SwapChainDescriptor descriptor; + descriptor.usage = wgpu::TextureUsage::OutputAttachment; + descriptor.format = wgpu::TextureFormat::BGRA8Unorm; + descriptor.width = 0; + descriptor.height = 0; + descriptor.presentMode = wgpu::PresentMode::Fifo; + + std::unique_ptr data = std::make_unique(); + data->window = window; + data->serial = windowSerial++; + data->surface = utils::CreateSurfaceForWindow(instance->Get(), window); + data->currentDesc = descriptor; + data->targetDesc = descriptor; + SyncFromWindow(data.get()); + + windows[window] = std::move(data); +} + +void DoRender(WindowData* data) { + wgpu::TextureView view = data->swapchain.GetCurrentTextureView(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + if (data->renderTriangle) { + utils::ComboRenderPassDescriptor desc({view}); + // Use Load to check the swapchain is lazy cleared (we shouldn't see garbage from previous + // frames). + desc.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&desc); + pass.SetPipeline(trianglePipeline); + pass.Draw(3); + pass.EndPass(); + } else { + data->clearCycle -= 1.0 / 60.f; + if (data->clearCycle < 0.0) { + data->clearCycle = 1.0f; + } + + utils::ComboRenderPassDescriptor desc({view}); + desc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + desc.cColorAttachments[0].clearColor = {data->clearCycle, 1.0f - data->clearCycle, 0.0f, + 1.0f}; + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&desc); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + data->swapchain.Present(); +} + +std::ostream& operator<<(std::ostream& o, const wgpu::SwapChainDescriptor& desc) { + // For now only output attachment is possible. + ASSERT(desc.usage == wgpu::TextureUsage::OutputAttachment); + o << "OutputAttachment "; + o << desc.width << "x" << desc.height << " "; + + // For now only BGRA is allowed + ASSERT(desc.format == wgpu::TextureFormat::BGRA8Unorm); + o << "BGRA8Unorm "; + + switch (desc.presentMode) { + case wgpu::PresentMode::Immediate: + o << "Immediate"; + break; + case wgpu::PresentMode::Fifo: + o << "Fifo"; + break; + case wgpu::PresentMode::Mailbox: + o << "Mailbox"; + break; + } + return o; +} + +void UpdateTitle(WindowData* data) { + std::ostringstream o; + + o << data->serial << " "; + if (data->divisor != 1) { + o << "Divisor:" << data->divisor << " "; + } + + if (data->latched) { + o << "Latched: (" << data->currentDesc << ") "; + o << "Target: (" << data->targetDesc << ")"; + } else { + o << "(" << data->currentDesc << ")"; + } + + glfwSetWindowTitle(data->window, o.str().c_str()); +} + +void OnKeyPress(GLFWwindow* window, int key, int, int action, int) { + if (action != GLFW_PRESS) { + return; + } + + ASSERT(windows.count(window) == 1); + + WindowData* data = windows[window].get(); + switch (key) { + case GLFW_KEY_W: + AddWindow(); + break; + + case GLFW_KEY_L: + data->latched = !data->latched; + UpdateTitle(data); + break; + + case GLFW_KEY_R: + data->renderTriangle = !data->renderTriangle; + UpdateTitle(data); + break; + + case GLFW_KEY_D: + data->divisor *= 2; + if (data->divisor > 32) { + data->divisor = 1; + } + break; + + case GLFW_KEY_P: + switch (data->targetDesc.presentMode) { + case wgpu::PresentMode::Immediate: + data->targetDesc.presentMode = wgpu::PresentMode::Fifo; + break; + case wgpu::PresentMode::Fifo: + data->targetDesc.presentMode = wgpu::PresentMode::Mailbox; + break; + case wgpu::PresentMode::Mailbox: + data->targetDesc.presentMode = wgpu::PresentMode::Immediate; + break; + } + break; + + default: + break; + } +} + +int main(int argc, const char* argv[]) { + // Setup GLFW + glfwSetErrorCallback([](int code, const char* message) { + dawn::ErrorLog() << "GLFW error " << code << " " << message; + }); + if (!glfwInit()) { + return 1; + } + + // Choose an adapter we like. + // TODO: allow switching the window between devices. + DawnProcTable procs = dawn_native::GetProcs(); + dawnProcSetProcs(&procs); + + instance = std::make_unique(); + instance->DiscoverDefaultAdapters(); + + std::vector adapters = instance->GetAdapters(); + dawn_native::Adapter chosenAdapter; + for (dawn_native::Adapter& adapter : adapters) { + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); + if (properties.backendType != wgpu::BackendType::Null) { + chosenAdapter = adapter; + break; + } + } + ASSERT(chosenAdapter); + + // Setup the device on that adapter. + device = wgpu::Device::Acquire(chosenAdapter.CreateDevice()); + device.SetUncapturedErrorCallback( + [](WGPUErrorType errorType, const char* message, void*) { + const char* errorTypeName = ""; + switch (errorType) { + case WGPUErrorType_Validation: + errorTypeName = "Validation"; + break; + case WGPUErrorType_OutOfMemory: + errorTypeName = "Out of memory"; + break; + case WGPUErrorType_Unknown: + errorTypeName = "Unknown"; + break; + case WGPUErrorType_DeviceLost: + errorTypeName = "Device lost"; + break; + default: + UNREACHABLE(); + return; + } + dawn::ErrorLog() << errorTypeName << " error: " << message; + }, + nullptr); + queue = device.GetDefaultQueue(); + + // The hacky pipeline to render a triangle. + utils::ComboRenderPipelineDescriptor pipelineDesc(device); + pipelineDesc.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + const vec2 pos[3] = vec2[3](vec2(0.0f, 0.5f), vec2(-0.5f, -0.5f), vec2(0.5f, -0.5f)); + void main() { + gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); + })"); + pipelineDesc.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(1.0, 0.0, 0.0, 1.0); + })"); + pipelineDesc.colorStateCount = 1; + // BGRA shouldn't be hardcoded. Consider having a map[format -> pipeline]. + pipelineDesc.cColorStates[0].format = wgpu::TextureFormat::BGRA8Unorm; + trianglePipeline = device.CreateRenderPipeline(&pipelineDesc); + + // Craete the first window, since the example exits when there are no windows. + AddWindow(); + + while (windows.size() != 0) { + glfwPollEvents(); + + for (auto it = windows.begin(); it != windows.end();) { + GLFWwindow* window = it->first; + + if (glfwWindowShouldClose(window)) { + glfwDestroyWindow(window); + it = windows.erase(it); + } else { + it++; + } + } + + for (auto& it : windows) { + WindowData* data = it.second.get(); + + SyncFromWindow(data); + if (!IsSameDescriptor(data->currentDesc, data->targetDesc) && !data->latched) { + data->swapchain = device.CreateSwapChain(data->surface, &data->targetDesc); + data->currentDesc = data->targetDesc; + } + UpdateTitle(data); + DoRender(data); + } + } +} diff --git a/third_party/dawn/examples/SampleUtils.cpp b/third_party/dawn/examples/SampleUtils.cpp index 7d4f5ee57c9..ee7e0061563 100644 --- a/third_party/dawn/examples/SampleUtils.cpp +++ b/third_party/dawn/examples/SampleUtils.cpp @@ -15,13 +15,14 @@ #include "SampleUtils.h" #include "common/Assert.h" +#include "common/Log.h" #include "common/Platform.h" #include "utils/BackendBinding.h" +#include "utils/GLFWUtils.h" #include "utils/TerribleCommandBuffer.h" -#include +#include #include -#include #include #include #include @@ -29,34 +30,51 @@ #include #include -#include -void PrintDeviceError(const char* message, void*) { - std::cout << "Device error: " << message << std::endl; +void PrintDeviceError(WGPUErrorType errorType, const char* message, void*) { + const char* errorTypeName = ""; + switch (errorType) { + case WGPUErrorType_Validation: + errorTypeName = "Validation"; + break; + case WGPUErrorType_OutOfMemory: + errorTypeName = "Out of memory"; + break; + case WGPUErrorType_Unknown: + errorTypeName = "Unknown"; + break; + case WGPUErrorType_DeviceLost: + errorTypeName = "Device lost"; + break; + default: + UNREACHABLE(); + return; + } + dawn::ErrorLog() << errorTypeName << " error: " << message; } void PrintGLFWError(int code, const char* message) { - std::cout << "GLFW error: " << code << " - " << message << std::endl; + dawn::ErrorLog() << "GLFW error: " << code << " - " << message; } enum class CmdBufType { None, Terrible, - //TODO(cwallez@chromium.org) double terrible cmdbuf + // TODO(cwallez@chromium.org): double terrible cmdbuf }; // Default to D3D12, Metal, Vulkan, OpenGL in that order as D3D12 and Metal are the preferred on // their respective platforms, and Vulkan is preferred to OpenGL #if defined(DAWN_ENABLE_BACKEND_D3D12) - static dawn_native::BackendType backendType = dawn_native::BackendType::D3D12; +static wgpu::BackendType backendType = wgpu::BackendType::D3D12; #elif defined(DAWN_ENABLE_BACKEND_METAL) - static dawn_native::BackendType backendType = dawn_native::BackendType::Metal; -#elif defined(DAWN_ENABLE_BACKEND_OPENGL) - static dawn_native::BackendType backendType = dawn_native::BackendType::OpenGL; +static wgpu::BackendType backendType = wgpu::BackendType::Metal; #elif defined(DAWN_ENABLE_BACKEND_VULKAN) - static dawn_native::BackendType backendType = dawn_native::BackendType::Vulkan; +static wgpu::BackendType backendType = wgpu::BackendType::Vulkan; +#elif defined(DAWN_ENABLE_BACKEND_OPENGL) +static wgpu::BackendType backendType = wgpu::BackendType::OpenGL; #else - #error +# error #endif static CmdBufType cmdBufType = CmdBufType::Terrible; @@ -70,17 +88,17 @@ static dawn_wire::WireClient* wireClient = nullptr; static utils::TerribleCommandBuffer* c2sBuf = nullptr; static utils::TerribleCommandBuffer* s2cBuf = nullptr; -dawn::Device CreateCppDawnDevice() { +wgpu::Device CreateCppDawnDevice() { glfwSetErrorCallback(PrintGLFWError); if (!glfwInit()) { - return dawn::Device(); + return wgpu::Device(); } // Create the test window and discover adapters using it (esp. for OpenGL) utils::SetupGLFWWindowHintsForBackend(backendType); window = glfwCreateWindow(640, 480, "Dawn window", nullptr, nullptr); if (!window) { - return dawn::Device(); + return wgpu::Device(); } instance = std::make_unique(); @@ -92,22 +110,24 @@ dawn::Device CreateCppDawnDevice() { std::vector adapters = instance->GetAdapters(); auto adapterIt = std::find_if(adapters.begin(), adapters.end(), [](const dawn_native::Adapter adapter) -> bool { - return adapter.GetBackendType() == backendType; - }); + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); + return properties.backendType == backendType; + }); ASSERT(adapterIt != adapters.end()); backendAdapter = *adapterIt; } - DawnDevice backendDevice = backendAdapter.CreateDevice(); + WGPUDevice backendDevice = backendAdapter.CreateDevice(); DawnProcTable backendProcs = dawn_native::GetProcs(); binding = utils::CreateBinding(backendType, window, backendDevice); if (binding == nullptr) { - return dawn::Device(); + return wgpu::Device(); } // Choose whether to use the backend procs and devices directly, or set up the wire. - DawnDevice cDevice = nullptr; + WGPUDevice cDevice = nullptr; DawnProcTable procs; switch (cmdBufType) { @@ -116,58 +136,63 @@ dawn::Device CreateCppDawnDevice() { cDevice = backendDevice; break; - case CmdBufType::Terrible: - { - c2sBuf = new utils::TerribleCommandBuffer(); - s2cBuf = new utils::TerribleCommandBuffer(); + case CmdBufType::Terrible: { + c2sBuf = new utils::TerribleCommandBuffer(); + s2cBuf = new utils::TerribleCommandBuffer(); - wireServer = new dawn_wire::WireServer(backendDevice, backendProcs, s2cBuf); - c2sBuf->SetHandler(wireServer); + dawn_wire::WireServerDescriptor serverDesc = {}; + serverDesc.device = backendDevice; + serverDesc.procs = &backendProcs; + serverDesc.serializer = s2cBuf; - wireClient = new dawn_wire::WireClient(c2sBuf); - DawnDevice clientDevice = wireClient->GetDevice(); - DawnProcTable clientProcs = wireClient->GetProcs(); - s2cBuf->SetHandler(wireClient); + wireServer = new dawn_wire::WireServer(serverDesc); + c2sBuf->SetHandler(wireServer); - procs = clientProcs; - cDevice = clientDevice; - } - break; + dawn_wire::WireClientDescriptor clientDesc = {}; + clientDesc.serializer = c2sBuf; + + wireClient = new dawn_wire::WireClient(clientDesc); + WGPUDevice clientDevice = wireClient->GetDevice(); + DawnProcTable clientProcs = dawn_wire::WireClient::GetProcs(); + s2cBuf->SetHandler(wireClient); + + procs = clientProcs; + cDevice = clientDevice; + } break; } - dawnSetProcs(&procs); - procs.deviceSetErrorCallback(cDevice, PrintDeviceError, nullptr); - return dawn::Device::Acquire(cDevice); + dawnProcSetProcs(&procs); + procs.deviceSetUncapturedErrorCallback(cDevice, PrintDeviceError, nullptr); + return wgpu::Device::Acquire(cDevice); } uint64_t GetSwapChainImplementation() { return binding->GetSwapChainImplementation(); } -dawn::TextureFormat GetPreferredSwapChainTextureFormat() { +wgpu::TextureFormat GetPreferredSwapChainTextureFormat() { DoFlush(); - return static_cast(binding->GetPreferredSwapChainTextureFormat()); + return static_cast(binding->GetPreferredSwapChainTextureFormat()); } -dawn::SwapChain GetSwapChain(const dawn::Device &device) { - dawn::SwapChainDescriptor swapChainDesc; +wgpu::SwapChain GetSwapChain(const wgpu::Device& device) { + wgpu::SwapChainDescriptor swapChainDesc; swapChainDesc.implementation = GetSwapChainImplementation(); - return device.CreateSwapChain(&swapChainDesc); + return device.CreateSwapChain(nullptr, &swapChainDesc); } -dawn::TextureView CreateDefaultDepthStencilView(const dawn::Device& device) { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; +wgpu::TextureView CreateDefaultDepthStencilView(const wgpu::Device& device) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = 640; descriptor.size.height = 480; descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::D32FloatS8Uint; + descriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment; + descriptor.usage = wgpu::TextureUsage::OutputAttachment; auto depthStencilTexture = device.CreateTexture(&descriptor); - return depthStencilTexture.CreateDefaultView(); + return depthStencilTexture.CreateView(); } bool InitSample(int argc, const char** argv) { @@ -175,26 +200,27 @@ bool InitSample(int argc, const char** argv) { if (std::string("-b") == argv[i] || std::string("--backend") == argv[i]) { i++; if (i < argc && std::string("d3d12") == argv[i]) { - backendType = dawn_native::BackendType::D3D12; + backendType = wgpu::BackendType::D3D12; continue; } if (i < argc && std::string("metal") == argv[i]) { - backendType = dawn_native::BackendType::Metal; + backendType = wgpu::BackendType::Metal; continue; } if (i < argc && std::string("null") == argv[i]) { - backendType = dawn_native::BackendType::Null; + backendType = wgpu::BackendType::Null; continue; } if (i < argc && std::string("opengl") == argv[i]) { - backendType = dawn_native::BackendType::OpenGL; + backendType = wgpu::BackendType::OpenGL; continue; } if (i < argc && std::string("vulkan") == argv[i]) { - backendType = dawn_native::BackendType::Vulkan; + backendType = wgpu::BackendType::Vulkan; continue; } - fprintf(stderr, "--backend expects a backend name (opengl, metal, d3d12, null, vulkan)\n"); + fprintf(stderr, + "--backend expects a backend name (opengl, metal, d3d12, null, vulkan)\n"); return false; } if (std::string("-c") == argv[i] || std::string("--command-buffer") == argv[i]) { diff --git a/third_party/dawn/examples/SampleUtils.h b/third_party/dawn/examples/SampleUtils.h index 3540109bf97..8c6fdd78a13 100644 --- a/third_party/dawn/examples/SampleUtils.h +++ b/third_party/dawn/examples/SampleUtils.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include +#include bool InitSample(int argc, const char** argv); void DoFlush(); @@ -22,8 +22,8 @@ bool ShouldQuit(); struct GLFWwindow; struct GLFWwindow* GetGLFWWindow(); -dawn::Device CreateCppDawnDevice(); +wgpu::Device CreateCppDawnDevice(); uint64_t GetSwapChainImplementation(); -dawn::TextureFormat GetPreferredSwapChainTextureFormat(); -dawn::SwapChain GetSwapChain(const dawn::Device& device); -dawn::TextureView CreateDefaultDepthStencilView(const dawn::Device& device); +wgpu::TextureFormat GetPreferredSwapChainTextureFormat(); +wgpu::SwapChain GetSwapChain(const wgpu::Device& device); +wgpu::TextureView CreateDefaultDepthStencilView(const wgpu::Device& device); diff --git a/third_party/dawn/generator/BUILD.gn b/third_party/dawn/generator/BUILD.gn new file mode 100644 index 00000000000..acf48736b3e --- /dev/null +++ b/third_party/dawn/generator/BUILD.gn @@ -0,0 +1,63 @@ +# Copyright 2019 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../scripts/dawn_overrides_with_defaults.gni") +import("dawn_generator.gni") + +# The list of directories in which to check for stale autogenerated files. +# It should include the list of all directories in which we ever generated +# files but we can't just put dawn_gen_root because there are more than +# autogenerated sources there. +_stale_dirs = [ + "dawn", + "dawn_native", + "dawn_wire", + "mock", + "src", +] + +_allowed_output_dirs_file = + "${dawn_gen_root}/removed_stale_autogen_files.allowed_output_dirs" +write_file(_allowed_output_dirs_file, dawn_allowed_gen_output_dirs) + +_stale_dirs_file = "${dawn_gen_root}/removed_stale_autogen_files.stale_dirs" +write_file(_stale_dirs_file, _stale_dirs) + +_stamp_file = "${dawn_gen_root}/removed_stale_autogen_files.stamp" + +# An action that removes autogenerated files that aren't in allowed directories +# see dawn_generator.gni for more details. +action("remove_stale_autogen_files") { + script = "remove_files.py" + args = [ + "--root-dir", + rebase_path(dawn_gen_root, root_build_dir), + "--allowed-output-dirs-file", + rebase_path(_allowed_output_dirs_file, root_build_dir), + "--stale-dirs-file", + rebase_path(_stale_dirs_file, root_build_dir), + "--stamp", + rebase_path(_stamp_file, root_build_dir), + ] + + # Have the "list of file" inputs as a dependency so that the action reruns + # as soon as they change. + inputs = [ + _allowed_output_dirs_file, + _stale_dirs_file, + ] + + # Output a stamp file so we don't re-run this action on every build. + outputs = [ _stamp_file ] +} diff --git a/third_party/dawn/generator/CMakeLists.txt b/third_party/dawn/generator/CMakeLists.txt new file mode 100644 index 00000000000..c21359c8dde --- /dev/null +++ b/third_party/dawn/generator/CMakeLists.txt @@ -0,0 +1,116 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +find_package(PythonInterp REQUIRED) +message(STATUS "Dawn: using python at ${PYTHON_EXECUTABLE}") + +# Check for Jinja2 +if (NOT DAWN_JINJA2_DIR) + message(STATUS "Dawn: Using system jinja2") + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "import jinja2" + RESULT_VARIABLE RET + ) + if (NOT RET EQUAL 0) + message(FATAL_ERROR "Dawn: Missing dependencies for code generation, please ensure you have python-jinja2 installed.") + endif() +else() + message(STATUS "Dawn: Using jinja2 at ${DAWN_JINJA2_DIR}") +endif() + +# Function to invoke a generator_lib.py generator. +# - SCRIPT is the name of the script to call +# - ARGS are the extra arguments to pass to the script in addition to the base generator_lib.py arguments +# - PRINT_NAME is the name to use when outputting status or errors +# - RESULT_VARIABLE will be modified to contain the list of files generated by this generator +function(DawnGenerator) + set(oneValueArgs SCRIPT RESULT_VARIABLE PRINT_NAME) + set(multiValueArgs ARGS) + cmake_parse_arguments(G "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Build the set of args common to all invocation of that generator. + set(BASE_ARGS + ${PYTHON_EXECUTABLE} + ${G_SCRIPT} + --template-dir + "${DAWN_TEMPLATE_DIR}" + --root-dir + "${Dawn_SOURCE_DIR}" + --output-dir + "${DAWN_BUILD_GEN_DIR}" + ${G_ARGS} + ) + if (DAWN_JINJA2_DIR) + list(APPEND BASE_ARGS --jinja2-path ${DAWN_JINJA2_DIR}) + endif() + + # Call the generator to get the list of its dependencies. + execute_process( + COMMAND ${BASE_ARGS} --print-cmake-dependencies + OUTPUT_VARIABLE DEPENDENCIES + RESULT_VARIABLE RET + ) + if (NOT RET EQUAL 0) + message(FATAL_ERROR "Dawn: Failed to get the dependencies for ${G_PRINT_NAME}.") + endif() + + # Ask CMake to re-run if any of the dependencies changed as it might modify the build graph. + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${DEPENDENCIES}) + endif() + + # Call the generator to get the list of its outputs. + execute_process( + COMMAND ${BASE_ARGS} --print-cmake-outputs + OUTPUT_VARIABLE OUTPUTS + RESULT_VARIABLE RET + ) + if (NOT RET EQUAL 0) + message(FATAL_ERROR "Dawn: Failed to get the outputs for ${G_PRINT_NAME}.") + endif() + + # Add the custom command that calls the generator. + add_custom_command( + COMMAND ${BASE_ARGS} + DEPENDS ${DEPENDENCIES} + OUTPUT ${OUTPUTS} + COMMENT "Dawn: Generating files for ${G_PRINT_NAME}." + ) + + # Return the list of outputs. + set(${G_RESULT_VARIABLE} ${OUTPUTS} PARENT_SCOPE) +endfunction() + +# Helper function to call dawn_generator.py: +# - TARGET is the generator target to build +# - PRINT_NAME and RESULT_VARIABLE are like for DawnGenerator +function(DawnJSONGenerator) + set(oneValueArgs TARGET RESULT_VARIABLE) + cmake_parse_arguments(G "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + DawnGenerator( + SCRIPT "${Dawn_SOURCE_DIR}/generator/dawn_json_generator.py" + ARGS --dawn-json + "${Dawn_SOURCE_DIR}/dawn.json" + --wire-json + "${Dawn_SOURCE_DIR}/dawn_wire.json" + --targets + ${G_TARGET} + RESULT_VARIABLE RET + ${G_UNPARSED_ARGUMENTS} + ) + + # Forward the result up one more scope + set(${G_RESULT_VARIABLE} ${RET} PARENT_SCOPE) +endfunction() diff --git a/third_party/dawn/generator/dawn_generator.gni b/third_party/dawn/generator/dawn_generator.gni index 19b2e23dcf4..370fc2c70b7 100644 --- a/third_party/dawn/generator/dawn_generator.gni +++ b/third_party/dawn/generator/dawn_generator.gni @@ -13,6 +13,39 @@ # limitations under the License. import("../scripts/dawn_overrides_with_defaults.gni") +import("generator_lib.gni") + +# Dawn used to put autogenerated files in a lot of different places. When we +# started to move them around, some compilation issues arised because some +# stale include files stayed in the build directory and were picked up. +# To counter this, now Dawn does the following: +# +# 1. The generated output file directory structure has to match the structure +# of the source tree, starting at dawn_gen_root (gen/ or +# gen/third_party/dawn depending on where we are). +# 2. src/include and dawn_gen_root/src/include has to match the structure of +# the source tree too. +# 3. Dawn files must use include relative to src/ or src/include such as +# "dawn/dawn.h" or "dawn_native/backend/BackendStuff.h". +# +# The allowed list below ensure 1). Include directory rules for Dawn ensure 3) +# and 2) is something we need to enforce in code review. +# +# However GN's toolchains automatically add some include directories for us +# which breaks 3) slightly. To avoid stale headers in for example +# dawn_gen_root/src/dawn/dawn/ to be picked up (instead of +# dawn_gen_root/src/dawn), we have a special action that removes files in +# disallowed gen directories. + +dawn_allowed_gen_output_dirs = [ + "src/dawn/", + "src/dawn_native/", + "src/dawn_native/opengl/", + "src/dawn_wire/client/", + "src/dawn_wire/server/", + "src/dawn_wire/", + "src/include/dawn/", +] # Template to help invoking Dawn code generators based on generator_lib # @@ -29,9 +62,6 @@ import("../scripts/dawn_overrides_with_defaults.gni") # "MyAwesomeTarget.cpp", # "MyAwesomeTarget.h", # ] -# -# # Optional, use a custom generated file directory. -# custom_gen_dir = "${target_gen_dir}/.." # } # # Using the generated files is done like so: @@ -42,94 +72,21 @@ import("../scripts/dawn_overrides_with_defaults.gni") # } # template("dawn_generator") { - generator_args = [] - if (defined(invoker.args)) { - generator_args += invoker.args - } - - generator_args += [ - "--template-dir", - rebase_path("${dawn_root}/generator/templates", root_build_dir), - ] - - # Use the Jinja2 version pulled from the DEPS file. We do it so we don't - # have version problems, and users don't have to install Jinja2. - jinja2_python_path = rebase_path("${dawn_jinja2_dir}/..") - generator_args += [ - "--extra-python-path", - jinja2_python_path, - ] - - # Chooses either the default gen_dir or the custom one required by the - # invoker. This allows moving the definition of code generators in different - # BUILD.gn files without changing the location of generated file. Without - # this generated headers could cause issues when old headers aren't removed. - gen_dir = target_gen_dir - if (defined(invoker.custom_gen_dir)) { - gen_dir = invoker.custom_gen_dir - } - - # For build parallelism GN wants to know the exact inputs and outputs of - # action targets like we use for our code generator. We avoid asking the - # generator about its inputs by using the "depfile" feature of GN/Ninja. - # - # A ninja limitation is that the depfile is a subset of Makefile that can - # contain a single target, so we output a single "JSON-tarball" instead. - json_tarball = "${gen_dir}/${target_name}.json_tarball" - json_tarball_depfile = "${json_tarball}.d" + generator_lib_action(target_name) { + forward_variables_from(invoker, "*") - generator_args += [ - "--output-json-tarball", - rebase_path(json_tarball, root_build_dir), - "--depfile", - rebase_path(json_tarball_depfile, root_build_dir), - ] + # Set arguments required to find the python libraries for the generator + generator_lib_dir = "${dawn_root}/generator" + jinja2_path = dawn_jinja2_dir - # After the JSON tarball is created we need an action target to extract it - # with a list of its outputs. The invoker provided a list of expected - # outputs. To make sure the list is in sync between the generator and the - # build files, we write it to a file and ask the generator to assert it is - # correct. - expected_outputs_file = "${gen_dir}/${target_name}.expected_outputs" - write_file(expected_outputs_file, invoker.outputs) - - generator_args += [ - "--expected-outputs-file", - rebase_path(expected_outputs_file, root_build_dir), - ] - - # The code generator invocation that will write the JSON tarball, check the - # outputs are what's expected and write a depfile for Ninja. - action("${target_name}_json_tarball") { - script = invoker.script - outputs = [ - json_tarball, - ] - depfile = json_tarball_depfile - args = generator_args - } - - # Extract the JSON tarball into the gen_dir - action(target_name) { - script = "${dawn_root}/generator/extract_json.py" - args = [ - rebase_path(json_tarball, root_build_dir), - rebase_path(gen_dir, root_build_dir), - ] - - deps = [ - ":${target_name}_json_tarball", - ] - inputs = [ - json_tarball, - ] + # Force Dawn's autogenerated file structure to mirror exactly the source + # tree but start at ${dawn_gen_root} instead of ${dawn_root} + allowed_output_dirs = dawn_allowed_gen_output_dirs + custom_gen_dir = dawn_gen_root - # The expected output list is relative to the gen_dir but action - # target outputs are from the root dir so we need to rebase them. - outputs = [] - foreach(source, invoker.outputs) { - outputs += [ "${gen_dir}/${source}" ] - } + # Make sure that we delete stale autogenerated file in directories that are + # no longer used by code generation to avoid include conflicts. + deps = [ "${dawn_root}/generator:remove_stale_autogen_files" ] } } diff --git a/third_party/dawn/generator/dawn_json_generator.py b/third_party/dawn/generator/dawn_json_generator.py index 9eb4c2e0f89..b7d9003e9d2 100644 --- a/third_party/dawn/generator/dawn_json_generator.py +++ b/third_party/dawn/generator/dawn_json_generator.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright 2017 The Dawn Authors # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,14 +22,19 @@ # OBJECT MODEL ############################################################ + class Name: def __init__(self, name, native=False): self.native = native + self.name = name if native: self.chunks = [name] else: self.chunks = name.split(' ') + def get(self): + return self.name + def CamelChunk(self, chunk): return chunk[0].upper() + chunk[1:] @@ -40,7 +45,8 @@ def concatcase(self): return ''.join(self.chunks) def camelCase(self): - return self.chunks[0] + ''.join([self.CamelChunk(chunk) for chunk in self.chunks[1:]]) + return self.chunks[0] + ''.join( + [self.CamelChunk(chunk) for chunk in self.chunks[1:]]) def CamelCase(self): return ''.join([self.CamelChunk(chunk) for chunk in self.chunks]) @@ -51,43 +57,93 @@ def SNAKE_CASE(self): def snake_case(self): return '_'.join(self.chunks) + def js_enum_case(self): + result = self.chunks[0].lower() + for chunk in self.chunks[1:]: + if not result[-1].isdigit(): + result += '-' + result += chunk.lower() + return result + + def concat_names(*names): return ' '.join([name.canonical_case() for name in names]) + class Type: def __init__(self, name, json_data, native=False): self.json_data = json_data self.dict_name = name self.name = Name(name, native=native) self.category = json_data['category'] + self.javascript = self.json_data.get('javascript', True) + + +EnumValue = namedtuple('EnumValue', ['name', 'value', 'valid', 'jsrepr']) + -EnumValue = namedtuple('EnumValue', ['name', 'value']) class EnumType(Type): def __init__(self, name, json_data): Type.__init__(self, name, json_data) - self.values = [EnumValue(Name(m['name']), m['value']) for m in self.json_data['values']] + + self.values = [] + self.contiguousFromZero = True + lastValue = -1 + for m in self.json_data['values']: + value = m['value'] + if value != lastValue + 1: + self.contiguousFromZero = False + lastValue = value + self.values.append( + EnumValue(Name(m['name']), value, m.get('valid', True), + m.get('jsrepr', None))) + + # Assert that all values are unique in enums + all_values = set() + for value in self.values: + if value.value in all_values: + raise Exception("Duplicate value {} in enum {}".format( + value.value, name)) + all_values.add(value.value) + BitmaskValue = namedtuple('BitmaskValue', ['name', 'value']) + + class BitmaskType(Type): def __init__(self, name, json_data): Type.__init__(self, name, json_data) - self.values = [BitmaskValue(Name(m['name']), m['value']) for m in self.json_data['values']] + self.values = [ + BitmaskValue(Name(m['name']), m['value']) + for m in self.json_data['values'] + ] self.full_mask = 0 for value in self.values: self.full_mask = self.full_mask | value.value + +class CallbackType(Type): + def __init__(self, name, json_data): + Type.__init__(self, name, json_data) + self.arguments = [] + + class NativeType(Type): def __init__(self, name, json_data): Type.__init__(self, name, json_data, native=True) -class NativelyDefined(Type): - def __init__(self, name, json_data): - Type.__init__(self, name, json_data) # Methods and structures are both "records", so record members correspond to # method arguments or structure members. class RecordMember: - def __init__(self, name, typ, annotation, optional, is_return_value): + def __init__(self, + name, + typ, + annotation, + optional=False, + is_return_value=False, + default_value=None, + skip_serialize=False): self.name = name self.type = typ self.annotation = annotation @@ -95,41 +151,60 @@ def __init__(self, name, typ, annotation, optional, is_return_value): self.optional = optional self.is_return_value = is_return_value self.handle_type = None + self.default_value = default_value + self.skip_serialize = skip_serialize def set_handle_type(self, handle_type): assert self.type.dict_name == "ObjectHandle" self.handle_type = handle_type + Method = namedtuple('Method', ['name', 'return_type', 'arguments']) + + class ObjectType(Type): def __init__(self, name, json_data): Type.__init__(self, name, json_data) self.methods = [] - self.native_methods = [] self.built_type = None + class Record: def __init__(self, name): self.name = Name(name) self.members = [] - self.has_dawn_object = False + self.may_have_dawn_object = False def update_metadata(self): - def has_dawn_object(member): + def may_have_dawn_object(member): if isinstance(member.type, ObjectType): return True elif isinstance(member.type, StructureType): - return member.type.has_dawn_object + return member.type.may_have_dawn_object else: return False - self.has_dawn_object = any(has_dawn_object(member) for member in self.members) + self.may_have_dawn_object = any( + may_have_dawn_object(member) for member in self.members) + + # Set may_have_dawn_object to true if the type is chained or + # extensible. Chained structs may contain a Dawn object. + if isinstance(self, StructureType): + self.may_have_dawn_object = (self.may_have_dawn_object + or self.chained or self.extensible) + class StructureType(Record, Type): def __init__(self, name, json_data): Record.__init__(self, name) Type.__init__(self, name, json_data) + self.chained = json_data.get("chained", False) self.extensible = json_data.get("extensible", False) + # Chained structs inherit from wgpu::ChainedStruct, which has + # nextInChain, so setting both extensible and chained would result in + # two nextInChain members. + assert not (self.extensible and self.chained) + class Command(Record): def __init__(self, name, members=None): @@ -138,13 +213,18 @@ def __init__(self, name, members=None): self.derived_object = None self.derived_method = None + def linked_record_members(json_data, types): members = [] members_by_name = {} for m in json_data: - member = RecordMember(Name(m['name']), types[m['type']], - m.get('annotation', 'value'), m.get('optional', False), - m.get('is_return_value', False)) + member = RecordMember(Name(m['name']), + types[m['type']], + m.get('annotation', 'value'), + optional=m.get('optional', False), + is_return_value=m.get('is_return_value', False), + default_value=m.get('default', None), + skip_serialize=m.get('skip_serialize', False)) handle_type = m.get('handle_type') if handle_type: member.set_handle_type(types[handle_type]) @@ -158,7 +238,7 @@ def linked_record_members(json_data, types): member.length = "constant" member.constant_length = 1 else: - assert(False) + assert False elif m['length'] == 'strlen': member.length = 'strlen' else: @@ -166,33 +246,42 @@ def linked_record_members(json_data, types): return members + ############################################################ # PARSE ############################################################ -def is_native_method(method): - return method.return_type.category == "natively defined" or \ - any([arg.type.category == "natively defined" for arg in method.arguments]) def link_object(obj, types): def make_method(json_data): arguments = linked_record_members(json_data.get('args', []), types) - return Method(Name(json_data['name']), types[json_data.get('returns', 'void')], arguments) + return Method(Name(json_data['name']), + types[json_data.get('returns', 'void')], arguments) + + obj.methods = [make_method(m) for m in obj.json_data.get('methods', [])] + obj.methods.sort(key=lambda method: method.name.canonical_case()) - methods = [make_method(m) for m in obj.json_data.get('methods', [])] - obj.methods = [method for method in methods if not is_native_method(method)] - obj.native_methods = [method for method in methods if is_native_method(method)] def link_structure(struct, types): struct.members = linked_record_members(struct.json_data['members'], types) -# Sort structures so that if struct A has struct B as a member, then B is listed before A -# This is a form of topological sort where we try to keep the order reasonably similar to the -# original order (though th sort isn't technically stable). -# It works by computing for each struct type what is the depth of its DAG of dependents, then -# resorting based on that depth using Python's stable sort. This makes a toposort because if -# A depends on B then its depth will be bigger than B's. It is also nice because all nodes -# with the same depth are kept in the input order. + +def link_callback(callback, types): + callback.arguments = linked_record_members(callback.json_data['args'], + types) + + +# Sort structures so that if struct A has struct B as a member, then B is +# listed before A. +# +# This is a form of topological sort where we try to keep the order reasonably +# similar to the original order (though the sort isn't technically stable). +# +# It works by computing for each struct type what is the depth of its DAG of +# dependents, then resorting based on that depth using Python's stable sort. +# This makes a toposort because if A depends on B then its depth will be bigger +# than B's. It is also nice because all nodes with the same depth are kept in +# the input order. def topo_sort_structure(structs): for struct in structs: struct.visited = False @@ -205,7 +294,8 @@ def compute_depth(struct): max_dependent_depth = 0 for member in struct.members: if member.type.category == 'structure': - max_dependent_depth = max(max_dependent_depth, compute_depth(member.type) + 1) + max_dependent_depth = max(max_dependent_depth, + compute_depth(member.type) + 1) struct.subdag_depth = max_dependent_depth struct.visited = True @@ -222,12 +312,13 @@ def compute_depth(struct): return result + def parse_json(json): category_to_parser = { 'bitmask': BitmaskType, 'enum': EnumType, 'native': NativeType, - 'natively defined': NativelyDefined, + 'callback': CallbackType, 'object': ObjectType, 'structure': StructureType, } @@ -252,23 +343,26 @@ def parse_json(json): for struct in by_category['structure']: link_structure(struct, types) + for callback in by_category['callback']: + link_callback(callback, types) + for category in by_category.keys(): - by_category[category] = sorted(by_category[category], key=lambda typ: typ.name.canonical_case()) + by_category[category] = sorted( + by_category[category], key=lambda typ: typ.name.canonical_case()) by_category['structure'] = topo_sort_structure(by_category['structure']) for struct in by_category['structure']: struct.update_metadata() - return { - 'types': types, - 'by_category': by_category - } + return {'types': types, 'by_category': by_category} + ############################################################ # WIRE STUFF ############################################################ + # Create wire commands from api methods def compute_wire_params(api_params, wire_json): wire_params = api_params.copy() @@ -277,27 +371,42 @@ def compute_wire_params(api_params, wire_json): commands = [] return_commands = [] + wire_json['special items']['client_handwritten_commands'] += wire_json[ + 'special items']['client_side_commands'] + # Generate commands from object methods for api_object in wire_params['by_category']['object']: for method in api_object.methods: command_name = concat_names(api_object.name, method.name) command_suffix = Name(command_name).CamelCase() - # Only object return values or void are supported. Other methods must be handwritten. - if method.return_type.category != 'object' and method.return_type.name.canonical_case() != 'void': - assert(command_suffix in wire_json['special items']['client_handwritten_commands']) + # Only object return values or void are supported. + # Other methods must be handwritten. + is_object = method.return_type.category == 'object' + is_void = method.return_type.name.canonical_case() == 'void' + if not (is_object or is_void): + assert command_suffix in ( + wire_json['special items']['client_handwritten_commands']) continue - if command_suffix in wire_json['special items']['client_side_commands']: + if command_suffix in ( + wire_json['special items']['client_side_commands']): continue # Create object method commands by prepending "self" - members = [RecordMember(Name('self'), types[api_object.dict_name], 'value', False, False)] + members = [ + RecordMember(Name('self'), types[api_object.dict_name], + 'value') + ] members += method.arguments - # Client->Server commands that return an object return the result object handle + # Client->Server commands that return an object return the + # result object handle if method.return_type.category == 'object': - result = RecordMember(Name('result'), types['ObjectHandle'], 'value', False, True) + result = RecordMember(Name('result'), + types['ObjectHandle'], + 'value', + is_return_value=True) result.set_handle_type(method.return_type) members.append(result) @@ -310,7 +419,8 @@ def compute_wire_params(api_params, wire_json): commands.append(Command(name, linked_record_members(json_data, types))) for (name, json_data) in wire_json['return commands'].items(): - return_commands.append(Command(name, linked_record_members(json_data, types))) + return_commands.append( + Command(name, linked_record_members(json_data, types))) wire_params['cmd_records'] = { 'command': commands, @@ -326,25 +436,49 @@ def compute_wire_params(api_params, wire_json): return wire_params + ############################################################# # Generator ############################################################# + def as_varName(*names): - return names[0].camelCase() + ''.join([name.CamelCase() for name in names[1:]]) + return names[0].camelCase() + ''.join( + [name.CamelCase() for name in names[1:]]) + def as_cType(name): + if name.native: + return name.concatcase() + else: + return 'WGPU' + name.CamelCase() + + +def as_cTypeDawn(name): if name.native: return name.concatcase() else: return 'Dawn' + name.CamelCase() + +def as_cTypeEnumSpecialCase(typ): + if typ.category == 'bitmask': + return as_cType(typ.name) + 'Flags' + return as_cType(typ.name) + + def as_cppType(name): if name.native: return name.concatcase() else: return name.CamelCase() + +def as_jsEnumValue(value): + if value.jsrepr: return value.jsrepr + return "'" + value.name.js_enum_case() + "'" + + def convert_cType_to_cppType(typ, annotation, arg, indent=0): if typ.category == 'native': return arg @@ -355,18 +489,20 @@ def convert_cType_to_cppType(typ, annotation, arg, indent=0): converted_members = [ convert_cType_to_cppType( member.type, member.annotation, - '{}.{}'.format(arg, as_varName(member.name)), - indent + 1) - for member in typ.members] + '{}.{}'.format(arg, as_varName(member.name)), indent + 1) + for member in typ.members + ] - converted_members = [(' ' * 4) + m for m in converted_members ] + converted_members = [(' ' * 4) + m for m in converted_members] converted_members = ',\n'.join(converted_members) return as_cppType(typ.name) + ' {\n' + converted_members + '\n}' else: return 'static_cast<{}>({})'.format(as_cppType(typ.name), arg) else: - return 'reinterpret_cast<{} {}>({})'.format(as_cppType(typ.name), annotation, arg) + return 'reinterpret_cast<{} {}>({})'.format(as_cppType(typ.name), + annotation, arg) + def decorate(name, typ, arg): if arg.annotation == 'value': @@ -378,63 +514,120 @@ def decorate(name, typ, arg): elif arg.annotation == 'const*const*': return 'const ' + typ + '* const * ' + name else: - assert(False) + assert False + def annotated(typ, arg): name = as_varName(arg.name) return decorate(name, typ, arg) + def as_cEnum(type_name, value_name): - assert(not type_name.native and not value_name.native) - return 'DAWN' + '_' + type_name.SNAKE_CASE() + '_' + value_name.SNAKE_CASE() + assert not type_name.native and not value_name.native + return 'WGPU' + type_name.CamelCase() + '_' + value_name.CamelCase() + + +def as_cEnumDawn(type_name, value_name): + assert not type_name.native and not value_name.native + return ('DAWN' + '_' + type_name.SNAKE_CASE() + '_' + + value_name.SNAKE_CASE()) + def as_cppEnum(value_name): - assert(not value_name.native) + assert not value_name.native if value_name.concatcase()[0].isdigit(): return "e" + value_name.CamelCase() return value_name.CamelCase() + def as_cMethod(type_name, method_name): - assert(not type_name.native and not method_name.native) + assert not type_name.native and not method_name.native + return 'wgpu' + type_name.CamelCase() + method_name.CamelCase() + + +def as_cMethodDawn(type_name, method_name): + assert not type_name.native and not method_name.native return 'dawn' + type_name.CamelCase() + method_name.CamelCase() + def as_MethodSuffix(type_name, method_name): - assert(not type_name.native and not method_name.native) + assert not type_name.native and not method_name.native return type_name.CamelCase() + method_name.CamelCase() + def as_cProc(type_name, method_name): - assert(not type_name.native and not method_name.native) + assert not type_name.native and not method_name.native + return 'WGPU' + 'Proc' + type_name.CamelCase() + method_name.CamelCase() + + +def as_cProcDawn(type_name, method_name): + assert not type_name.native and not method_name.native return 'Dawn' + 'Proc' + type_name.CamelCase() + method_name.CamelCase() + def as_frontendType(typ): if typ.category == 'object': return typ.name.CamelCase() + 'Base*' elif typ.category in ['bitmask', 'enum']: - return 'dawn::' + typ.name.CamelCase() + return 'wgpu::' + typ.name.CamelCase() elif typ.category == 'structure': return as_cppType(typ.name) else: return as_cType(typ.name) -def cpp_native_methods(types, typ): - return typ.methods + typ.native_methods -def c_native_methods(types, typ): - return cpp_native_methods(types, typ) + [ +def as_wireType(typ): + if typ.category == 'object': + return typ.name.CamelCase() + '*' + elif typ.category in ['bitmask', 'enum']: + return 'WGPU' + typ.name.CamelCase() + else: + return as_cppType(typ.name) + + +def c_methods(types, typ): + return typ.methods + [ Method(Name('reference'), types['void'], []), Method(Name('release'), types['void'], []), ] + +def get_c_methods_sorted_by_name(api_params): + unsorted = [(as_MethodSuffix(typ.name, method.name), typ, method) \ + for typ in api_params['by_category']['object'] \ + for method in c_methods(api_params['types'], typ) ] + return [(typ, method) for (_, typ, method) in sorted(unsorted)] + + +def has_callback_arguments(method): + return any(arg.type.category == 'callback' for arg in method.arguments) + + class MultiGeneratorFromDawnJSON(Generator): def get_description(self): return 'Generates code for various target from Dawn.json.' def add_commandline_arguments(self, parser): - allowed_targets = ['dawn_headers', 'libdawn', 'mock_dawn', 'dawn_wire', "dawn_native_utils"] - - parser.add_argument('--dawn-json', required=True, type=str, help ='The DAWN JSON definition to use.') - parser.add_argument('--wire-json', default=None, type=str, help='The DAWN WIRE JSON definition to use.') - parser.add_argument('--targets', required=True, type=str, help='Comma-separated subset of targets to output. Available targets: ' + ', '.join(allowed_targets)) + allowed_targets = [ + 'dawn_headers', 'dawncpp_headers', 'dawncpp', 'dawn_proc', + 'mock_webgpu', 'dawn_wire', "dawn_native_utils" + ] + + parser.add_argument('--dawn-json', + required=True, + type=str, + help='The DAWN JSON definition to use.') + parser.add_argument('--wire-json', + default=None, + type=str, + help='The DAWN WIRE JSON definition to use.') + parser.add_argument( + '--targets', + required=True, + type=str, + help= + 'Comma-separated subset of targets to output. Available targets: ' + + ', '.join(allowed_targets)) def get_file_renders(self, args): with open(args.dawn_json) as f: @@ -450,80 +643,165 @@ def get_file_renders(self, args): base_params = { 'Name': lambda name: Name(name), - - 'as_annotated_cType': lambda arg: annotated(as_cType(arg.type.name), arg), - 'as_annotated_cppType': lambda arg: annotated(as_cppType(arg.type.name), arg), + 'as_annotated_cType': \ + lambda arg: annotated(as_cTypeEnumSpecialCase(arg.type), arg), + 'as_annotated_cppType': \ + lambda arg: annotated(as_cppType(arg.type.name), arg), 'as_cEnum': as_cEnum, + 'as_cEnumDawn': as_cEnumDawn, 'as_cppEnum': as_cppEnum, 'as_cMethod': as_cMethod, + 'as_cMethodDawn': as_cMethodDawn, 'as_MethodSuffix': as_MethodSuffix, 'as_cProc': as_cProc, + 'as_cProcDawn': as_cProcDawn, 'as_cType': as_cType, + 'as_cTypeDawn': as_cTypeDawn, 'as_cppType': as_cppType, + 'as_jsEnumValue': as_jsEnumValue, 'convert_cType_to_cppType': convert_cType_to_cppType, 'as_varName': as_varName, 'decorate': decorate, + 'c_methods': lambda typ: c_methods(api_params['types'], typ), + 'c_methods_sorted_by_name': \ + get_c_methods_sorted_by_name(api_params), } renders = [] - c_params = {'native_methods': lambda typ: c_native_methods(api_params['types'], typ)} - cpp_params = {'native_methods': lambda typ: cpp_native_methods(api_params['types'], typ)} - if 'dawn_headers' in targets: - renders.append(FileRender('api.h', 'dawn/dawn.h', [base_params, api_params, c_params])) - renders.append(FileRender('apicpp.h', 'dawn/dawncpp.h', [base_params, api_params, cpp_params])) - - if 'libdawn' in targets: - additional_params = {'native_methods': lambda typ: cpp_native_methods(api_params['types'], typ)} - renders.append(FileRender('api.c', 'dawn/dawn.c', [base_params, api_params, c_params])) - renders.append(FileRender('apicpp.cpp', 'dawn/dawncpp.cpp', [base_params, api_params, cpp_params])) - - if 'mock_dawn' in targets: - renders.append(FileRender('mock_api.h', 'mock/mock_dawn.h', [base_params, api_params, c_params])) - renders.append(FileRender('mock_api.cpp', 'mock/mock_dawn.cpp', [base_params, api_params, c_params])) + renders.append( + FileRender('webgpu.h', 'src/include/dawn/webgpu.h', + [base_params, api_params])) + renders.append( + FileRender('dawn_proc_table.h', + 'src/include/dawn/dawn_proc_table.h', + [base_params, api_params])) + + if 'dawncpp_headers' in targets: + renders.append( + FileRender('webgpu_cpp.h', 'src/include/dawn/webgpu_cpp.h', + [base_params, api_params])) + + if 'dawn_proc' in targets: + renders.append( + FileRender('dawn_proc.c', 'src/dawn/dawn_proc.c', + [base_params, api_params])) + + if 'dawncpp' in targets: + renders.append( + FileRender('webgpu_cpp.cpp', 'src/dawn/webgpu_cpp.cpp', + [base_params, api_params])) + + if 'emscripten_bits' in targets: + renders.append( + FileRender('webgpu_struct_info.json', + 'src/dawn/webgpu_struct_info.json', + [base_params, api_params])) + renders.append( + FileRender('library_webgpu_enum_tables.js', + 'src/dawn/library_webgpu_enum_tables.js', + [base_params, api_params])) + + if 'mock_webgpu' in targets: + mock_params = [ + base_params, api_params, { + 'has_callback_arguments': has_callback_arguments + } + ] + renders.append( + FileRender('mock_webgpu.h', 'src/dawn/mock_webgpu.h', + mock_params)) + renders.append( + FileRender('mock_webgpu.cpp', 'src/dawn/mock_webgpu.cpp', + mock_params)) if 'dawn_native_utils' in targets: frontend_params = [ base_params, api_params, - c_params, { - 'as_frontendType': lambda typ: as_frontendType(typ), # TODO as_frontendType and friends take a Type and not a Name :( - 'as_annotated_frontendType': lambda arg: annotated(as_frontendType(arg.type), arg) + # TODO: as_frontendType and co. take a Type, not a Name :( + 'as_frontendType': lambda typ: as_frontendType(typ), + 'as_annotated_frontendType': \ + lambda arg: annotated(as_frontendType(arg.type), arg), } ] - renders.append(FileRender('dawn_native/ValidationUtils.h', 'dawn_native/ValidationUtils_autogen.h', frontend_params)) - renders.append(FileRender('dawn_native/ValidationUtils.cpp', 'dawn_native/ValidationUtils_autogen.cpp', frontend_params)) - renders.append(FileRender('dawn_native/api_structs.h', 'dawn_native/dawn_structs_autogen.h', frontend_params)) - renders.append(FileRender('dawn_native/api_structs.cpp', 'dawn_native/dawn_structs_autogen.cpp', frontend_params)) - renders.append(FileRender('dawn_native/ProcTable.cpp', 'dawn_native/ProcTable.cpp', frontend_params)) + renders.append( + FileRender('dawn_native/ValidationUtils.h', + 'src/dawn_native/ValidationUtils_autogen.h', + frontend_params)) + renders.append( + FileRender('dawn_native/ValidationUtils.cpp', + 'src/dawn_native/ValidationUtils_autogen.cpp', + frontend_params)) + renders.append( + FileRender('dawn_native/wgpu_structs.h', + 'src/dawn_native/wgpu_structs_autogen.h', + frontend_params)) + renders.append( + FileRender('dawn_native/wgpu_structs.cpp', + 'src/dawn_native/wgpu_structs_autogen.cpp', + frontend_params)) + renders.append( + FileRender('dawn_native/ProcTable.cpp', + 'src/dawn_native/ProcTable.cpp', frontend_params)) if 'dawn_wire' in targets: additional_params = compute_wire_params(api_params, wire_json) wire_params = [ - base_params, - api_params, - c_params, - { - 'as_wireType': lambda typ: typ.name.CamelCase() + '*' if typ.category == 'object' else as_cppType(typ.name) - }, - additional_params + base_params, api_params, { + 'as_wireType': as_wireType, + 'as_annotated_wireType': \ + lambda arg: annotated(as_wireType(arg.type), arg), + }, additional_params ] - renders.append(FileRender('dawn_wire/WireCmd.h', 'dawn_wire/WireCmd_autogen.h', wire_params)) - renders.append(FileRender('dawn_wire/WireCmd.cpp', 'dawn_wire/WireCmd_autogen.cpp', wire_params)) - renders.append(FileRender('dawn_wire/client/ApiObjects.h', 'dawn_wire/client/ApiObjects_autogen.h', wire_params)) - renders.append(FileRender('dawn_wire/client/ApiProcs.cpp', 'dawn_wire/client/ApiProcs_autogen.cpp', wire_params)) - renders.append(FileRender('dawn_wire/client/ApiProcs.h', 'dawn_wire/client/ApiProcs_autogen.h', wire_params)) - renders.append(FileRender('dawn_wire/client/ClientBase.h', 'dawn_wire/client/ClientBase_autogen.h', wire_params)) - renders.append(FileRender('dawn_wire/client/ClientHandlers.cpp', 'dawn_wire/client/ClientHandlers_autogen.cpp', wire_params)) - renders.append(FileRender('dawn_wire/client/ClientPrototypes.inc', 'dawn_wire/client/ClientPrototypes_autogen.inc', wire_params)) - renders.append(FileRender('dawn_wire/server/ServerBase.h', 'dawn_wire/server/ServerBase_autogen.h', wire_params)) - renders.append(FileRender('dawn_wire/server/ServerDoers.cpp', 'dawn_wire/server/ServerDoers_autogen.cpp', wire_params)) - renders.append(FileRender('dawn_wire/server/ServerHandlers.cpp', 'dawn_wire/server/ServerHandlers_autogen.cpp', wire_params)) - renders.append(FileRender('dawn_wire/server/ServerPrototypes.inc', 'dawn_wire/server/ServerPrototypes_autogen.inc', wire_params)) + renders.append( + FileRender('dawn_wire/WireCmd.h', + 'src/dawn_wire/WireCmd_autogen.h', wire_params)) + renders.append( + FileRender('dawn_wire/WireCmd.cpp', + 'src/dawn_wire/WireCmd_autogen.cpp', wire_params)) + renders.append( + FileRender('dawn_wire/client/ApiObjects.h', + 'src/dawn_wire/client/ApiObjects_autogen.h', + wire_params)) + renders.append( + FileRender('dawn_wire/client/ApiProcs.cpp', + 'src/dawn_wire/client/ApiProcs_autogen.cpp', + wire_params)) + renders.append( + FileRender('dawn_wire/client/ClientBase.h', + 'src/dawn_wire/client/ClientBase_autogen.h', + wire_params)) + renders.append( + FileRender('dawn_wire/client/ClientHandlers.cpp', + 'src/dawn_wire/client/ClientHandlers_autogen.cpp', + wire_params)) + renders.append( + FileRender( + 'dawn_wire/client/ClientPrototypes.inc', + 'src/dawn_wire/client/ClientPrototypes_autogen.inc', + wire_params)) + renders.append( + FileRender('dawn_wire/server/ServerBase.h', + 'src/dawn_wire/server/ServerBase_autogen.h', + wire_params)) + renders.append( + FileRender('dawn_wire/server/ServerDoers.cpp', + 'src/dawn_wire/server/ServerDoers_autogen.cpp', + wire_params)) + renders.append( + FileRender('dawn_wire/server/ServerHandlers.cpp', + 'src/dawn_wire/server/ServerHandlers_autogen.cpp', + wire_params)) + renders.append( + FileRender( + 'dawn_wire/server/ServerPrototypes.inc', + 'src/dawn_wire/server/ServerPrototypes_autogen.inc', + wire_params)) return renders @@ -533,5 +811,6 @@ def get_dependencies(self, args): deps += [os.path.abspath(args.wire_json)] return deps + if __name__ == '__main__': sys.exit(run_generator(MultiGeneratorFromDawnJSON())) diff --git a/third_party/dawn/generator/extract_json.py b/third_party/dawn/generator/extract_json.py index 2cc4b3d6f61..4afefae946a 100644 --- a/third_party/dawn/generator/extract_json.py +++ b/third_party/dawn/generator/extract_json.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright 2018 The Dawn Authors # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,7 +24,7 @@ output_dir = sys.argv[2] - for (name, content) in files.iteritems(): + for (name, content) in files.items(): output_file = output_dir + os.path.sep + name directory = os.path.dirname(output_file) diff --git a/third_party/dawn/generator/generator_lib.gni b/third_party/dawn/generator/generator_lib.gni new file mode 100644 index 00000000000..8b9e04cc955 --- /dev/null +++ b/third_party/dawn/generator/generator_lib.gni @@ -0,0 +1,164 @@ +# Copyright 2019 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Template to help invoking code generators based on generator_lib.py +# Internal use only, this should only be called from templates implementing +# generator-specific actions. +# +# Variables: +# script: Path to generator script. +# +# args: List of extra command-line arguments passed to the generator. +# +# outputs: List of expected outputs, generation will fail if there is a +# mistmatch. +# +# deps: additional deps for the code generation targets. +# +# generator_lib_dir: directory where generator_lib.py is located. +# +# custom_gen_dir: Optional custom target gen dir. Defaults to $target_gen_dir +# but allows output files to not depend on the location of the BUILD.gn +# that generates them. +# +# template_dir: Optional template root directory. Defaults to +# "${generator_lib_dir}/templates". +# +# jinja2_path: Optional Jinja2 installation path. +# +# allowed_output_dirs: Optional list of directories that are the only +# directories in which files of `outputs` are allowed to be (and not +# in children directories). Generation will fail if an output isn't +# in a directory in the list. +# +# root_dir: Optional root source dir for Python dependencies +# computation. Defaults to "${generator_lib_dir}/..". Any dependency +# outside of this directory is considered a system file and will be +# omitted. +# +template("generator_lib_action") { + _generator_args = [] + if (defined(invoker.args)) { + _generator_args += invoker.args + } + + assert(defined(invoker.generator_lib_dir), + "generator_lib_dir must be defined before calling this action!") + + _template_dir = "${invoker.generator_lib_dir}/templates" + if (defined(invoker.template_dir)) { + _template_dir = invoker.template_dir + } + _generator_args += [ + "--template-dir", + rebase_path(_template_dir), + ] + + if (defined(invoker.root_dir)) { + _generator_args += [ + "--root-dir", + rebase_path(_root_dir, root_build_dir), + ] + } + + if (defined(invoker.jinja2_path)) { + _generator_args += [ + "--jinja2-path", + rebase_path(invoker.jinja2_path), + ] + } + + # Chooses either the default gen_dir or the custom one required by the + # invoker. This allows moving the definition of code generators in different + # BUILD.gn files without changing the location of generated file. Without + # this generated headers could cause issues when old headers aren't removed. + _gen_dir = target_gen_dir + if (defined(invoker.custom_gen_dir)) { + _gen_dir = invoker.custom_gen_dir + } + + # For build parallelism GN wants to know the exact inputs and outputs of + # action targets like we use for our code generator. We avoid asking the + # generator about its inputs by using the "depfile" feature of GN/Ninja. + # + # A ninja limitation is that the depfile is a subset of Makefile that can + # contain a single target, so we output a single "JSON-tarball" instead. + _json_tarball = "${_gen_dir}/${target_name}.json_tarball" + _json_tarball_target = "${target_name}__json_tarball" + _json_tarball_depfile = "${_json_tarball}.d" + + _generator_args += [ + "--output-json-tarball", + rebase_path(_json_tarball, root_build_dir), + "--depfile", + rebase_path(_json_tarball_depfile, root_build_dir), + ] + + # After the JSON tarball is created we need an action target to extract it + # with a list of its outputs. The invoker provided a list of expected + # outputs. To make sure the list is in sync between the generator and the + # build files, we write it to a file and ask the generator to assert it is + # correct. + _expected_outputs_file = "${_gen_dir}/${target_name}.expected_outputs" + write_file(_expected_outputs_file, invoker.outputs) + + _generator_args += [ + "--expected-outputs-file", + rebase_path(_expected_outputs_file, root_build_dir), + ] + + # Check that all of the outputs are in a directory that's allowed. This is + # useful to keep the list of directories in sink with other parts of the + # build. + if (defined(invoker.allowed_output_dirs)) { + _allowed_output_dirs_file = "${_gen_dir}/${target_name}.allowed_output_dirs" + write_file(_allowed_output_dirs_file, invoker.allowed_output_dirs) + + _generator_args += [ + "--allowed-output-dirs-file", + rebase_path(_allowed_output_dirs_file, root_build_dir), + ] + } + + # The code generator invocation that will write the JSON tarball, check the + # outputs are what's expected and write a depfile for Ninja. + action(_json_tarball_target) { + script = invoker.script + outputs = [ _json_tarball ] + depfile = _json_tarball_depfile + args = _generator_args + if (defined(invoker.deps)) { + deps = invoker.deps + } + } + + # Extract the JSON tarball into the gen_dir + action(target_name) { + script = "${invoker.generator_lib_dir}/extract_json.py" + args = [ + rebase_path(_json_tarball, root_build_dir), + rebase_path(_gen_dir, root_build_dir), + ] + + deps = [ ":${_json_tarball_target}" ] + inputs = [ _json_tarball ] + + # The expected output list is relative to the gen_dir but action + # target outputs are from the root dir so we need to rebase them. + outputs = [] + foreach(source, invoker.outputs) { + outputs += [ "${_gen_dir}/${source}" ] + } + } +} diff --git a/third_party/dawn/generator/generator_lib.py b/third_party/dawn/generator/generator_lib.py index a1723ab583e..5e3734d7833 100644 --- a/third_party/dawn/generator/generator_lib.py +++ b/third_party/dawn/generator/generator_lib.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright 2019 The Dawn Authors # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -12,36 +12,88 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +"""Module to create generators that render multiple Jinja2 templates for GN. + +A helper module that can be used to create generator scripts (clients) +that expand one or more Jinja2 templates, without outputs usable from +GN and Ninja build-based systems. See generator_lib.gni as well. + +Clients should create a Generator sub-class, then call run_generator() +with a proper derived class instance. + +Clients specify a list of FileRender operations, each one of them will +output a file into a temporary output directory through Jinja2 expansion. +All temporary output files are then grouped and written to into a single JSON +file, that acts as a convenient single GN output target. Use extract_json.py +to extract the output files from the JSON tarball in another GN action. + +--depfile can be used to specify an output Ninja dependency file for the +JSON tarball, to ensure it is regenerated any time one of its dependencies +changes. + +Finally, --expected-output-files can be used to check the list of generated +output files. +""" import argparse, json, os, re, sys from collections import namedtuple +# A FileRender represents a single Jinja2 template render operation: +# +# template: Jinja2 template name, relative to --template-dir path. +# +# output: Output file path, relative to temporary output directory. +# +# params_dicts: iterable of (name:string -> value:string) dictionaries. +# All of them will be merged before being sent as Jinja2 template +# expansion parameters. +# +# Example: +# FileRender('api.c', 'src/project_api.c', [{'PROJECT_VERSION': '1.0.0'}]) +# +FileRender = namedtuple('FileRender', ['template', 'output', 'params_dicts']) + + # The interface that must be implemented by generators. class Generator: def get_description(self): + """Return generator description for --help.""" return "" def add_commandline_arguments(self, parser): + """Add generator-specific argparse arguments.""" pass def get_file_renders(self, args): + """Return the list of FileRender objects to process.""" return [] def get_dependencies(self, args): + """Return a list of extra input dependencies.""" return [] -FileRender = namedtuple('FileRender', ['template', 'output', 'params_dicts']) -# Try using an additional python path from the arguments if present. This -# isn't done through the regular argparse because PreprocessingLoader uses -# jinja2 in the global scope before "main" gets to run. -kExtraPythonPath = '--extra-python-path' -if kExtraPythonPath in sys.argv: - path = sys.argv[sys.argv.index(kExtraPythonPath) + 1] +# Allow custom Jinja2 installation path through an additional python +# path from the arguments if present. This isn't done through the regular +# argparse because PreprocessingLoader uses jinja2 in the global scope before +# "main" gets to run. +# +# NOTE: If this argument appears several times, this only uses the first +# value, while argparse would typically keep the last one! +kJinja2Path = '--jinja2-path' +try: + jinja2_path_argv_index = sys.argv.index(kJinja2Path) + # Add parent path for the import to succeed. + path = os.path.join(sys.argv[jinja2_path_argv_index + 1], os.pardir) sys.path.insert(1, path) +except ValueError: + # --jinja2-path isn't passed, ignore the exception and just import Jinja2 + # assuming it already is in the Python PATH. + pass import jinja2 + # A custom Jinja2 template loader that removes the extra indentation # of the template blocks so that the output is correctly indented class _PreprocessingLoader(jinja2.BaseLoader): @@ -57,19 +109,27 @@ def get_source(self, environment, template): source = self.preprocess(f.read()) return source, path, lambda: mtime == os.path.getmtime(path) - blockstart = re.compile('{%-?\s*(if|for|block)[^}]*%}') - blockend = re.compile('{%-?\s*end(if|for|block)[^}]*%}') + blockstart = re.compile('{%-?\s*(if|elif|else|for|block|macro)[^}]*%}') + blockend = re.compile('{%-?\s*(end(if|for|block|macro)|elif|else)[^}]*%}') def preprocess(self, source): lines = source.split('\n') - # Compute the current indentation level of the template blocks and remove their indentation + # Compute the current indentation level of the template blocks and + # remove their indentation result = [] indentation_level = 0 + # Filter lines that are pure comments. line_comment_prefix is not + # enough because it removes the comment but doesn't completely remove + # the line, resulting in more verbose output. + lines = filter(lambda line: not line.strip().startswith('//*'), lines) + + # Remove indentation templates have for the Jinja control flow. for line in lines: - # The capture in the regex adds one element per block start or end so we divide by two - # there is also an extra line chunk corresponding to the line end, so we substract it. + # The capture in the regex adds one element per block start or end, + # so we divide by two. There is also an extra line chunk + # corresponding to the line end, so we subtract it. numends = (len(self.blockend.split(line)) - 1) // 2 indentation_level -= numends @@ -87,14 +147,19 @@ def remove_indentation(self, line, n): elif line.startswith('\t'): line = line[1:] else: - assert(line.strip() == '') + assert line.strip() == '' return line + _FileOutput = namedtuple('FileOutput', ['name', 'content']) + def _do_renders(renders, template_dir): loader = _PreprocessingLoader(template_dir) - env = jinja2.Environment(loader=loader, lstrip_blocks=True, trim_blocks=True, line_comment_prefix='//*') + env = jinja2.Environment(loader=loader, + lstrip_blocks=True, + trim_blocks=True, + line_comment_prefix='//*') def do_assert(expr): assert expr @@ -122,19 +187,23 @@ def debug(text): return outputs + # Compute the list of imported, non-system Python modules. -# It assumes that any path outside of Dawn's root directory is system. -def _compute_python_dependencies(): - dawn_root = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) +# It assumes that any path outside of the root directory is system. +def _compute_python_dependencies(root_dir=None): + if not root_dir: + # Assume this script is under generator/ by default. + root_dir = os.path.join(os.path.dirname(__file__), os.pardir) + root_dir = os.path.abspath(root_dir) module_paths = (module.__file__ for module in sys.modules.values() - if module and hasattr(module, '__file__')) + if module and hasattr(module, '__file__')) paths = set() for path in module_paths: path = os.path.abspath(path) - if not path.startswith(dawn_root): + if not path.startswith(root_dir): continue if (path.endswith('.pyc') @@ -145,22 +214,89 @@ def _compute_python_dependencies(): return paths + def run_generator(generator): parser = argparse.ArgumentParser( - description = generator.get_description(), - formatter_class = argparse.ArgumentDefaultsHelpFormatter + description=generator.get_description(), + formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) - generator.add_commandline_arguments(parser); - parser.add_argument('-t', '--template-dir', default='templates', type=str, help='Directory with template files.') - parser.add_argument(kExtraPythonPath, default=None, type=str, help='Additional python path to set before loading Jinja2') - parser.add_argument('--output-json-tarball', default=None, type=str, help='Name of the "JSON tarball" to create (tar is too annoying to use in python).') - parser.add_argument('--depfile', default=None, type=str, help='Name of the Ninja depfile to create for the JSON tarball') - parser.add_argument('--expected-outputs-file', default=None, type=str, help="File to compare outputs with and fail if it doesn't match") + generator.add_commandline_arguments(parser) + parser.add_argument('--template-dir', + default='templates', + type=str, + help='Directory with template files.') + parser.add_argument( + kJinja2Path, + default=None, + type=str, + help='Additional python path to set before loading Jinja2') + parser.add_argument( + '--output-json-tarball', + default=None, + type=str, + help=('Name of the "JSON tarball" to create (tar is too annoying ' + 'to use in python).')) + parser.add_argument( + '--depfile', + default=None, + type=str, + help='Name of the Ninja depfile to create for the JSON tarball') + parser.add_argument( + '--expected-outputs-file', + default=None, + type=str, + help="File to compare outputs with and fail if it doesn't match") + parser.add_argument( + '--root-dir', + default=None, + type=str, + help=('Optional source root directory for Python dependency ' + 'computations')) + parser.add_argument( + '--allowed-output-dirs-file', + default=None, + type=str, + help=("File containing a list of allowed directories where files " + "can be output.")) + parser.add_argument( + '--print-cmake-dependencies', + default=False, + action="store_true", + help=("Prints a semi-colon separated list of dependencies to " + "stdout and exits.")) + parser.add_argument( + '--print-cmake-outputs', + default=False, + action="store_true", + help=("Prints a semi-colon separated list of outputs to " + "stdout and exits.")) + parser.add_argument('--output-dir', + default=None, + type=str, + help='Directory where to output generate files.') args = parser.parse_args() - renders = generator.get_file_renders(args); + renders = generator.get_file_renders(args) + + # Output a list of all dependencies for CMake or the tarball for GN/Ninja. + if args.depfile != None or args.print_cmake_dependencies: + dependencies = generator.get_dependencies(args) + dependencies += [ + args.template_dir + os.path.sep + render.template + for render in renders + ] + dependencies += _compute_python_dependencies(args.root_dir) + + if args.depfile != None: + with open(args.depfile, 'w') as f: + f.write(args.output_json_tarball + ": " + + " ".join(dependencies)) + + if args.print_cmake_dependencies: + sys.stdout.write(";".join(dependencies)) + return 0 # The caller wants to assert that the outputs are what it expects. # Load the file and compare with our renders. @@ -168,23 +304,50 @@ def run_generator(generator): with open(args.expected_outputs_file) as f: expected = set([line.strip() for line in f.readlines()]) - actual = set() - actual.update([render.output for render in renders]) + actual = {render.output for render in renders} if actual != expected: - print("Wrong expected outputs, caller expected:\n " + repr(list(expected))) - print("Actual output:\n " + repr(list(actual))) + print("Wrong expected outputs, caller expected:\n " + + repr(sorted(expected))) + print("Actual output:\n " + repr(sorted(actual))) return 1 - # Add a any extra Python path before importing Jinja2 so invokers can point - # to a checkout of Jinja2 and note require it to be installed on the system - if args.extra_python_path != None: - sys.path.insert(1, args.extra_python_path) - import jinja2 + # Print the list of all the outputs for cmake. + if args.print_cmake_outputs: + sys.stdout.write(";".join([ + os.path.join(args.output_dir, render.output) for render in renders + ])) + return 0 outputs = _do_renders(renders, args.template_dir) - # Output the tarball and its depfile + # The caller wants to assert that the outputs are only in specific + # directories. + if args.allowed_output_dirs_file != None: + with open(args.allowed_output_dirs_file) as f: + allowed_dirs = set([line.strip() for line in f.readlines()]) + + for directory in allowed_dirs: + if not directory.endswith('/'): + print('Allowed directory entry "{}" doesn\'t ' + 'end with /'.format(directory)) + return 1 + + def check_in_subdirectory(path, directory): + return path.startswith( + directory) and not '/' in path[len(directory):] + + for render in renders: + if not any( + check_in_subdirectory(render.output, directory) + for directory in allowed_dirs): + print('Output file "{}" is not in the allowed directory ' + 'list below:'.format(render.output)) + for directory in sorted(allowed_dirs): + print(' "{}"'.format(directory)) + return 1 + + # Output the JSON tarball if args.output_json_tarball != None: json_root = {} for output in outputs: @@ -193,11 +356,14 @@ def run_generator(generator): with open(args.output_json_tarball, 'w') as f: f.write(json.dumps(json_root)) - # Output a list of all dependencies for the tarball for Ninja. - if args.depfile != None: - dependencies = generator.get_dependencies(args) - dependencies += [args.template_dir + os.path.sep + render.template for render in renders] - dependencies += _compute_python_dependencies() + # Output the files directly. + if args.output_dir != None: + for output in outputs: + output_path = os.path.join(args.output_dir, output.name) + + directory = os.path.dirname(output_path) + if not os.path.exists(directory): + os.makedirs(directory) - with open(args.depfile, 'w') as f: - f.write(args.output_json_tarball + ": " + " ".join(dependencies)) + with open(output_path, 'w') as outfile: + outfile.write(output.content) diff --git a/third_party/dawn/generator/opengl_loader_generator.py b/third_party/dawn/generator/opengl_loader_generator.py index 73af9755b3b..e080c4e800b 100644 --- a/third_party/dawn/generator/opengl_loader_generator.py +++ b/third_party/dawn/generator/opengl_loader_generator.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2 +#!/usr/bin/env python3 # Copyright 2019 The Dawn Authors # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,15 +13,16 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, sys +import os, json, sys from collections import namedtuple import xml.etree.ElementTree as etree from generator_lib import Generator, run_generator, FileRender -class Proc: + +class ProcName: def __init__(self, gl_name, proc_name=None): - assert(gl_name.startswith('gl')) + assert gl_name.startswith('gl') if proc_name == None: proc_name = gl_name[2:] @@ -40,78 +41,240 @@ def PFNPROCNAME(self): def __repr__(self): return 'Proc("{}", "{}")'.format(self.gl_name, self.proc_name) + +ProcParam = namedtuple('ProcParam', ['name', 'type']) + + +class Proc: + def __init__(self, element): + # Type declaration for return values and arguments all have the same + # (weird) format. + # [A][B][C] + # + # Some examples are: + # void glFinish + # GLenumglFenceSync + # const GLubyte *glGetString + # + # This handles all the shapes found in gl.xml except for this one that + # has an array specifier after : + # GLuint baseAndCount[2] + def parse_type_declaration(element): + result = '' + if element.text != None: + result += element.text + ptype = element.find('ptype') + if ptype != None: + result += ptype.text + if ptype.tail != None: + result += ptype.tail + return result.strip() + + proto = element.find('proto') + + self.return_type = parse_type_declaration(proto) + + self.params = [] + for param in element.findall('./param'): + self.params.append( + ProcParam( + param.find('name').text, parse_type_declaration(param))) + + self.gl_name = proto.find('name').text + self.alias = None + if element.find('alias') != None: + self.alias = element.find('alias').attrib['name'] + + def glProcName(self): + return self.gl_name + + def ProcName(self): + assert self.gl_name.startswith('gl') + return self.gl_name[2:] + + def PFNGLPROCNAME(self): + return 'PFN' + self.gl_name.upper() + 'PROC' + + def __repr__(self): + return 'Proc("{}")'.format(self.gl_name) + + +EnumDefine = namedtuple('EnumDefine', ['name', 'value']) Version = namedtuple('Version', ['major', 'minor']) -VersionProcBlock = namedtuple('ProcBlock', ['version', 'procs']) -HeaderProcBlock = namedtuple('ProcBlock', ['description', 'procs']) +VersionBlock = namedtuple('VersionBlock', ['version', 'procs', 'enums']) +HeaderBlock = namedtuple('HeaderBlock', ['description', 'procs', 'enums']) +ExtensionBlock = namedtuple('ExtensionBlock', + ['extension', 'procs', 'enums', 'supported_specs']) + def parse_version(version): return Version(*map(int, version.split('.'))) -def compute_params(root): - # Add proc blocks for OpenGL ES - gles_blocks = [] - for gles_section in root.findall('''feature[@api='gles2']'''): - section_procs = [] - for proc in gles_section.findall('./require/command'): - section_procs.append(Proc(proc.attrib['name'])) - gles_blocks.append(VersionProcBlock(parse_version(gles_section.attrib['number']), section_procs)) + +def compute_params(root, supported_extensions): + # Parse all the commands and enums + all_procs = {} + for command in root.findall('''commands[@namespace='GL']/command'''): + proc = Proc(command) + assert proc.gl_name not in all_procs + all_procs[proc.gl_name] = proc + + all_enums = {} + for enum in root.findall('''enums[@namespace='GL']/enum'''): + enum_name = enum.attrib['name'] + # Special case an enum we'll never use that has different values in GL and GLES + if enum_name == 'GL_ACTIVE_PROGRAM_EXT': + continue + + assert enum_name not in all_enums + all_enums[enum_name] = EnumDefine(enum_name, enum.attrib['value']) # Get the list of all Desktop OpenGL function removed by the Core Profile. core_removed_procs = set() - for removed_section in root.findall('feature/remove'): - assert(removed_section.attrib['profile'] == 'core') - for proc in removed_section.findall('./command'): - core_removed_procs.add(proc.attrib['name']) - - # Add proc blocks for Desktop GL - desktop_gl_blocks = [] - for gl_section in root.findall('''feature[@api='gl']'''): + for proc in root.findall('''feature/remove[@profile='core']/command'''): + core_removed_procs.add(proc.attrib['name']) + + # Get list of enums and procs per OpenGL ES/Desktop OpenGL version + def parse_version_blocks(api, removed_procs=set()): + blocks = [] + for section in root.findall('''feature[@api='{}']'''.format(api)): + section_procs = [] + for command in section.findall('./require/command'): + proc_name = command.attrib['name'] + assert all_procs[proc_name].alias == None + if proc_name not in removed_procs: + section_procs.append(all_procs[proc_name]) + + section_enums = [] + for enum in section.findall('./require/enum'): + section_enums.append(all_enums[enum.attrib['name']]) + + blocks.append( + VersionBlock(parse_version(section.attrib['number']), + section_procs, section_enums)) + + return blocks + + gles_blocks = parse_version_blocks('gles2') + desktop_gl_blocks = parse_version_blocks('gl', core_removed_procs) + + def parse_extension_block(extension): + section = root.find( + '''extensions/extension[@name='{}']'''.format(extension)) + supported_specs = section.attrib['supported'].split('|') section_procs = [] - for proc in gl_section.findall('./require/command'): - if proc.attrib['name'] not in core_removed_procs: - section_procs.append(Proc(proc.attrib['name'])) - desktop_gl_blocks.append(VersionProcBlock(parse_version(gl_section.attrib['number']), section_procs)) + for command in section.findall('./require/command'): + proc_name = command.attrib['name'] + assert all_procs[proc_name].alias == None + if proc_name not in removed_procs: + section_procs.append(all_procs[proc_name]) + + section_enums = [] + for enum in section.findall('./require/enum'): + section_enums.append(all_enums[enum.attrib['name']]) + return ExtensionBlock(extension, section_procs, section_enums, + supported_specs) + + extension_desktop_gl_blocks = [] + extension_gles_blocks = [] + for extension in supported_extensions: + extension_block = parse_extension_block(extension) + if 'gl' in extension_block.supported_specs: + extension_desktop_gl_blocks.append(extension_block) + if 'gles2' in extension_block.supported_specs: + extension_gles_blocks.append(extension_block) + + # Compute the blocks for headers such that there is no duplicate definition already_added_header_procs = set() + already_added_header_enums = set() header_blocks = [] - def add_header_block(description, procs): + + def add_header_block(description, block): block_procs = [] - for proc in procs: + for proc in block.procs: if not proc.glProcName() in already_added_header_procs: already_added_header_procs.add(proc.glProcName()) block_procs.append(proc) - if len(block_procs) > 0: - header_blocks.append(HeaderProcBlock(description, block_procs)) + + block_enums = [] + for enum in block.enums: + if not enum.name in already_added_header_enums: + already_added_header_enums.add(enum.name) + block_enums.append(enum) + + if len(block_procs) > 0 or len(block_enums) > 0: + header_blocks.append( + HeaderBlock(description, block_procs, block_enums)) for block in gles_blocks: - add_header_block('OpenGL ES {}.{}'.format(block.version.major, block.version.minor), block.procs) + add_header_block( + 'OpenGL ES {}.{}'.format(block.version.major, block.version.minor), + block) for block in desktop_gl_blocks: - add_header_block('Desktop OpenGL {}.{}'.format(block.version.major, block.version.minor), block.procs) + add_header_block( + 'Desktop OpenGL {}.{}'.format(block.version.major, + block.version.minor), block) + + for block in extension_desktop_gl_blocks: + add_header_block(block.extension, block) + + for block in extension_gles_blocks: + add_header_block(block.extension, block) return { 'gles_blocks': gles_blocks, 'desktop_gl_blocks': desktop_gl_blocks, + 'extension_desktop_gl_blocks': extension_desktop_gl_blocks, + 'extension_gles_blocks': extension_gles_blocks, 'header_blocks': header_blocks, } + class OpenGLLoaderGenerator(Generator): def get_description(self): return 'Generates code to load OpenGL function pointers' def add_commandline_arguments(self, parser): - parser.add_argument('--gl-xml', required=True, type=str, help ='The Khronos gl.xml to use.') + parser.add_argument('--gl-xml', + required=True, + type=str, + help='The Khronos gl.xml to use.') + parser.add_argument( + '--supported-extensions', + required=True, + type=str, + help= + 'The JSON file that defines the OpenGL and GLES extensions to use.' + ) def get_file_renders(self, args): - params = compute_params(etree.parse(args.gl_xml).getroot()) + supported_extensions = [] + with open(args.supported_extensions) as f: + supported_extensions_json = json.loads(f.read()) + supported_extensions = supported_extensions_json[ + 'supported_extensions'] + + params = compute_params( + etree.parse(args.gl_xml).getroot(), supported_extensions) return [ - FileRender('opengl/OpenGLFunctionsBase.cpp', 'dawn_native/opengl/OpenGLFunctionsBase_autogen.cpp', [params]), - FileRender('opengl/OpenGLFunctionsBase.h', 'dawn_native/opengl/OpenGLFunctionsBase_autogen.h', [params]), + FileRender( + 'opengl/OpenGLFunctionsBase.cpp', + 'src/dawn_native/opengl/OpenGLFunctionsBase_autogen.cpp', + [params]), + FileRender('opengl/OpenGLFunctionsBase.h', + 'src/dawn_native/opengl/OpenGLFunctionsBase_autogen.h', + [params]), + FileRender('opengl/opengl_platform.h', + 'src/dawn_native/opengl/opengl_platform_autogen.h', + [params]), ] def get_dependencies(self, args): return [os.path.abspath(args.gl_xml)] + if __name__ == '__main__': sys.exit(run_generator(OpenGLLoaderGenerator())) diff --git a/third_party/dawn/generator/remove_files.py b/third_party/dawn/generator/remove_files.py new file mode 100644 index 00000000000..6ddf463667d --- /dev/null +++ b/third_party/dawn/generator/remove_files.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# Copyright 2019 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import argparse, glob, os, sys + + +def check_in_subdirectory(path, directory): + return path.startswith(directory) and not '/' in path[len(directory):] + + +def check_is_allowed(path, allowed_dirs): + return any( + check_in_subdirectory(path, directory) for directory in allowed_dirs) + + +def get_all_files_in_dir(find_directory): + result = [] + for (directory, _, files) in os.walk(find_directory): + result += [os.path.join(directory, filename) for filename in files] + return result + + +def run(): + # Parse command line arguments + parser = argparse.ArgumentParser( + description="Removes stale autogenerated files from gen/ directories.") + parser.add_argument( + '--root-dir', + type=str, + help='The root directory, all other paths in files are relative to it.' + ) + parser.add_argument( + '--allowed-output-dirs-file', + type=str, + help='The file containing a list of allowed directories') + parser.add_argument( + '--stale-dirs-file', + type=str, + help= + 'The file containing a list of directories to check for stale files') + parser.add_argument('--stamp', + type=str, + help='A stamp written once this script completes') + args = parser.parse_args() + + root_dir = args.root_dir + stamp_file = args.stamp + + # Load the list of allowed and stale directories + with open(args.allowed_output_dirs_file) as f: + allowed_dirs = set( + [os.path.join(root_dir, line.strip()) for line in f.readlines()]) + + for directory in allowed_dirs: + if not directory.endswith('/'): + print('Allowed directory entry "{}" doesn\'t end with /'.format( + directory)) + return 1 + + with open(args.stale_dirs_file) as f: + stale_dirs = set([line.strip() for line in f.readlines()]) + + # Remove all files in stale dirs that aren't in the allowed dirs. + for stale_dir in stale_dirs: + stale_dir = os.path.join(root_dir, stale_dir) + + for candidate in get_all_files_in_dir(stale_dir): + if not check_is_allowed(candidate, allowed_dirs): + os.remove(candidate) + + # Finished! Write the stamp file so ninja knows to not run this again. + with open(stamp_file, "w") as f: + f.write("") + + return 0 + + +if __name__ == "__main__": + sys.exit(run()) diff --git a/third_party/dawn/generator/templates/.clang-format b/third_party/dawn/generator/templates/.clang-format new file mode 100644 index 00000000000..9d159247d51 --- /dev/null +++ b/third_party/dawn/generator/templates/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false diff --git a/third_party/dawn/generator/templates/dawn_native/ProcTable.cpp b/third_party/dawn/generator/templates/dawn_native/ProcTable.cpp index 186b334c143..88b780c9074 100644 --- a/third_party/dawn/generator/templates/dawn_native/ProcTable.cpp +++ b/third_party/dawn/generator/templates/dawn_native/ProcTable.cpp @@ -12,12 +12,11 @@ //* See the License for the specific language governing permissions and //* limitations under the License. -#include "common/Assert.h" - #include "dawn_native/dawn_platform.h" #include "dawn_native/DawnNative.h" -#include "dawn_native/ErrorData.h" -#include "dawn_native/ValidationUtils_autogen.h" + +#include +#include {% for type in by_category["object"] %} {% if type.name.canonical_case() not in ["texture view"] %} @@ -27,12 +26,22 @@ namespace dawn_native { + // Type aliases to make all frontend types appear as if they have "Base" at the end when some + // of them are actually pure-frontend and don't have the Base. + using CommandEncoderBase = CommandEncoder; + using ComputePassEncoderBase = ComputePassEncoder; + using FenceBase = Fence; + using RenderPassEncoderBase = RenderPassEncoder; + using RenderBundleEncoderBase = RenderBundleEncoder; + using SurfaceBase = Surface; + namespace { + {% for type in by_category["object"] %} - {% for method in native_methods(type) %} + {% for method in c_methods(type) %} {% set suffix = as_MethodSuffix(type.name, method.name) %} - {{as_cType(method.return_type.name)}} CToCpp{{suffix}}( + {{as_cType(method.return_type.name)}} Native{{suffix}}( {{-as_cType(type.name)}} cSelf {%- for arg in method.arguments -%} , {{as_annotated_cType(arg)}} @@ -71,13 +80,68 @@ namespace dawn_native { } {% endfor %} {% endfor %} + + struct ProcEntry { + WGPUProc proc; + const char* name; + }; + static const ProcEntry sProcMap[] = { + {% for (type, method) in c_methods_sorted_by_name %} + { reinterpret_cast(Native{{as_MethodSuffix(type.name, method.name)}}), "{{as_cMethod(type.name, method.name)}}" }, + {% endfor %} + }; + static constexpr size_t sProcMapSize = sizeof(sProcMap) / sizeof(sProcMap[0]); + } + + WGPUInstance NativeCreateInstance(WGPUInstanceDescriptor const* cDescriptor) { + const dawn_native::InstanceDescriptor* descriptor = + reinterpret_cast(cDescriptor); + return reinterpret_cast(InstanceBase::Create(descriptor)); + } + + WGPUProc NativeGetProcAddress(WGPUDevice, const char* procName) { + if (procName == nullptr) { + return nullptr; + } + + const ProcEntry* entry = std::lower_bound(&sProcMap[0], &sProcMap[sProcMapSize], procName, + [](const ProcEntry &a, const char *b) -> bool { + return strcmp(a.name, b) < 0; + } + ); + + if (entry != &sProcMap[sProcMapSize] && strcmp(entry->name, procName) == 0) { + return entry->proc; + } + + // Special case the two free-standing functions of the API. + if (strcmp(procName, "wgpuGetProcAddress") == 0) { + return reinterpret_cast(NativeGetProcAddress); + } + + if (strcmp(procName, "wgpuCreateInstance") == 0) { + return reinterpret_cast(NativeCreateInstance); + } + + return nullptr; + } + + std::vector GetProcMapNamesForTestingInternal() { + std::vector result; + result.reserve(sProcMapSize); + for (const ProcEntry& entry : sProcMap) { + result.push_back(entry.name); + } + return result; } DawnProcTable GetProcsAutogen() { DawnProcTable table; + table.getProcAddress = NativeGetProcAddress; + table.createInstance = NativeCreateInstance; {% for type in by_category["object"] %} - {% for method in native_methods(type) %} - table.{{as_varName(type.name, method.name)}} = CToCpp{{as_MethodSuffix(type.name, method.name)}}; + {% for method in c_methods(type) %} + table.{{as_varName(type.name, method.name)}} = Native{{as_MethodSuffix(type.name, method.name)}}; {% endfor %} {% endfor %} return table; diff --git a/third_party/dawn/generator/templates/dawn_native/ValidationUtils.cpp b/third_party/dawn/generator/templates/dawn_native/ValidationUtils.cpp index ac98da478e0..199371e0e09 100644 --- a/third_party/dawn/generator/templates/dawn_native/ValidationUtils.cpp +++ b/third_party/dawn/generator/templates/dawn_native/ValidationUtils.cpp @@ -17,10 +17,10 @@ namespace dawn_native { {% for type in by_category["enum"] %} - MaybeError Validate{{type.name.CamelCase()}}(dawn::{{as_cppType(type.name)}} value) { + MaybeError Validate{{type.name.CamelCase()}}(wgpu::{{as_cppType(type.name)}} value) { switch (value) { - {% for value in type.values %} - case dawn::{{as_cppType(type.name)}}::{{as_cppEnum(value.name)}}: + {% for value in type.values if value.valid %} + case wgpu::{{as_cppType(type.name)}}::{{as_cppEnum(value.name)}}: return {}; {% endfor %} default: @@ -31,8 +31,8 @@ namespace dawn_native { {% endfor %} {% for type in by_category["bitmask"] %} - MaybeError Validate{{type.name.CamelCase()}}(dawn::{{as_cppType(type.name)}} value) { - if ((value & static_cast(~{{type.full_mask}})) == 0) { + MaybeError Validate{{type.name.CamelCase()}}(wgpu::{{as_cppType(type.name)}} value) { + if ((value & static_cast(~{{type.full_mask}})) == 0) { return {}; } return DAWN_VALIDATION_ERROR("Invalid value for {{as_cType(type.name)}}"); diff --git a/third_party/dawn/generator/templates/dawn_native/ValidationUtils.h b/third_party/dawn/generator/templates/dawn_native/ValidationUtils.h index 0e0d3854cf1..1983249c457 100644 --- a/third_party/dawn/generator/templates/dawn_native/ValidationUtils.h +++ b/third_party/dawn/generator/templates/dawn_native/ValidationUtils.h @@ -15,7 +15,7 @@ #ifndef BACKEND_VALIDATIONUTILS_H_ #define BACKEND_VALIDATIONUTILS_H_ -#include "dawn/dawncpp.h" +#include "dawn/webgpu_cpp.h" #include "dawn_native/Error.h" @@ -23,7 +23,7 @@ namespace dawn_native { // Helper functions to check the value of enums and bitmasks {% for type in by_category["enum"] + by_category["bitmask"] %} - MaybeError Validate{{type.name.CamelCase()}}(dawn::{{as_cppType(type.name)}} value); + MaybeError Validate{{type.name.CamelCase()}}(wgpu::{{as_cppType(type.name)}} value); {% endfor %} } // namespace dawn_native diff --git a/third_party/dawn/generator/templates/dawn_native/wgpu_structs.cpp b/third_party/dawn/generator/templates/dawn_native/wgpu_structs.cpp new file mode 100644 index 00000000000..83f24eea43e --- /dev/null +++ b/third_party/dawn/generator/templates/dawn_native/wgpu_structs.cpp @@ -0,0 +1,46 @@ +//* Copyright 2018 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#include "dawn_native/wgpu_structs_autogen.h" + +namespace dawn_native { + + static_assert(sizeof(ChainedStruct) == sizeof(WGPUChainedStruct), + "sizeof mismatch for ChainedStruct"); + static_assert(alignof(ChainedStruct) == alignof(WGPUChainedStruct), + "alignof mismatch for ChainedStruct"); + static_assert(offsetof(ChainedStruct, nextInChain) == offsetof(WGPUChainedStruct, next), + "offsetof mismatch for ChainedStruct::nextInChain"); + static_assert(offsetof(ChainedStruct, sType) == offsetof(WGPUChainedStruct, sType), + "offsetof mismatch for ChainedStruct::sType"); + + {% for type in by_category["structure"] %} + {% set CppType = as_cppType(type.name) %} + {% set CType = as_cType(type.name) %} + + static_assert(sizeof({{CppType}}) == sizeof({{CType}}), "sizeof mismatch for {{CppType}}"); + static_assert(alignof({{CppType}}) == alignof({{CType}}), "alignof mismatch for {{CppType}}"); + + {% if type.extensible %} + static_assert(offsetof({{CppType}}, nextInChain) == offsetof({{CType}}, nextInChain), + "offsetof mismatch for {{CppType}}::nextInChain"); + {% endif %} + {% for member in type.members %} + {% set memberName = member.name.camelCase() %} + static_assert(offsetof({{CppType}}, {{memberName}}) == offsetof({{CType}}, {{memberName}}), + "offsetof mismatch for {{CppType}}::{{memberName}}"); + {% endfor %} + + {% endfor %} +} diff --git a/third_party/dawn/generator/templates/dawn_native/wgpu_structs.h b/third_party/dawn/generator/templates/dawn_native/wgpu_structs.h new file mode 100644 index 00000000000..887de9e76f2 --- /dev/null +++ b/third_party/dawn/generator/templates/dawn_native/wgpu_structs.h @@ -0,0 +1,67 @@ +//* Copyright 2017 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#ifndef DAWNNATIVE_WGPU_STRUCTS_H_ +#define DAWNNATIVE_WGPU_STRUCTS_H_ + +#include "dawn/webgpu_cpp.h" +#include "dawn_native/Forward.h" + +namespace dawn_native { + +{% macro render_cpp_default_value(member) -%} + {%- if member.annotation in ["*", "const*", "const*const*"] and member.optional -%} + {{" "}}= nullptr + {%- elif member.type.category in ["enum", "bitmask"] and member.default_value != None -%} + {{" "}}= wgpu::{{as_cppType(member.type.name)}}::{{as_cppEnum(Name(member.default_value))}} + {%- elif member.type.category == "native" and member.default_value != None -%} + {{" "}}= {{member.default_value}} + {%- else -%} + {{assert(member.default_value == None)}} + {%- endif -%} +{%- endmacro %} + + struct ChainedStruct { + ChainedStruct const * nextInChain = nullptr; + wgpu::SType sType = wgpu::SType::Invalid; + }; + + {% for type in by_category["structure"] %} + {% if type.chained %} + struct {{as_cppType(type.name)}} : ChainedStruct { + {{as_cppType(type.name)}}() { + sType = wgpu::SType::{{type.name.CamelCase()}}; + } + {% else %} + struct {{as_cppType(type.name)}} { + {% endif %} + {% if type.extensible %} + ChainedStruct const * nextInChain = nullptr; + {% endif %} + {% for member in type.members %} + {% set member_declaration = as_annotated_frontendType(member) + render_cpp_default_value(member) %} + {% if type.chained and loop.first %} + //* Align the first member to ChainedStruct to match the C struct layout. + alignas(ChainedStruct) {{member_declaration}}; + {% else %} + {{member_declaration}}; + {% endif %} + {% endfor %} + }; + + {% endfor %} + +} // namespace dawn_native + +#endif // DAWNNATIVE_WGPU_STRUCTS_H_ diff --git a/third_party/dawn/generator/templates/dawn_proc.c b/third_party/dawn/generator/templates/dawn_proc.c new file mode 100644 index 00000000000..9d77755de74 --- /dev/null +++ b/third_party/dawn/generator/templates/dawn_proc.c @@ -0,0 +1,54 @@ +//* Copyright 2017 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#include "dawn/dawn_proc.h" + +static DawnProcTable procs; + +static DawnProcTable nullProcs; + +void dawnProcSetProcs(const DawnProcTable* procs_) { + if (procs_) { + procs = *procs_; + } else { + procs = nullProcs; + } +} + +WGPUInstance wgpuCreateInstance(WGPUInstanceDescriptor const * descriptor) { + return procs.createInstance(descriptor); +} + +WGPUProc wgpuGetProcAddress(WGPUDevice device, const char* procName) { + return procs.getProcAddress(device, procName); +} + +{% for type in by_category["object"] %} + {% for method in c_methods(type) %} + {{as_cType(method.return_type.name)}} {{as_cMethod(type.name, method.name)}}( + {{-as_cType(type.name)}} {{as_varName(type.name)}} + {%- for arg in method.arguments -%} + , {{as_annotated_cType(arg)}} + {%- endfor -%} + ) { + {% if method.return_type.name.canonical_case() != "void" %}return {% endif %} + procs.{{as_varName(type.name, method.name)}}({{as_varName(type.name)}} + {%- for arg in method.arguments -%} + , {{as_varName(arg.name)}} + {%- endfor -%} + ); + } + {% endfor %} + +{% endfor %} diff --git a/third_party/dawn/generator/templates/dawn_proc_table.h b/third_party/dawn/generator/templates/dawn_proc_table.h new file mode 100644 index 00000000000..197f3001bbb --- /dev/null +++ b/third_party/dawn/generator/templates/dawn_proc_table.h @@ -0,0 +1,32 @@ +//* Copyright 2019 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#ifndef DAWN_DAWN_PROC_TABLE_H_ +#define DAWN_DAWN_PROC_TABLE_H_ + +#include "dawn/webgpu.h" + +typedef struct DawnProcTable { + WGPUProcGetProcAddress getProcAddress; + WGPUProcCreateInstance createInstance; + + {% for type in by_category["object"] %} + {% for method in c_methods(type) %} + {{as_cProc(type.name, method.name)}} {{as_varName(type.name, method.name)}}; + {% endfor %} + + {% endfor %} +} DawnProcTable; + +#endif // DAWN_DAWN_PROC_TABLE_H_ diff --git a/third_party/dawn/generator/templates/dawn_wire/WireCmd.cpp b/third_party/dawn/generator/templates/dawn_wire/WireCmd.cpp index 007bf1da6a7..a914de14248 100644 --- a/third_party/dawn/generator/templates/dawn_wire/WireCmd.cpp +++ b/third_party/dawn/generator/templates/dawn_wire/WireCmd.cpp @@ -15,7 +15,9 @@ #include "dawn_wire/WireCmd_autogen.h" #include "common/Assert.h" +#include "dawn_wire/Wire.h" +#include #include #include @@ -36,6 +38,8 @@ ObjectId {%- elif member.type.category == "structure" -%} {{as_cType(member.type.name)}}Transfer + {%- elif member.type.category == "bitmask" -%} + {{as_cType(member.type.name)}}Flags {%- else -%} {{as_cType(member.type.name)}} {%- endif -%} @@ -52,7 +56,7 @@ {%- set Optional = "Optional" if member.optional else "" -%} {{out}} = provider.Get{{Optional}}Id({{in}}); {% elif member.type.category == "structure"%} - {%- set Provider = ", provider" if member.type.has_dawn_object else "" -%} + {%- set Provider = ", provider" if member.type.may_have_dawn_object else "" -%} {% if member.annotation == "const*const*" %} {{as_cType(member.type.name)}}Serialize(*{{in}}, &{{out}}, buffer{{Provider}}); {% else %} @@ -70,7 +74,7 @@ DESERIALIZE_TRY(resolver.Get{{Optional}}FromId({{in}}, &{{out}})); {%- elif member.type.category == "structure" -%} DESERIALIZE_TRY({{as_cType(member.type.name)}}Deserialize(&{{out}}, &{{in}}, buffer, size, allocator - {%- if member.type.has_dawn_object -%} + {%- if member.type.may_have_dawn_object -%} , resolver {%- endif -%} )); @@ -79,6 +83,15 @@ {%- endif -%} {% endmacro %} +namespace { + + struct WGPUChainedStructTransfer { + WGPUSType sType; + bool hasNext; + }; + +} // anonymous namespace + //* The main [de]serialization macro //* Methods are very similar to structures that have one member corresponding to each arguments. //* This macro takes advantage of the similarity to output [de]serialization code for a record @@ -91,9 +104,15 @@ //* are embedded directly in the structure. Other members are assumed to be in the //* memory directly following the structure in the buffer. struct {{Return}}{{name}}Transfer { + static_assert({{[is_cmd, record.extensible, record.chained].count(True)}} <= 1, + "Record must be at most one of is_cmd, extensible, and chained."); {% if is_cmd %} //* Start the transfer structure with the command ID, so that casting to WireCmd gives the ID. {{Return}}WireCmd commandId; + {% elif record.extensible %} + bool hasNextInChain; + {% elif record.chained %} + WGPUChainedStructTransfer chain; {% endif %} //* Value types are directly in the command, objects being replaced with their IDs. @@ -106,40 +125,63 @@ size_t {{as_varName(member.name)}}Strlen; {% endfor %} - {% for member in members if member.annotation != "value" and member.type.category != "object" %} + {% for member in members if member.optional and member.annotation != "value" and member.type.category != "object" %} bool has_{{as_varName(member.name)}}; {% endfor %} }; + {% if record.chained %} + static_assert(offsetof({{Return}}{{name}}Transfer, chain) == 0, ""); + {% endif %} + //* Returns the required transfer size for `record` in addition to the transfer structure. DAWN_DECLARE_UNUSED size_t {{Return}}{{name}}GetExtraRequiredSize(const {{Return}}{{name}}{{Cmd}}& record) { DAWN_UNUSED(record); size_t result = 0; + //* Gather how much space will be needed for the extension chain. + {% if record.extensible %} + if (record.nextInChain != nullptr) { + result += GetChainedStructExtraRequiredSize(record.nextInChain); + } + {% endif %} + //* Special handling of const char* that have their length embedded directly in the command {% for member in members if member.length == "strlen" %} - result += std::strlen(record.{{as_varName(member.name)}}); + {% set memberName = as_varName(member.name) %} + + {% if member.optional %} + bool has_{{memberName}} = record.{{memberName}} != nullptr; + if (has_{{memberName}}) + {% endif %} + { + result += std::strlen(record.{{memberName}}); + } {% endfor %} //* Gather how much space will be needed for pointer members. - {% for member in members if member.annotation != "value" and member.length != "strlen" %} + {% for member in members if member.length != "strlen" and not member.skip_serialize %} {% if member.type.category != "object" and member.optional %} if (record.{{as_varName(member.name)}} != nullptr) {% endif %} { - size_t memberLength = {{member_length(member, "record.")}}; - result += memberLength * {{member_transfer_sizeof(member)}}; - - //* Structures might contain more pointers so we need to add their extra size as well. - {% if member.type.category == "structure" %} - for (size_t i = 0; i < memberLength; ++i) { - {% if member.annotation == "const*const*" %} - result += {{as_cType(member.type.name)}}GetExtraRequiredSize(*record.{{as_varName(member.name)}}[i]); - {% else %} - result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}}[i]); - {% endif %} - } + {% if member.annotation != "value" %} + size_t memberLength = {{member_length(member, "record.")}}; + result += memberLength * {{member_transfer_sizeof(member)}}; + //* Structures might contain more pointers so we need to add their extra size as well. + {% if member.type.category == "structure" %} + for (size_t i = 0; i < memberLength; ++i) { + {% if member.annotation == "const*const*" %} + result += {{as_cType(member.type.name)}}GetExtraRequiredSize(*record.{{as_varName(member.name)}}[i]); + {% else %} + {{assert(member.annotation == "const*")}} + result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}}[i]); + {% endif %} + } + {% endif %} + {% elif member.type.category == "structure" %} + result += {{as_cType(member.type.name)}}GetExtraRequiredSize(record.{{as_varName(member.name)}}); {% endif %} } {% endfor %} @@ -154,7 +196,7 @@ //* and `provider` to serialize objects. DAWN_DECLARE_UNUSED void {{Return}}{{name}}Serialize(const {{Return}}{{name}}{{Cmd}}& record, {{Return}}{{name}}Transfer* transfer, char** buffer - {%- if record.has_dawn_object -%} + {%- if record.may_have_dawn_object -%} , const ObjectIdProvider& provider {%- endif -%} ) { @@ -171,17 +213,40 @@ {{serialize_member(member, "record." + memberName, "transfer->" + memberName)}} {% endfor %} + {% if record.extensible %} + if (record.nextInChain != nullptr) { + transfer->hasNextInChain = true; + SerializeChainedStruct(record.nextInChain, buffer, provider); + } else { + transfer->hasNextInChain = false; + } + {% endif %} + + {% if record.chained %} + //* Should be set by the root descriptor's call to SerializeChainedStruct. + ASSERT(transfer->chain.sType == {{as_cEnum(types["s type"].name, record.name)}}); + ASSERT(transfer->chain.hasNext == (record.chain.next != nullptr)); + {% endif %} + //* Special handling of const char* that have their length embedded directly in the command {% for member in members if member.length == "strlen" %} {% set memberName = as_varName(member.name) %} + + {% if member.optional %} + bool has_{{memberName}} = record.{{memberName}} != nullptr; + transfer->has_{{memberName}} = has_{{memberName}}; + if (has_{{memberName}}) + {% endif %} + { transfer->{{memberName}}Strlen = std::strlen(record.{{memberName}}); memcpy(*buffer, record.{{memberName}}, transfer->{{memberName}}Strlen); *buffer += transfer->{{memberName}}Strlen; + } {% endfor %} //* Allocate space and write the non-value arguments in it. - {% for member in members if member.annotation != "value" and member.length != "strlen" %} + {% for member in members if member.annotation != "value" and member.length != "strlen" and not member.skip_serialize %} {% set memberName = as_varName(member.name) %} {% if member.type.category != "object" and member.optional %} @@ -192,8 +257,8 @@ { size_t memberLength = {{member_length(member, "record.")}}; auto memberBuffer = reinterpret_cast<{{member_transfer_type(member)}}*>(*buffer); - *buffer += memberLength * {{member_transfer_sizeof(member)}}; + for (size_t i = 0; i < memberLength; ++i) { {{serialize_member(member, "record." + memberName + "[i]", "memberBuffer[i]" )}} } @@ -205,9 +270,9 @@ //* Deserializes `transfer` into `record` getting more serialized data from `buffer` and `size` //* if needed, using `allocator` to store pointed-to values and `resolver` to translate object //* Ids to actual objects. - DAWN_DECLARE_UNUSED DeserializeResult {{Return}}{{name}}Deserialize({{Return}}{{name}}{{Cmd}}* record, const {{Return}}{{name}}Transfer* transfer, - const char** buffer, size_t* size, DeserializeAllocator* allocator - {%- if record.has_dawn_object -%} + DAWN_DECLARE_UNUSED DeserializeResult {{Return}}{{name}}Deserialize({{Return}}{{name}}{{Cmd}}* record, const volatile {{Return}}{{name}}Transfer* transfer, + const volatile char** buffer, size_t* size, DeserializeAllocator* allocator + {%- if record.may_have_dawn_object -%} , const ObjectIdResolver& resolver {%- endif -%} ) { @@ -219,10 +284,6 @@ ASSERT(transfer->commandId == {{Return}}WireCmd::{{name}}); {% endif %} - {% if record.extensible %} - record->nextInChain = nullptr; - {% endif %} - {% if record.derived_method %} record->selfId = transfer->self; {% endif %} @@ -233,17 +294,38 @@ {{deserialize_member(member, "transfer->" + memberName, "record->" + memberName)}} {% endfor %} + {% if record.extensible %} + record->nextInChain = nullptr; + if (transfer->hasNextInChain) { + DESERIALIZE_TRY(DeserializeChainedStruct(&record->nextInChain, buffer, size, allocator, resolver)); + } + {% endif %} + + {% if record.chained %} + //* Should be set by the root descriptor's call to DeserializeChainedStruct. + //* Don't check |record->chain.next| matches because it is not set until the + //* next iteration inside DeserializeChainedStruct. + ASSERT(record->chain.sType == {{as_cEnum(types["s type"].name, record.name)}}); + ASSERT(record->chain.next == nullptr); + {% endif %} + //* Special handling of const char* that have their length embedded directly in the command {% for member in members if member.length == "strlen" %} {% set memberName = as_varName(member.name) %} + + {% if member.optional %} + bool has_{{memberName}} = transfer->has_{{memberName}}; + record->{{memberName}} = nullptr; + if (has_{{memberName}}) + {% endif %} { size_t stringLength = transfer->{{memberName}}Strlen; - const char* stringInBuffer = nullptr; + const volatile char* stringInBuffer = nullptr; DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, stringLength, &stringInBuffer)); char* copiedString = nullptr; DESERIALIZE_TRY(GetSpace(allocator, stringLength + 1, &copiedString)); - memcpy(copiedString, stringInBuffer, stringLength); + std::copy(stringInBuffer, stringInBuffer + stringLength, copiedString); copiedString[stringLength] = '\0'; record->{{memberName}} = copiedString; } @@ -260,7 +342,7 @@ {% endif %} { size_t memberLength = {{member_length(member, "record->")}}; - auto memberBuffer = reinterpret_cast(buffer); + auto memberBuffer = reinterpret_cast(buffer); DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, memberLength, &memberBuffer)); {{as_cType(member.type.name)}}* copiedMembers = nullptr; @@ -298,7 +380,7 @@ } void {{Cmd}}::Serialize(char* buffer - {%- if command.has_dawn_object -%} + {%- if not is_return -%} , const ObjectIdProvider& objectIdProvider {%- endif -%} ) const { @@ -306,22 +388,22 @@ buffer += sizeof({{Name}}Transfer); {{Name}}Serialize(*this, transfer, &buffer - {%- if command.has_dawn_object -%} + {%- if command.may_have_dawn_object -%} , objectIdProvider {%- endif -%} ); } - DeserializeResult {{Cmd}}::Deserialize(const char** buffer, size_t* size, DeserializeAllocator* allocator - {%- if command.has_dawn_object -%} + DeserializeResult {{Cmd}}::Deserialize(const volatile char** buffer, size_t* size, DeserializeAllocator* allocator + {%- if command.may_have_dawn_object -%} , const ObjectIdResolver& resolver {%- endif -%} ) { - const {{Name}}Transfer* transfer = nullptr; + const volatile {{Name}}Transfer* transfer = nullptr; DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, 1, &transfer)); return {{Name}}Deserialize(this, transfer, buffer, size, allocator - {%- if command.has_dawn_object -%} + {%- if command.may_have_dawn_object -%} , resolver {%- endif -%} ); @@ -332,11 +414,36 @@ namespace dawn_wire { // Macro to simplify error handling, similar to DAWN_TRY but for DeserializeResult. #define DESERIALIZE_TRY(EXPR) \ - { \ + do { \ DeserializeResult exprResult = EXPR; \ if (exprResult != DeserializeResult::Success) { \ return exprResult; \ } \ + } while (0) + + ObjectHandle::ObjectHandle() = default; + ObjectHandle::ObjectHandle(ObjectId id, ObjectGeneration generation) + : id(id), generation(generation) { + } + + ObjectHandle::ObjectHandle(const volatile ObjectHandle& rhs) + : id(rhs.id), generation(rhs.generation) { + } + ObjectHandle& ObjectHandle::operator=(const volatile ObjectHandle& rhs) { + id = rhs.id; + generation = rhs.generation; + return *this; + } + + ObjectHandle& ObjectHandle::AssignFrom(const ObjectHandle& rhs) { + id = rhs.id; + generation = rhs.generation; + return *this; + } + ObjectHandle& ObjectHandle::AssignFrom(const volatile ObjectHandle& rhs) { + id = rhs.id; + generation = rhs.generation; + return *this; } namespace { @@ -344,7 +451,7 @@ namespace dawn_wire { // Consumes from (buffer, size) enough memory to contain T[count] and return it in data. // Returns FatalError if not enough memory was available template - DeserializeResult GetPtrFromBuffer(const char** buffer, size_t* size, size_t count, const T** data) { + DeserializeResult GetPtrFromBuffer(const volatile char** buffer, size_t* size, size_t count, const volatile T** data) { constexpr size_t kMaxCountWithoutOverflows = std::numeric_limits::max() / sizeof(T); if (count > kMaxCountWithoutOverflows) { return DeserializeResult::FatalError; @@ -355,7 +462,7 @@ namespace dawn_wire { return DeserializeResult::FatalError; } - *data = reinterpret_cast(*buffer); + *data = reinterpret_cast(*buffer); *buffer += totalSize; *size -= totalSize; @@ -380,6 +487,16 @@ namespace dawn_wire { return DeserializeResult::Success; } + size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct); + void SerializeChainedStruct(WGPUChainedStruct const* chainedStruct, + char** buffer, + const ObjectIdProvider& provider); + DeserializeResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext, + const volatile char** buffer, + size_t* size, + DeserializeAllocator* allocator, + const ObjectIdResolver& resolver); + //* Output structure [de]serialization first because it is used by commands. {% for type in by_category["structure"] %} {% set name = as_cType(type.name) %} @@ -389,6 +506,116 @@ namespace dawn_wire { {% endif %} {% endfor %} + size_t GetChainedStructExtraRequiredSize(const WGPUChainedStruct* chainedStruct) { + ASSERT(chainedStruct != nullptr); + size_t result = 0; + while (chainedStruct != nullptr) { + switch (chainedStruct->sType) { + {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %} + case {{as_cEnum(types["s type"].name, sType.name)}}: { + const auto& typedStruct = *reinterpret_cast<{{as_cType(sType.name)}} const *>(chainedStruct); + result += sizeof({{as_cType(sType.name)}}Transfer); + result += {{as_cType(sType.name)}}GetExtraRequiredSize(typedStruct); + chainedStruct = typedStruct.chain.next; + break; + } + {% endfor %} + default: + // Invalid enum. Reserve space just for the transfer header (sType and hasNext). + // Stop iterating because this is an error. + // TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded. + ASSERT(chainedStruct->sType == WGPUSType_Invalid); + result += sizeof(WGPUChainedStructTransfer); + return result; + } + } + return result; + } + + void SerializeChainedStruct(WGPUChainedStruct const* chainedStruct, + char** buffer, + const ObjectIdProvider& provider) { + ASSERT(chainedStruct != nullptr); + ASSERT(buffer != nullptr); + do { + switch (chainedStruct->sType) { + {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %} + {% set CType = as_cType(sType.name) %} + case {{as_cEnum(types["s type"].name, sType.name)}}: { + + auto* transfer = reinterpret_cast<{{CType}}Transfer*>(*buffer); + transfer->chain.sType = chainedStruct->sType; + transfer->chain.hasNext = chainedStruct->next != nullptr; + + *buffer += sizeof({{CType}}Transfer); + {{CType}}Serialize(*reinterpret_cast<{{CType}} const*>(chainedStruct), transfer, buffer + {%- if types[sType.name.get()].may_have_dawn_object -%} + , provider + {%- endif -%} + ); + + chainedStruct = chainedStruct->next; + } break; + {% endfor %} + default: { + // Invalid enum. Serialize just the transfer header with Invalid as the sType. + // TODO(crbug.com/dawn/369): Unknown sTypes are silently discarded. + ASSERT(chainedStruct->sType == WGPUSType_Invalid); + WGPUChainedStructTransfer* transfer = reinterpret_cast(*buffer); + transfer->sType = WGPUSType_Invalid; + transfer->hasNext = false; + + *buffer += sizeof(WGPUChainedStructTransfer); + return; + } + } + } while (chainedStruct != nullptr); + } + + DeserializeResult DeserializeChainedStruct(const WGPUChainedStruct** outChainNext, + const volatile char** buffer, + size_t* size, + DeserializeAllocator* allocator, + const ObjectIdResolver& resolver) { + bool hasNext; + do { + if (*size < sizeof(WGPUChainedStructTransfer)) { + return DeserializeResult::FatalError; + } + WGPUSType sType = + reinterpret_cast(*buffer)->sType; + switch (sType) { + {% for sType in types["s type"].values if sType.valid and sType.name.CamelCase() not in client_side_structures %} + {% set CType = as_cType(sType.name) %} + case {{as_cEnum(types["s type"].name, sType.name)}}: { + const volatile {{CType}}Transfer* transfer = nullptr; + DESERIALIZE_TRY(GetPtrFromBuffer(buffer, size, 1, &transfer)); + + {{CType}}* outStruct = nullptr; + DESERIALIZE_TRY(GetSpace(allocator, sizeof({{CType}}), &outStruct)); + outStruct->chain.sType = sType; + outStruct->chain.next = nullptr; + + *outChainNext = &outStruct->chain; + outChainNext = &outStruct->chain.next; + + DESERIALIZE_TRY({{CType}}Deserialize(outStruct, transfer, buffer, size, allocator + {%- if types[sType.name.get()].may_have_dawn_object -%} + , resolver + {%- endif -%} + )); + + hasNext = transfer->chain.hasNext; + } break; + {% endfor %} + default: + return DeserializeResult::FatalError; + } + } while (hasNext); + + return DeserializeResult::Success; + } + //* Output [de]serialization helpers for commands {% for command in cmd_records["command"] %} {% set name = command.name.CamelCase() %} @@ -412,4 +639,34 @@ namespace dawn_wire { {{ write_command_serialization_methods(command, True) }} {% endfor %} + // Implementations of serialization/deserialization of WPGUDeviceProperties. + size_t SerializedWGPUDevicePropertiesSize(const WGPUDeviceProperties* deviceProperties) { + return sizeof(WGPUDeviceProperties) + + WGPUDevicePropertiesGetExtraRequiredSize(*deviceProperties); + } + + void SerializeWGPUDeviceProperties(const WGPUDeviceProperties* deviceProperties, + char* serializeBuffer) { + size_t devicePropertiesSize = SerializedWGPUDevicePropertiesSize(deviceProperties); + WGPUDevicePropertiesTransfer* transfer = + reinterpret_cast(serializeBuffer); + serializeBuffer += devicePropertiesSize; + + WGPUDevicePropertiesSerialize(*deviceProperties, transfer, &serializeBuffer); + } + + bool DeserializeWGPUDeviceProperties(WGPUDeviceProperties* deviceProperties, + const volatile char* deserializeBuffer) { + size_t devicePropertiesSize = SerializedWGPUDevicePropertiesSize(deviceProperties); + const volatile WGPUDevicePropertiesTransfer* transfer = nullptr; + if (GetPtrFromBuffer(&deserializeBuffer, &devicePropertiesSize, 1, &transfer) != + DeserializeResult::Success) { + return false; + } + + return WGPUDevicePropertiesDeserialize(deviceProperties, transfer, &deserializeBuffer, + &devicePropertiesSize, + nullptr) == DeserializeResult::Success; + } + } // namespace dawn_wire diff --git a/third_party/dawn/generator/templates/dawn_wire/WireCmd.h b/third_party/dawn/generator/templates/dawn_wire/WireCmd.h index ad3a839e981..3a87795dba5 100644 --- a/third_party/dawn/generator/templates/dawn_wire/WireCmd.h +++ b/third_party/dawn/generator/templates/dawn_wire/WireCmd.h @@ -15,15 +15,30 @@ #ifndef DAWNWIRE_WIRECMD_AUTOGEN_H_ #define DAWNWIRE_WIRECMD_AUTOGEN_H_ -#include +#include namespace dawn_wire { using ObjectId = uint32_t; - using ObjectSerial = uint32_t; + using ObjectGeneration = uint32_t; struct ObjectHandle { ObjectId id; - ObjectSerial serial; + ObjectGeneration generation; + + ObjectHandle(); + ObjectHandle(ObjectId id, ObjectGeneration generation); + + ObjectHandle(const volatile ObjectHandle& rhs); + ObjectHandle& operator=(const volatile ObjectHandle& rhs); + + // MSVC has a bug where it thinks the volatile copy assignment is a duplicate. + // Workaround this by forwarding to a different function AssignFrom. + template + ObjectHandle& operator=(const T& rhs) { + return AssignFrom(rhs); + } + ObjectHandle& AssignFrom(const ObjectHandle& rhs); + ObjectHandle& AssignFrom(const volatile ObjectHandle& rhs); }; enum class DeserializeResult { @@ -87,7 +102,7 @@ namespace dawn_wire { //* Serialize the structure and everything it points to into serializeBuffer which must be //* big enough to contain all the data (as queried from GetRequiredSize). void Serialize(char* serializeBuffer - {%- if command.has_dawn_object -%} + {%- if not is_return_command -%} , const ObjectIdProvider& objectIdProvider {%- endif -%} ) const; @@ -99,8 +114,8 @@ namespace dawn_wire { //* Deserialize returns: //* - Success if everything went well (yay!) //* - FatalError is something bad happened (buffer too small for example) - DeserializeResult Deserialize(const char** buffer, size_t* size, DeserializeAllocator* allocator - {%- if command.has_dawn_object -%} + DeserializeResult Deserialize(const volatile char** buffer, size_t* size, DeserializeAllocator* allocator + {%- if command.may_have_dawn_object -%} , const ObjectIdResolver& resolver {%- endif -%} ); diff --git a/third_party/dawn/generator/templates/dawn_wire/client/ApiObjects.h b/third_party/dawn/generator/templates/dawn_wire/client/ApiObjects.h index 5c2ae3f613c..288c7004de1 100644 --- a/third_party/dawn/generator/templates/dawn_wire/client/ApiObjects.h +++ b/third_party/dawn/generator/templates/dawn_wire/client/ApiObjects.h @@ -16,10 +16,24 @@ #define DAWNWIRE_CLIENT_APIOBJECTS_AUTOGEN_H_ namespace dawn_wire { namespace client { - {% for type in by_category["object"] if not type.name.CamelCase() in client_special_objects %} - struct {{type.name.CamelCase()}} : ObjectBase { - using ObjectBase::ObjectBase; - }; + + {% for type in by_category["object"] %} + {% set Type = type.name.CamelCase() %} + {% if type.name.CamelCase() in client_special_objects %} + class {{Type}}; + {% else %} + struct {{type.name.CamelCase()}} : ObjectBase { + using ObjectBase::ObjectBase; + }; + {% endif %} + + inline {{Type}}* FromAPI(WGPU{{Type}} obj) { + return reinterpret_cast<{{Type}}*>(obj); + } + inline WGPU{{Type}} ToAPI({{Type}}* obj) { + return reinterpret_cast(obj); + } + {% endfor %} }} // namespace dawn_wire::client diff --git a/third_party/dawn/generator/templates/dawn_wire/client/ApiProcs.cpp b/third_party/dawn/generator/templates/dawn_wire/client/ApiProcs.cpp index 5b3fa95444c..3edba1a22b2 100644 --- a/third_party/dawn/generator/templates/dawn_wire/client/ApiProcs.cpp +++ b/third_party/dawn/generator/templates/dawn_wire/client/ApiProcs.cpp @@ -12,11 +12,108 @@ //* See the License for the specific language governing permissions and //* limitations under the License. +#include "common/Log.h" #include "dawn_wire/client/ApiObjects.h" -#include "dawn_wire/client/ApiProcs_autogen.h" #include "dawn_wire/client/Client.h" +#include +#include +#include +#include + namespace dawn_wire { namespace client { + namespace { + + //* Outputs an rvalue that's the number of elements a pointer member points to. + {% macro member_length(member, accessor) -%} + {%- if member.length == "constant" -%} + {{member.constant_length}} + {%- else -%} + {{accessor}}{{as_varName(member.length.name)}} + {%- endif -%} + {%- endmacro %} + + {% for type in by_category["object"] %} + DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} obj) { + return device == reinterpret_cast(obj)->device; + } + + DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) { + ASSERT(count == 0 || obj != nullptr); + for (uint32_t i = 0; i < count; ++i) { + if (!DeviceMatches(device, obj[i])) { + return false; + } + } + return true; + } + {% endfor %} + + bool DeviceMatches(const Device* device, WGPUChainedStruct const* chainedStruct); + + {% for type in by_category["structure"] if type.may_have_dawn_object %} + DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}}& obj) { + {% if type.extensible %} + if (!DeviceMatches(device, obj.nextInChain)) { + return false; + } + {% endif %} + {% for member in type.members if member.type.may_have_dawn_object or member.type.category == "object" %} + {% if member.optional %} + if (obj.{{as_varName(member.name)}} != nullptr) + {% endif %} + { + if (!DeviceMatches(device, obj.{{as_varName(member.name)}} + {%- if member.annotation != "value" and member.length != "strlen" -%} + , {{member_length(member, "obj.")}} + {%- endif -%})) { + return false; + } + } + {% endfor %} + return true; + } + + DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) { + for (uint32_t i = 0; i < count; ++i) { + if (!DeviceMatches(device, obj[i])) { + return false; + } + } + return true; + } + {% endfor %} + + bool DeviceMatches(const Device* device, WGPUChainedStruct const* chainedStruct) { + while (chainedStruct != nullptr) { + switch (chainedStruct->sType) { + {% for sType in types["s type"].values if sType.valid %} + {% set CType = as_cType(sType.name) %} + case {{as_cEnum(types["s type"].name, sType.name)}}: { + {% if types[sType.name.get()].may_have_dawn_object %} + if (!DeviceMatches(device, reinterpret_cast(chainedStruct))) { + return false; + } + {% endif %} + break; + } + {% endfor %} + case WGPUSType_Invalid: + break; + default: + UNREACHABLE(); + dawn::WarningLog() + << "All objects may not be from the same device. " + << "Unknown sType " << chainedStruct->sType << " discarded."; + return false; + } + chainedStruct = chainedStruct->next; + } + return true; + } + + } // anonymous namespace + //* Implementation of the client API functions. {% for type in by_category["object"] %} {% set Type = type.name.CamelCase() %} @@ -24,14 +121,61 @@ namespace dawn_wire { namespace client { {% for method in type.methods %} {% set Suffix = as_MethodSuffix(type.name, method.name) %} - {% if Suffix not in client_handwritten_commands %} - {{as_cType(method.return_type.name)}} Client{{Suffix}}( - {{-cType}} cSelf - {%- for arg in method.arguments -%} - , {{as_annotated_cType(arg)}} - {%- endfor -%} - ) { - auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf); + + {% if Suffix in client_handwritten_commands %} + static + {% endif %} + {{as_cType(method.return_type.name)}} Client{{Suffix}}( + {{-cType}} cSelf + {%- for arg in method.arguments -%} + , {{as_annotated_cType(arg)}} + {%- endfor -%} + ) { + {% if len(method.arguments) > 0 %} + { + bool sameDevice = true; + auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf); + Device* device = self->device; + DAWN_UNUSED(device); + + do { + {% for arg in method.arguments if arg.type.may_have_dawn_object or arg.type.category == "object" %} + {% if arg.optional %} + if ({{as_varName(arg.name)}} != nullptr) + {% endif %} + { + if (!DeviceMatches(device, {{as_varName(arg.name)}} + {%- if arg.annotation != "value" and arg.length != "strlen" -%} + , {{member_length(arg, "")}} + {%- endif -%})) { + sameDevice = false; + break; + } + } + {% endfor %} + } while (false); + + if (DAWN_UNLIKELY(!sameDevice)) { + device->InjectError(WGPUErrorType_Validation, + "All objects must be from the same device."); + {% if method.return_type.category == "object" %} + // Allocate an object without registering it on the server. This is backed by a real allocation on + // the client so commands can be sent with it. But because it's not allocated on the server, it will + // be a fatal error to use it. + auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf); + auto* allocation = self->device->GetClient()->{{method.return_type.name.CamelCase()}}Allocator().New(self->device); + return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get()); + {% elif method.return_type.name.canonical_case() == "void" %} + return; + {% else %} + return {}; + {% endif %} + } + } + {% endif %} + + auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf); + {% if Suffix not in client_handwritten_commands %} Device* device = self->device; {{Suffix}}Cmd cmd; @@ -42,7 +186,7 @@ namespace dawn_wire { namespace client { //* For object creation, store the object ID the client will use for the result. {% if method.return_type.category == "object" %} auto* allocation = self->device->GetClient()->{{method.return_type.name.CamelCase()}}Allocator().New(self->device); - cmd.result = ObjectHandle{allocation->object->id, allocation->serial}; + cmd.result = ObjectHandle{allocation->object->id, allocation->generation}; {% endif %} {% for arg in method.arguments %} @@ -50,15 +194,18 @@ namespace dawn_wire { namespace client { {% endfor %} //* Allocate space to send the command and copy the value args over. - size_t requiredSize = cmd.GetRequiredSize(); - char* allocatedBuffer = static_cast(device->GetClient()->GetCmdSpace(requiredSize)); - cmd.Serialize(allocatedBuffer, *device->GetClient()); + device->GetClient()->SerializeCommand(cmd); {% if method.return_type.category == "object" %} return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get()); {% endif %} - } - {% endif %} + {% else %} + return self->{{method.name.CamelCase()}}( + {%- for arg in method.arguments -%} + {%if not loop.first %}, {% endif %} {{as_varName(arg.name)}} + {%- endfor -%}); + {% endif %} + } {% endfor %} {% if not type.name.canonical_case() == "device" %} @@ -75,10 +222,7 @@ namespace dawn_wire { namespace client { cmd.objectType = ObjectType::{{type.name.CamelCase()}}; cmd.objectId = obj->id; - size_t requiredSize = cmd.GetRequiredSize(); - char* allocatedBuffer = static_cast(obj->device->GetClient()->GetCmdSpace(requiredSize)); - cmd.Serialize(allocatedBuffer); - + obj->device->GetClient()->SerializeCommand(cmd); obj->device->GetClient()->{{type.name.CamelCase()}}Allocator().Free(obj); } @@ -89,6 +233,66 @@ namespace dawn_wire { namespace client { {% endif %} {% endfor %} + namespace { + WGPUInstance ClientCreateInstance(WGPUInstanceDescriptor const* descriptor) { + UNREACHABLE(); + return nullptr; + } + + void ClientDeviceReference(WGPUDevice) { + } + + void ClientDeviceRelease(WGPUDevice) { + } + + struct ProcEntry { + WGPUProc proc; + const char* name; + }; + static const ProcEntry sProcMap[] = { + {% for (type, method) in c_methods_sorted_by_name %} + { reinterpret_cast(Client{{as_MethodSuffix(type.name, method.name)}}), "{{as_cMethod(type.name, method.name)}}" }, + {% endfor %} + }; + static constexpr size_t sProcMapSize = sizeof(sProcMap) / sizeof(sProcMap[0]); + } // anonymous namespace + + WGPUProc ClientGetProcAddress(WGPUDevice, const char* procName) { + if (procName == nullptr) { + return nullptr; + } + + const ProcEntry* entry = std::lower_bound(&sProcMap[0], &sProcMap[sProcMapSize], procName, + [](const ProcEntry &a, const char *b) -> bool { + return strcmp(a.name, b) < 0; + } + ); + + if (entry != &sProcMap[sProcMapSize] && strcmp(entry->name, procName) == 0) { + return entry->proc; + } + + // Special case the two free-standing functions of the API. + if (strcmp(procName, "wgpuGetProcAddress") == 0) { + return reinterpret_cast(ClientGetProcAddress); + } + + if (strcmp(procName, "wgpuCreateInstance") == 0) { + return reinterpret_cast(ClientCreateInstance); + } + + return nullptr; + } + + std::vector GetProcMapNamesForTesting() { + std::vector result; + result.reserve(sProcMapSize); + for (const ProcEntry& entry : sProcMap) { + result.push_back(entry.name); + } + return result; + } + //* Some commands don't have a custom wire format, but need to be handled manually to update //* some client-side state tracking. For these we have two functions: //* - An autogenerated Client{{suffix}} method that sends the command on the wire @@ -96,8 +300,10 @@ namespace dawn_wire { namespace client { //* the autogenerated one, and that will have to call Client{{suffix}} DawnProcTable GetProcs() { DawnProcTable table; + table.getProcAddress = ClientGetProcAddress; + table.createInstance = ClientCreateInstance; {% for type in by_category["object"] %} - {% for method in native_methods(type) %} + {% for method in c_methods(type) %} {% set suffix = as_MethodSuffix(type.name, method.name) %} table.{{as_varName(type.name, method.name)}} = Client{{suffix}}; {% endfor %} diff --git a/third_party/dawn/generator/templates/dawn_wire/client/ClientHandlers.cpp b/third_party/dawn/generator/templates/dawn_wire/client/ClientHandlers.cpp index 60eb9ee1f70..0055e66b8ce 100644 --- a/third_party/dawn/generator/templates/dawn_wire/client/ClientHandlers.cpp +++ b/third_party/dawn/generator/templates/dawn_wire/client/ClientHandlers.cpp @@ -19,7 +19,7 @@ namespace dawn_wire { namespace client { {% for command in cmd_records["return command"] %} - bool Client::Handle{{command.name.CamelCase()}}(const char** commands, size_t* size) { + bool Client::Handle{{command.name.CamelCase()}}(const volatile char** commands, size_t* size) { Return{{command.name.CamelCase()}}Cmd cmd; DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator); @@ -33,8 +33,8 @@ namespace dawn_wire { namespace client { {% if member.type.dict_name == "ObjectHandle" %} {{Type}}* {{name}} = {{Type}}Allocator().GetObject(cmd.{{name}}.id); - uint32_t {{name}}Serial = {{Type}}Allocator().GetSerial(cmd.{{name}}.id); - if ({{name}}Serial != cmd.{{name}}.serial) { + uint32_t {{name}}Generation = {{Type}}Allocator().GetGeneration(cmd.{{name}}.id); + if ({{name}}Generation != cmd.{{name}}.generation) { {{name}} = nullptr; } {% endif %} @@ -53,9 +53,9 @@ namespace dawn_wire { namespace client { } {% endfor %} - const char* Client::HandleCommands(const char* commands, size_t size) { + const volatile char* Client::HandleCommands(const volatile char* commands, size_t size) { while (size >= sizeof(ReturnWireCmd)) { - ReturnWireCmd cmdId = *reinterpret_cast(commands); + ReturnWireCmd cmdId = *reinterpret_cast(commands); bool success = false; switch (cmdId) { diff --git a/third_party/dawn/generator/templates/dawn_wire/client/ClientPrototypes.inc b/third_party/dawn/generator/templates/dawn_wire/client/ClientPrototypes.inc index 6e473039c59..df188965875 100644 --- a/third_party/dawn/generator/templates/dawn_wire/client/ClientPrototypes.inc +++ b/third_party/dawn/generator/templates/dawn_wire/client/ClientPrototypes.inc @@ -14,7 +14,7 @@ //* Return command handlers {% for command in cmd_records["return command"] %} - bool Handle{{command.name.CamelCase()}}(const char** commands, size_t* size); + bool Handle{{command.name.CamelCase()}}(const volatile char** commands, size_t* size); {% endfor %} //* Return command doers @@ -22,9 +22,9 @@ bool Do{{command.name.CamelCase()}}( {%- for member in command.members -%} {%- if member.handle_type -%} - {{as_cppType(member.handle_type.name)}}* {{as_varName(member.name)}} + {{as_wireType(member.handle_type)}} {{as_varName(member.name)}} {%- else -%} - {{as_annotated_cppType(member)}} + {{as_annotated_wireType(member)}} {%- endif -%} {%- if not loop.last -%}, {% endif %} {%- endfor -%} diff --git a/third_party/dawn/generator/templates/dawn_wire/server/ServerBase.h b/third_party/dawn/generator/templates/dawn_wire/server/ServerBase.h index 926c86d173c..fe26311f296 100644 --- a/third_party/dawn/generator/templates/dawn_wire/server/ServerBase.h +++ b/third_party/dawn/generator/templates/dawn_wire/server/ServerBase.h @@ -15,6 +15,7 @@ #ifndef DAWNWIRE_SERVER_SERVERBASE_H_ #define DAWNWIRE_SERVER_SERVERBASE_H_ +#include "dawn/dawn_proc_table.h" #include "dawn_wire/Wire.h" #include "dawn_wire/WireCmd_autogen.h" #include "dawn_wire/WireDeserializeAllocator.h" diff --git a/third_party/dawn/generator/templates/dawn_wire/server/ServerHandlers.cpp b/third_party/dawn/generator/templates/dawn_wire/server/ServerHandlers.cpp index 4a99bb66a14..8dc155e3d78 100644 --- a/third_party/dawn/generator/templates/dawn_wire/server/ServerHandlers.cpp +++ b/third_party/dawn/generator/templates/dawn_wire/server/ServerHandlers.cpp @@ -23,79 +23,77 @@ namespace dawn_wire { namespace server { {% set returns = is_method and method.return_type.name.canonical_case() != "void" %} {% set Suffix = command.name.CamelCase() %} - {% if Suffix not in client_side_commands %} - //* The generic command handlers - bool Server::Handle{{Suffix}}(const char** commands, size_t* size) { - {{Suffix}}Cmd cmd; - DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator - {%- if command.has_dawn_object -%} - , *this - {%- endif -%} - ); + //* The generic command handlers + bool Server::Handle{{Suffix}}(const volatile char** commands, size_t* size) { + {{Suffix}}Cmd cmd; + DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator + {%- if command.may_have_dawn_object -%} + , *this + {%- endif -%} + ); + + if (deserializeResult == DeserializeResult::FatalError) { + return false; + } - if (deserializeResult == DeserializeResult::FatalError) { + {% if Suffix in server_custom_pre_handler_commands %} + if (!PreHandle{{Suffix}}(cmd)) { return false; } + {% endif %} - {% if Suffix in server_custom_pre_handler_commands %} - if (!PreHandle{{Suffix}}(cmd)) { - return false; - } - {% endif %} - - //* Allocate any result objects - {%- for member in command.members if member.is_return_value -%} - {{ assert(member.handle_type) }} - {% set Type = member.handle_type.name.CamelCase() %} - {% set name = as_varName(member.name) %} - - auto* {{name}}Data = {{Type}}Objects().Allocate(cmd.{{name}}.id); - if ({{name}}Data == nullptr) { - return false; - } - {{name}}Data->serial = cmd.{{name}}.serial; - {% endfor %} + //* Allocate any result objects + {%- for member in command.members if member.is_return_value -%} + {{ assert(member.handle_type) }} + {% set Type = member.handle_type.name.CamelCase() %} + {% set name = as_varName(member.name) %} - //* Do command - bool success = Do{{Suffix}}( - {%- for member in command.members -%} - {%- if member.is_return_value -%} - {%- if member.handle_type -%} - &{{as_varName(member.name)}}Data->handle //* Pass the handle of the output object to be written by the doer - {%- else -%} - &cmd.{{as_varName(member.name)}} - {%- endif -%} + auto* {{name}}Data = {{Type}}Objects().Allocate(cmd.{{name}}.id); + if ({{name}}Data == nullptr) { + return false; + } + {{name}}Data->generation = cmd.{{name}}.generation; + {% endfor %} + + //* Do command + bool success = Do{{Suffix}}( + {%- for member in command.members -%} + {%- if member.is_return_value -%} + {%- if member.handle_type -%} + &{{as_varName(member.name)}}Data->handle //* Pass the handle of the output object to be written by the doer {%- else -%} - cmd.{{as_varName(member.name)}} + &cmd.{{as_varName(member.name)}} {%- endif -%} - {%- if not loop.last -%}, {% endif %} - {%- endfor -%} - ); + {%- else -%} + cmd.{{as_varName(member.name)}} + {%- endif -%} + {%- if not loop.last -%}, {% endif %} + {%- endfor -%} + ); - if (!success) { - return false; - } + if (!success) { + return false; + } - {%- for member in command.members if member.is_return_value and member.handle_type -%} - {% set Type = member.handle_type.name.CamelCase() %} - {% set name = as_varName(member.name) %} + {%- for member in command.members if member.is_return_value and member.handle_type -%} + {% set Type = member.handle_type.name.CamelCase() %} + {% set name = as_varName(member.name) %} - {% if Type in server_reverse_lookup_objects %} - //* For created objects, store a mapping from them back to their client IDs - {{Type}}ObjectIdTable().Store({{name}}Data->handle, cmd.{{name}}.id); - {% endif %} - {% endfor %} + {% if Type in server_reverse_lookup_objects %} + //* For created objects, store a mapping from them back to their client IDs + {{Type}}ObjectIdTable().Store({{name}}Data->handle, cmd.{{name}}.id); + {% endif %} + {% endfor %} - return true; - } - {% endif %} + return true; + } {% endfor %} - const char* Server::HandleCommands(const char* commands, size_t size) { + const volatile char* Server::HandleCommands(const volatile char* commands, size_t size) { mProcs.deviceTick(DeviceObjects().Get(1)->handle); while (size >= sizeof(WireCmd)) { - WireCmd cmdId = *reinterpret_cast(commands); + WireCmd cmdId = *reinterpret_cast(commands); bool success = false; switch (cmdId) { diff --git a/third_party/dawn/generator/templates/dawn_wire/server/ServerPrototypes.inc b/third_party/dawn/generator/templates/dawn_wire/server/ServerPrototypes.inc index 03b7b4aa7b4..a9c03e2dbb4 100644 --- a/third_party/dawn/generator/templates/dawn_wire/server/ServerPrototypes.inc +++ b/third_party/dawn/generator/templates/dawn_wire/server/ServerPrototypes.inc @@ -13,9 +13,9 @@ //* limitations under the License. // Command handlers & doers -{% for command in cmd_records["command"] if command.name.CamelCase() not in client_side_commands %} +{% for command in cmd_records["command"] %} {% set Suffix = command.name.CamelCase() %} - bool Handle{{Suffix}}(const char** commands, size_t* size); + bool Handle{{Suffix}}(const volatile char** commands, size_t* size); bool Do{{Suffix}}( {%- for member in command.members -%} diff --git a/third_party/dawn/generator/templates/library_webgpu_enum_tables.js b/third_party/dawn/generator/templates/library_webgpu_enum_tables.js new file mode 100644 index 00000000000..44048ad3a9e --- /dev/null +++ b/third_party/dawn/generator/templates/library_webgpu_enum_tables.js @@ -0,0 +1,35 @@ +//* Copyright 2020 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. +//* +//* +//* This generator is used to produce the number-to-string mappings for +//* Emscripten's library_webgpu.js. +//* https://github.com/emscripten-core/emscripten/blob/master/src/library_webgpu.js +//* + {% for type in by_category["enum"] if type.javascript %} + {{type.name.CamelCase()}}: {% if type.contiguousFromZero -%} + [ + {% for value in type.values %} + {{as_jsEnumValue(value)}}, + {% endfor %} + ] + {%- else -%} + { + {% for value in type.values %} + {{value.value}}: {{as_jsEnumValue(value)}}, + {% endfor %} + } + {%- endif -%} + , + {% endfor %} diff --git a/third_party/dawn/generator/templates/mock_webgpu.cpp b/third_party/dawn/generator/templates/mock_webgpu.cpp new file mode 100644 index 00000000000..0abfe9a93b4 --- /dev/null +++ b/third_party/dawn/generator/templates/mock_webgpu.cpp @@ -0,0 +1,178 @@ +//* Copyright 2017 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#include "mock_webgpu.h" + +using namespace testing; + +namespace { + {% for type in by_category["object"] %} + {% for method in c_methods(type) if len(method.arguments) < 10 %} + {{as_cType(method.return_type.name)}} Forward{{as_MethodSuffix(type.name, method.name)}}( + {{-as_cType(type.name)}} self + {%- for arg in method.arguments -%} + , {{as_annotated_cType(arg)}} + {%- endfor -%} + ) { + auto object = reinterpret_cast(self); + return object->procs->{{as_MethodSuffix(type.name, method.name)}}(self + {%- for arg in method.arguments -%} + , {{as_varName(arg.name)}} + {%- endfor -%} + ); + } + {% endfor %} + + {% endfor %} +} + +ProcTableAsClass::~ProcTableAsClass() { +} + +void ProcTableAsClass::GetProcTableAndDevice(DawnProcTable* table, WGPUDevice* device) { + *device = GetNewDevice(); + + {% for type in by_category["object"] %} + {% for method in c_methods(type) if len(method.arguments) < 10 %} + table->{{as_varName(type.name, method.name)}} = reinterpret_cast<{{as_cProc(type.name, method.name)}}>(Forward{{as_MethodSuffix(type.name, method.name)}}); + {% endfor %} + {% endfor %} +} + +void ProcTableAsClass::DeviceSetUncapturedErrorCallback(WGPUDevice self, + WGPUErrorCallback callback, + void* userdata) { + auto object = reinterpret_cast(self); + object->deviceErrorCallback = callback; + object->userdata = userdata; + + OnDeviceSetUncapturedErrorCallback(self, callback, userdata); +} + +void ProcTableAsClass::DeviceSetDeviceLostCallback(WGPUDevice self, + WGPUDeviceLostCallback callback, + void* userdata) { + auto object = reinterpret_cast(self); + object->deviceLostCallback = callback; + object->userdata = userdata; + + OnDeviceSetDeviceLostCallback(self, callback, userdata); +} + +bool ProcTableAsClass::DevicePopErrorScope(WGPUDevice self, + WGPUErrorCallback callback, + void* userdata) { + return OnDevicePopErrorScopeCallback(self, callback, userdata); +} + +void ProcTableAsClass::BufferMapReadAsync(WGPUBuffer self, + WGPUBufferMapReadCallback callback, + void* userdata) { + auto object = reinterpret_cast(self); + object->mapReadCallback = callback; + object->userdata = userdata; + + OnBufferMapReadAsyncCallback(self, callback, userdata); +} + +void ProcTableAsClass::BufferMapWriteAsync(WGPUBuffer self, + WGPUBufferMapWriteCallback callback, + void* userdata) { + auto object = reinterpret_cast(self); + object->mapWriteCallback = callback; + object->userdata = userdata; + + OnBufferMapWriteAsyncCallback(self, callback, userdata); +} + +void ProcTableAsClass::BufferMapAsync(WGPUBuffer self, + WGPUMapModeFlags mode, + size_t offset, + size_t size, + WGPUBufferMapCallback callback, + void* userdata) { + auto object = reinterpret_cast(self); + object->mapAsyncCallback = callback; + object->userdata = userdata; + + OnBufferMapAsyncCallback(self, callback, userdata); +} + +void ProcTableAsClass::FenceOnCompletion(WGPUFence self, + uint64_t value, + WGPUFenceOnCompletionCallback callback, + void* userdata) { + auto object = reinterpret_cast(self); + object->fenceOnCompletionCallback = callback; + object->userdata = userdata; + + OnFenceOnCompletionCallback(self, value, callback, userdata); +} + +void ProcTableAsClass::CallDeviceErrorCallback(WGPUDevice device, + WGPUErrorType type, + const char* message) { + auto object = reinterpret_cast(device); + object->deviceErrorCallback(type, message, object->userdata); +} + +void ProcTableAsClass::CallDeviceLostCallback(WGPUDevice device, const char* message) { + auto object = reinterpret_cast(device); + object->deviceLostCallback(message, object->userdata); +} + +void ProcTableAsClass::CallMapReadCallback(WGPUBuffer buffer, + WGPUBufferMapAsyncStatus status, + const void* data, + uint64_t dataLength) { + auto object = reinterpret_cast(buffer); + object->mapReadCallback(status, data, dataLength, object->userdata); +} + +void ProcTableAsClass::CallMapWriteCallback(WGPUBuffer buffer, + WGPUBufferMapAsyncStatus status, + void* data, + uint64_t dataLength) { + auto object = reinterpret_cast(buffer); + object->mapWriteCallback(status, data, dataLength, object->userdata); +} + +void ProcTableAsClass::CallMapAsyncCallback(WGPUBuffer buffer, WGPUBufferMapAsyncStatus status) { + auto object = reinterpret_cast(buffer); + object->mapAsyncCallback(status, object->userdata); +} + +void ProcTableAsClass::CallFenceOnCompletionCallback(WGPUFence fence, + WGPUFenceCompletionStatus status) { + auto object = reinterpret_cast(fence); + object->fenceOnCompletionCallback(status, object->userdata); +} + +{% for type in by_category["object"] %} + {{as_cType(type.name)}} ProcTableAsClass::GetNew{{type.name.CamelCase()}}() { + mObjects.emplace_back(new Object); + mObjects.back()->procs = this; + return reinterpret_cast<{{as_cType(type.name)}}>(mObjects.back().get()); + } +{% endfor %} + +MockProcTable::MockProcTable() = default; + +MockProcTable::~MockProcTable() = default; + +void MockProcTable::IgnoreAllReleaseCalls() { + {% for type in by_category["object"] %} + EXPECT_CALL(*this, {{as_MethodSuffix(type.name, Name("release"))}}(_)).Times(AnyNumber()); + {% endfor %} +} diff --git a/third_party/dawn/generator/templates/mock_webgpu.h b/third_party/dawn/generator/templates/mock_webgpu.h new file mode 100644 index 00000000000..0a44248e004 --- /dev/null +++ b/third_party/dawn/generator/templates/mock_webgpu.h @@ -0,0 +1,161 @@ +//* Copyright 2017 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#ifndef MOCK_WEBGPU_H +#define MOCK_WEBGPU_H + +#include +#include +#include + +#include + +// An abstract base class representing a proc table so that API calls can be mocked. Most API calls +// are directly represented by a delete virtual method but others need minimal state tracking to be +// useful as mocks. +class ProcTableAsClass { + public: + virtual ~ProcTableAsClass(); + + void GetProcTableAndDevice(DawnProcTable* table, WGPUDevice* device); + + // Creates an object that can be returned by a mocked call as in WillOnce(Return(foo)). + // It returns an object of the write type that isn't equal to any previously returned object. + // Otherwise some mock expectation could be triggered by two different objects having the same + // value. + {% for type in by_category["object"] %} + {{as_cType(type.name)}} GetNew{{type.name.CamelCase()}}(); + {% endfor %} + + {% for type in by_category["object"] %} + {% for method in type.methods if len(method.arguments) < 10 and not has_callback_arguments(method) %} + virtual {{as_cType(method.return_type.name)}} {{as_MethodSuffix(type.name, method.name)}}( + {{-as_cType(type.name)}} {{as_varName(type.name)}} + {%- for arg in method.arguments -%} + , {{as_annotated_cType(arg)}} + {%- endfor -%} + ) = 0; + {% endfor %} + virtual void {{as_MethodSuffix(type.name, Name("reference"))}}({{as_cType(type.name)}} self) = 0; + virtual void {{as_MethodSuffix(type.name, Name("release"))}}({{as_cType(type.name)}} self) = 0; + {% endfor %} + + // Stores callback and userdata and calls the On* methods + void DeviceSetUncapturedErrorCallback(WGPUDevice self, + WGPUErrorCallback callback, + void* userdata); + void DeviceSetDeviceLostCallback(WGPUDevice self, + WGPUDeviceLostCallback callback, + void* userdata); + bool DevicePopErrorScope(WGPUDevice self, WGPUErrorCallback callback, void* userdata); + void BufferMapReadAsync(WGPUBuffer self, + WGPUBufferMapReadCallback callback, + void* userdata); + void BufferMapWriteAsync(WGPUBuffer self, + WGPUBufferMapWriteCallback callback, + void* userdata); + void BufferMapAsync(WGPUBuffer self, + WGPUMapModeFlags mode, + size_t offset, + size_t size, + WGPUBufferMapCallback callback, + void* userdata); + void FenceOnCompletion(WGPUFence self, + uint64_t value, + WGPUFenceOnCompletionCallback callback, + void* userdata); + + // Special cased mockable methods + virtual void OnDeviceSetUncapturedErrorCallback(WGPUDevice device, + WGPUErrorCallback callback, + void* userdata) = 0; + virtual void OnDeviceSetDeviceLostCallback(WGPUDevice device, + WGPUDeviceLostCallback callback, + void* userdata) = 0; + virtual bool OnDevicePopErrorScopeCallback(WGPUDevice device, + WGPUErrorCallback callback, + void* userdata) = 0; + virtual void OnBufferMapReadAsyncCallback(WGPUBuffer buffer, + WGPUBufferMapReadCallback callback, + void* userdata) = 0; + virtual void OnBufferMapWriteAsyncCallback(WGPUBuffer buffer, + WGPUBufferMapWriteCallback callback, + void* userdata) = 0; + virtual void OnBufferMapAsyncCallback(WGPUBuffer buffer, + WGPUBufferMapCallback callback, + void* userdata) = 0; + virtual void OnFenceOnCompletionCallback(WGPUFence fence, + uint64_t value, + WGPUFenceOnCompletionCallback callback, + void* userdata) = 0; + + // Calls the stored callbacks + void CallDeviceErrorCallback(WGPUDevice device, WGPUErrorType type, const char* message); + void CallDeviceLostCallback(WGPUDevice device, const char* message); + void CallMapReadCallback(WGPUBuffer buffer, WGPUBufferMapAsyncStatus status, const void* data, uint64_t dataLength); + void CallMapWriteCallback(WGPUBuffer buffer, WGPUBufferMapAsyncStatus status, void* data, uint64_t dataLength); + void CallMapAsyncCallback(WGPUBuffer buffer, WGPUBufferMapAsyncStatus status); + void CallFenceOnCompletionCallback(WGPUFence fence, WGPUFenceCompletionStatus status); + + struct Object { + ProcTableAsClass* procs = nullptr; + WGPUErrorCallback deviceErrorCallback = nullptr; + WGPUDeviceLostCallback deviceLostCallback = nullptr; + WGPUBufferMapReadCallback mapReadCallback = nullptr; + WGPUBufferMapWriteCallback mapWriteCallback = nullptr; + WGPUBufferMapCallback mapAsyncCallback = nullptr; + WGPUFenceOnCompletionCallback fenceOnCompletionCallback = nullptr; + void* userdata = 0; + }; + + private: + // Remembers the values returned by GetNew* so they can be freed. + std::vector> mObjects; +}; + +class MockProcTable : public ProcTableAsClass { + public: + MockProcTable(); + ~MockProcTable() override; + + void IgnoreAllReleaseCalls(); + + {% for type in by_category["object"] %} + {% for method in type.methods if len(method.arguments) < 10 and not has_callback_arguments(method) %} + MOCK_METHOD({{as_cType(method.return_type.name)}},{{" "}} + {{-as_MethodSuffix(type.name, method.name)}}, ( + {{-as_cType(type.name)}} {{as_varName(type.name)}} + {%- for arg in method.arguments -%} + , {{as_annotated_cType(arg)}} + {%- endfor -%} + ), (override)); + {% endfor %} + + MOCK_METHOD(void, {{as_MethodSuffix(type.name, Name("reference"))}}, ({{as_cType(type.name)}} self), (override)); + MOCK_METHOD(void, {{as_MethodSuffix(type.name, Name("release"))}}, ({{as_cType(type.name)}} self), (override)); + {% endfor %} + + MOCK_METHOD(void, OnDeviceSetUncapturedErrorCallback, (WGPUDevice device, WGPUErrorCallback callback, void* userdata), (override)); + MOCK_METHOD(void, OnDeviceSetDeviceLostCallback, (WGPUDevice device, WGPUDeviceLostCallback callback, void* userdata), (override)); + MOCK_METHOD(bool, OnDevicePopErrorScopeCallback, (WGPUDevice device, WGPUErrorCallback callback, void* userdata), (override)); + MOCK_METHOD(void, OnBufferMapReadAsyncCallback, (WGPUBuffer buffer, WGPUBufferMapReadCallback callback, void* userdata), (override)); + MOCK_METHOD(void, OnBufferMapWriteAsyncCallback, (WGPUBuffer buffer, WGPUBufferMapWriteCallback callback, void* userdata), (override)); + MOCK_METHOD(void, + OnBufferMapAsyncCallback, + (WGPUBuffer buffer, WGPUBufferMapCallback callback, void* userdata), + (override)); + MOCK_METHOD(void, OnFenceOnCompletionCallback, (WGPUFence fence, uint64_t value, WGPUFenceOnCompletionCallback callback, void* userdata), (override)); +}; + +#endif // MOCK_WEBGPU_H diff --git a/third_party/dawn/generator/templates/opengl/OpenGLFunctionsBase.cpp b/third_party/dawn/generator/templates/opengl/OpenGLFunctionsBase.cpp index 81e6f9f0a73..79fc5b15298 100644 --- a/third_party/dawn/generator/templates/opengl/OpenGLFunctionsBase.cpp +++ b/third_party/dawn/generator/templates/opengl/OpenGLFunctionsBase.cpp @@ -20,7 +20,7 @@ template MaybeError OpenGLFunctionsBase::LoadProc(GetProcAddress getProc, T* memberProc, const char* name) { *memberProc = reinterpret_cast(getProc(name)); if (DAWN_UNLIKELY(memberProc == nullptr)) { - return DAWN_CONTEXT_LOST_ERROR(std::string("Couldn't load GL proc: ") + name); + return DAWN_INTERNAL_ERROR(std::string("Couldn't load GL proc: ") + name); } return {}; } diff --git a/third_party/dawn/generator/templates/opengl/OpenGLFunctionsBase.h b/third_party/dawn/generator/templates/opengl/OpenGLFunctionsBase.h index 8c9ebfdf215..fcaa6d3ad78 100644 --- a/third_party/dawn/generator/templates/opengl/OpenGLFunctionsBase.h +++ b/third_party/dawn/generator/templates/opengl/OpenGLFunctionsBase.h @@ -13,8 +13,7 @@ //* limitations under the License. #include "dawn_native/Error.h" - -#include +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { using GetProcAddress = void* (*) (const char*); @@ -24,7 +23,7 @@ namespace dawn_native { namespace opengl { {% for block in header_blocks %} // {{block.description}} {% for proc in block.procs %} - {{proc.PFNPROCNAME()}} {{proc.ProcName()}} = nullptr; + {{proc.PFNGLPROCNAME()}} {{proc.ProcName()}} = nullptr; {% endfor %} {% endfor%} diff --git a/third_party/dawn/generator/templates/opengl/opengl_platform.h b/third_party/dawn/generator/templates/opengl/opengl_platform.h new file mode 100644 index 00000000000..9b2a53aeacc --- /dev/null +++ b/third_party/dawn/generator/templates/opengl/opengl_platform.h @@ -0,0 +1,72 @@ +//* Copyright 2019 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#include + +using GLvoid = void; +using GLchar = char; +using GLenum = unsigned int; +using GLboolean = unsigned char; +using GLbitfield = unsigned int; +using GLbyte = khronos_int8_t; +using GLshort = short; +using GLint = int; +using GLsizei = int; +using GLubyte = khronos_uint8_t; +using GLushort = unsigned short; +using GLuint = unsigned int; +using GLfloat = khronos_float_t; +using GLclampf = khronos_float_t; +using GLdouble = double; +using GLclampd = double; +using GLfixed = khronos_int32_t; +using GLintptr = khronos_intptr_t; +using GLsizeiptr = khronos_ssize_t; +using GLhalf = unsigned short; +using GLint64 = khronos_int64_t; +using GLuint64 = khronos_uint64_t; +using GLsync = struct __GLsync*; +using GLDEBUGPROC = void(KHRONOS_APIENTRY*)(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam); +using GLDEBUGPROCARB = GLDEBUGPROC; +using GLDEBUGPROCKHR = GLDEBUGPROC; +using GLDEBUGPROCAMD = void(KHRONOS_APIENTRY*)(GLuint id, + GLenum category, + GLenum severity, + GLsizei length, + const GLchar* message, + void* userParam); + +{% for block in header_blocks %} + // {{block.description}} + {% for enum in block.enums %} + #define {{enum.name}} {{enum.value}} + {% endfor %} + + {% for proc in block.procs %} + using {{proc.PFNGLPROCNAME()}} = {{proc.return_type}}(KHRONOS_APIENTRY *)( + {%- for param in proc.params -%} + {%- if not loop.first %}, {% endif -%} + {{param.type}} {{param.name}} + {%- endfor -%} + ); + {% endfor %} + +{% endfor%} +#undef DAWN_GL_APIENTRY diff --git a/third_party/dawn/generator/templates/webgpu.h b/third_party/dawn/generator/templates/webgpu.h new file mode 100644 index 00000000000..4e1c7c8ef34 --- /dev/null +++ b/third_party/dawn/generator/templates/webgpu.h @@ -0,0 +1,173 @@ +//* Copyright 2020 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. +//* +//* +//* This template itself is part of the Dawn source and follows Dawn's license +//* but the generated file is used for "WebGPU native". The template comments +//* using //* at the top of the file are removed during generation such that +//* the resulting file starts with the BSD 3-Clause comment. +//* +//* +// BSD 3-Clause License +// +// Copyright (c) 2019, "WebGPU native" developers +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#ifndef WEBGPU_H_ +#define WEBGPU_H_ + +#if defined(WGPU_SHARED_LIBRARY) +# if defined(_WIN32) +# if defined(WGPU_IMPLEMENTATION) +# define WGPU_EXPORT __declspec(dllexport) +# else +# define WGPU_EXPORT __declspec(dllimport) +# endif +# else // defined(_WIN32) +# if defined(WGPU_IMPLEMENTATION) +# define WGPU_EXPORT __attribute__((visibility("default"))) +# else +# define WGPU_EXPORT +# endif +# endif // defined(_WIN32) +#else // defined(WGPU_SHARED_LIBRARY) +# define WGPU_EXPORT +#endif // defined(WGPU_SHARED_LIBRARY) + +#include +#include +#include + +#define WGPU_WHOLE_SIZE (0xffffffffffffffffULL) + +typedef uint32_t WGPUFlags; + +{% for type in by_category["object"] %} + typedef struct {{as_cType(type.name)}}Impl* {{as_cType(type.name)}}; +{% endfor %} + +{% for type in by_category["enum"] + by_category["bitmask"] %} + typedef enum {{as_cType(type.name)}} { + {% for value in type.values %} + {{as_cEnum(type.name, value.name)}} = 0x{{format(value.value, "08X")}}, + {% endfor %} + {{as_cEnum(type.name, Name("force32"))}} = 0x7FFFFFFF + } {{as_cType(type.name)}}; + {% if type.category == "bitmask" %} + typedef WGPUFlags {{as_cType(type.name)}}Flags; + {% endif %} + +{% endfor %} + +typedef struct WGPUChainedStruct { + struct WGPUChainedStruct const * next; + WGPUSType sType; +} WGPUChainedStruct; + +{% for type in by_category["structure"] %} + typedef struct {{as_cType(type.name)}} { + {% if type.extensible %} + WGPUChainedStruct const * nextInChain; + {% endif %} + {% if type.chained %} + WGPUChainedStruct chain; + {% endif %} + {% for member in type.members %} + {{as_annotated_cType(member)}}; + {% endfor %} + } {{as_cType(type.name)}}; + +{% endfor %} + +#ifdef __cplusplus +extern "C" { +#endif + +{% for type in by_category["callback"] %} + typedef void (*{{as_cType(type.name)}})( + {%- for arg in type.arguments -%} + {% if not loop.first %}, {% endif %}{{as_annotated_cType(arg)}} + {%- endfor -%} + ); +{% endfor %} + +typedef void (*WGPUProc)(void); + +#if !defined(WGPU_SKIP_PROCS) + +typedef WGPUInstance (*WGPUProcCreateInstance)(WGPUInstanceDescriptor const * descriptor); +typedef WGPUProc (*WGPUProcGetProcAddress)(WGPUDevice device, char const * procName); + +{% for type in by_category["object"] if len(c_methods(type)) > 0 %} + // Procs of {{type.name.CamelCase()}} + {% for method in c_methods(type) %} + typedef {{as_cType(method.return_type.name)}} (*{{as_cProc(type.name, method.name)}})( + {{-as_cType(type.name)}} {{as_varName(type.name)}} + {%- for arg in method.arguments -%} + , {{as_annotated_cType(arg)}} + {%- endfor -%} + ); + {% endfor %} + +{% endfor %} +#endif // !defined(WGPU_SKIP_PROCS) + +#if !defined(WGPU_SKIP_DECLARATIONS) + +WGPU_EXPORT WGPUInstance wgpuCreateInstance(WGPUInstanceDescriptor const * descriptor); +WGPU_EXPORT WGPUProc wgpuGetProcAddress(WGPUDevice device, char const * procName); + +{% for type in by_category["object"] if len(c_methods(type)) > 0 %} + // Methods of {{type.name.CamelCase()}} + {% for method in c_methods(type) %} + WGPU_EXPORT {{as_cType(method.return_type.name)}} {{as_cMethod(type.name, method.name)}}( + {{-as_cType(type.name)}} {{as_varName(type.name)}} + {%- for arg in method.arguments -%} + , {{as_annotated_cType(arg)}} + {%- endfor -%} + ); + {% endfor %} + +{% endfor %} +#endif // !defined(WGPU_SKIP_DECLARATIONS) + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBGPU_H_ diff --git a/third_party/dawn/generator/templates/webgpu_cpp.cpp b/third_party/dawn/generator/templates/webgpu_cpp.cpp new file mode 100644 index 00000000000..7d22f640af4 --- /dev/null +++ b/third_party/dawn/generator/templates/webgpu_cpp.cpp @@ -0,0 +1,147 @@ +//* Copyright 2017 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#include "dawn/webgpu_cpp.h" + +namespace wgpu { + + {% for type in by_category["enum"] %} + {% set CppType = as_cppType(type.name) %} + {% set CType = as_cType(type.name) %} + + static_assert(sizeof({{CppType}}) == sizeof({{CType}}), "sizeof mismatch for {{CppType}}"); + static_assert(alignof({{CppType}}) == alignof({{CType}}), "alignof mismatch for {{CppType}}"); + + {% for value in type.values %} + static_assert(static_cast({{CppType}}::{{as_cppEnum(value.name)}}) == {{as_cEnum(type.name, value.name)}}, "value mismatch for {{CppType}}::{{as_cppEnum(value.name)}}"); + {% endfor %} + + {% endfor %} + + {% for type in by_category["bitmask"] %} + {% set CppType = as_cppType(type.name) %} + {% set CType = as_cType(type.name) + "Flags" %} + + static_assert(sizeof({{CppType}}) == sizeof({{CType}}), "sizeof mismatch for {{CppType}}"); + static_assert(alignof({{CppType}}) == alignof({{CType}}), "alignof mismatch for {{CppType}}"); + + {% for value in type.values %} + static_assert(static_cast({{CppType}}::{{as_cppEnum(value.name)}}) == {{as_cEnum(type.name, value.name)}}, "value mismatch for {{CppType}}::{{as_cppEnum(value.name)}}"); + {% endfor %} + + {% endfor %} + + static_assert(sizeof(ChainedStruct) == sizeof(WGPUChainedStruct), + "sizeof mismatch for ChainedStruct"); + static_assert(alignof(ChainedStruct) == alignof(WGPUChainedStruct), + "alignof mismatch for ChainedStruct"); + static_assert(offsetof(ChainedStruct, nextInChain) == offsetof(WGPUChainedStruct, next), + "offsetof mismatch for ChainedStruct::nextInChain"); + static_assert(offsetof(ChainedStruct, sType) == offsetof(WGPUChainedStruct, sType), + "offsetof mismatch for ChainedStruct::sType"); + + {% for type in by_category["structure"] %} + {% set CppType = as_cppType(type.name) %} + {% set CType = as_cType(type.name) %} + + static_assert(sizeof({{CppType}}) == sizeof({{CType}}), "sizeof mismatch for {{CppType}}"); + static_assert(alignof({{CppType}}) == alignof({{CType}}), "alignof mismatch for {{CppType}}"); + + {% if type.extensible %} + static_assert(offsetof({{CppType}}, nextInChain) == offsetof({{CType}}, nextInChain), + "offsetof mismatch for {{CppType}}::nextInChain"); + {% endif %} + {% for member in type.members %} + {% set memberName = member.name.camelCase() %} + static_assert(offsetof({{CppType}}, {{memberName}}) == offsetof({{CType}}, {{memberName}}), + "offsetof mismatch for {{CppType}}::{{memberName}}"); + {% endfor %} + + {% endfor %} + + {% for type in by_category["object"] %} + {% set CppType = as_cppType(type.name) %} + {% set CType = as_cType(type.name) %} + + static_assert(sizeof({{CppType}}) == sizeof({{CType}}), "sizeof mismatch for {{CppType}}"); + static_assert(alignof({{CppType}}) == alignof({{CType}}), "alignof mismatch for {{CppType}}"); + + {% macro render_cpp_method_declaration(type, method) %} + {% set CppType = as_cppType(type.name) %} + {{as_cppType(method.return_type.name)}} {{CppType}}::{{method.name.CamelCase()}}( + {%- for arg in method.arguments -%} + {%- if not loop.first %}, {% endif -%} + {%- if arg.type.category == "object" and arg.annotation == "value" -%} + {{as_cppType(arg.type.name)}} const& {{as_varName(arg.name)}} + {%- else -%} + {{as_annotated_cppType(arg)}} + {%- endif -%} + {%- endfor -%} + ) const + {%- endmacro %} + + {% macro render_cpp_to_c_method_call(type, method) -%} + {{as_cMethod(type.name, method.name)}}(Get() + {%- for arg in method.arguments -%},{{" "}} + {%- if arg.annotation == "value" -%} + {%- if arg.type.category == "object" -%} + {{as_varName(arg.name)}}.Get() + {%- elif arg.type.category == "enum" or arg.type.category == "bitmask" -%} + static_cast<{{as_cType(arg.type.name)}}>({{as_varName(arg.name)}}) + {%- elif arg.type.category in ["callback", "native"] -%} + {{as_varName(arg.name)}} + {%- else -%} + UNHANDLED + {%- endif -%} + {%- else -%} + reinterpret_cast<{{decorate("", as_cType(arg.type.name), arg)}}>({{as_varName(arg.name)}}) + {%- endif -%} + {%- endfor -%} + ) + {%- endmacro %} + + {% for method in type.methods %} + {{render_cpp_method_declaration(type, method)}} { + {% if method.return_type.name.concatcase() == "void" %} + {{render_cpp_to_c_method_call(type, method)}}; + {% else %} + auto result = {{render_cpp_to_c_method_call(type, method)}}; + return {{convert_cType_to_cppType(method.return_type, 'value', 'result') | indent(8)}}; + {% endif %} + } + {% endfor %} + void {{CppType}}::WGPUReference({{CType}} handle) { + if (handle != nullptr) { + {{as_cMethod(type.name, Name("reference"))}}(handle); + } + } + void {{CppType}}::WGPURelease({{CType}} handle) { + if (handle != nullptr) { + {{as_cMethod(type.name, Name("release"))}}(handle); + } + } + + {% endfor %} + + Instance CreateInstance(const InstanceDescriptor* descriptor) { + const WGPUInstanceDescriptor* cDescriptor = + reinterpret_cast(descriptor); + return Instance::Acquire(wgpuCreateInstance(cDescriptor)); + } + + Proc GetProcAddress(Device const& device, const char* procName) { + return reinterpret_cast(wgpuGetProcAddress(device.Get(), procName)); + } + +} diff --git a/third_party/dawn/generator/templates/webgpu_cpp.h b/third_party/dawn/generator/templates/webgpu_cpp.h new file mode 100644 index 00000000000..3dde1e803e3 --- /dev/null +++ b/third_party/dawn/generator/templates/webgpu_cpp.h @@ -0,0 +1,221 @@ +//* Copyright 2017 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. + +#ifndef WEBGPU_CPP_H_ +#define WEBGPU_CPP_H_ + +#include "dawn/webgpu.h" +#include "dawn/EnumClassBitmasks.h" + +namespace wgpu { + + static constexpr uint64_t kWholeSize = WGPU_WHOLE_SIZE; + + {% for type in by_category["enum"] %} + enum class {{as_cppType(type.name)}} : uint32_t { + {% for value in type.values %} + {{as_cppEnum(value.name)}} = 0x{{format(value.value, "08X")}}, + {% endfor %} + }; + + {% endfor %} + + {% for type in by_category["bitmask"] %} + enum class {{as_cppType(type.name)}} : uint32_t { + {% for value in type.values %} + {{as_cppEnum(value.name)}} = 0x{{format(value.value, "08X")}}, + {% endfor %} + }; + + {% endfor %} + + {% for type in by_category["bitmask"] %} + template<> + struct IsDawnBitmask<{{as_cppType(type.name)}}> { + static constexpr bool enable = true; + }; + + {% endfor %} + + using Proc = WGPUProc; + {% for type in by_category["callback"] %} + using {{as_cppType(type.name)}} = {{as_cType(type.name)}}; + {% endfor %} + + {% for type in by_category["object"] %} + class {{as_cppType(type.name)}}; + {% endfor %} + + {% for type in by_category["structure"] %} + struct {{as_cppType(type.name)}}; + {% endfor %} + + template + class ObjectBase { + public: + ObjectBase() = default; + ObjectBase(CType handle): mHandle(handle) { + if (mHandle) Derived::WGPUReference(mHandle); + } + ~ObjectBase() { + if (mHandle) Derived::WGPURelease(mHandle); + } + + ObjectBase(ObjectBase const& other) + : ObjectBase(other.Get()) { + } + Derived& operator=(ObjectBase const& other) { + if (&other != this) { + if (mHandle) Derived::WGPURelease(mHandle); + mHandle = other.mHandle; + if (mHandle) Derived::WGPUReference(mHandle); + } + + return static_cast(*this); + } + + ObjectBase(ObjectBase&& other) { + mHandle = other.mHandle; + other.mHandle = 0; + } + Derived& operator=(ObjectBase&& other) { + if (&other != this) { + if (mHandle) Derived::WGPURelease(mHandle); + mHandle = other.mHandle; + other.mHandle = 0; + } + + return static_cast(*this); + } + + ObjectBase(std::nullptr_t) {} + Derived& operator=(std::nullptr_t) { + if (mHandle != nullptr) { + Derived::WGPURelease(mHandle); + mHandle = nullptr; + } + return static_cast(*this); + } + + bool operator==(std::nullptr_t) const { + return mHandle == nullptr; + } + bool operator!=(std::nullptr_t) const { + return mHandle != nullptr; + } + + explicit operator bool() const { + return mHandle != nullptr; + } + CType Get() const { + return mHandle; + } + CType Release() { + CType result = mHandle; + mHandle = 0; + return result; + } + static Derived Acquire(CType handle) { + Derived result; + result.mHandle = handle; + return result; + } + + protected: + CType mHandle = nullptr; + }; + +{% macro render_cpp_default_value(member) -%} + {%- if member.annotation in ["*", "const*", "const*const*"] and member.optional -%} + {{" "}}= nullptr + {%- elif member.type.category in ["enum", "bitmask"] and member.default_value != None -%} + {{" "}}= {{as_cppType(member.type.name)}}::{{as_cppEnum(Name(member.default_value))}} + {%- elif member.type.category == "native" and member.default_value != None -%} + {{" "}}= {{member.default_value}} + {%- else -%} + {{assert(member.default_value == None)}} + {%- endif -%} +{%- endmacro %} + +{% macro render_cpp_method_declaration(type, method) %} + {% set CppType = as_cppType(type.name) %} + {{as_cppType(method.return_type.name)}} {{method.name.CamelCase()}}( + {%- for arg in method.arguments -%} + {%- if not loop.first %}, {% endif -%} + {%- if arg.type.category == "object" and arg.annotation == "value" -%} + {{as_cppType(arg.type.name)}} const& {{as_varName(arg.name)}} + {%- else -%} + {{as_annotated_cppType(arg)}} + {%- endif -%} + {{render_cpp_default_value(arg)}} + {%- endfor -%} + ) const +{%- endmacro %} + + {% for type in by_category["object"] %} + {% set CppType = as_cppType(type.name) %} + {% set CType = as_cType(type.name) %} + class {{CppType}} : public ObjectBase<{{CppType}}, {{CType}}> { + public: + using ObjectBase::ObjectBase; + using ObjectBase::operator=; + + {% for method in type.methods %} + {{render_cpp_method_declaration(type, method)}}; + {% endfor %} + + private: + friend ObjectBase<{{CppType}}, {{CType}}>; + static void WGPUReference({{CType}} handle); + static void WGPURelease({{CType}} handle); + }; + + {% endfor %} + + Instance CreateInstance(InstanceDescriptor const * descriptor = nullptr); + Proc GetProcAddress(Device const& device, const char* procName); + + struct ChainedStruct { + ChainedStruct const * nextInChain = nullptr; + SType sType = SType::Invalid; + }; + + {% for type in by_category["structure"] %} + {% if type.chained %} + struct {{as_cppType(type.name)}} : ChainedStruct { + {{as_cppType(type.name)}}() { + sType = SType::{{type.name.CamelCase()}}; + } + {% else %} + struct {{as_cppType(type.name)}} { + {% endif %} + {% if type.extensible %} + ChainedStruct const * nextInChain = nullptr; + {% endif %} + {% for member in type.members %} + {% set member_declaration = as_annotated_cppType(member) + render_cpp_default_value(member) %} + {% if type.chained and loop.first %} + //* Align the first member to ChainedStruct to match the C struct layout. + alignas(ChainedStruct) {{member_declaration}}; + {% else %} + {{member_declaration}}; + {% endif %} + {% endfor %} + }; + + {% endfor %} + +} // namespace wgpu + +#endif // WEBGPU_CPP_H_ diff --git a/third_party/dawn/generator/templates/webgpu_struct_info.json b/third_party/dawn/generator/templates/webgpu_struct_info.json new file mode 100644 index 00000000000..c4b40001ccd --- /dev/null +++ b/third_party/dawn/generator/templates/webgpu_struct_info.json @@ -0,0 +1,50 @@ +//* Copyright 2020 The Dawn Authors +//* +//* Licensed under the Apache License, Version 2.0 (the "License"); +//* you may not use this file except in compliance with the License. +//* You may obtain a copy of the License at +//* +//* http://www.apache.org/licenses/LICENSE-2.0 +//* +//* Unless required by applicable law or agreed to in writing, software +//* distributed under the License is distributed on an "AS IS" BASIS, +//* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +//* See the License for the specific language governing permissions and +//* limitations under the License. +//* +//* +//* This generator is used to produce part of Emscripten's struct_info.json, +//* which is a list of struct fields that it uses to generate field offset +//* information for its own code generators. +//* https://github.com/emscripten-core/emscripten/blob/master/src/struct_info.json +//* + { + "file": "webgpu/webgpu.h", + "defines": [], + "structs": { + "WGPUChainedStruct": [ + "next", + "sType" + ], + {% for type in by_category["structure"] if type.javascript %} + "{{as_cType(type.name)}}": [ + {% if type.chained %} + "chain" + {%- elif type.extensible %} + "nextInChain" + {%- endif %} + {% for member in type.members -%} + {%- if (type.chained or type.extensible) or not loop.first -%} + , + {% endif %} + "{{as_varName(member.name)}}" + {%- endfor %} + + ] + {%- if not loop.last -%} + , + {% endif %} + {% endfor %} + + } + } diff --git a/third_party/dawn/infra/config/PRESUBMIT.py b/third_party/dawn/infra/config/PRESUBMIT.py new file mode 100644 index 00000000000..de9b7eb0236 --- /dev/null +++ b/third_party/dawn/infra/config/PRESUBMIT.py @@ -0,0 +1,23 @@ +# Copyright 2018 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +def CheckChangeOnUpload(input_api, output_api): + return input_api.canned_checks.CheckChangedLUCIConfigs( + input_api, output_api) + + +def CheckChangeOnCommit(input_api, output_api): + return input_api.canned_checks.CheckChangedLUCIConfigs( + input_api, output_api) diff --git a/third_party/dawn/infra/config/global/commit-queue.cfg b/third_party/dawn/infra/config/global/commit-queue.cfg index a01d0d71445..89dac1fbf14 100644 --- a/third_party/dawn/infra/config/global/commit-queue.cfg +++ b/third_party/dawn/infra/config/global/commit-queue.cfg @@ -9,6 +9,7 @@ submit_options { } } config_groups { + name: "Dawn-CQ" gerrit { url: "https://dawn-review.googlesource.com" projects { @@ -60,26 +61,6 @@ config_groups { name: "chromium/try/win-dawn-rel" } - # Experimental builders - # These are experimental while Dawn's API is changing and we expect - # to break integration with Chromium - builders { - name: "chromium/try/dawn-linux-x64-deps-rel" - experiment_percentage: 100 - } - builders { - name: "chromium/try/dawn-mac-x64-deps-rel" - experiment_percentage: 100 - } - builders { - name: "chromium/try/dawn-win10-x64-deps-rel" - experiment_percentage: 100 - } - builders { - name: "chromium/try/dawn-win10-x86-deps-rel" - experiment_percentage: 100 - } - retry_config { single_quota: 1 global_quota: 2 diff --git a/third_party/dawn/infra/config/global/cr-buildbucket.cfg b/third_party/dawn/infra/config/global/cr-buildbucket.cfg index 6dca5f64169..8f9ffbad4da 100644 --- a/third_party/dawn/infra/config/global/cr-buildbucket.cfg +++ b/third_party/dawn/infra/config/global/cr-buildbucket.cfg @@ -70,13 +70,52 @@ builder_mixins { properties: "target_cpu:x64" } } +builder_mixins { + name: "linux", + dimensions: "os:Ubuntu-16.04" + recipe { + properties_j: < newest_mtime): + newest_binary = binary_path + newest_mtime = binary_mtime + +perftests_path = newest_binary + +if perftests_path == None or not os.path.exists(perftests_path): + print('Cannot find Release %s!' % binary_name) + sys.exit(1) + +if len(sys.argv) >= 2: + test_name = sys.argv[1] + +print('Using test executable: ' + perftests_path) +print('Test name: ' + test_name) + + +def get_results(metric, extra_args=[]): + process = subprocess.Popen( + [perftests_path, '--gtest_filter=' + test_name] + extra_args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + output, err = process.communicate() + + m = re.search(r'Running (\d+) tests', output) + if m and int(m.group(1)) > 1: + print("Found more than one test result in output:") + print(output) + sys.exit(3) + + pattern = metric + r'.*= ([0-9.]+)' + m = re.findall(pattern, output) + if not m: + print("Did not find the metric '%s' in the test output:" % metric) + print(output) + sys.exit(1) + + return [float(value) for value in m] + + +# Calibrate the number of steps +steps = get_results("steps", ["--calibration"])[0] +print("running with %d steps." % steps) + +# Loop 'max_experiments' times, running the tests. +for experiment in range(max_experiments): + experiment_scores = get_results(metric, ["--override-steps", str(steps)]) + + for score in experiment_scores: + sys.stdout.write("%s: %.2f" % (metric, score)) + scores.append(score) + + if (len(scores) > 1): + sys.stdout.write(", mean: %.2f" % mean(scores)) + sys.stdout.write(", variation: %.2f%%" % + (coefficient_of_variation(scores) * 100.0)) + + if (len(scores) > 7): + truncation_n = len(scores) >> 3 + sys.stdout.write(", truncated mean: %.2f" % + truncated_mean(scores, truncation_n)) + sys.stdout.write(", variation: %.2f%%" % + (truncated_cov(scores, truncation_n) * 100.0)) + + print("") diff --git a/third_party/dawn/scripts/roll-shader-deps.sh b/third_party/dawn/scripts/roll-shader-deps.sh new file mode 100755 index 00000000000..db16800867b --- /dev/null +++ b/third_party/dawn/scripts/roll-shader-deps.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +# Copyright 2019 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Attempts to roll all entries in DEPS to tip-of-tree and create a commit. +# +# Depends on roll-dep from depot_path being in PATH. + +glslang_dir="third_party/glslang/" +glslang_trunk="origin/master" +shaderc_dir="third_party/shaderc/" +shaderc_trunk="origin/main" +spirv_cross_dir="third_party/spirv-cross/" +spirv_cross_trunk="origin/master" +spirv_headers_dir="third_party/spirv-headers/" +spirv_headers_trunk="origin/master" +spirv_tools_dir="third_party/SPIRV-Tools/" +spirv_tools_trunk="origin/master" +tint_dir="third_party/tint/" +tint_trunk="origin/main" + +# This script assumes it's parent directory is the repo root. +repo_path=$(dirname "$0")/.. + +cd "$repo_path" + +if [[ $(git diff --stat) != '' ]]; then + echo "Working tree is dirty, commit changes before attempting to roll DEPS" + exit 1 +fi + +old_head=$(git rev-parse HEAD) + +roll-dep --ignore-dirty-tree --roll-to="${glslang_trunk}" "${glslang_dir}" +roll-dep --ignore-dirty-tree --roll-to="${shaderc_trunk}" "${shaderc_dir}" +roll-dep --ignore-dirty-tree --roll-to="${spirv_cross_trunk}" "${spirv_cross_dir}" +roll-dep --ignore-dirty-tree --roll-to="${spirv_headers_trunk}" "${spirv_headers_dir}" +roll-dep --ignore-dirty-tree --roll-to="${spirv_tools_trunk}" "${spirv_tools_dir}" +roll-dep --ignore-dirty-tree --roll-to="${tint_trunk}" "${tint_dir}" + +git rebase --interactive "${old_head}" diff --git a/third_party/dawn/scripts/update_fuzzer_seed_corpus.sh b/third_party/dawn/scripts/update_fuzzer_seed_corpus.sh new file mode 100755 index 00000000000..966c1d6c89b --- /dev/null +++ b/third_party/dawn/scripts/update_fuzzer_seed_corpus.sh @@ -0,0 +1,98 @@ +#!/bin/bash + +# Copyright 2019 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Generates a seed corpus for fuzzing based on dumping wire traces +# from running Dawn tests + +# Exit if anything fails +set -e + +if [ "$#" -lt 3 ]; then +cat << EOF + +Usage: + $0 [additional_test_args...] + +Example: + $0 out/fuzz dawn_wire_server_and_vulkan_backend_fuzzer dawn_end2end_tests --gtest_filter=*Vulkan + +EOF + exit 1 +fi + +all_args=("$@") +out_dir=$1 +fuzzer_name=$2 +test_name=$3 +additional_test_args=("${all_args[@]:3}") + +testcase_dir="/tmp/testcases/${fuzzer_name}/" +injected_error_testcase_dir="/tmp/testcases/${fuzzer_name}_injected/" +minimized_testcase_dir="/tmp/testcases/${fuzzer_name}_minimized/" + +# Print commands so it's clear what is being executed +set -x + +# Make a directory for temporarily storing testcases +mkdir -p "$testcase_dir" + +# Make an empty directory for temporarily storing testcases with injected errors +rm -rf "$injected_error_testcase_dir" +mkdir -p "$injected_error_testcase_dir" + +# Make an empty directory for temporarily storing minimized testcases +rm -rf "$minimized_testcase_dir" +mkdir -p "$minimized_testcase_dir" + +# Build the fuzzer and test +autoninja -C $out_dir $fuzzer_name $test_name + +fuzzer_binary="${out_dir}/${fuzzer_name}" +test_binary="${out_dir}/${test_name}" + +# Run the test binary +$test_binary --use-wire --wire-trace-dir="$testcase_dir" $additional_test_args + +# Run the fuzzer over the testcases to inject errors +$fuzzer_binary --injected-error-testcase-dir="$injected_error_testcase_dir" -runs=0 "$testcase_dir" + +# Run the fuzzer to minimize the testcases + injected errors +$fuzzer_binary -merge=1 "$minimized_testcase_dir" "$injected_error_testcase_dir" "$testcase_dir" + +# Turn off command printing +set +x + +if [ -z "$(ls -A $minimized_testcase_dir)" ]; then +cat << EOF + +Minimized testcase directory is empty! +Are you building with use_libfuzzer=true ? + +EOF + exit 1 +fi + +cat << EOF + +Please test the corpus in $minimized_testcase_dir with $fuzzer_name and confirm it works as expected. + + $fuzzer_binary $minimized_testcase_dir + +Then, run the following command to upload new testcases to the seed corpus: + + gsutil -m rsync $minimized_testcase_dir gs://clusterfuzz-corpus/libfuzzer/${fuzzer_name}/ + +EOF diff --git a/third_party/dawn/src/Dummy.cpp b/third_party/dawn/src/Dummy.cpp new file mode 100644 index 00000000000..5959a87bb60 --- /dev/null +++ b/third_party/dawn/src/Dummy.cpp @@ -0,0 +1,18 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// CMake requires that targets contain at least on file. This file is used when we want to create +// empty targets. + +int someSymbolToMakeXCodeHappy = 0; diff --git a/third_party/dawn/src/common/Assert.cpp b/third_party/dawn/src/common/Assert.cpp index fc88fd2a8e7..8802c202f38 100644 --- a/third_party/dawn/src/common/Assert.cpp +++ b/third_party/dawn/src/common/Assert.cpp @@ -13,14 +13,19 @@ // limitations under the License. #include "common/Assert.h" +#include "common/Log.h" -#include +#include void HandleAssertionFailure(const char* file, const char* function, int line, const char* condition) { - std::cerr << "Assertion failure at " << file << ":" << line << " (" << function - << "): " << condition << std::endl; + dawn::ErrorLog() << "Assertion failure at " << file << ":" << line << " (" << function + << "): " << condition; +#if defined(DAWN_ABORT_ON_ASSERT) + abort(); +#else DAWN_BREAKPOINT(); +#endif } diff --git a/third_party/dawn/src/common/BUILD.gn b/third_party/dawn/src/common/BUILD.gn index c61f8722101..c924a164e57 100644 --- a/third_party/dawn/src/common/BUILD.gn +++ b/third_party/dawn/src/common/BUILD.gn @@ -25,25 +25,40 @@ if (build_with_chromium) { dcheck_always_on = false } +if (build_with_chromium) { + import("//build/config/sanitizers/sanitizers.gni") +} else { + use_fuzzing_engine = false +} + ############################################################################### # Common dawn configs ############################################################################### config("dawn_public_include_dirs") { include_dirs = [ - "${target_gen_dir}/../..", + "${target_gen_dir}/../../src/include", "${dawn_root}/src/include", ] } config("dawn_internal") { - include_dirs = [ "${dawn_root}/src" ] + include_dirs = [ + "${target_gen_dir}/../../src", + "${dawn_root}/src", + ] defines = [] - if (dawn_always_assert || dcheck_always_on || is_debug) { + if (dawn_always_assert || dcheck_always_on || is_debug || + use_fuzzing_engine) { defines += [ "DAWN_ENABLE_ASSERTS" ] } + if (use_fuzzing_engine) { + # Does a hard abort when an assertion fails so that fuzzers catch and parse the failure. + defines += [ "DAWN_ABORT_ON_ASSERT" ] + } + if (dawn_enable_d3d12) { defines += [ "DAWN_ENABLE_BACKEND_D3D12" ] } @@ -60,9 +75,63 @@ config("dawn_internal") { defines += [ "DAWN_ENABLE_BACKEND_VULKAN" ] } + if (dawn_use_x11) { + defines += [ "DAWN_USE_X11" ] + } + + if (dawn_enable_error_injection) { + defines += [ "DAWN_ENABLE_ERROR_INJECTION" ] + } + # Only internal Dawn targets can use this config, this means only targets in # this BUILD.gn file. visibility = [ ":*" ] + cflags = [] + + # Enable more warnings that were found when using Dawn in other projects + if (is_clang) { + cflags += [ + "-Wconditional-uninitialized", + "-Wcstring-format-directive", + "-Wc++11-narrowing", + "-Wdeprecated-copy", + "-Wextra-semi-stmt", + "-Wimplicit-fallthrough", + "-Winconsistent-missing-destructor-override", + "-Winvalid-offsetof", + "-Wmissing-field-initializers", + "-Wnon-c-typedef-for-linkage", + "-Wpessimizing-move", + "-Wreturn-std-move-in-c++11", + "-Wshadow-field", + "-Wstrict-prototypes", + "-Wtautological-unsigned-zero-compare", + ] + + # Allow comparison against type limits that might be tautological on 32bit + # or 64bit systems. Without this the following produces an error on 64bit: + # + # if (myUint64 > std::numeric_limits::max()) {...} + cflags += [ "-Wno-tautological-type-limit-compare" ] + + if (is_win) { + cflags += [ + # clang-cl doesn't know -pedantic, pass it explicitly to the clang driver + "/clang:-pedantic", + + # Allow the use of __uuidof() + "-Wno-language-extension-token", + ] + } else { + cflags += [ "-pedantic" ] + } + } + + if (!is_clang && is_win) { + # Dawn extends wgpu enums with internal enums. + # MSVC considers these invalid switch values. crbug.com/dawn/397. + cflags += [ "/wd4063" ] + } } ############################################################################### @@ -72,7 +141,7 @@ config("dawn_internal") { # This GN file is discovered by all Chromium builds, but common doesn't support # all of Chromium's OSes so we explicitly make the target visible only on # systems we know Dawn is able to compile on. -if (is_win || is_linux || is_mac) { +if (is_win || is_linux || is_mac || is_fuchsia || is_android) { static_library("common") { sources = [ "Assert.cpp", @@ -82,23 +151,49 @@ if (is_win || is_linux || is_mac) { "Constants.h", "DynamicLib.cpp", "DynamicLib.h", + "GPUInfo.cpp", + "GPUInfo.h", "HashUtils.h", + "LinkedList.h", + "Log.cpp", + "Log.h", "Math.cpp", "Math.h", + "PlacementAllocated.h", "Platform.h", + "RefCounted.cpp", + "RefCounted.h", + "Result.cpp", "Result.h", "Serial.h", "SerialMap.h", "SerialQueue.h", "SerialStorage.h", + "SlabAllocator.cpp", + "SlabAllocator.h", + "StackContainer.h", "SwapChainUtils.h", + "SystemUtils.cpp", + "SystemUtils.h", + "TypedInteger.h", + "UnderlyingType.h", + "ityp_array.h", + "ityp_bitset.h", + "ityp_span.h", + "ityp_stack_vec.h", + "ityp_vector.h", "vulkan_platform.h", "windows_with_undefs.h", + "xlib_with_undefs.h", ] - configs += [ ":dawn_internal" ] - deps = [ - "${dawn_root}/src/dawn:dawn_headers", - ] + public_configs = [ ":dawn_internal" ] + deps = [ "${dawn_root}/src/dawn:dawn_headers" ] + if (dawn_enable_vulkan) { + public_deps = [ "${dawn_root}/third_party/khronos:vulkan_headers" ] + } + if (is_android) { + libs = [ "log" ] + } } } diff --git a/third_party/dawn/src/common/BitSetIterator.h b/third_party/dawn/src/common/BitSetIterator.h index 432433a9403..14ba4856ff7 100644 --- a/third_party/dawn/src/common/BitSetIterator.h +++ b/third_party/dawn/src/common/BitSetIterator.h @@ -17,6 +17,7 @@ #include "common/Assert.h" #include "common/Math.h" +#include "common/UnderlyingType.h" #include #include @@ -44,14 +45,17 @@ class BitSetIterator final { bool operator==(const Iterator& other) const; bool operator!=(const Iterator& other) const; + T operator*() const { - return static_cast(mCurrentBit); + using U = UnderlyingType; + ASSERT(static_cast(mCurrentBit) <= std::numeric_limits::max()); + return static_cast(static_cast(mCurrentBit)); } private: unsigned long getNextBit(); - static const size_t BitsPerWord = sizeof(uint32_t) * 8; + static constexpr size_t kBitsPerWord = sizeof(uint32_t) * 8; std::bitset mBits; unsigned long mCurrentBit; unsigned long mOffset; @@ -88,7 +92,7 @@ BitSetIterator::Iterator::Iterator(const std::bitset& bits) if (bits.any()) { mCurrentBit = getNextBit(); } else { - mOffset = static_cast(roundUp(N, BitsPerWord)); + mOffset = static_cast(roundUp(N, kBitsPerWord)); } } @@ -120,8 +124,8 @@ unsigned long BitSetIterator::Iterator::getNextBit() { return ScanForward(wordBits) + mOffset; } - mBits >>= BitsPerWord; - mOffset += BitsPerWord; + mBits >>= kBitsPerWord; + mOffset += kBitsPerWord; } return 0; } diff --git a/third_party/dawn/src/common/CMakeLists.txt b/third_party/dawn/src/common/CMakeLists.txt new file mode 100644 index 00000000000..a6f320e33fe --- /dev/null +++ b/third_party/dawn/src/common/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_library(dawn_common STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawn_common PRIVATE + "Assert.cpp" + "Assert.h" + "BitSetIterator.h" + "Compiler.h" + "Constants.h" + "DynamicLib.cpp" + "DynamicLib.h" + "GPUInfo.cpp" + "GPUInfo.h" + "HashUtils.h" + "LinkedList.h" + "Log.cpp" + "Log.h" + "Math.cpp" + "Math.h" + "PlacementAllocated.h" + "Platform.h" + "RefCounted.cpp" + "RefCounted.h" + "Result.cpp" + "Result.h" + "Serial.h" + "SerialMap.h" + "SerialQueue.h" + "SerialStorage.h" + "SlabAllocator.cpp" + "SlabAllocator.h" + "StackContainer.h" + "SwapChainUtils.h" + "SystemUtils.cpp" + "SystemUtils.h" + "TypedInteger.h" + "UnderlyingType.h" + "ityp_array.h" + "ityp_bitset.h" + "ityp_span.h" + "ityp_stack_vec.h" + "ityp_vector.h" + "vulkan_platform.h" + "windows_with_undefs.h" + "xlib_with_undefs.h" +) +target_link_libraries(dawn_common PRIVATE dawn_internal_config) + +# TODO Android Log support +# TODO Vulkan headers support diff --git a/third_party/dawn/src/common/Compiler.h b/third_party/dawn/src/common/Compiler.h index 3bbcee6a188..49e6db4c5e1 100644 --- a/third_party/dawn/src/common/Compiler.h +++ b/third_party/dawn/src/common/Compiler.h @@ -61,6 +61,9 @@ # endif # define DAWN_DECLARE_UNUSED __attribute__((unused)) +# if defined(NDEBUG) +# define DAWN_FORCE_INLINE inline __attribute__((always_inline)) +# endif // MSVC #elif defined(_MSC_VER) @@ -77,6 +80,9 @@ extern void __cdecl __debugbreak(void); # endif # define DAWN_DECLARE_UNUSED +# if defined(NDEBUG) +# define DAWN_FORCE_INLINE __forceinline +# endif #else # error "Unsupported compiler" @@ -97,5 +103,14 @@ extern void __cdecl __debugbreak(void); #if !defined(DAWN_NO_DISCARD) # define DAWN_NO_DISCARD #endif +#if !defined(DAWN_FORCE_INLINE) +# define DAWN_FORCE_INLINE inline +#endif + +#if defined(__clang__) +# define DAWN_FALLTHROUGH [[clang::fallthrough]] +#else +# define DAWN_FALLTHROUGH +#endif #endif // COMMON_COMPILER_H_ diff --git a/third_party/dawn/src/common/Constants.h b/third_party/dawn/src/common/Constants.h index 722244dd175..7576d665c7a 100644 --- a/third_party/dawn/src/common/Constants.h +++ b/third_party/dawn/src/common/Constants.h @@ -18,8 +18,6 @@ #include static constexpr uint32_t kMaxBindGroups = 4u; -// TODO(cwallez@chromium.org): investigate bindgroup limits -static constexpr uint32_t kMaxBindingsPerGroup = 16u; static constexpr uint32_t kMaxVertexAttributes = 16u; // Vulkan has a standalone limit named maxVertexInputAttributeOffset (2047u at least) for vertex // attribute offset. The limit might be meaningless because Vulkan has another limit named @@ -32,9 +30,24 @@ static constexpr uint32_t kMaxVertexBuffers = 16u; static constexpr uint32_t kMaxVertexBufferStride = 2048u; static constexpr uint32_t kNumStages = 3; static constexpr uint32_t kMaxColorAttachments = 4u; -static constexpr uint32_t kTextureRowPitchAlignment = 256u; +static constexpr uint32_t kTextureBytesPerRowAlignment = 256u; // Dynamic buffer offsets require offset to be divisible by 256 static constexpr uint64_t kMinDynamicBufferOffsetAlignment = 256u; + +// Per stage limits +static constexpr uint32_t kMaxSampledTexturesPerShaderStage = 16; +static constexpr uint32_t kMaxSamplersPerShaderStage = 16; +static constexpr uint32_t kMaxStorageBuffersPerShaderStage = 6; +static constexpr uint32_t kMaxStorageTexturesPerShaderStage = 4; +static constexpr uint32_t kMaxUniformBuffersPerShaderStage = 12; + +// Per pipeline layout limits +static constexpr uint32_t kMaxDynamicUniformBuffersPerPipelineLayout = 8u; +static constexpr uint32_t kMaxDynamicStorageBuffersPerPipelineLayout = 4u; + +// Max size of uniform buffer binding +static constexpr uint64_t kMaxUniformBufferBindingSize = 16384u; + // Indirect command sizes static constexpr uint64_t kDispatchIndirectSize = 3 * sizeof(uint32_t); static constexpr uint64_t kDrawIndirectSize = 4 * sizeof(uint32_t); @@ -44,13 +57,6 @@ static constexpr uint64_t kDrawIndexedIndirectSize = 5 * sizeof(uint32_t); static constexpr float kLodMin = 0.0; static constexpr float kLodMax = 1000.0; -static constexpr uint32_t kVendorID_AMD = 0x1002; -static constexpr uint32_t kVendorID_ARM = 0x13B5; -static constexpr uint32_t kVendorID_ImgTec = 0x1010; -static constexpr uint32_t kVendorID_Intel = 0x8086; -static constexpr uint32_t kVendorID_Nvidia = 0x10DE; -static constexpr uint32_t kVendorID_Qualcomm = 0x5143; - // Max texture size constants static constexpr uint32_t kMaxTextureSize = 8192u; static constexpr uint32_t kMaxTexture2DArrayLayers = 256u; diff --git a/third_party/dawn/src/common/GPUInfo.cpp b/third_party/dawn/src/common/GPUInfo.cpp new file mode 100644 index 00000000000..c3ea9cefb5c --- /dev/null +++ b/third_party/dawn/src/common/GPUInfo.cpp @@ -0,0 +1,42 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/GPUInfo.h" + +namespace gpu_info { + bool IsAMD(PCIVendorID vendorId) { + return vendorId == kVendorID_AMD; + } + bool IsARM(PCIVendorID vendorId) { + return vendorId == kVendorID_ARM; + } + bool IsImgTec(PCIVendorID vendorId) { + return vendorId == kVendorID_ImgTec; + } + bool IsIntel(PCIVendorID vendorId) { + return vendorId == kVendorID_Intel; + } + bool IsNvidia(PCIVendorID vendorId) { + return vendorId == kVendorID_Nvidia; + } + bool IsQualcomm(PCIVendorID vendorId) { + return vendorId == kVendorID_Qualcomm; + } + bool IsSwiftshader(PCIVendorID vendorId, PCIDeviceID deviceId) { + return vendorId == kVendorID_Google && deviceId == kDeviceID_Swiftshader; + } + bool IsWARP(PCIVendorID vendorId, PCIDeviceID deviceId) { + return vendorId == kVendorID_Microsoft && deviceId == kDeviceID_WARP; + } +} // namespace gpu_info diff --git a/third_party/dawn/src/common/GPUInfo.h b/third_party/dawn/src/common/GPUInfo.h new file mode 100644 index 00000000000..87efbbc9dfb --- /dev/null +++ b/third_party/dawn/src/common/GPUInfo.h @@ -0,0 +1,47 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_GPUINFO_H +#define COMMON_GPUINFO_H + +#include + +using PCIVendorID = uint32_t; +using PCIDeviceID = uint32_t; + +namespace gpu_info { + + static constexpr PCIVendorID kVendorID_AMD = 0x1002; + static constexpr PCIVendorID kVendorID_ARM = 0x13B5; + static constexpr PCIVendorID kVendorID_ImgTec = 0x1010; + static constexpr PCIVendorID kVendorID_Intel = 0x8086; + static constexpr PCIVendorID kVendorID_Nvidia = 0x10DE; + static constexpr PCIVendorID kVendorID_Qualcomm = 0x5143; + static constexpr PCIVendorID kVendorID_Google = 0x1AE0; + static constexpr PCIVendorID kVendorID_Microsoft = 0x1414; + + static constexpr PCIDeviceID kDeviceID_Swiftshader = 0xC0DE; + static constexpr PCIDeviceID kDeviceID_WARP = 0x8c; + + bool IsAMD(PCIVendorID vendorId); + bool IsARM(PCIVendorID vendorId); + bool IsImgTec(PCIVendorID vendorId); + bool IsIntel(PCIVendorID vendorId); + bool IsNvidia(PCIVendorID vendorId); + bool IsQualcomm(PCIVendorID vendorId); + bool IsSwiftshader(PCIVendorID vendorId, PCIDeviceID deviceId); + bool IsWARP(PCIVendorID vendorId, PCIDeviceID deviceId); + +} // namespace gpu_info +#endif // COMMON_GPUINFO_H diff --git a/third_party/dawn/src/common/HashUtils.h b/third_party/dawn/src/common/HashUtils.h index e0cd6e7b809..1c33a3f2c1c 100644 --- a/third_party/dawn/src/common/HashUtils.h +++ b/third_party/dawn/src/common/HashUtils.h @@ -16,7 +16,10 @@ #define COMMON_HASHUTILS_H_ #include "common/Platform.h" +#include "common/TypedInteger.h" +#include "common/ityp_bitset.h" +#include #include // Wrapper around std::hash to make it a templated function instead of a functor. It is marginally @@ -26,6 +29,12 @@ size_t Hash(const T& value) { return std::hash()(value); } +// Add hashing of TypedIntegers +template +size_t Hash(const TypedInteger& value) { + return Hash(static_cast(value)); +} + // When hashing sparse structures we want to iteratively build a hash value with only parts of the // data. HashCombine "hashes" together an existing hash and hashable values. // @@ -79,4 +88,14 @@ size_t Hash(const std::bitset& value) { } #endif +namespace std { + template + struct hash> { + public: + size_t operator()(const ityp::bitset& value) const { + return Hash(static_cast&>(value)); + } + }; +} // namespace std + #endif // COMMON_HASHUTILS_H_ diff --git a/third_party/dawn/src/common/LinkedList.h b/third_party/dawn/src/common/LinkedList.h new file mode 100644 index 00000000000..47ca1eb1040 --- /dev/null +++ b/third_party/dawn/src/common/LinkedList.h @@ -0,0 +1,200 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is a copy of Chromium's /src/base/containers/linked_list.h + +#ifndef COMMON_LINKED_LIST_H +#define COMMON_LINKED_LIST_H + +#include "common/Assert.h" + +// Simple LinkedList type. (See the Q&A section to understand how this +// differs from std::list). +// +// To use, start by declaring the class which will be contained in the linked +// list, as extending LinkNode (this gives it next/previous pointers). +// +// class MyNodeType : public LinkNode { +// ... +// }; +// +// Next, to keep track of the list's head/tail, use a LinkedList instance: +// +// LinkedList list; +// +// To add elements to the list, use any of LinkedList::Append, +// LinkNode::InsertBefore, or LinkNode::InsertAfter: +// +// LinkNode* n1 = ...; +// LinkNode* n2 = ...; +// LinkNode* n3 = ...; +// +// list.Append(n1); +// list.Append(n3); +// n3->InsertBefore(n3); +// +// Lastly, to iterate through the linked list forwards: +// +// for (LinkNode* node = list.head(); +// node != list.end(); +// node = node->next()) { +// MyNodeType* value = node->value(); +// ... +// } +// +// Or to iterate the linked list backwards: +// +// for (LinkNode* node = list.tail(); +// node != list.end(); +// node = node->previous()) { +// MyNodeType* value = node->value(); +// ... +// } +// +// Questions and Answers: +// +// Q. Should I use std::list or base::LinkedList? +// +// A. The main reason to use base::LinkedList over std::list is +// performance. If you don't care about the performance differences +// then use an STL container, as it makes for better code readability. +// +// Comparing the performance of base::LinkedList to std::list: +// +// * Erasing an element of type T* from base::LinkedList is +// an O(1) operation. Whereas for std::list it is O(n). +// That is because with std::list you must obtain an +// iterator to the T* element before you can call erase(iterator). +// +// * Insertion operations with base::LinkedList never require +// heap allocations. +// +// Q. How does base::LinkedList implementation differ from std::list? +// +// A. Doubly-linked lists are made up of nodes that contain "next" and +// "previous" pointers that reference other nodes in the list. +// +// With base::LinkedList, the type being inserted already reserves +// space for the "next" and "previous" pointers (base::LinkNode*). +// Whereas with std::list the type can be anything, so the implementation +// needs to glue on the "next" and "previous" pointers using +// some internal node type. + +template +class LinkNode { + public: + LinkNode() : previous_(nullptr), next_(nullptr) { + } + LinkNode(LinkNode* previous, LinkNode* next) : previous_(previous), next_(next) { + } + + LinkNode(LinkNode&& rhs) { + next_ = rhs.next_; + rhs.next_ = nullptr; + previous_ = rhs.previous_; + rhs.previous_ = nullptr; + + // If the node belongs to a list, next_ and previous_ are both non-null. + // Otherwise, they are both null. + if (next_) { + next_->previous_ = this; + previous_->next_ = this; + } + } + + // Insert |this| into the linked list, before |e|. + void InsertBefore(LinkNode* e) { + this->next_ = e; + this->previous_ = e->previous_; + e->previous_->next_ = this; + e->previous_ = this; + } + + // Insert |this| into the linked list, after |e|. + void InsertAfter(LinkNode* e) { + this->next_ = e->next_; + this->previous_ = e; + e->next_->previous_ = this; + e->next_ = this; + } + + // Check if |this| is in a list. + bool IsInList() const { + ASSERT((this->previous_ == nullptr) == (this->next_ == nullptr)); + return this->next_ != nullptr; + } + + // Remove |this| from the linked list. + void RemoveFromList() { + this->previous_->next_ = this->next_; + this->next_->previous_ = this->previous_; + // next() and previous() return non-null if and only this node is not in any + // list. + this->next_ = nullptr; + this->previous_ = nullptr; + } + + LinkNode* previous() const { + return previous_; + } + + LinkNode* next() const { + return next_; + } + + // Cast from the node-type to the value type. + const T* value() const { + return static_cast(this); + } + + T* value() { + return static_cast(this); + } + + private: + LinkNode* previous_; + LinkNode* next_; +}; + +template +class LinkedList { + public: + // The "root" node is self-referential, and forms the basis of a circular + // list (root_.next() will point back to the start of the list, + // and root_->previous() wraps around to the end of the list). + LinkedList() : root_(&root_, &root_) { + } + + ~LinkedList() { + // If any LinkNodes still exist in the LinkedList, there will be outstanding references to + // root_ even after it has been freed. We should remove root_ from the list to prevent any + // future access. + root_.RemoveFromList(); + } + + // Appends |e| to the end of the linked list. + void Append(LinkNode* e) { + e->InsertBefore(&root_); + } + + LinkNode* head() const { + return root_.next(); + } + + LinkNode* tail() const { + return root_.previous(); + } + + const LinkNode* end() const { + return &root_; + } + + bool empty() const { + return head() == end(); + } + + private: + LinkNode root_; +}; +#endif // COMMON_LINKED_LIST_H diff --git a/third_party/dawn/src/common/Log.cpp b/third_party/dawn/src/common/Log.cpp new file mode 100644 index 00000000000..04aeb08a951 --- /dev/null +++ b/third_party/dawn/src/common/Log.cpp @@ -0,0 +1,116 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Log.h" + +#include "common/Assert.h" +#include "common/Platform.h" + +#include + +#if defined(DAWN_PLATFORM_ANDROID) +# include +#endif + +namespace dawn { + + namespace { + + const char* SeverityName(LogSeverity severity) { + switch (severity) { + case LogSeverity::Debug: + return "Debug"; + case LogSeverity::Info: + return "Info"; + case LogSeverity::Warning: + return "Warning"; + case LogSeverity::Error: + return "Error"; + default: + UNREACHABLE(); + return ""; + } + } + +#if defined(DAWN_PLATFORM_ANDROID) + android_LogPriority AndroidLogPriority(LogSeverity severity) { + switch (severity) { + case LogSeverity::Debug: + return ANDROID_LOG_INFO; + case LogSeverity::Info: + return ANDROID_LOG_INFO; + case LogSeverity::Warning: + return ANDROID_LOG_WARN; + case LogSeverity::Error: + return ANDROID_LOG_ERROR; + default: + UNREACHABLE(); + return ANDROID_LOG_ERROR; + } + } +#endif // defined(DAWN_PLATFORM_ANDROID) + + } // anonymous namespace + + LogMessage::LogMessage(LogSeverity severity) : mSeverity(severity) { + } + + LogMessage::~LogMessage() { + std::string fullMessage = mStream.str(); + + // If this message has been moved, its stream is empty. + if (fullMessage.empty()) { + return; + } + + const char* severityName = SeverityName(mSeverity); + + FILE* outputStream = stdout; + if (mSeverity == LogSeverity::Warning || mSeverity == LogSeverity::Error) { + outputStream = stderr; + } + +#if defined(DAWN_PLATFORM_ANDROID) + android_LogPriority androidPriority = AndroidLogPriority(mSeverity); + __android_log_print(androidPriority, "Dawn", "%s: %s\n", severityName, fullMessage.c_str()); +#else // defined(DAWN_PLATFORM_ANDROID) + // Note: we use fprintf because includes static initializers. + fprintf(outputStream, "%s: %s\n", severityName, fullMessage.c_str()); + fflush(outputStream); +#endif // defined(DAWN_PLATFORM_ANDROID) + } + + LogMessage DebugLog() { + return {LogSeverity::Debug}; + } + + LogMessage InfoLog() { + return {LogSeverity::Info}; + } + + LogMessage WarningLog() { + return {LogSeverity::Warning}; + } + + LogMessage ErrorLog() { + return {LogSeverity::Error}; + } + + LogMessage DebugLog(const char* file, const char* function, int line) { + LogMessage message = DebugLog(); + message << file << ":" << line << "(" << function << ")"; + return message; + } + +} // namespace dawn diff --git a/third_party/dawn/src/common/Log.h b/third_party/dawn/src/common/Log.h new file mode 100644 index 00000000000..0504af61ed3 --- /dev/null +++ b/third_party/dawn/src/common/Log.h @@ -0,0 +1,95 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_LOG_H_ +#define COMMON_LOG_H_ + +// Dawn targets shouldn't use iostream or printf directly for several reasons: +// - iostream adds static initializers which we want to avoid. +// - printf and iostream don't show up in logcat on Android so printf debugging doesn't work but +// log-message debugging does. +// - log severity helps provide intent compared to a printf. +// +// Logging should in general be avoided: errors should go through the regular WebGPU error reporting +// mechanism and others form of logging should (TODO: eventually) go through the logging dependency +// injection, so for example they show up in Chromium's about:gpu page. Nonetheless there are some +// cases where logging is necessary and when this file was first introduced we needed to replace all +// uses of iostream so we could see them in Android's logcat. +// +// Regular logging is done using the [Debug|Info|Warning|Error]Log() function this way: +// +// InfoLog() << things << that << ostringstream << supports; // No need for a std::endl or "\n" +// +// It creates a LogMessage object that isn't stored anywhere and gets its destructor called +// immediately which outputs the stored ostringstream in the right place. +// +// This file also contains DAWN_DEBUG for "printf debugging" which works on Android and +// additionally outputs the file, line and function name. Use it this way: +// +// // Pepper this throughout code to get a log of the execution +// DAWN_DEBUG(); +// +// // Get more information +// DAWN_DEBUG() << texture.GetFormat(); + +#include + +namespace dawn { + + // Log levels mostly used to signal intent where the log message is produced and used to route + // the message to the correct output. + enum class LogSeverity { + Debug, + Info, + Warning, + Error, + }; + + // Essentially an ostringstream that will print itself in its destructor. + class LogMessage { + public: + LogMessage(LogSeverity severity); + ~LogMessage(); + + LogMessage(LogMessage&& other) = default; + LogMessage& operator=(LogMessage&& other) = default; + + template + LogMessage& operator<<(T&& value) { + mStream << value; + return *this; + } + + private: + LogMessage(const LogMessage& other) = delete; + LogMessage& operator=(const LogMessage& other) = delete; + + LogSeverity mSeverity; + std::ostringstream mStream; + }; + + // Short-hands to create a LogMessage with the respective severity. + LogMessage DebugLog(); + LogMessage InfoLog(); + LogMessage WarningLog(); + LogMessage ErrorLog(); + + // DAWN_DEBUG is a helper macro that creates a DebugLog and outputs file/line/function + // information + LogMessage DebugLog(const char* file, const char* function, int line); +#define DAWN_DEBUG() ::dawn::DebugLog(__FILE__, __func__, __LINE__) + +} // namespace dawn + +#endif // COMMON_LOG_H_ diff --git a/third_party/dawn/src/common/Math.cpp b/third_party/dawn/src/common/Math.cpp index d9217c8e443..8e5985f7e73 100644 --- a/third_party/dawn/src/common/Math.cpp +++ b/third_party/dawn/src/common/Math.cpp @@ -15,8 +15,11 @@ #include "common/Math.h" #include "common/Assert.h" +#include "common/Platform.h" #include +#include +#include #if defined(DAWN_COMPILER_MSVC) # include @@ -46,7 +49,37 @@ uint32_t Log2(uint32_t value) { #endif } -bool IsPowerOfTwo(size_t n) { +uint32_t Log2(uint64_t value) { + ASSERT(value != 0); +#if defined(DAWN_COMPILER_MSVC) +# if defined(DAWN_PLATFORM_64_BIT) + unsigned long firstBitIndex = 0ul; + unsigned char ret = _BitScanReverse64(&firstBitIndex, value); + ASSERT(ret != 0); + return firstBitIndex; +# else // defined(DAWN_PLATFORM_64_BIT) + unsigned long firstBitIndex = 0ul; + if (_BitScanReverse(&firstBitIndex, value >> 32)) { + return firstBitIndex + 32; + } + unsigned char ret = _BitScanReverse(&firstBitIndex, value & 0xFFFFFFFF); + ASSERT(ret != 0); + return firstBitIndex; +# endif // defined(DAWN_PLATFORM_64_BIT) +#else // defined(DAWN_COMPILER_MSVC) + return 63 - static_cast(__builtin_clzll(value)); +#endif // defined(DAWN_COMPILER_MSVC) +} + +uint64_t NextPowerOfTwo(uint64_t n) { + if (n <= 1) { + return 1; + } + + return 1ull << (Log2(n - 1) + 1); +} + +bool IsPowerOfTwo(uint64_t n) { ASSERT(n != 0); return (n & (n - 1)) == 0; } @@ -57,13 +90,6 @@ bool IsPtrAligned(const void* ptr, size_t alignment) { return (reinterpret_cast(ptr) & (alignment - 1)) == 0; } -void* AlignVoidPtr(void* ptr, size_t alignment) { - ASSERT(IsPowerOfTwo(alignment)); - ASSERT(alignment != 0); - return reinterpret_cast((reinterpret_cast(ptr) + (alignment - 1)) & - ~(alignment - 1)); -} - bool IsAligned(uint32_t value, size_t alignment) { ASSERT(alignment <= UINT32_MAX); ASSERT(IsPowerOfTwo(alignment)); @@ -85,8 +111,10 @@ uint16_t Float32ToFloat16(float fp32) { uint32_t sign16 = (fp32i & 0x80000000) >> 16; uint32_t mantissaAndExponent = fp32i & 0x7FFFFFFF; - if (mantissaAndExponent > 0x47FFEFFF) { // Infinity - return static_cast(sign16 | 0x7FFF); + if (mantissaAndExponent > 0x7F800000) { // NaN + return 0x7FFF; + } else if (mantissaAndExponent > 0x47FFEFFF) { // Infinity + return static_cast(sign16 | 0x7C00); } else if (mantissaAndExponent < 0x38800000) { // Denormal uint32_t mantissa = (mantissaAndExponent & 0x007FFFFF) | 0x00800000; int32_t exponent = 113 - (mantissaAndExponent >> 23); @@ -105,3 +133,30 @@ uint16_t Float32ToFloat16(float fp32) { 13); } } + +bool IsFloat16NaN(uint16_t fp16) { + return (fp16 & 0x7FFF) > 0x7C00; +} + +// Based on the Khronos Data Format Specification 1.2 Section 13.3 sRGB transfer functions +float SRGBToLinear(float srgb) { + // sRGB is always used in unsigned normalized formats so clamp to [0.0, 1.0] + if (srgb <= 0.0f) { + return 0.0f; + } else if (srgb > 1.0f) { + return 1.0f; + } + + if (srgb < 0.04045f) { + return srgb / 12.92f; + } else { + return std::pow((srgb + 0.055f) / 1.055f, 2.4f); + } +} + +uint64_t RoundUp(uint64_t n, uint64_t m) { + ASSERT(m > 0); + ASSERT(n > 0); + ASSERT(m <= std::numeric_limits::max() - n); + return ((n + m - 1) / m) * m; +} diff --git a/third_party/dawn/src/common/Math.h b/third_party/dawn/src/common/Math.h index 16cf2a15aea..c673785e260 100644 --- a/third_party/dawn/src/common/Math.h +++ b/third_party/dawn/src/common/Math.h @@ -15,6 +15,8 @@ #ifndef COMMON_MATH_H_ #define COMMON_MATH_H_ +#include "common/Assert.h" + #include #include #include @@ -25,21 +27,46 @@ // The following are not valid for 0 uint32_t ScanForward(uint32_t bits); uint32_t Log2(uint32_t value); -bool IsPowerOfTwo(size_t n); +uint32_t Log2(uint64_t value); +bool IsPowerOfTwo(uint64_t n); +uint64_t RoundUp(uint64_t n, uint64_t m); + +constexpr uint32_t ConstexprLog2(uint64_t v) { + return v <= 1 ? 0 : 1 + ConstexprLog2(v / 2); +} + +constexpr uint32_t ConstexprLog2Ceil(uint64_t v) { + return v <= 1 ? 0 : ConstexprLog2(v - 1) + 1; +} +inline uint32_t Log2Ceil(uint32_t v) { + return v <= 1 ? 0 : Log2(v - 1) + 1; +} + +inline uint32_t Log2Ceil(uint64_t v) { + return v <= 1 ? 0 : Log2(v - 1) + 1; +} + +uint64_t NextPowerOfTwo(uint64_t n); bool IsPtrAligned(const void* ptr, size_t alignment); void* AlignVoidPtr(void* ptr, size_t alignment); bool IsAligned(uint32_t value, size_t alignment); uint32_t Align(uint32_t value, size_t alignment); template -T* AlignPtr(T* ptr, size_t alignment) { - return static_cast(AlignVoidPtr(ptr, alignment)); +DAWN_FORCE_INLINE T* AlignPtr(T* ptr, size_t alignment) { + ASSERT(IsPowerOfTwo(alignment)); + ASSERT(alignment != 0); + return reinterpret_cast((reinterpret_cast(ptr) + (alignment - 1)) & + ~(alignment - 1)); } template -const T* AlignPtr(const T* ptr, size_t alignment) { - return static_cast(AlignVoidPtr(const_cast(ptr), alignment)); +DAWN_FORCE_INLINE const T* AlignPtr(const T* ptr, size_t alignment) { + ASSERT(IsPowerOfTwo(alignment)); + ASSERT(alignment != 0); + return reinterpret_cast((reinterpret_cast(ptr) + (alignment - 1)) & + ~(alignment - 1)); } template @@ -51,5 +78,8 @@ destType BitCast(const sourceType& source) { } uint16_t Float32ToFloat16(float fp32); +bool IsFloat16NaN(uint16_t fp16); + +float SRGBToLinear(float srgb); #endif // COMMON_MATH_H_ diff --git a/third_party/dawn/src/common/PlacementAllocated.h b/third_party/dawn/src/common/PlacementAllocated.h new file mode 100644 index 00000000000..6bb329c3d5c --- /dev/null +++ b/third_party/dawn/src/common/PlacementAllocated.h @@ -0,0 +1,37 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_PLACEMENTALLOCATED_H_ +#define COMMON_PLACEMENTALLOCATED_H_ + +#include + +class PlacementAllocated { + public: + // Delete the default new operator so this can only be created with placement new. + void* operator new(size_t) = delete; + + void* operator new(size_t size, void* ptr) { + // Pass through the pointer of the allocation. This is essentially the default + // placement-new implementation, but we must define it if we delete the default + // new operator. + return ptr; + } + + void operator delete(void* ptr) { + // Object is placement-allocated. Don't free the memory. + } +}; + +#endif // COMMON_PLACEMENTALLOCATED_H_ diff --git a/third_party/dawn/src/common/Platform.h b/third_party/dawn/src/common/Platform.h index 7e767f1322d..af7b1751518 100644 --- a/third_party/dawn/src/common/Platform.h +++ b/third_party/dawn/src/common/Platform.h @@ -17,12 +17,34 @@ #if defined(_WIN32) || defined(_WIN64) # define DAWN_PLATFORM_WINDOWS 1 + #elif defined(__linux__) # define DAWN_PLATFORM_LINUX 1 # define DAWN_PLATFORM_POSIX 1 +# if defined(__ANDROID__) +# define DAWN_PLATFORM_ANDROID 1 +# endif + #elif defined(__APPLE__) # define DAWN_PLATFORM_APPLE 1 # define DAWN_PLATFORM_POSIX 1 +# include +# if TARGET_OS_IPHONE +# define DAWN_PLATFORM_IOS +# elif TARGET_OS_MAC +# define DAWN_PLATFORM_MACOS +# else +# error "Unsupported Apple platform." +# endif + +#elif defined(__Fuchsia__) +# define DAWN_PLATFORM_FUCHSIA 1 +# define DAWN_PLATFORM_POSIX 1 + +#elif defined(__EMSCRIPTEN__) +# define DAWN_PLATFORM_EMSCRIPTEN 1 +# define DAWN_PLATFORM_POSIX 1 + #else # error "Unsupported platform." #endif @@ -42,7 +64,7 @@ # define DAWN_PLATFORM_64_BIT 1 static_assert(sizeof(sizeof(char)) == 8, "Expect sizeof(size_t) == 8"); #elif defined(_WIN32) || defined(__arm__) || defined(__i386__) || defined(__mips32__) || \ - defined(__s390__) + defined(__s390__) || defined(__EMSCRIPTEN__) # define DAWN_PLATFORM_32_BIT 1 static_assert(sizeof(sizeof(char)) == 4, "Expect sizeof(size_t) == 4"); #else diff --git a/third_party/dawn/src/common/RefCounted.cpp b/third_party/dawn/src/common/RefCounted.cpp new file mode 100644 index 00000000000..af38fc668e2 --- /dev/null +++ b/third_party/dawn/src/common/RefCounted.cpp @@ -0,0 +1,78 @@ +// Copyright 2017 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/RefCounted.h" + +#include "common/Assert.h" + +#include + +static constexpr size_t kPayloadBits = 1; +static constexpr uint64_t kPayloadMask = (uint64_t(1) << kPayloadBits) - 1; +static constexpr uint64_t kRefCountIncrement = (uint64_t(1) << kPayloadBits); + +RefCounted::RefCounted(uint64_t payload) : mRefCount(kRefCountIncrement + payload) { + ASSERT((payload & kPayloadMask) == payload); +} + +uint64_t RefCounted::GetRefCountForTesting() const { + return mRefCount >> kPayloadBits; +} + +uint64_t RefCounted::GetRefCountPayload() const { + // We only care about the payload bits of the refcount. These never change after + // initialization so we can use the relaxed memory order. The order doesn't guarantee + // anything except the atomicity of the load, which is enough since any past values of the + // atomic will have the correct payload bits. + return kPayloadMask & mRefCount.load(std::memory_order_relaxed); +} + +void RefCounted::Reference() { + ASSERT((mRefCount & ~kPayloadMask) != 0); + + // The relaxed ordering guarantees only the atomicity of the update, which is enough here + // because the reference we are copying from still exists and makes sure other threads + // don't delete `this`. + // See the explanation in the Boost documentation: + // https://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html + mRefCount.fetch_add(kRefCountIncrement, std::memory_order_relaxed); +} + +void RefCounted::Release() { + ASSERT((mRefCount & ~kPayloadMask) != 0); + + // The release fence here is to make sure all accesses to the object on a thread A + // happen-before the object is deleted on a thread B. The release memory order ensures that + // all accesses on thread A happen-before the refcount is decreased and the atomic variable + // makes sure the refcount decrease in A happens-before the refcount decrease in B. Finally + // the acquire fence in the destruction case makes sure the refcount decrease in B + // happens-before the `delete this`. + // + // See the explanation in the Boost documentation: + // https://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html + uint64_t previousRefCount = mRefCount.fetch_sub(kRefCountIncrement, std::memory_order_release); + + // Check that the previous reference count was strictly less than 2, ignoring payload bits. + if (previousRefCount < 2 * kRefCountIncrement) { + // Note that on ARM64 this will generate a `dmb ish` instruction which is a global + // memory barrier, when an acquire load on mRefCount (using the `ldar` instruction) + // should be enough and could end up being faster. + std::atomic_thread_fence(std::memory_order_acquire); + DeleteThis(); + } +} + +void RefCounted::DeleteThis() { + delete this; +} diff --git a/third_party/dawn/src/common/RefCounted.h b/third_party/dawn/src/common/RefCounted.h new file mode 100644 index 00000000000..90144a6043b --- /dev/null +++ b/third_party/dawn/src/common/RefCounted.h @@ -0,0 +1,191 @@ +// Copyright 2017 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_REFCOUNTED_H_ +#define COMMON_REFCOUNTED_H_ + +#include +#include + +class RefCounted { + public: + RefCounted(uint64_t payload = 0); + + uint64_t GetRefCountForTesting() const; + uint64_t GetRefCountPayload() const; + + // Dawn API + void Reference(); + void Release(); + + protected: + virtual ~RefCounted() = default; + // A Derived class may override this if they require a custom deleter. + virtual void DeleteThis(); + + private: + std::atomic_uint64_t mRefCount; +}; + +template +class Ref { + public: + Ref() = default; + + constexpr Ref(std::nullptr_t) { + } + + Ref& operator=(std::nullptr_t) { + Release(); + mPointee = nullptr; + return *this; + } + + template + Ref(U* p) : mPointee(p) { + static_assert(std::is_convertible::value, ""); + Reference(); + } + + Ref(const Ref& other) : mPointee(other.mPointee) { + Reference(); + } + template + Ref(const Ref& other) : mPointee(other.mPointee) { + static_assert(std::is_convertible::value, ""); + Reference(); + } + + Ref& operator=(const Ref& other) { + if (&other == this) + return *this; + + other.Reference(); + Release(); + mPointee = other.mPointee; + + return *this; + } + + template + Ref& operator=(const Ref& other) { + static_assert(std::is_convertible::value, ""); + + other.Reference(); + Release(); + mPointee = other.mPointee; + + return *this; + } + + template + Ref(Ref&& other) { + static_assert(std::is_convertible::value, ""); + mPointee = other.mPointee; + other.mPointee = nullptr; + } + + Ref& operator=(Ref&& other) { + if (&other == this) + return *this; + + Release(); + mPointee = other.mPointee; + other.mPointee = nullptr; + + return *this; + } + template + Ref& operator=(Ref&& other) { + static_assert(std::is_convertible::value, ""); + + Release(); + mPointee = other.mPointee; + other.mPointee = nullptr; + + return *this; + } + + ~Ref() { + Release(); + mPointee = nullptr; + } + + bool operator==(const T* other) const { + return mPointee == other; + } + + bool operator!=(const T* other) const { + return mPointee != other; + } + + operator bool() { + return mPointee != nullptr; + } + + const T& operator*() const { + return *mPointee; + } + T& operator*() { + return *mPointee; + } + + const T* operator->() const { + return mPointee; + } + T* operator->() { + return mPointee; + } + + const T* Get() const { + return mPointee; + } + T* Get() { + return mPointee; + } + + T* Detach() { + T* pointee = mPointee; + mPointee = nullptr; + return pointee; + } + + private: + // Friend is needed so that instances of Ref can assign mPointee + // members of Ref. + template + friend class Ref; + + void Reference() const { + if (mPointee != nullptr) { + mPointee->Reference(); + } + } + void Release() const { + if (mPointee != nullptr) { + mPointee->Release(); + } + } + + T* mPointee = nullptr; +}; + +template +Ref AcquireRef(T* pointee) { + Ref ref(pointee); + ref->Release(); + return ref; +} + +#endif // COMMON_REFCOUNTED_H_ diff --git a/third_party/dawn/src/common/Result.cpp b/third_party/dawn/src/common/Result.cpp new file mode 100644 index 00000000000..a4132cd0be1 --- /dev/null +++ b/third_party/dawn/src/common/Result.cpp @@ -0,0 +1,30 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Result.h" + +// Implementation details of the tagged pointer Results +namespace detail { + + intptr_t MakePayload(const void* pointer, PayloadType type) { + intptr_t payload = reinterpret_cast(pointer); + ASSERT((payload & 3) == 0); + return payload | type; + } + + PayloadType GetPayloadType(intptr_t payload) { + return static_cast(payload & 3); + } + +} // namespace detail diff --git a/third_party/dawn/src/common/Result.h b/third_party/dawn/src/common/Result.h index b4824fe62f4..4cea7f2f925 100644 --- a/third_party/dawn/src/common/Result.h +++ b/third_party/dawn/src/common/Result.h @@ -20,6 +20,8 @@ #include #include +#include +#include #include // Result is the following sum type (Haskell notation): @@ -37,10 +39,10 @@ template class Result; -// The interface of Result shoud look like the following. +// The interface of Result should look like the following. // public: // Result(T&& success); -// Result(E&& error); +// Result(std::unique_ptr error); // // Result(Result&& other); // Result& operator=(Result&& other); @@ -51,18 +53,18 @@ class Result; // bool IsSuccess() const; // // T&& AcquireSuccess(); -// E&& AcquireError(); +// std::unique_ptr AcquireError(); // Specialization of Result for returning errors only via pointers. It is basically a pointer // where nullptr is both Success and Empty. template -class DAWN_NO_DISCARD Result { +class DAWN_NO_DISCARD Result { public: Result(); - Result(E* error); + Result(std::unique_ptr error); - Result(Result&& other); - Result& operator=(Result&& other); + Result(Result&& other); + Result& operator=(Result&& other); ~Result(); @@ -70,10 +72,10 @@ class DAWN_NO_DISCARD Result { bool IsSuccess() const; void AcquireSuccess(); - E* AcquireError(); + std::unique_ptr AcquireError(); private: - E* mError = nullptr; + std::unique_ptr mError; }; // Uses SFINAE to try to get alignof(T) but fallback to Default if T isn't defined. @@ -85,8 +87,29 @@ constexpr size_t alignof_if_defined_else_default + static T* GetSuccessFromPayload(intptr_t payload); + template + static E* GetErrorFromPayload(intptr_t payload); + + constexpr static intptr_t kEmptyPayload = Empty; +} // namespace detail + template -class DAWN_NO_DISCARD Result { +class DAWN_NO_DISCARD Result { public: static_assert(alignof_if_defined_else_default >= 4, "Result reserves two bits for tagging pointers"); @@ -94,10 +117,13 @@ class DAWN_NO_DISCARD Result { "Result reserves two bits for tagging pointers"); Result(T* success); - Result(E* error); + Result(std::unique_ptr error); - Result(Result&& other); - Result& operator=(Result&& other); + // Support returning a Result from a Result + template + Result(Result&& other); + template + Result& operator=(Result&& other); ~Result(); @@ -105,24 +131,74 @@ class DAWN_NO_DISCARD Result { bool IsSuccess() const; T* AcquireSuccess(); - E* AcquireError(); + std::unique_ptr AcquireError(); private: - enum PayloadType { - Success = 0, - Error = 1, - Empty = 2, - }; + template + friend class Result; - // Utility functions to manipulate the tagged pointer. Some of them don't need to be templated - // but we really want them inlined so we keep them in the headers - static intptr_t MakePayload(void* pointer, PayloadType type); - static PayloadType GetPayloadType(intptr_t payload); - static T* GetSuccessFromPayload(intptr_t payload); - static E* GetErrorFromPayload(intptr_t payload); + intptr_t mPayload = detail::kEmptyPayload; +}; - constexpr static intptr_t kEmptyPayload = Empty; - intptr_t mPayload = kEmptyPayload; +template +class DAWN_NO_DISCARD Result { + public: + static_assert(alignof_if_defined_else_default >= 4, + "Result reserves two bits for tagging pointers"); + static_assert(alignof_if_defined_else_default >= 4, + "Result reserves two bits for tagging pointers"); + + Result(const T* success); + Result(std::unique_ptr error); + + Result(Result&& other); + Result& operator=(Result&& other); + + ~Result(); + + bool IsError() const; + bool IsSuccess() const; + + const T* AcquireSuccess(); + std::unique_ptr AcquireError(); + + private: + intptr_t mPayload = detail::kEmptyPayload; +}; + +template +class Ref; + +template +class DAWN_NO_DISCARD Result, E> { + public: + static_assert(alignof_if_defined_else_default >= 4, + "Result, E> reserves two bits for tagging pointers"); + static_assert(alignof_if_defined_else_default >= 4, + "Result, E> reserves two bits for tagging pointers"); + + template + Result(Ref&& success); + Result(std::unique_ptr error); + + template + Result(Result, E>&& other); + template + Result, E>& operator=(Result, E>&& other); + + ~Result(); + + bool IsError() const; + bool IsSuccess() const; + + Ref AcquireSuccess(); + std::unique_ptr AcquireError(); + + private: + template + friend class Result; + + intptr_t mPayload = detail::kEmptyPayload; }; // Catchall definition of Result implemented as a tagged struct. It could be improved to use @@ -132,7 +208,7 @@ template class DAWN_NO_DISCARD Result { public: Result(T&& success); - Result(E&& error); + Result(std::unique_ptr error); Result(Result&& other); Result& operator=(Result&& other); @@ -143,7 +219,7 @@ class DAWN_NO_DISCARD Result { bool IsSuccess() const; T&& AcquireSuccess(); - E&& AcquireError(); + std::unique_ptr AcquireError(); private: enum PayloadType { @@ -153,131 +229,239 @@ class DAWN_NO_DISCARD Result { }; PayloadType mType; - E mError; + std::unique_ptr mError; T mSuccess; }; -// Implementation of Result +// Implementation of Result template -Result::Result() { +Result::Result() { } template -Result::Result(E* error) : mError(error) { +Result::Result(std::unique_ptr error) : mError(std::move(error)) { } template -Result::Result(Result&& other) : mError(other.mError) { - other.mError = nullptr; +Result::Result(Result&& other) : mError(std::move(other.mError)) { } template -Result& Result::operator=(Result&& other) { +Result& Result::operator=(Result&& other) { ASSERT(mError == nullptr); - mError = other.mError; - other.mError = nullptr; + mError = std::move(other.mError); return *this; } template -Result::~Result() { +Result::~Result() { ASSERT(mError == nullptr); } template -bool Result::IsError() const { +bool Result::IsError() const { return mError != nullptr; } template -bool Result::IsSuccess() const { +bool Result::IsSuccess() const { return mError == nullptr; } template -void Result::AcquireSuccess() { +void Result::AcquireSuccess() { } template -E* Result::AcquireError() { - E* error = mError; - mError = nullptr; - return error; +std::unique_ptr Result::AcquireError() { + return std::move(mError); } -// Implementation of Result +// Implementation details of the tagged pointer Results +namespace detail { + + template + T* GetSuccessFromPayload(intptr_t payload) { + ASSERT(GetPayloadType(payload) == Success); + return reinterpret_cast(payload); + } + + template + E* GetErrorFromPayload(intptr_t payload) { + ASSERT(GetPayloadType(payload) == Error); + return reinterpret_cast(payload ^ 1); + } + +} // namespace detail + +// Implementation of Result template -Result::Result(T* success) : mPayload(MakePayload(success, Success)) { +Result::Result(T* success) : mPayload(detail::MakePayload(success, detail::Success)) { } template -Result::Result(E* error) : mPayload(MakePayload(error, Error)) { +Result::Result(std::unique_ptr error) + : mPayload(detail::MakePayload(error.release(), detail::Error)) { } template -Result::Result(Result&& other) : mPayload(other.mPayload) { - other.mPayload = kEmptyPayload; +template +Result::Result(Result&& other) : mPayload(other.mPayload) { + other.mPayload = detail::kEmptyPayload; + static_assert(std::is_same::value || std::is_base_of::value, ""); } template -Result& Result::operator=(Result&& other) { - ASSERT(mPayload == kEmptyPayload); +template +Result& Result::operator=(Result&& other) { + ASSERT(mPayload == detail::kEmptyPayload); + static_assert(std::is_same::value || std::is_base_of::value, ""); mPayload = other.mPayload; - other.mPayload = kEmptyPayload; + other.mPayload = detail::kEmptyPayload; return *this; } template -Result::~Result() { - ASSERT(mPayload == kEmptyPayload); +Result::~Result() { + ASSERT(mPayload == detail::kEmptyPayload); } template -bool Result::IsError() const { - return GetPayloadType(mPayload) == Error; +bool Result::IsError() const { + return detail::GetPayloadType(mPayload) == detail::Error; } template -bool Result::IsSuccess() const { - return GetPayloadType(mPayload) == Success; +bool Result::IsSuccess() const { + return detail::GetPayloadType(mPayload) == detail::Success; } template -T* Result::AcquireSuccess() { - T* success = GetSuccessFromPayload(mPayload); - mPayload = kEmptyPayload; +T* Result::AcquireSuccess() { + T* success = detail::GetSuccessFromPayload(mPayload); + mPayload = detail::kEmptyPayload; return success; } template -E* Result::AcquireError() { - E* error = GetErrorFromPayload(mPayload); - mPayload = kEmptyPayload; - return error; +std::unique_ptr Result::AcquireError() { + std::unique_ptr error(detail::GetErrorFromPayload(mPayload)); + mPayload = detail::kEmptyPayload; + return std::move(error); +} + +// Implementation of Result +template +Result::Result(const T* success) + : mPayload(detail::MakePayload(success, detail::Success)) { } template -intptr_t Result::MakePayload(void* pointer, PayloadType type) { - intptr_t payload = reinterpret_cast(pointer); - ASSERT((payload & 3) == 0); - return payload | type; +Result::Result(std::unique_ptr error) + : mPayload(detail::MakePayload(error.release(), detail::Error)) { } template -typename Result::PayloadType Result::GetPayloadType(intptr_t payload) { - return static_cast(payload & 3); +Result::Result(Result&& other) : mPayload(other.mPayload) { + other.mPayload = detail::kEmptyPayload; } template -T* Result::GetSuccessFromPayload(intptr_t payload) { - ASSERT(GetPayloadType(payload) == Success); - return reinterpret_cast(payload); +Result& Result::operator=(Result&& other) { + ASSERT(mPayload == detail::kEmptyPayload); + mPayload = other.mPayload; + other.mPayload = detail::kEmptyPayload; + return *this; +} + +template +Result::~Result() { + ASSERT(mPayload == detail::kEmptyPayload); +} + +template +bool Result::IsError() const { + return detail::GetPayloadType(mPayload) == detail::Error; +} + +template +bool Result::IsSuccess() const { + return detail::GetPayloadType(mPayload) == detail::Success; +} + +template +const T* Result::AcquireSuccess() { + T* success = detail::GetSuccessFromPayload(mPayload); + mPayload = detail::kEmptyPayload; + return success; +} + +template +std::unique_ptr Result::AcquireError() { + std::unique_ptr error(detail::GetErrorFromPayload(mPayload)); + mPayload = detail::kEmptyPayload; + return std::move(error); +} + +// Implementation of Result, E> +template +template +Result, E>::Result(Ref&& success) + : mPayload(detail::MakePayload(success.Detach(), detail::Success)) { + static_assert(std::is_convertible::value, ""); +} + +template +Result, E>::Result(std::unique_ptr error) + : mPayload(detail::MakePayload(error.release(), detail::Error)) { +} + +template +template +Result, E>::Result(Result, E>&& other) : mPayload(other.mPayload) { + static_assert(std::is_convertible::value, ""); + other.mPayload = detail::kEmptyPayload; +} + +template +template +Result, E>& Result, E>::operator=(Result, E>&& other) { + static_assert(std::is_convertible::value, ""); + ASSERT(mPayload == detail::kEmptyPayload); + mPayload = other.mPayload; + other.mPayload = detail::kEmptyPayload; + return *this; +} + +template +Result, E>::~Result() { + ASSERT(mPayload == detail::kEmptyPayload); +} + +template +bool Result, E>::IsError() const { + return detail::GetPayloadType(mPayload) == detail::Error; +} + +template +bool Result, E>::IsSuccess() const { + return detail::GetPayloadType(mPayload) == detail::Success; +} + +template +Ref Result, E>::AcquireSuccess() { + ASSERT(IsSuccess()); + Ref success = AcquireRef(detail::GetSuccessFromPayload(mPayload)); + mPayload = detail::kEmptyPayload; + return success; } template -E* Result::GetErrorFromPayload(intptr_t payload) { - ASSERT(GetPayloadType(payload) == Error); - return reinterpret_cast(payload ^ 1); +std::unique_ptr Result, E>::AcquireError() { + ASSERT(IsError()); + std::unique_ptr error(detail::GetErrorFromPayload(mPayload)); + mPayload = detail::kEmptyPayload; + return std::move(error); } // Implementation of Result @@ -286,7 +470,7 @@ Result::Result(T&& success) : mType(Success), mSuccess(std::move(success)) } template -Result::Result(E&& error) : mType(Error), mError(std::move(error)) { +Result::Result(std::unique_ptr error) : mType(Error), mError(std::move(error)) { } template @@ -326,7 +510,7 @@ T&& Result::AcquireSuccess() { } template -E&& Result::AcquireError() { +std::unique_ptr Result::AcquireError() { ASSERT(mType == Error); mType = Acquired; return std::move(mError); diff --git a/third_party/dawn/src/common/SerialStorage.h b/third_party/dawn/src/common/SerialStorage.h index 6f382134a72..d71c7dd07d5 100644 --- a/third_party/dawn/src/common/SerialStorage.h +++ b/third_party/dawn/src/common/SerialStorage.h @@ -280,8 +280,8 @@ SerialStorage::ConstIterator::ConstIterator( } template -typename SerialStorage::ConstIterator& SerialStorage::ConstIterator:: -operator++() { +typename SerialStorage::ConstIterator& +SerialStorage::ConstIterator::operator++() { const Value* vectorData = mStorageIterator->second.data(); if (mSerialIterator == nullptr) { diff --git a/third_party/dawn/src/common/SlabAllocator.cpp b/third_party/dawn/src/common/SlabAllocator.cpp new file mode 100644 index 00000000000..61948873ba8 --- /dev/null +++ b/third_party/dawn/src/common/SlabAllocator.cpp @@ -0,0 +1,249 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/SlabAllocator.h" + +#include "common/Assert.h" +#include "common/Math.h" + +#include +#include +#include +#include + +// IndexLinkNode + +SlabAllocatorImpl::IndexLinkNode::IndexLinkNode(Index index, Index nextIndex) + : index(index), nextIndex(nextIndex) { +} + +// Slab + +SlabAllocatorImpl::Slab::Slab(std::unique_ptr allocation, IndexLinkNode* head) + : allocation(std::move(allocation)), + freeList(head), + prev(nullptr), + next(nullptr), + blocksInUse(0) { +} + +SlabAllocatorImpl::Slab::Slab(Slab&& rhs) = default; + +SlabAllocatorImpl::SentinelSlab::SentinelSlab() : Slab(nullptr, nullptr) { +} + +SlabAllocatorImpl::SentinelSlab::SentinelSlab(SentinelSlab&& rhs) = default; + +SlabAllocatorImpl::SentinelSlab::~SentinelSlab() { + Slab* slab = this->next; + while (slab != nullptr) { + Slab* next = slab->next; + ASSERT(slab->blocksInUse == 0); + slab->~Slab(); + slab = next; + } +} + +// SlabAllocatorImpl + +SlabAllocatorImpl::Index SlabAllocatorImpl::kInvalidIndex = + std::numeric_limits::max(); + +SlabAllocatorImpl::SlabAllocatorImpl(Index blocksPerSlab, + uint32_t objectSize, + uint32_t objectAlignment) + : mAllocationAlignment(std::max(static_cast(alignof(Slab)), objectAlignment)), + mSlabBlocksOffset(Align(sizeof(Slab), objectAlignment)), + mIndexLinkNodeOffset(Align(objectSize, alignof(IndexLinkNode))), + mBlockStride(Align(mIndexLinkNodeOffset + sizeof(IndexLinkNode), objectAlignment)), + mBlocksPerSlab(blocksPerSlab), + mTotalAllocationSize( + // required allocation size + static_cast(mSlabBlocksOffset) + mBlocksPerSlab * mBlockStride + + // Pad the allocation size by mAllocationAlignment so that the aligned allocation still + // fulfills the required size. + mAllocationAlignment) { + ASSERT(IsPowerOfTwo(mAllocationAlignment)); +} + +SlabAllocatorImpl::SlabAllocatorImpl(SlabAllocatorImpl&& rhs) + : mAllocationAlignment(rhs.mAllocationAlignment), + mSlabBlocksOffset(rhs.mSlabBlocksOffset), + mIndexLinkNodeOffset(rhs.mIndexLinkNodeOffset), + mBlockStride(rhs.mBlockStride), + mBlocksPerSlab(rhs.mBlocksPerSlab), + mTotalAllocationSize(rhs.mTotalAllocationSize), + mAvailableSlabs(std::move(rhs.mAvailableSlabs)), + mFullSlabs(std::move(rhs.mFullSlabs)), + mRecycledSlabs(std::move(rhs.mRecycledSlabs)) { +} + +SlabAllocatorImpl::~SlabAllocatorImpl() = default; + +SlabAllocatorImpl::IndexLinkNode* SlabAllocatorImpl::OffsetFrom( + IndexLinkNode* node, + std::make_signed_t offset) const { + return reinterpret_cast(reinterpret_cast(node) + + static_cast(mBlockStride) * offset); +} + +SlabAllocatorImpl::IndexLinkNode* SlabAllocatorImpl::NodeFromObject(void* object) const { + return reinterpret_cast(static_cast(object) + + mIndexLinkNodeOffset); +} + +void* SlabAllocatorImpl::ObjectFromNode(IndexLinkNode* node) const { + return static_cast(reinterpret_cast(node) - mIndexLinkNodeOffset); +} + +bool SlabAllocatorImpl::IsNodeInSlab(Slab* slab, IndexLinkNode* node) const { + char* firstObjectPtr = reinterpret_cast(slab) + mSlabBlocksOffset; + IndexLinkNode* firstNode = NodeFromObject(firstObjectPtr); + IndexLinkNode* lastNode = OffsetFrom(firstNode, mBlocksPerSlab - 1); + return node >= firstNode && node <= lastNode && node->index < mBlocksPerSlab; +} + +void SlabAllocatorImpl::PushFront(Slab* slab, IndexLinkNode* node) const { + ASSERT(IsNodeInSlab(slab, node)); + + IndexLinkNode* head = slab->freeList; + if (head == nullptr) { + node->nextIndex = kInvalidIndex; + } else { + ASSERT(IsNodeInSlab(slab, head)); + node->nextIndex = head->index; + } + slab->freeList = node; + + ASSERT(slab->blocksInUse != 0); + slab->blocksInUse--; +} + +SlabAllocatorImpl::IndexLinkNode* SlabAllocatorImpl::PopFront(Slab* slab) const { + ASSERT(slab->freeList != nullptr); + + IndexLinkNode* head = slab->freeList; + if (head->nextIndex == kInvalidIndex) { + slab->freeList = nullptr; + } else { + ASSERT(IsNodeInSlab(slab, head)); + slab->freeList = OffsetFrom(head, head->nextIndex - head->index); + ASSERT(IsNodeInSlab(slab, slab->freeList)); + } + + ASSERT(slab->blocksInUse < mBlocksPerSlab); + slab->blocksInUse++; + return head; +} + +void SlabAllocatorImpl::SentinelSlab::Prepend(SlabAllocatorImpl::Slab* slab) { + if (this->next != nullptr) { + this->next->prev = slab; + } + slab->prev = this; + slab->next = this->next; + this->next = slab; +} + +void SlabAllocatorImpl::Slab::Splice() { + SlabAllocatorImpl::Slab* originalPrev = this->prev; + SlabAllocatorImpl::Slab* originalNext = this->next; + + this->prev = nullptr; + this->next = nullptr; + + ASSERT(originalPrev != nullptr); + + // Set the originalNext's prev pointer. + if (originalNext != nullptr) { + originalNext->prev = originalPrev; + } + + // Now, set the originalNext as the originalPrev's new next. + originalPrev->next = originalNext; +} + +void* SlabAllocatorImpl::Allocate() { + if (mAvailableSlabs.next == nullptr) { + GetNewSlab(); + } + + Slab* slab = mAvailableSlabs.next; + IndexLinkNode* node = PopFront(slab); + ASSERT(node != nullptr); + + // Move full slabs to a separate list, so allocate can always return quickly. + if (slab->blocksInUse == mBlocksPerSlab) { + slab->Splice(); + mFullSlabs.Prepend(slab); + } + + return ObjectFromNode(node); +} + +void SlabAllocatorImpl::Deallocate(void* ptr) { + IndexLinkNode* node = NodeFromObject(ptr); + + ASSERT(node->index < mBlocksPerSlab); + void* firstAllocation = ObjectFromNode(OffsetFrom(node, -node->index)); + Slab* slab = reinterpret_cast(static_cast(firstAllocation) - mSlabBlocksOffset); + ASSERT(slab != nullptr); + + bool slabWasFull = slab->blocksInUse == mBlocksPerSlab; + + ASSERT(slab->blocksInUse != 0); + PushFront(slab, node); + + if (slabWasFull) { + // Slab is in the full list. Move it to the recycled list. + ASSERT(slab->freeList != nullptr); + slab->Splice(); + mRecycledSlabs.Prepend(slab); + } + + // TODO(enga): Occasionally prune slabs if |blocksInUse == 0|. + // Doing so eagerly hurts performance. +} + +void SlabAllocatorImpl::GetNewSlab() { + // Should only be called when there are no available slabs. + ASSERT(mAvailableSlabs.next == nullptr); + + if (mRecycledSlabs.next != nullptr) { + // If the recycled list is non-empty, swap their contents. + std::swap(mAvailableSlabs.next, mRecycledSlabs.next); + + // We swapped the next pointers, so the prev pointer is wrong. + // Update it here. + mAvailableSlabs.next->prev = &mAvailableSlabs; + ASSERT(mRecycledSlabs.next == nullptr); + return; + } + + // TODO(enga): Use aligned_alloc with C++17. + auto allocation = std::unique_ptr(new char[mTotalAllocationSize]); + char* alignedPtr = AlignPtr(allocation.get(), mAllocationAlignment); + + char* dataStart = alignedPtr + mSlabBlocksOffset; + + IndexLinkNode* node = NodeFromObject(dataStart); + for (uint32_t i = 0; i < mBlocksPerSlab; ++i) { + new (OffsetFrom(node, i)) IndexLinkNode(i, i + 1); + } + + IndexLinkNode* lastNode = OffsetFrom(node, mBlocksPerSlab - 1); + lastNode->nextIndex = kInvalidIndex; + + mAvailableSlabs.Prepend(new (alignedPtr) Slab(std::move(allocation), node)); +} diff --git a/third_party/dawn/src/common/SlabAllocator.h b/third_party/dawn/src/common/SlabAllocator.h new file mode 100644 index 00000000000..939f1c029d1 --- /dev/null +++ b/third_party/dawn/src/common/SlabAllocator.h @@ -0,0 +1,184 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_SLABALLOCATOR_H_ +#define COMMON_SLABALLOCATOR_H_ + +#include "common/PlacementAllocated.h" + +#include +#include +#include + +// The SlabAllocator allocates objects out of one or more fixed-size contiguous "slabs" of memory. +// This makes it very quick to allocate and deallocate fixed-size objects because the allocator only +// needs to index an offset into pre-allocated memory. It is similar to a pool-allocator that +// recycles memory from previous allocations, except multiple allocations are hosted contiguously in +// one large slab. +// +// Internally, the SlabAllocator stores slabs as a linked list to avoid extra indirections indexing +// into an std::vector. To service an allocation request, the allocator only needs to know the first +// currently available slab. There are three backing linked lists: AVAILABLE, FULL, and RECYCLED. +// A slab that is AVAILABLE can be used to immediately service allocation requests. Once it has no +// remaining space, it is moved to the FULL state. When a FULL slab sees any deallocations, it is +// moved to the RECYCLED state. The RECYCLED state is separate from the AVAILABLE state so that +// deallocations don't immediately prepend slabs to the AVAILABLE list, and change the current slab +// servicing allocations. When the AVAILABLE list becomes empty is it swapped with the RECYCLED +// list. +// +// Allocated objects are placement-allocated with some extra info at the end (we'll call the Object +// plus the extra bytes a "block") used to specify the constant index of the block in its parent +// slab, as well as the index of the next available block. So, following the block next-indices +// forms a linked list of free blocks. +// +// Slab creation: When a new slab is allocated, sufficient memory is allocated for it, and then the +// slab metadata plus all of its child blocks are placement-allocated into the memory. Indices and +// next-indices are initialized to form the free-list of blocks. +// +// Allocation: When an object is allocated, if there is no space available in an existing slab, a +// new slab is created (or an old slab is recycled). The first block of the slab is removed and +// returned. +// +// Deallocation: When an object is deallocated, it can compute the pointer to its parent slab +// because it stores the index of its own allocation. That block is then prepended to the slab's +// free list. +class SlabAllocatorImpl { + public: + // Allocations host their current index and the index of the next free block. + // Because this is an index, and not a byte offset, it can be much smaller than a size_t. + // TODO(enga): Is uint8_t sufficient? + using Index = uint16_t; + + SlabAllocatorImpl(SlabAllocatorImpl&& rhs); + + protected: + // This is essentially a singly linked list using indices instead of pointers, + // so we store the index of "this" in |this->index|. + struct IndexLinkNode : PlacementAllocated { + IndexLinkNode(Index index, Index nextIndex); + + const Index index; // The index of this block in the slab. + Index nextIndex; // The index of the next available block. kInvalidIndex, if none. + }; + + struct Slab : PlacementAllocated { + // A slab is placement-allocated into an aligned pointer from a separate allocation. + // Ownership of the allocation is transferred to the slab on creation. + // | ---------- allocation --------- | + // | pad | Slab | data ------------> | + Slab(std::unique_ptr allocation, IndexLinkNode* head); + Slab(Slab&& rhs); + + void Splice(); + + std::unique_ptr allocation; + IndexLinkNode* freeList; + Slab* prev; + Slab* next; + Index blocksInUse; + }; + + SlabAllocatorImpl(Index blocksPerSlab, uint32_t objectSize, uint32_t objectAlignment); + ~SlabAllocatorImpl(); + + // Allocate a new block of memory. + void* Allocate(); + + // Deallocate a block of memory. + void Deallocate(void* ptr); + + private: + // The maximum value is reserved to indicate the end of the list. + static Index kInvalidIndex; + + // Get the IndexLinkNode |offset| slots away. + IndexLinkNode* OffsetFrom(IndexLinkNode* node, std::make_signed_t offset) const; + + // Compute the pointer to the IndexLinkNode from an allocated object. + IndexLinkNode* NodeFromObject(void* object) const; + + // Compute the pointer to the object from an IndexLinkNode. + void* ObjectFromNode(IndexLinkNode* node) const; + + bool IsNodeInSlab(Slab* slab, IndexLinkNode* node) const; + + // The Slab stores a linked-list of free allocations. + // PushFront/PopFront adds/removes an allocation from the free list. + void PushFront(Slab* slab, IndexLinkNode* node) const; + IndexLinkNode* PopFront(Slab* slab) const; + + // Replace the current slab with a new one, and chain the old one off of it. + // Both slabs may still be used for for allocation/deallocation, but older slabs + // will be a little slower to get allocations from. + void GetNewSlab(); + + const uint32_t mAllocationAlignment; + + // | Slab | pad | Obj | pad | Node | pad | Obj | pad | Node | pad | .... + // | -----------| mSlabBlocksOffset + // | | ---------------------- | mBlockStride + // | | ----------| mIndexLinkNodeOffset + // | --------------------------------------> (mSlabBlocksOffset + mBlocksPerSlab * mBlockStride) + + // A Slab is metadata, followed by the aligned memory to allocate out of. |mSlabBlocksOffset| is + // the offset to the start of the aligned memory region. + const uint32_t mSlabBlocksOffset; + + // The IndexLinkNode is stored after the Allocation itself. This is the offset to it. + const uint32_t mIndexLinkNodeOffset; + + // Because alignment of allocations may introduce padding, |mBlockStride| is the + // distance between aligned blocks of (Allocation + IndexLinkNode) + const uint32_t mBlockStride; + + const Index mBlocksPerSlab; // The total number of blocks in a slab. + + const size_t mTotalAllocationSize; + + struct SentinelSlab : Slab { + SentinelSlab(); + ~SentinelSlab(); + + SentinelSlab(SentinelSlab&& rhs); + + void Prepend(Slab* slab); + }; + + SentinelSlab mAvailableSlabs; // Available slabs to service allocations. + SentinelSlab mFullSlabs; // Full slabs. Stored here so we can skip checking them. + SentinelSlab mRecycledSlabs; // Recycled slabs. Not immediately added to |mAvailableSlabs| so + // we don't thrash the current "active" slab. +}; + +template +class SlabAllocator : public SlabAllocatorImpl { + public: + SlabAllocator(size_t totalObjectBytes, + uint32_t objectSize = sizeof(T), + uint32_t objectAlignment = alignof(T)) + : SlabAllocatorImpl(totalObjectBytes / objectSize, objectSize, objectAlignment) { + } + + template + T* Allocate(Args&&... args) { + void* ptr = SlabAllocatorImpl::Allocate(); + return new (ptr) T(std::forward(args)...); + } + + void Deallocate(T* object) { + SlabAllocatorImpl::Deallocate(object); + } +}; + +#endif // COMMON_SLABALLOCATOR_H_ diff --git a/third_party/dawn/src/common/StackContainer.h b/third_party/dawn/src/common/StackContainer.h new file mode 100644 index 00000000000..be3cf32d0de --- /dev/null +++ b/third_party/dawn/src/common/StackContainer.h @@ -0,0 +1,262 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is a modified copy of Chromium's /src/base/containers/stack_container.h + +#ifndef COMMON_STACKCONTAINER_H_ +#define COMMON_STACKCONTAINER_H_ + +#include "common/Compiler.h" + +#include +#include + +// This allocator can be used with STL containers to provide a stack buffer +// from which to allocate memory and overflows onto the heap. This stack buffer +// would be allocated on the stack and allows us to avoid heap operations in +// some situations. +// +// STL likes to make copies of allocators, so the allocator itself can't hold +// the data. Instead, we make the creator responsible for creating a +// StackAllocator::Source which contains the data. Copying the allocator +// merely copies the pointer to this shared source, so all allocators created +// based on our allocator will share the same stack buffer. +// +// This stack buffer implementation is very simple. The first allocation that +// fits in the stack buffer will use the stack buffer. Any subsequent +// allocations will not use the stack buffer, even if there is unused room. +// This makes it appropriate for array-like containers, but the caller should +// be sure to reserve() in the container up to the stack buffer size. Otherwise +// the container will allocate a small array which will "use up" the stack +// buffer. +template +class StackAllocator : public std::allocator { + public: + typedef typename std::allocator::pointer pointer; + typedef typename std::allocator::size_type size_type; + + // Backing store for the allocator. The container owner is responsible for + // maintaining this for as long as any containers using this allocator are + // live. + struct Source { + Source() : used_stack_buffer_(false) { + } + + // Casts the buffer in its right type. + T* stack_buffer() { + return reinterpret_cast(stack_buffer_); + } + const T* stack_buffer() const { + return reinterpret_cast(&stack_buffer_); + } + + // The buffer itself. It is not of type T because we don't want the + // constructors and destructors to be automatically called. Define a POD + // buffer of the right size instead. + alignas(T) char stack_buffer_[sizeof(T[stack_capacity])]; +#if defined(DAWN_COMPILER_GCC) && !defined(__x86_64__) && !defined(__i386__) + static_assert(alignof(T) <= 16, "http://crbug.com/115612"); +#endif + + // Set when the stack buffer is used for an allocation. We do not track + // how much of the buffer is used, only that somebody is using it. + bool used_stack_buffer_; + }; + + // Used by containers when they want to refer to an allocator of type U. + template + struct rebind { + typedef StackAllocator other; + }; + + // For the straight up copy c-tor, we can share storage. + StackAllocator(const StackAllocator& rhs) + : std::allocator(), source_(rhs.source_) { + } + + // ISO C++ requires the following constructor to be defined, + // and std::vector in VC++2008SP1 Release fails with an error + // in the class _Container_base_aux_alloc_real (from ) + // if the constructor does not exist. + // For this constructor, we cannot share storage; there's + // no guarantee that the Source buffer of Ts is large enough + // for Us. + // TODO: If we were fancy pants, perhaps we could share storage + // iff sizeof(T) == sizeof(U). + template + StackAllocator(const StackAllocator& other) : source_(nullptr) { + } + + // This constructor must exist. It creates a default allocator that doesn't + // actually have a stack buffer. glibc's std::string() will compare the + // current allocator against the default-constructed allocator, so this + // should be fast. + StackAllocator() : source_(nullptr) { + } + + explicit StackAllocator(Source* source) : source_(source) { + } + + // Actually do the allocation. Use the stack buffer if nobody has used it yet + // and the size requested fits. Otherwise, fall through to the standard + // allocator. + pointer allocate(size_type n) { + if (source_ && !source_->used_stack_buffer_ && n <= stack_capacity) { + source_->used_stack_buffer_ = true; + return source_->stack_buffer(); + } else { + return std::allocator::allocate(n); + } + } + + // Free: when trying to free the stack buffer, just mark it as free. For + // non-stack-buffer pointers, just fall though to the standard allocator. + void deallocate(pointer p, size_type n) { + if (source_ && p == source_->stack_buffer()) + source_->used_stack_buffer_ = false; + else + std::allocator::deallocate(p, n); + } + + private: + Source* source_; +}; + +// A wrapper around STL containers that maintains a stack-sized buffer that the +// initial capacity of the vector is based on. Growing the container beyond the +// stack capacity will transparently overflow onto the heap. The container must +// support reserve(). +// +// This will not work with std::string since some implementations allocate +// more bytes than requested in calls to reserve(), forcing the allocation onto +// the heap. http://crbug.com/709273 +// +// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this +// type. This object is really intended to be used only internally. You'll want +// to use the wrappers below for different types. +template +class StackContainer { + public: + typedef TContainerType ContainerType; + typedef typename ContainerType::value_type ContainedType; + typedef StackAllocator Allocator; + + // Allocator must be constructed before the container! + StackContainer() : allocator_(&stack_data_), container_(allocator_) { + // Make the container use the stack allocation by reserving our buffer size + // before doing anything else. + container_.reserve(stack_capacity); + } + + // Getters for the actual container. + // + // Danger: any copies of this made using the copy constructor must have + // shorter lifetimes than the source. The copy will share the same allocator + // and therefore the same stack buffer as the original. Use std::copy to + // copy into a "real" container for longer-lived objects. + ContainerType& container() { + return container_; + } + const ContainerType& container() const { + return container_; + } + + // Support operator-> to get to the container. This allows nicer syntax like: + // StackContainer<...> foo; + // std::sort(foo->begin(), foo->end()); + ContainerType* operator->() { + return &container_; + } + const ContainerType* operator->() const { + return &container_; + } + + // Retrieves the stack source so that that unit tests can verify that the + // buffer is being used properly. + const typename Allocator::Source& stack_data() const { + return stack_data_; + } + + protected: + typename Allocator::Source stack_data_; + Allocator allocator_; + ContainerType container_; + + private: + StackContainer(const StackContainer& rhs) = delete; + StackContainer& operator=(const StackContainer& rhs) = delete; + StackContainer(StackContainer&& rhs) = delete; + StackContainer& operator=(StackContainer&& rhs) = delete; +}; + +// Range-based iteration support for StackContainer. +template +auto begin(const StackContainer& stack_container) + -> decltype(begin(stack_container.container())) { + return begin(stack_container.container()); +} + +template +auto begin(StackContainer& stack_container) + -> decltype(begin(stack_container.container())) { + return begin(stack_container.container()); +} + +template +auto end(StackContainer& stack_container) + -> decltype(end(stack_container.container())) { + return end(stack_container.container()); +} + +template +auto end(const StackContainer& stack_container) + -> decltype(end(stack_container.container())) { + return end(stack_container.container()); +} + +// StackVector ----------------------------------------------------------------- + +// Example: +// StackVector foo; +// foo->push_back(22); // we have overloaded operator-> +// foo[0] = 10; // as well as operator[] +template +class StackVector + : public StackContainer>, stack_capacity> { + public: + StackVector() + : StackContainer>, stack_capacity>() { + } + + // We need to put this in STL containers sometimes, which requires a copy + // constructor. We can't call the regular copy constructor because that will + // take the stack buffer from the original. Here, we create an empty object + // and make a stack buffer of its own. + StackVector(const StackVector& other) + : StackContainer>, stack_capacity>() { + this->container().assign(other->begin(), other->end()); + } + + StackVector& operator=(const StackVector& other) { + this->container().assign(other->begin(), other->end()); + return *this; + } + + // Vectors are commonly indexed, which isn't very convenient even with + // operator-> (using "->at()" does exception stuff we don't want). + T& operator[](size_t i) { + return this->container().operator[](i); + } + const T& operator[](size_t i) const { + return this->container().operator[](i); + } + + private: + // StackVector(const StackVector& rhs) = delete; + // StackVector& operator=(const StackVector& rhs) = delete; + StackVector(StackVector&& rhs) = delete; + StackVector& operator=(StackVector&& rhs) = delete; +}; + +#endif // COMMON_STACKCONTAINER_H_ diff --git a/third_party/dawn/src/common/SwapChainUtils.h b/third_party/dawn/src/common/SwapChainUtils.h index a736227d79b..c1ad5f2e62a 100644 --- a/third_party/dawn/src/common/SwapChainUtils.h +++ b/third_party/dawn/src/common/SwapChainUtils.h @@ -26,7 +26,7 @@ DawnSwapChainImplementation CreateSwapChainImplementation(T* swapChain) { reinterpret_cast(userData)->Init(ctx); }; impl.Destroy = [](void* userData) { delete reinterpret_cast(userData); }; - impl.Configure = [](void* userData, DawnTextureFormat format, DawnTextureUsageBit allowedUsage, + impl.Configure = [](void* userData, WGPUTextureFormat format, WGPUTextureUsage allowedUsage, uint32_t width, uint32_t height) { return static_cast(userData)->Configure(format, allowedUsage, width, height); }; diff --git a/third_party/dawn/src/common/SystemUtils.cpp b/third_party/dawn/src/common/SystemUtils.cpp new file mode 100644 index 00000000000..58aac01c443 --- /dev/null +++ b/third_party/dawn/src/common/SystemUtils.cpp @@ -0,0 +1,148 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/SystemUtils.h" + +#include "common/Assert.h" + +#if defined(DAWN_PLATFORM_WINDOWS) +# include +# include +#elif defined(DAWN_PLATFORM_LINUX) +# include +# include +# include +#elif defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS) +# include +# include +#endif + +#include + +#if defined(DAWN_PLATFORM_WINDOWS) +const char* GetPathSeparator() { + return "\\"; +} + +std::string GetEnvironmentVar(const char* variableName) { + // First pass a size of 0 to get the size of variable value. + char* tempBuf = nullptr; + DWORD result = GetEnvironmentVariableA(variableName, tempBuf, 0); + if (result == 0) { + return ""; + } + + // Then get variable value with its actual size. + std::vector buffer(result + 1); + if (GetEnvironmentVariableA(variableName, buffer.data(), static_cast(buffer.size())) == + 0) { + return ""; + } + return std::string(buffer.data()); +} + +bool SetEnvironmentVar(const char* variableName, const char* value) { + return SetEnvironmentVariableA(variableName, value) == TRUE; +} +#elif defined(DAWN_PLATFORM_POSIX) +const char* GetPathSeparator() { + return "/"; +} + +std::string GetEnvironmentVar(const char* variableName) { + char* value = getenv(variableName); + return value == nullptr ? "" : std::string(value); +} + +bool SetEnvironmentVar(const char* variableName, const char* value) { + return setenv(variableName, value, 1) == 0; +} +#else +# error "Implement Get/SetEnvironmentVar for your platform." +#endif + +#if defined(DAWN_PLATFORM_WINDOWS) +std::string GetExecutablePath() { + std::array executableFileBuf; + DWORD executablePathLen = GetModuleFileNameA(nullptr, executableFileBuf.data(), + static_cast(executableFileBuf.size())); + return executablePathLen > 0 ? std::string(executableFileBuf.data()) : ""; +} +#elif defined(DAWN_PLATFORM_LINUX) +std::string GetExecutablePath() { + std::array path; + ssize_t result = readlink("/proc/self/exe", path.data(), PATH_MAX - 1); + if (result < 0 || static_cast(result) >= PATH_MAX - 1) { + return ""; + } + + path[result] = '\0'; + return path.data(); +} +#elif defined(DAWN_PLATFORM_MACOS) || defined(DAWN_PLATFORM_IOS) +std::string GetExecutablePath() { + uint32_t size = 0; + _NSGetExecutablePath(nullptr, &size); + + std::vector buffer(size + 1); + if (_NSGetExecutablePath(buffer.data(), &size) != 0) { + return ""; + } + + buffer[size] = '\0'; + return buffer.data(); +} +#elif defined(DAWN_PLATFORM_FUCHSIA) +std::string GetExecutablePath() { + // TODO: Implement on Fuchsia + return ""; +} +#elif defined(DAWN_PLATFORM_EMSCRIPTEN) +std::string GetExecutablePath() { + UNREACHABLE(); + return ""; +} +#else +# error "Implement GetExecutablePath for your platform." +#endif + +std::string GetExecutableDirectory() { + std::string exePath = GetExecutablePath(); + size_t lastPathSepLoc = exePath.find_last_of(GetPathSeparator()); + return lastPathSepLoc != std::string::npos ? exePath.substr(0, lastPathSepLoc + 1) : ""; +} + +// ScopedEnvironmentVar + +ScopedEnvironmentVar::ScopedEnvironmentVar(const char* variableName, const char* value) + : mName(variableName), + mOriginalValue(GetEnvironmentVar(variableName)), + mIsSet(SetEnvironmentVar(variableName, value)) { +} + +ScopedEnvironmentVar::~ScopedEnvironmentVar() { + if (mIsSet) { + bool success = SetEnvironmentVar(mName.c_str(), mOriginalValue.c_str()); + // If we set the environment variable in the constructor, we should never fail restoring it. + ASSERT(success); + } +} + +bool ScopedEnvironmentVar::Set(const char* variableName, const char* value) { + ASSERT(!mIsSet); + mName = variableName; + mOriginalValue = GetEnvironmentVar(variableName); + mIsSet = SetEnvironmentVar(variableName, value); + return mIsSet; +} diff --git a/third_party/dawn/src/common/SystemUtils.h b/third_party/dawn/src/common/SystemUtils.h new file mode 100644 index 00000000000..ed18c31e661 --- /dev/null +++ b/third_party/dawn/src/common/SystemUtils.h @@ -0,0 +1,44 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_SYSTEMUTILS_H_ +#define COMMON_SYSTEMUTILS_H_ + +#include "common/Platform.h" + +#include + +const char* GetPathSeparator(); +std::string GetEnvironmentVar(const char* variableName); +bool SetEnvironmentVar(const char* variableName, const char* value); +std::string GetExecutableDirectory(); + +class ScopedEnvironmentVar { + public: + ScopedEnvironmentVar() = default; + ScopedEnvironmentVar(const char* variableName, const char* value); + ~ScopedEnvironmentVar(); + + ScopedEnvironmentVar(const ScopedEnvironmentVar& rhs) = delete; + ScopedEnvironmentVar& operator=(const ScopedEnvironmentVar& rhs) = delete; + + bool Set(const char* variableName, const char* value); + + private: + std::string mName; + std::string mOriginalValue; + bool mIsSet = false; +}; + +#endif // COMMON_SYSTEMUTILS_H_ diff --git a/third_party/dawn/src/common/TypedInteger.h b/third_party/dawn/src/common/TypedInteger.h new file mode 100644 index 00000000000..5474d9a920b --- /dev/null +++ b/third_party/dawn/src/common/TypedInteger.h @@ -0,0 +1,212 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_TYPEDINTEGER_H_ +#define COMMON_TYPEDINTEGER_H_ + +#include "common/Assert.h" + +#include +#include + +// TypedInteger is helper class that provides additional type safety in Debug. +// - Integers of different (Tag, BaseIntegerType) may not be used interoperably +// - Allows casts only to the underlying type. +// - Integers of the same (Tag, BaseIntegerType) may be compared or assigned. +// This class helps ensure that the many types of indices in Dawn aren't mixed up and used +// interchangably. +// In Release builds, when DAWN_ENABLE_ASSERTS is not defined, TypedInteger is a passthrough +// typedef of the underlying type. +// +// Example: +// using UintA = TypedInteger; +// using UintB = TypedInteger; +// +// in Release: +// using UintA = uint32_t; +// using UintB = uint32_t; +// +// in Debug: +// using UintA = detail::TypedIntegerImpl; +// using UintB = detail::TypedIntegerImpl; +// +// Assignment, construction, comparison, and arithmetic with TypedIntegerImpl are allowed +// only for typed integers of exactly the same type. Further, they must be +// created / cast explicitly; there is no implicit conversion. +// +// UintA a(2); +// uint32_t aValue = static_cast(a); +// +namespace detail { + template + class TypedIntegerImpl; +} // namespace detail + +template ::value>> +#if defined(DAWN_ENABLE_ASSERTS) +using TypedInteger = detail::TypedIntegerImpl; +#else +using TypedInteger = T; +#endif + +namespace detail { + template + class alignas(T) TypedIntegerImpl { + static_assert(std::is_integral::value, "TypedInteger must be integral"); + T mValue; + + public: + constexpr TypedIntegerImpl() : mValue(0) { + static_assert(alignof(TypedIntegerImpl) == alignof(T), ""); + static_assert(sizeof(TypedIntegerImpl) == sizeof(T), ""); + } + + // Construction from non-narrowing integral types. + template ::value && + std::numeric_limits::max() <= std::numeric_limits::max() && + std::numeric_limits::min() >= std::numeric_limits::min()>> + explicit constexpr TypedIntegerImpl(I rhs) : mValue(static_cast(rhs)) { + } + + // Allow explicit casts only to the underlying type. If you're casting out of an + // TypedInteger, you should know what what you're doing, and exactly what type you + // expect. + explicit constexpr operator T() const { + return static_cast(this->mValue); + } + +// Same-tag TypedInteger comparison operators +#define TYPED_COMPARISON(op) \ + constexpr bool operator op(const TypedIntegerImpl& rhs) const { \ + return mValue op rhs.mValue; \ + } + TYPED_COMPARISON(<) + TYPED_COMPARISON(<=) + TYPED_COMPARISON(>) + TYPED_COMPARISON(>=) + TYPED_COMPARISON(==) + TYPED_COMPARISON(!=) +#undef TYPED_COMPARISON + + // Increment / decrement operators for for-loop iteration + constexpr TypedIntegerImpl& operator++() { + ASSERT(this->mValue < std::numeric_limits::max()); + ++this->mValue; + return *this; + } + + constexpr TypedIntegerImpl operator++(int) { + TypedIntegerImpl ret = *this; + + ASSERT(this->mValue < std::numeric_limits::max()); + ++this->mValue; + return ret; + } + + constexpr TypedIntegerImpl& operator--() { + assert(this->mValue > std::numeric_limits::min()); + --this->mValue; + return *this; + } + + constexpr TypedIntegerImpl operator--(int) { + TypedIntegerImpl ret = *this; + + ASSERT(this->mValue > std::numeric_limits::min()); + --this->mValue; + return ret; + } + + template + constexpr std::enable_if_t::value, TypedIntegerImpl> operator-() const { + static_assert(std::is_same::value, ""); + // The negation of the most negative value cannot be represented. + ASSERT(this->mValue != std::numeric_limits::min()); + return TypedIntegerImpl(-this->mValue); + } + + template + constexpr std::enable_if_t::value, TypedIntegerImpl> operator+( + TypedIntegerImpl rhs) const { + static_assert(std::is_same::value, ""); + // Overflow would wrap around + ASSERT(this->mValue + rhs.mValue >= this->mValue); + + return TypedIntegerImpl(this->mValue + rhs.mValue); + } + + template + constexpr std::enable_if_t::value, TypedIntegerImpl> operator-( + TypedIntegerImpl rhs) const { + static_assert(std::is_same::value, ""); + // Overflow would wrap around + ASSERT(this->mValue - rhs.mValue <= this->mValue); + return TypedIntegerImpl(this->mValue - rhs.mValue); + } + + template + constexpr std::enable_if_t::value, TypedIntegerImpl> operator+( + TypedIntegerImpl rhs) const { + static_assert(std::is_same::value, ""); + if (this->mValue > 0) { + // rhs is positive: |rhs| is at most the distance between max and |this|. + // rhs is negative: (positive + negative) won't overflow + ASSERT(rhs.mValue <= std::numeric_limits::max() - this->mValue); + } else { + // rhs is postive: (negative + positive) won't underflow + // rhs is negative: |rhs| isn't less than the (negative) distance between min + // and |this| + ASSERT(rhs.mValue >= std::numeric_limits::min() - this->mValue); + } + return TypedIntegerImpl(this->mValue + rhs.mValue); + } + + template + constexpr std::enable_if_t::value, TypedIntegerImpl> operator-( + TypedIntegerImpl rhs) const { + static_assert(std::is_same::value, ""); + if (this->mValue > 0) { + // rhs is positive: positive minus positive won't overflow + // rhs is negative: |rhs| isn't less than the (negative) distance between |this| + // and max. + ASSERT(rhs.mValue >= this->mValue - std::numeric_limits::max()); + } else { + // rhs is positive: |rhs| is at most the distance between min and |this| + // rhs is negative: negative minus negative won't overflow + ASSERT(rhs.mValue <= this->mValue - std::numeric_limits::min()); + } + return TypedIntegerImpl(this->mValue - rhs.mValue); + } + }; + +} // namespace detail + +namespace std { + + template + class numeric_limits> : public numeric_limits { + public: + static detail::TypedIntegerImpl max() noexcept { + return detail::TypedIntegerImpl(std::numeric_limits::max()); + } + static detail::TypedIntegerImpl min() noexcept { + return detail::TypedIntegerImpl(std::numeric_limits::min()); + } + }; + +} // namespace std + +#endif // COMMON_TYPEDINTEGER_H_ diff --git a/third_party/dawn/src/common/UnderlyingType.h b/third_party/dawn/src/common/UnderlyingType.h new file mode 100644 index 00000000000..09c72c023f9 --- /dev/null +++ b/third_party/dawn/src/common/UnderlyingType.h @@ -0,0 +1,51 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_UNDERLYINGTYPE_H_ +#define COMMON_UNDERLYINGTYPE_H_ + +#include + +// UnderlyingType is similar to std::underlying_type_t. It is a passthrough for already +// integer types which simplifies getting the underlying primitive type for an arbitrary +// template parameter. It includes a specialization for detail::TypedIntegerImpl which yields +// the wrapped integer type. +namespace detail { + template + struct UnderlyingTypeImpl; + + template + struct UnderlyingTypeImpl::value>> { + using type = I; + }; + + template + struct UnderlyingTypeImpl::value>> { + using type = std::underlying_type_t; + }; + + // Forward declare the TypedInteger impl. + template + class TypedIntegerImpl; + + template + struct UnderlyingTypeImpl> { + using type = typename UnderlyingTypeImpl::type; + }; +} // namespace detail + +template +using UnderlyingType = typename detail::UnderlyingTypeImpl::type; + +#endif // COMMON_UNDERLYINGTYPE_H_ diff --git a/third_party/dawn/src/common/ityp_array.h b/third_party/dawn/src/common/ityp_array.h new file mode 100644 index 00000000000..fc772178ccc --- /dev/null +++ b/third_party/dawn/src/common/ityp_array.h @@ -0,0 +1,96 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_ITYP_ARRAY_H_ +#define COMMON_ITYP_ARRAY_H_ + +#include "common/TypedInteger.h" +#include "common/UnderlyingType.h" + +#include +#include + +namespace ityp { + + // ityp::array is a helper class that wraps std::array with the restriction that + // indices must be a particular type |Index|. Dawn uses multiple flat maps of + // index-->data, and this class helps ensure an indices cannot be passed interchangably + // to a flat map of a different type. + template + class array : private std::array { + using I = UnderlyingType; + using Base = std::array; + + static_assert(Size <= std::numeric_limits::max(), ""); + + public: + constexpr array() = default; + + template + constexpr array(Values&&... values) : Base{std::forward(values)...} { + } + + Value& operator[](Index i) { + I index = static_cast(i); + ASSERT(index >= 0 && index < Size); + return Base::operator[](index); + } + + constexpr const Value& operator[](Index i) const { + I index = static_cast(i); + ASSERT(index >= 0 && index < Size); + return Base::operator[](index); + } + + Value& at(Index i) { + I index = static_cast(i); + ASSERT(index >= 0 && index < Size); + return Base::at(index); + } + + constexpr const Value& at(Index i) const { + I index = static_cast(i); + ASSERT(index >= 0 && index < Size); + return Base::at(index); + } + + typename Base::iterator begin() noexcept { + return Base::begin(); + } + + typename Base::const_iterator begin() const noexcept { + return Base::begin(); + } + + typename Base::iterator end() noexcept { + return Base::end(); + } + + typename Base::const_iterator end() const noexcept { + return Base::end(); + } + + constexpr Index size() const { + return Index(static_cast(Size)); + } + + using Base::back; + using Base::data; + using Base::empty; + using Base::front; + }; + +} // namespace ityp + +#endif // COMMON_ITYP_ARRAY_H_ diff --git a/third_party/dawn/src/common/ityp_bitset.h b/third_party/dawn/src/common/ityp_bitset.h new file mode 100644 index 00000000000..339cf182937 --- /dev/null +++ b/third_party/dawn/src/common/ityp_bitset.h @@ -0,0 +1,134 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_ITYP_BITSET_H_ +#define COMMON_ITYP_BITSET_H_ + +#include "common/BitSetIterator.h" +#include "common/TypedInteger.h" +#include "common/UnderlyingType.h" + +namespace ityp { + + // ityp::bitset is a helper class that wraps std::bitset with the restriction that + // indices must be a particular type |Index|. + template + class bitset : private std::bitset { + using I = UnderlyingType; + using Base = std::bitset; + + static_assert(sizeof(I) <= sizeof(size_t), ""); + + constexpr bitset(const Base& rhs) : Base(rhs) { + } + + public: + constexpr bitset() noexcept : Base() { + } + + constexpr bitset(unsigned long long value) noexcept : Base(value) { + } + + constexpr bool operator[](Index i) const { + return Base::operator[](static_cast(i)); + } + + typename Base::reference operator[](Index i) { + return Base::operator[](static_cast(i)); + } + + bool test(Index i) const { + return Base::test(static_cast(i)); + } + + using Base::all; + using Base::any; + using Base::count; + using Base::none; + using Base::size; + + bool operator==(const bitset& other) const noexcept { + return Base::operator==(static_cast(other)); + } + + bool operator!=(const bitset& other) const noexcept { + return Base::operator!=(static_cast(other)); + } + + bitset& operator&=(const bitset& other) noexcept { + return static_cast(Base::operator&=(static_cast(other))); + } + + bitset& operator|=(const bitset& other) noexcept { + return static_cast(Base::operator|=(static_cast(other))); + } + + bitset& operator^=(const bitset& other) noexcept { + return static_cast(Base::operator^=(static_cast(other))); + } + + bitset operator~() const noexcept { + return bitset(*this).flip(); + } + + bitset& set() noexcept { + return static_cast(Base::set()); + } + + bitset& set(Index i, bool value = true) { + return static_cast(Base::set(static_cast(i), value)); + } + + bitset& reset() noexcept { + return static_cast(Base::reset()); + } + + bitset& reset(Index i) { + return static_cast(Base::reset(static_cast(i))); + } + + bitset& flip() noexcept { + return static_cast(Base::flip()); + } + + bitset& flip(Index i) { + return static_cast(Base::flip(static_cast(i))); + } + + using Base::to_string; + using Base::to_ullong; + using Base::to_ulong; + + friend bitset operator&(const bitset& lhs, const bitset& rhs) noexcept { + return bitset(static_cast(lhs) & static_cast(rhs)); + } + + friend bitset operator|(const bitset& lhs, const bitset& rhs) noexcept { + return bitset(static_cast(lhs) | static_cast(rhs)); + } + + friend bitset operator^(const bitset& lhs, const bitset& rhs) noexcept { + return bitset(static_cast(lhs) ^ static_cast(rhs)); + } + + friend BitSetIterator IterateBitSet(const bitset& bitset) { + return BitSetIterator(static_cast(bitset)); + } + + friend struct std::hash; + }; + +} // namespace ityp + +#endif // COMMON_ITYP_BITSET_H_ diff --git a/third_party/dawn/src/common/ityp_span.h b/third_party/dawn/src/common/ityp_span.h new file mode 100644 index 00000000000..00ba93f7503 --- /dev/null +++ b/third_party/dawn/src/common/ityp_span.h @@ -0,0 +1,103 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_ITYP_SPAN_H_ +#define COMMON_ITYP_SPAN_H_ + +#include "common/TypedInteger.h" +#include "common/UnderlyingType.h" + +#include + +namespace ityp { + + // ityp::span is a helper class that wraps an unowned packed array of type |Value|. + // It stores the size and pointer to first element. It has the restriction that + // indices must be a particular type |Index|. This provides a type-safe way to index + // raw pointers. + template + class span { + using I = UnderlyingType; + + public: + constexpr span() : mData(nullptr), mSize(0) { + } + constexpr span(Value* data, Index size) : mData(data), mSize(size) { + } + + constexpr Value& operator[](Index i) const { + ASSERT(i < mSize); + return mData[static_cast(i)]; + } + + Value* data() noexcept { + return mData; + } + + const Value* data() const noexcept { + return mData; + } + + Value* begin() noexcept { + return mData; + } + + const Value* begin() const noexcept { + return mData; + } + + Value* end() noexcept { + return mData + static_cast(mSize); + } + + const Value* end() const noexcept { + return mData + static_cast(mSize); + } + + Value& front() { + ASSERT(mData != nullptr); + ASSERT(static_cast(mSize) >= 0); + return *mData; + } + + const Value& front() const { + ASSERT(mData != nullptr); + ASSERT(static_cast(mSize) >= 0); + return *mData; + } + + Value& back() { + ASSERT(mData != nullptr); + ASSERT(static_cast(mSize) >= 0); + return *(mData + static_cast(mSize) - 1); + } + + const Value& back() const { + ASSERT(mData != nullptr); + ASSERT(static_cast(mSize) >= 0); + return *(mData + static_cast(mSize) - 1); + } + + Index size() const { + return mSize; + } + + private: + Value* mData; + Index mSize; + }; + +} // namespace ityp + +#endif // COMMON_ITYP_SPAN_H_ diff --git a/third_party/dawn/src/common/ityp_stack_vec.h b/third_party/dawn/src/common/ityp_stack_vec.h new file mode 100644 index 00000000000..b88888b7897 --- /dev/null +++ b/third_party/dawn/src/common/ityp_stack_vec.h @@ -0,0 +1,103 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_ITYP_STACK_VEC_H_ +#define COMMON_ITYP_STACK_VEC_H_ + +#include "common/Assert.h" +#include "common/StackContainer.h" +#include "common/UnderlyingType.h" + +namespace ityp { + + template + class stack_vec : private StackVector { + using I = UnderlyingType; + using Base = StackVector; + using VectorBase = std::vector>; + static_assert(StaticCapacity <= std::numeric_limits::max(), ""); + + public: + stack_vec() : Base() { + } + stack_vec(Index size) : Base() { + this->container().resize(static_cast(size)); + } + + Value& operator[](Index i) { + ASSERT(i < size()); + return Base::operator[](static_cast(i)); + } + + constexpr const Value& operator[](Index i) const { + ASSERT(i < size()); + return Base::operator[](static_cast(i)); + } + + void resize(Index size) { + this->container().resize(static_cast(size)); + } + + void reserve(Index size) { + this->container().reserve(static_cast(size)); + } + + Value* data() { + return this->container().data(); + } + + const Value* data() const { + return this->container().data(); + } + + typename VectorBase::iterator begin() noexcept { + return this->container().begin(); + } + + typename VectorBase::const_iterator begin() const noexcept { + return this->container().begin(); + } + + typename VectorBase::iterator end() noexcept { + return this->container().end(); + } + + typename VectorBase::const_iterator end() const noexcept { + return this->container().end(); + } + + typename VectorBase::reference front() { + return this->container().front(); + } + + typename VectorBase::const_reference front() const { + return this->container().front(); + } + + typename VectorBase::reference back() { + return this->container().back(); + } + + typename VectorBase::const_reference back() const { + return this->container().back(); + } + + Index size() const { + return Index(static_cast(this->container().size())); + } + }; + +} // namespace ityp + +#endif // COMMON_ITYP_STACK_VEC_H_ diff --git a/third_party/dawn/src/common/ityp_vector.h b/third_party/dawn/src/common/ityp_vector.h new file mode 100644 index 00000000000..a747d5aeb53 --- /dev/null +++ b/third_party/dawn/src/common/ityp_vector.h @@ -0,0 +1,108 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_ITYP_VECTOR_H_ +#define COMMON_ITYP_VECTOR_H_ + +#include "common/TypedInteger.h" +#include "common/UnderlyingType.h" + +#include +#include + +namespace ityp { + + // ityp::vector is a helper class that wraps std::vector with the restriction that + // indices must be a particular type |Index|. + template + class vector : public std::vector { + using I = UnderlyingType; + using Base = std::vector; + + private: + // Disallow access to base constructors and untyped index/size-related operators. + using Base::Base; + using Base::operator=; + using Base::operator[]; + using Base::at; + using Base::reserve; + using Base::resize; + using Base::size; + + public: + vector() : Base() { + } + + explicit vector(Index size) : Base(static_cast(size)) { + } + + vector(Index size, const Value& init) : Base(static_cast(size), init) { + } + + vector(const vector& rhs) : Base(static_cast(rhs)) { + } + + vector(vector&& rhs) : Base(static_cast(rhs)) { + } + + vector(std::initializer_list init) : Base(init) { + } + + vector& operator=(const vector& rhs) { + Base::operator=(static_cast(rhs)); + return *this; + } + + vector& operator=(vector&& rhs) noexcept { + Base::operator=(static_cast(rhs)); + return *this; + } + + Value& operator[](Index i) { + ASSERT(i >= Index(0) && i < size()); + return Base::operator[](static_cast(i)); + } + + constexpr const Value& operator[](Index i) const { + ASSERT(i >= Index(0) && i < size()); + return Base::operator[](static_cast(i)); + } + + Value& at(Index i) { + ASSERT(i >= Index(0) && i < size()); + return Base::at(static_cast(i)); + } + + constexpr const Value& at(Index i) const { + ASSERT(i >= Index(0) && i < size()); + return Base::at(static_cast(i)); + } + + constexpr Index size() const { + ASSERT(std::numeric_limits::max() >= Base::size()); + return Index(static_cast(Base::size())); + } + + void resize(Index size) { + Base::resize(static_cast(size)); + } + + void reserve(Index size) { + Base::reserve(static_cast(size)); + } + }; + +} // namespace ityp + +#endif // COMMON_ITYP_VECTOR_H_ diff --git a/third_party/dawn/src/common/vulkan_platform.h b/third_party/dawn/src/common/vulkan_platform.h index 81f0406bc55..236c68236c0 100644 --- a/third_party/dawn/src/common/vulkan_platform.h +++ b/third_party/dawn/src/common/vulkan_platform.h @@ -18,6 +18,9 @@ #if !defined(DAWN_ENABLE_BACKEND_VULKAN) # error "vulkan_platform.h included without the Vulkan backend enabled" #endif +#if defined(VULKAN_CORE_H_) +# error "vulkan.h included before vulkan_platform.h" +#endif #include "common/Platform.h" @@ -33,10 +36,19 @@ // (like vulkan.h on 64 bit) but makes sure the types are different on 32 bit architectures. #if defined(DAWN_PLATFORM_64_BIT) -# define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) \ - using object##Native = struct object##_T*; +# define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object = struct object##_T*; +// This function is needed because MSVC doesn't accept reinterpret_cast from uint64_t from uint64_t +// TODO(cwallez@chromium.org): Remove this once we rework vulkan_platform.h +template +T NativeNonDispatachableHandleFromU64(uint64_t u64) { + return reinterpret_cast(u64); +} #elif defined(DAWN_PLATFORM_32_BIT) -# define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object##Native = uint64_t; +# define DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) using object = uint64_t; +template +T NativeNonDispatachableHandleFromU64(uint64_t u64) { + return u64; +} #else # error "Unsupported platform" #endif @@ -53,108 +65,135 @@ DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(VkSomeHandle) // One way to get the alignment inside structures of a type is to look at the alignment of it // wrapped in a structure. Hence VkSameHandleNativeWrappe -template -struct WrapperStruct { - T member; -}; - -template -static constexpr size_t AlignOfInStruct = alignof(WrapperStruct); - -static constexpr size_t kNativeVkHandleAlignment = AlignOfInStruct; -static constexpr size_t kUint64Alignment = AlignOfInStruct; - -// Simple handle types that supports "nullptr_t" as a 0 value. -template -class alignas(kNativeVkHandleAlignment) VkNonDispatchableHandle { - public: - // Default constructor and assigning of VK_NULL_HANDLE - VkNonDispatchableHandle() = default; - VkNonDispatchableHandle(std::nullptr_t) : mHandle(0) { +namespace dawn_native { namespace vulkan { + + namespace detail { + template + struct WrapperStruct { + T member; + }; + + template + static constexpr size_t AlignOfInStruct = alignof(WrapperStruct); + + static constexpr size_t kNativeVkHandleAlignment = AlignOfInStruct; + static constexpr size_t kUint64Alignment = AlignOfInStruct; + + // Simple handle types that supports "nullptr_t" as a 0 value. + template + class alignas(detail::kNativeVkHandleAlignment) VkHandle { + public: + // Default constructor and assigning of VK_NULL_HANDLE + VkHandle() = default; + VkHandle(std::nullptr_t) { + } + + // Use default copy constructor/assignment + VkHandle(const VkHandle& other) = default; + VkHandle& operator=(const VkHandle&) = default; + + // Comparisons between handles + bool operator==(VkHandle other) const { + return mHandle == other.mHandle; + } + bool operator!=(VkHandle other) const { + return mHandle != other.mHandle; + } + + // Comparisons between handles and VK_NULL_HANDLE + bool operator==(std::nullptr_t) const { + return mHandle == 0; + } + bool operator!=(std::nullptr_t) const { + return mHandle != 0; + } + + // Implicit conversion to real Vulkan types. + operator HandleType() const { + return GetHandle(); + } + + HandleType GetHandle() const { + return mHandle; + } + + HandleType& operator*() { + return mHandle; + } + + static VkHandle CreateFromHandle(HandleType handle) { + return VkHandle{handle}; + } + + private: + explicit VkHandle(HandleType handle) : mHandle(handle) { + } + + HandleType mHandle = 0; + }; + } // namespace detail + + static constexpr std::nullptr_t VK_NULL_HANDLE = nullptr; + + template + HandleType* AsVkArray(detail::VkHandle* handle) { + return reinterpret_cast(handle); } - // Use default copy constructor/assignment - VkNonDispatchableHandle(const VkNonDispatchableHandle& other) = default; - VkNonDispatchableHandle& operator=(const VkNonDispatchableHandle&) = default; +}} // namespace dawn_native::vulkan + +#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) \ + DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) \ + namespace dawn_native { namespace vulkan { \ + using object = detail::VkHandle; \ + static_assert(sizeof(object) == sizeof(uint64_t), ""); \ + static_assert(alignof(object) == detail::kUint64Alignment, ""); \ + static_assert(sizeof(object) == sizeof(::object), ""); \ + static_assert(alignof(object) == detail::kNativeVkHandleAlignment, ""); \ + } \ + } // namespace dawn_native::vulkan + +// Import additional parts of Vulkan that are supported on our architecture and preemptively include +// headers that vulkan.h includes that we have "undefs" for. +#if defined(DAWN_PLATFORM_WINDOWS) +# define VK_USE_PLATFORM_WIN32_KHR +# include "common/windows_with_undefs.h" +#endif // DAWN_PLATFORM_WINDOWS - // Comparisons between handles - bool operator==(VkNonDispatchableHandle other) { - return mHandle == other.mHandle; - } - bool operator!=(VkNonDispatchableHandle other) { - return mHandle != other.mHandle; - } +#if defined(DAWN_USE_X11) +# define VK_USE_PLATFORM_XLIB_KHR +# include "common/xlib_with_undefs.h" +#endif // defined(DAWN_USE_X11) - // Comparisons between handles and VK_NULL_HANDLE - bool operator==(std::nullptr_t) { - return mHandle == 0; - } - bool operator!=(std::nullptr_t) { - return mHandle != 0; - } +#if defined(DAWN_ENABLE_BACKEND_METAL) +# define VK_USE_PLATFORM_METAL_EXT +#endif // defined(DAWN_ENABLE_BACKEND_METAL) - // The regular Vulkan handle type depends on the pointer width but is always 64 bits wide. - // - On 64bit it is an opaque pointer type, probably to help with type safety - // - On 32bit it is a uint64_t because pointers aren't wide enough (and non dispatchable - // handles can be optimized to not be pointer but contain GPU virtual addresses or the - // data in a packed form). - // Because of this we need two types of conversions from our handle type: to uint64_t and to - // the "native" Vulkan type that may not be an uint64_t +#if defined(DAWN_PLATFORM_ANDROID) +# define VK_USE_PLATFORM_ANDROID_KHR +#endif // defined(DAWN_PLATFORM_ANDROID) - static VkNonDispatchableHandle CreateFromU64(uint64_t handle) { - return {handle}; - } +#if defined(DAWN_PLATFORM_FUCHSIA) +# define VK_USE_PLATFORM_FUCHSIA +#endif // defined(DAWN_PLATFORM_FUCHSIA) - uint64_t GetU64() const { - return mHandle; - } +// The actual inclusion of vulkan.h! +#define VK_NO_PROTOTYPES +#include +// Redefine VK_NULL_HANDLE for better type safety where possible. +#undef VK_NULL_HANDLE #if defined(DAWN_PLATFORM_64_BIT) - static VkNonDispatchableHandle CreateFromHandle(HandleType handle) { - return CreateFromU64(static_cast(reinterpret_cast(handle))); - } - - HandleType GetHandle() const { - return mHandle; - } +static constexpr std::nullptr_t VK_NULL_HANDLE = nullptr; #elif defined(DAWN_PLATFORM_32_BIT) - static VkNonDispatchableHandle CreateFromHandle(HandleType handle) { - return {handle}; - } - - HandleType GetHandle() const { - return mHandle; - } +static constexpr uint64_t VK_NULL_HANDLE = 0; #else # error "Unsupported platform" #endif - private: - VkNonDispatchableHandle(uint64_t handle) : mHandle(handle) { - } - - uint64_t mHandle = 0; -}; - -#define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) \ - struct VkTag##object; \ - DAWN_DEFINE_NATIVE_NON_DISPATCHABLE_HANDLE(object) \ - using object = VkNonDispatchableHandle; \ - static_assert(sizeof(object) == sizeof(uint64_t), ""); \ - static_assert(alignof(object) == kUint64Alignment, ""); \ - static_assert(sizeof(object) == sizeof(object##Native), ""); \ - static_assert(alignof(object) == kNativeVkHandleAlignment, ""); - -# include - - // VK_NULL_HANDLE is defined to 0 but we don't want our handle type to compare to arbitrary - // integers. Redefine VK_NULL_HANDLE to nullptr that has its own type. -# undef VK_NULL_HANDLE -# define VK_NULL_HANDLE nullptr - -// Remove windows.h macros after vulkan_platform's include of windows.h -#if defined(DAWN_PLATFORM_WINDOWS) -# include "common/windows_with_undefs.h" -#endif +// Include Fuchsia-specific definitions that are not upstreamed yet. +#if defined(DAWN_PLATFORM_FUCHSIA) +# include +#endif // defined(DAWN_PLATFORM_FUCHSIA) #endif // COMMON_VULKANPLATFORM_H_ diff --git a/third_party/dawn/src/common/windows_with_undefs.h b/third_party/dawn/src/common/windows_with_undefs.h index c4815464a62..6d8649ca8dd 100644 --- a/third_party/dawn/src/common/windows_with_undefs.h +++ b/third_party/dawn/src/common/windows_with_undefs.h @@ -15,7 +15,7 @@ #ifndef COMMON_WINDOWS_WITH_UNDEFS_H_ #define COMMON_WINDOWS_WITH_UNDEFS_H_ -#include "common/Compiler.h" +#include "common/Platform.h" #if !defined(DAWN_PLATFORM_WINDOWS) # error "windows_with_undefs.h included on non-Windows" @@ -26,6 +26,10 @@ #include // Macros defined for ANSI / Unicode support +#undef CreateWindow #undef GetMessage +// Macros defined to produce compiler intrinsics +#undef MemoryBarrier + #endif // COMMON_WINDOWS_WITH_UNDEFS_H_ diff --git a/third_party/dawn/src/common/xlib_with_undefs.h b/third_party/dawn/src/common/xlib_with_undefs.h new file mode 100644 index 00000000000..f82a19aa2d3 --- /dev/null +++ b/third_party/dawn/src/common/xlib_with_undefs.h @@ -0,0 +1,35 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_XLIB_WITH_UNDEFS_H_ +#define COMMON_XLIB_WITH_UNDEFS_H_ + +#include "common/Platform.h" + +#if !defined(DAWN_PLATFORM_LINUX) +# error "xlib_with_undefs.h included on non-Linux" +#endif + +// This header includes but removes all the extra defines that conflict with +// identifiers in internal code. It should never be included in something that is part of the public +// interface. +#include + +#undef Success +#undef None +#undef Always + +using XErrorHandler = int (*)(Display*, XErrorEvent*); + +#endif // COMMON_XLIB_WITH_UNDEFS_H_ diff --git a/third_party/dawn/src/dawn/BUILD.gn b/third_party/dawn/src/dawn/BUILD.gn index 7e2efade3ec..9034be436f0 100644 --- a/third_party/dawn/src/dawn/BUILD.gn +++ b/third_party/dawn/src/dawn/BUILD.gn @@ -14,8 +14,8 @@ import("../../scripts/dawn_overrides_with_defaults.gni") -import("${dawn_root}/scripts/dawn_component.gni") import("${dawn_root}/generator/dawn_generator.gni") +import("${dawn_root}/scripts/dawn_component.gni") ############################################################################### # Dawn headers @@ -23,51 +23,78 @@ import("${dawn_root}/generator/dawn_generator.gni") dawn_json_generator("dawn_headers_gen") { target = "dawn_headers" + outputs = [ + "src/include/dawn/dawn_proc_table.h", + "src/include/dawn/webgpu.h", + ] +} - # Generate as if we were in the main BUILD.gn because that was historically - # the case and we can't move generated files without clobbering the build. - custom_gen_dir = "${target_gen_dir}/../.." +dawn_json_generator("emscripten_bits_gen") { + target = "emscripten_bits" outputs = [ - "dawn/dawncpp.h", - "dawn/dawn.h", + "src/dawn/webgpu_struct_info.json", + "src/dawn/library_webgpu_enum_tables.js", ] } source_set("dawn_headers") { all_dependent_configs = [ "${dawn_root}/src/common:dawn_public_include_dirs" ] - deps = [ - ":dawn_headers_gen", - ] + public_deps = [ ":dawn_headers_gen" ] sources = get_target_outputs(":dawn_headers_gen") - sources += [ - "${dawn_root}/src/include/dawn/EnumClassBitmasks.h", - "${dawn_root}/src/include/dawn/dawn_export.h", - "${dawn_root}/src/include/dawn/dawn_wsi.h", - ] + sources += [ "${dawn_root}/src/include/dawn/dawn_wsi.h" ] } ############################################################################### -# libdawn +# Dawn C++ headers ############################################################################### -dawn_json_generator("libdawn_gen") { - target = "libdawn" - outputs = [ - "dawn/dawncpp.cpp", - "dawn/dawn.c", - ] +dawn_json_generator("dawncpp_headers_gen") { + target = "dawncpp_headers" + outputs = [ "src/include/dawn/webgpu_cpp.h" ] } -dawn_component("libdawn") { - DEFINE_PREFIX = "DAWN" - +source_set("dawncpp_headers") { public_deps = [ ":dawn_headers", + ":dawncpp_headers_gen", ] + sources = get_target_outputs(":dawncpp_headers_gen") + sources += [ "${dawn_root}/src/include/dawn/EnumClassBitmasks.h" ] +} + +############################################################################### +# Dawn C++ wrapper +############################################################################### + +dawn_json_generator("dawncpp_gen") { + target = "dawncpp" + outputs = [ "src/dawn/webgpu_cpp.cpp" ] +} + +source_set("dawncpp") { deps = [ - ":libdawn_gen", + ":dawncpp_gen", + ":dawncpp_headers", ] - sources = get_target_outputs(":libdawn_gen") + sources = get_target_outputs(":dawncpp_gen") +} + +############################################################################### +# dawn_proc +############################################################################### + +dawn_json_generator("dawn_proc_gen") { + target = "dawn_proc" + outputs = [ "src/dawn/dawn_proc.c" ] +} + +dawn_component("dawn_proc") { + DEFINE_PREFIX = "WGPU" + + public_deps = [ ":dawn_headers" ] + deps = [ ":dawn_proc_gen" ] + sources = get_target_outputs(":dawn_proc_gen") + sources += [ "${dawn_root}/src/include/dawn/dawn_proc.h" ] } diff --git a/third_party/dawn/src/dawn/CMakeLists.txt b/third_party/dawn/src/dawn/CMakeLists.txt new file mode 100644 index 00000000000..0517343216c --- /dev/null +++ b/third_party/dawn/src/dawn/CMakeLists.txt @@ -0,0 +1,85 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +############################################################################### +# Dawn headers +############################################################################### + +DawnJSONGenerator( + TARGET "dawn_headers" + PRINT_NAME "Dawn headers" + RESULT_VARIABLE "DAWN_HEADERS_GEN_SOURCES" +) + +# Headers only INTERFACE library with generated headers don't work in CMake +# because the GENERATED property is local to a directory. Instead we make a +# STATIC library with a Dummy cpp file. +# +# INTERFACE libraries can only have INTERFACE sources so the sources get added +# to the dependant's list of sources. If these dependents are in another +# directory, they don't see the GENERATED property and fail to configure +# because the file doesn't exist on disk. +add_library(dawn_headers STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawn_headers PRIVATE + "${DAWN_INCLUDE_DIR}/dawn/dawn_wsi.h" + ${DAWN_HEADERS_GEN_SOURCES} +) +target_link_libraries(dawn_headers INTERFACE dawn_public_config) + +############################################################################### +# Dawn C++ headers +############################################################################### + +DawnJSONGenerator( + TARGET "dawncpp_headers" + PRINT_NAME "Dawn C++ headers" + RESULT_VARIABLE "DAWNCPP_HEADERS_GEN_SOURCES" +) + +# This headers only library needs to be a STATIC library, see comment for +# dawn_headers above. +add_library(dawncpp_headers STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawncpp_headers PRIVATE + "${DAWN_INCLUDE_DIR}/dawn/EnumClassBitmasks.h" + ${DAWNCPP_HEADERS_GEN_SOURCES} +) +target_link_libraries(dawncpp_headers INTERFACE dawn_headers) + +############################################################################### +# Dawn C++ wrapper +############################################################################### + +DawnJSONGenerator( + TARGET "dawncpp" + PRINT_NAME "Dawn C++ wrapper" + RESULT_VARIABLE "DAWNCPP_GEN_SOURCES" +) + +add_library(dawncpp STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawncpp PRIVATE ${DAWNCPP_GEN_SOURCES}) +target_link_libraries(dawncpp PUBLIC dawncpp_headers) + +############################################################################### +# libdawn_proc +############################################################################### + +DawnJSONGenerator( + TARGET "dawn_proc" + PRINT_NAME "Dawn C++ wrapper" + RESULT_VARIABLE "DAWNPROC_GEN_SOURCES" +) + +add_library(dawn_proc STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawn_proc PRIVATE ${DAWNPROC_GEN_SOURCES}) +target_link_libraries(dawn_proc PUBLIC dawn_headers) diff --git a/third_party/dawn/src/dawn_native/Adapter.cpp b/third_party/dawn/src/dawn_native/Adapter.cpp index 6e51f17f593..aa3ede7e23e 100644 --- a/third_party/dawn/src/dawn_native/Adapter.cpp +++ b/third_party/dawn/src/dawn_native/Adapter.cpp @@ -18,16 +18,16 @@ namespace dawn_native { - AdapterBase::AdapterBase(InstanceBase* instance, BackendType backend) + AdapterBase::AdapterBase(InstanceBase* instance, wgpu::BackendType backend) : mInstance(instance), mBackend(backend) { } - BackendType AdapterBase::GetBackendType() const { + wgpu::BackendType AdapterBase::GetBackendType() const { return mBackend; } - DeviceType AdapterBase::GetDeviceType() const { - return mDeviceType; + wgpu::AdapterType AdapterBase::GetAdapterType() const { + return mAdapterType; } const PCIInfo& AdapterBase::GetPCIInfo() const { @@ -38,6 +38,31 @@ namespace dawn_native { return mInstance; } + ExtensionsSet AdapterBase::GetSupportedExtensions() const { + return mSupportedExtensions; + } + + bool AdapterBase::SupportsAllRequestedExtensions( + const std::vector& requestedExtensions) const { + for (const char* extensionStr : requestedExtensions) { + Extension extensionEnum = mInstance->ExtensionNameToEnum(extensionStr); + if (extensionEnum == Extension::InvalidEnum) { + return false; + } + if (!mSupportedExtensions.IsEnabled(extensionEnum)) { + return false; + } + } + return true; + } + + WGPUDeviceProperties AdapterBase::GetAdapterProperties() const { + WGPUDeviceProperties adapterProperties = {}; + + mSupportedExtensions.InitializeDeviceProperties(&adapterProperties); + return adapterProperties; + } + DeviceBase* AdapterBase::CreateDevice(const DeviceDescriptor* descriptor) { DeviceBase* result = nullptr; @@ -50,6 +75,12 @@ namespace dawn_native { MaybeError AdapterBase::CreateDeviceInternal(DeviceBase** result, const DeviceDescriptor* descriptor) { + if (descriptor != nullptr) { + if (!SupportsAllRequestedExtensions(descriptor->requiredExtensions)) { + return DAWN_VALIDATION_ERROR("One or more requested extensions are not supported"); + } + } + // TODO(cwallez@chromium.org): This will eventually have validation that the device // descriptor is valid and is a subset what's allowed on this adapter. DAWN_TRY_ASSIGN(*result, CreateDeviceImpl(descriptor)); diff --git a/third_party/dawn/src/dawn_native/Adapter.h b/third_party/dawn/src/dawn_native/Adapter.h index 6c9d3f16947..d0089349abd 100644 --- a/third_party/dawn/src/dawn_native/Adapter.h +++ b/third_party/dawn/src/dawn_native/Adapter.h @@ -18,6 +18,10 @@ #include "dawn_native/DawnNative.h" #include "dawn_native/Error.h" +#include "dawn_native/Extensions.h" +#include "dawn_native/dawn_platform.h" + +#include namespace dawn_native { @@ -25,19 +29,25 @@ namespace dawn_native { class AdapterBase { public: - AdapterBase(InstanceBase* instance, BackendType backend); + AdapterBase(InstanceBase* instance, wgpu::BackendType backend); virtual ~AdapterBase() = default; - BackendType GetBackendType() const; - DeviceType GetDeviceType() const; + wgpu::BackendType GetBackendType() const; + wgpu::AdapterType GetAdapterType() const; const PCIInfo& GetPCIInfo() const; InstanceBase* GetInstance() const; DeviceBase* CreateDevice(const DeviceDescriptor* descriptor = nullptr); + ExtensionsSet GetSupportedExtensions() const; + bool SupportsAllRequestedExtensions( + const std::vector& requestedExtensions) const; + WGPUDeviceProperties GetAdapterProperties() const; + protected: PCIInfo mPCIInfo = {}; - DeviceType mDeviceType = DeviceType::Unknown; + wgpu::AdapterType mAdapterType = wgpu::AdapterType::Unknown; + ExtensionsSet mSupportedExtensions; private: virtual ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) = 0; @@ -45,7 +55,7 @@ namespace dawn_native { MaybeError CreateDeviceInternal(DeviceBase** result, const DeviceDescriptor* descriptor); InstanceBase* mInstance = nullptr; - BackendType mBackend; + wgpu::BackendType mBackend; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/AttachmentState.cpp b/third_party/dawn/src/dawn_native/AttachmentState.cpp new file mode 100644 index 00000000000..5ff33b3309b --- /dev/null +++ b/third_party/dawn/src/dawn_native/AttachmentState.cpp @@ -0,0 +1,148 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/AttachmentState.h" + +#include "common/BitSetIterator.h" +#include "common/HashUtils.h" +#include "dawn_native/Device.h" +#include "dawn_native/Texture.h" + +namespace dawn_native { + + AttachmentStateBlueprint::AttachmentStateBlueprint( + const RenderBundleEncoderDescriptor* descriptor) + : mSampleCount(descriptor->sampleCount) { + for (uint32_t i = 0; i < descriptor->colorFormatsCount; ++i) { + mColorAttachmentsSet.set(i); + mColorFormats[i] = descriptor->colorFormats[i]; + } + mDepthStencilFormat = descriptor->depthStencilFormat; + } + + AttachmentStateBlueprint::AttachmentStateBlueprint(const RenderPipelineDescriptor* descriptor) + : mSampleCount(descriptor->sampleCount) { + for (uint32_t i = 0; i < descriptor->colorStateCount; ++i) { + mColorAttachmentsSet.set(i); + mColorFormats[i] = descriptor->colorStates[i].format; + } + if (descriptor->depthStencilState != nullptr) { + mDepthStencilFormat = descriptor->depthStencilState->format; + } + } + + AttachmentStateBlueprint::AttachmentStateBlueprint(const RenderPassDescriptor* descriptor) { + for (uint32_t i = 0; i < descriptor->colorAttachmentCount; ++i) { + TextureViewBase* attachment = descriptor->colorAttachments[i].attachment; + mColorAttachmentsSet.set(i); + mColorFormats[i] = attachment->GetFormat().format; + if (mSampleCount == 0) { + mSampleCount = attachment->GetTexture()->GetSampleCount(); + } else { + ASSERT(mSampleCount == attachment->GetTexture()->GetSampleCount()); + } + } + if (descriptor->depthStencilAttachment != nullptr) { + TextureViewBase* attachment = descriptor->depthStencilAttachment->attachment; + mDepthStencilFormat = attachment->GetFormat().format; + if (mSampleCount == 0) { + mSampleCount = attachment->GetTexture()->GetSampleCount(); + } else { + ASSERT(mSampleCount == attachment->GetTexture()->GetSampleCount()); + } + } + ASSERT(mSampleCount > 0); + } + + AttachmentStateBlueprint::AttachmentStateBlueprint(const AttachmentStateBlueprint& rhs) = + default; + + size_t AttachmentStateBlueprint::HashFunc::operator()( + const AttachmentStateBlueprint* attachmentState) const { + size_t hash = 0; + + // Hash color formats + HashCombine(&hash, attachmentState->mColorAttachmentsSet); + for (uint32_t i : IterateBitSet(attachmentState->mColorAttachmentsSet)) { + HashCombine(&hash, attachmentState->mColorFormats[i]); + } + + // Hash depth stencil attachment + HashCombine(&hash, attachmentState->mDepthStencilFormat); + + // Hash sample count + HashCombine(&hash, attachmentState->mSampleCount); + + return hash; + } + + bool AttachmentStateBlueprint::EqualityFunc::operator()( + const AttachmentStateBlueprint* a, + const AttachmentStateBlueprint* b) const { + // Check set attachments + if (a->mColorAttachmentsSet != b->mColorAttachmentsSet) { + return false; + } + + // Check color formats + for (uint32_t i : IterateBitSet(a->mColorAttachmentsSet)) { + if (a->mColorFormats[i] != b->mColorFormats[i]) { + return false; + } + } + + // Check depth stencil format + if (a->mDepthStencilFormat != b->mDepthStencilFormat) { + return false; + } + + // Check sample count + if (a->mSampleCount != b->mSampleCount) { + return false; + } + + return true; + } + + AttachmentState::AttachmentState(DeviceBase* device, const AttachmentStateBlueprint& blueprint) + : AttachmentStateBlueprint(blueprint), CachedObject(device) { + } + + AttachmentState::~AttachmentState() { + GetDevice()->UncacheAttachmentState(this); + } + + std::bitset AttachmentState::GetColorAttachmentsMask() const { + return mColorAttachmentsSet; + } + + wgpu::TextureFormat AttachmentState::GetColorAttachmentFormat(uint32_t index) const { + ASSERT(mColorAttachmentsSet[index]); + return mColorFormats[index]; + } + + bool AttachmentState::HasDepthStencilAttachment() const { + return mDepthStencilFormat != wgpu::TextureFormat::Undefined; + } + + wgpu::TextureFormat AttachmentState::GetDepthStencilFormat() const { + ASSERT(HasDepthStencilAttachment()); + return mDepthStencilFormat; + } + + uint32_t AttachmentState::GetSampleCount() const { + return mSampleCount; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/AttachmentState.h b/third_party/dawn/src/dawn_native/AttachmentState.h new file mode 100644 index 00000000000..f5d01594b16 --- /dev/null +++ b/third_party/dawn/src/dawn_native/AttachmentState.h @@ -0,0 +1,75 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_ATTACHMENTSTATE_H_ +#define DAWNNATIVE_ATTACHMENTSTATE_H_ + +#include "common/Constants.h" +#include "dawn_native/CachedObject.h" + +#include "dawn_native/dawn_platform.h" + +#include +#include + +namespace dawn_native { + + class DeviceBase; + + // AttachmentStateBlueprint and AttachmentState are separated so the AttachmentState + // can be constructed by copying the blueprint state instead of traversing descriptors. + // Also, AttachmentStateBlueprint does not need a refcount like AttachmentState. + class AttachmentStateBlueprint { + public: + // Note: Descriptors must be validated before the AttachmentState is constructed. + explicit AttachmentStateBlueprint(const RenderBundleEncoderDescriptor* descriptor); + explicit AttachmentStateBlueprint(const RenderPipelineDescriptor* descriptor); + explicit AttachmentStateBlueprint(const RenderPassDescriptor* descriptor); + + AttachmentStateBlueprint(const AttachmentStateBlueprint& rhs); + + // Functors necessary for the unordered_set-based cache. + struct HashFunc { + size_t operator()(const AttachmentStateBlueprint* attachmentState) const; + }; + struct EqualityFunc { + bool operator()(const AttachmentStateBlueprint* a, + const AttachmentStateBlueprint* b) const; + }; + + protected: + std::bitset mColorAttachmentsSet; + std::array mColorFormats; + // Default (texture format Undefined) indicates there is no depth stencil attachment. + wgpu::TextureFormat mDepthStencilFormat = wgpu::TextureFormat::Undefined; + uint32_t mSampleCount = 0; + }; + + class AttachmentState final : public AttachmentStateBlueprint, public CachedObject { + public: + AttachmentState(DeviceBase* device, const AttachmentStateBlueprint& blueprint); + + std::bitset GetColorAttachmentsMask() const; + wgpu::TextureFormat GetColorAttachmentFormat(uint32_t index) const; + bool HasDepthStencilAttachment() const; + wgpu::TextureFormat GetDepthStencilFormat() const; + uint32_t GetSampleCount() const; + + private: + ~AttachmentState() override; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_ATTACHMENTSTATE_H_ diff --git a/third_party/dawn/src/dawn_native/BUILD.gn b/third_party/dawn/src/dawn_native/BUILD.gn new file mode 100644 index 00000000000..daee013869a --- /dev/null +++ b/third_party/dawn/src/dawn_native/BUILD.gn @@ -0,0 +1,632 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../scripts/dawn_overrides_with_defaults.gni") + +import("//build_overrides/build.gni") +import("${dawn_root}/generator/dawn_generator.gni") +import("${dawn_root}/scripts/dawn_component.gni") +import("${dawn_root}/scripts/dawn_features.gni") + +# Import mac_min_system_version +if (is_mac) { + if (dawn_has_build) { + import("//build/config/mac/mac_sdk.gni") + } else { + mac_min_system_version = "10.11.0" + } +} + +# The VVLs are an optional dependency, only use it if the path has been set. +enable_vulkan_validation_layers = dawn_enable_vulkan_validation_layers && + dawn_vulkan_validation_layers_dir != "" +if (enable_vulkan_validation_layers) { + import("//build_overrides/vulkan_validation_layers.gni") +} + +# Swiftshader is an optional dependency, only use it if the path has been set. +use_swiftshader = dawn_use_swiftshader && dawn_swiftshader_dir != "" +if (use_swiftshader) { + assert(dawn_enable_vulkan, + "dawn_use_swiftshader requires dawn_enable_vulkan=true") + import("${dawn_swiftshader_dir}/src/Vulkan/vulkan.gni") +} + +# The Vulkan loader is an optional dependency, only use it if the path has been +# set. +if (dawn_enable_vulkan) { + enable_vulkan_loader = + dawn_enable_vulkan_loader && dawn_vulkan_loader_dir != "" +} + +config("dawn_native_internal") { + configs = [ "${dawn_root}/src/common:dawn_internal" ] + + # Suppress warnings that Metal isn't in the deployment target of Chrome: + # initialization of the Metal backend is behind a IsMetalSupported check so + # Dawn won't call Metal functions on macOS 10.10. + # At the time this is written Chromium supports 10.10.0 and above, so if we + # aren't on 10.11 it means we are on 10.11 and above, and Metal is available. + # Skipping this check on 10.11 and above is important as it allows getting + # proper compilation warning when using 10.12 and above feature for example. + # TODO(cwallez@chromium.org): Consider using API_AVAILABLE annotations on all + # metal code in dawn once crbug.com/1004024 is sorted out if Chromium still + # supports 10.10 then. + if (is_mac && mac_min_system_version == "10.10.0") { + cflags_objcc = [ "-Wno-unguarded-availability" ] + } +} + +config("dawn_native_weak_framework") { + if (is_mac && dawn_enable_metal) { + weak_frameworks = [ "Metal.framework" ] + } +} + +dawn_json_generator("dawn_native_utils_gen") { + target = "dawn_native_utils" + outputs = [ + "src/dawn_native/ProcTable.cpp", + "src/dawn_native/wgpu_structs_autogen.h", + "src/dawn_native/wgpu_structs_autogen.cpp", + "src/dawn_native/ValidationUtils_autogen.h", + "src/dawn_native/ValidationUtils_autogen.cpp", + ] +} + +if (dawn_enable_opengl) { + dawn_generator("dawn_native_opengl_loader_gen") { + script = "${dawn_root}/generator/opengl_loader_generator.py" + args = [ + "--gl-xml", + rebase_path("${dawn_root}/third_party/khronos/gl.xml", root_build_dir), + "--supported-extensions", + rebase_path("opengl/supported_extensions.json", root_build_dir), + ] + outputs = [ + "src/dawn_native/opengl/OpenGLFunctionsBase_autogen.cpp", + "src/dawn_native/opengl/OpenGLFunctionsBase_autogen.h", + "src/dawn_native/opengl/opengl_platform_autogen.h", + ] + } +} + +# Public dawn_native headers so they can be publicly visible for +# dependencies of dawn_native +source_set("dawn_native_headers") { + public_deps = [ "${dawn_root}/src/dawn:dawncpp_headers" ] + all_dependent_configs = [ "${dawn_root}/src/common:dawn_public_include_dirs" ] + sources = [ + "${dawn_root}/src/include/dawn_native/DawnNative.h", + "${dawn_root}/src/include/dawn_native/dawn_native_export.h", + + # Include all backend's public headers so that dependencies can include + # them even when the backends are disabled. + "${dawn_root}/src/include/dawn_native/D3D12Backend.h", + "${dawn_root}/src/include/dawn_native/MetalBackend.h", + "${dawn_root}/src/include/dawn_native/NullBackend.h", + "${dawn_root}/src/include/dawn_native/OpenGLBackend.h", + "${dawn_root}/src/include/dawn_native/VulkanBackend.h", + ] +} + +# The meat of the compilation for dawn_native so that we can cheaply have +# shared_library / static_library versions of it. It compiles all the files +# except those that define exported symbols. +source_set("dawn_native_sources") { + deps = [ + ":dawn_native_headers", + ":dawn_native_utils_gen", + "${dawn_root}/src/common", + "${dawn_shaderc_dir}:spirv_cross", + "${dawn_spirv_tools_dir}:spvtools_val", + ] + defines = [] + libs = [] + data_deps = [] + + configs += [ ":dawn_native_internal" ] + + # Dependencies that are needed to compile dawn_native entry points in + # FooBackend.cpp need to be public deps so they are propagated to the + # dawn_native target + public_deps = [ + "${dawn_root}/src/dawn_platform", + "${dawn_shaderc_dir}:libshaderc_spvc", + ] + + sources = get_target_outputs(":dawn_native_utils_gen") + sources += [ + "Adapter.cpp", + "Adapter.h", + "AttachmentState.cpp", + "AttachmentState.h", + "BackendConnection.cpp", + "BackendConnection.h", + "BindGroup.cpp", + "BindGroup.h", + "BindGroupAndStorageBarrierTracker.h", + "BindGroupLayout.cpp", + "BindGroupLayout.h", + "BindGroupTracker.h", + "BindingInfo.cpp", + "BindingInfo.h", + "BuddyAllocator.cpp", + "BuddyAllocator.h", + "BuddyMemoryAllocator.cpp", + "BuddyMemoryAllocator.h", + "Buffer.cpp", + "Buffer.h", + "CachedObject.cpp", + "CachedObject.h", + "CommandAllocator.cpp", + "CommandAllocator.h", + "CommandBuffer.cpp", + "CommandBuffer.h", + "CommandBufferStateTracker.cpp", + "CommandBufferStateTracker.h", + "CommandEncoder.cpp", + "CommandEncoder.h", + "CommandValidation.cpp", + "CommandValidation.h", + "Commands.cpp", + "Commands.h", + "ComputePassEncoder.cpp", + "ComputePassEncoder.h", + "ComputePipeline.cpp", + "ComputePipeline.h", + "Device.cpp", + "Device.h", + "DynamicUploader.cpp", + "DynamicUploader.h", + "EncodingContext.cpp", + "EncodingContext.h", + "Error.cpp", + "Error.h", + "ErrorData.cpp", + "ErrorData.h", + "ErrorInjector.cpp", + "ErrorInjector.h", + "ErrorScope.cpp", + "ErrorScope.h", + "ErrorScopeTracker.cpp", + "ErrorScopeTracker.h", + "Extensions.cpp", + "Extensions.h", + "Fence.cpp", + "Fence.h", + "FenceSignalTracker.cpp", + "FenceSignalTracker.h", + "Format.cpp", + "Format.h", + "Forward.h", + "Instance.cpp", + "Instance.h", + "MapRequestTracker.cpp", + "MapRequestTracker.h", + "ObjectBase.cpp", + "ObjectBase.h", + "PassResourceUsage.h", + "PassResourceUsageTracker.cpp", + "PassResourceUsageTracker.h", + "PerStage.cpp", + "PerStage.h", + "Pipeline.cpp", + "Pipeline.h", + "PipelineLayout.cpp", + "PipelineLayout.h", + "ProgrammablePassEncoder.cpp", + "ProgrammablePassEncoder.h", + "QuerySet.cpp", + "QuerySet.h", + "Queue.cpp", + "Queue.h", + "RenderBundle.cpp", + "RenderBundle.h", + "RenderBundleEncoder.cpp", + "RenderBundleEncoder.h", + "RenderEncoderBase.cpp", + "RenderEncoderBase.h", + "RenderPassEncoder.cpp", + "RenderPassEncoder.h", + "RenderPipeline.cpp", + "RenderPipeline.h", + "ResourceHeap.h", + "ResourceHeapAllocator.h", + "ResourceMemoryAllocation.cpp", + "ResourceMemoryAllocation.h", + "RingBufferAllocator.cpp", + "RingBufferAllocator.h", + "Sampler.cpp", + "Sampler.h", + "ShaderModule.cpp", + "ShaderModule.h", + "StagingBuffer.cpp", + "StagingBuffer.h", + "Surface.cpp", + "Surface.h", + "SwapChain.cpp", + "SwapChain.h", + "Texture.cpp", + "Texture.h", + "ToBackend.h", + "Toggles.cpp", + "Toggles.h", + "dawn_platform.h", + ] + + if (dawn_use_x11) { + libs += [ "X11" ] + } + + if (is_win) { + libs += [ "user32.lib" ] + } + + if (dawn_enable_d3d12) { + libs += [ "dxguid.lib" ] + sources += [ + "d3d12/AdapterD3D12.cpp", + "d3d12/AdapterD3D12.h", + "d3d12/BackendD3D12.cpp", + "d3d12/BackendD3D12.h", + "d3d12/BindGroupD3D12.cpp", + "d3d12/BindGroupD3D12.h", + "d3d12/BindGroupLayoutD3D12.cpp", + "d3d12/BindGroupLayoutD3D12.h", + "d3d12/BufferD3D12.cpp", + "d3d12/BufferD3D12.h", + "d3d12/CPUDescriptorHeapAllocationD3D12.cpp", + "d3d12/CPUDescriptorHeapAllocationD3D12.h", + "d3d12/CommandAllocatorManager.cpp", + "d3d12/CommandAllocatorManager.h", + "d3d12/CommandBufferD3D12.cpp", + "d3d12/CommandBufferD3D12.h", + "d3d12/CommandRecordingContext.cpp", + "d3d12/CommandRecordingContext.h", + "d3d12/ComputePipelineD3D12.cpp", + "d3d12/ComputePipelineD3D12.h", + "d3d12/D3D12Error.cpp", + "d3d12/D3D12Error.h", + "d3d12/D3D12Info.cpp", + "d3d12/D3D12Info.h", + "d3d12/DeviceD3D12.cpp", + "d3d12/DeviceD3D12.h", + "d3d12/Forward.h", + "d3d12/GPUDescriptorHeapAllocationD3D12.cpp", + "d3d12/GPUDescriptorHeapAllocationD3D12.h", + "d3d12/HeapAllocatorD3D12.cpp", + "d3d12/HeapAllocatorD3D12.h", + "d3d12/HeapD3D12.cpp", + "d3d12/HeapD3D12.h", + "d3d12/NativeSwapChainImplD3D12.cpp", + "d3d12/NativeSwapChainImplD3D12.h", + "d3d12/PageableD3D12.cpp", + "d3d12/PageableD3D12.h", + "d3d12/PipelineLayoutD3D12.cpp", + "d3d12/PipelineLayoutD3D12.h", + "d3d12/PlatformFunctions.cpp", + "d3d12/PlatformFunctions.h", + "d3d12/QuerySetD3D12.cpp", + "d3d12/QuerySetD3D12.h", + "d3d12/QueueD3D12.cpp", + "d3d12/QueueD3D12.h", + "d3d12/RenderPassBuilderD3D12.cpp", + "d3d12/RenderPassBuilderD3D12.h", + "d3d12/RenderPipelineD3D12.cpp", + "d3d12/RenderPipelineD3D12.h", + "d3d12/ResidencyManagerD3D12.cpp", + "d3d12/ResidencyManagerD3D12.h", + "d3d12/ResourceAllocatorManagerD3D12.cpp", + "d3d12/ResourceAllocatorManagerD3D12.h", + "d3d12/ResourceHeapAllocationD3D12.cpp", + "d3d12/ResourceHeapAllocationD3D12.h", + "d3d12/SamplerD3D12.cpp", + "d3d12/SamplerD3D12.h", + "d3d12/SamplerHeapCacheD3D12.cpp", + "d3d12/SamplerHeapCacheD3D12.h", + "d3d12/ShaderModuleD3D12.cpp", + "d3d12/ShaderModuleD3D12.h", + "d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp", + "d3d12/ShaderVisibleDescriptorAllocatorD3D12.h", + "d3d12/StagingBufferD3D12.cpp", + "d3d12/StagingBufferD3D12.h", + "d3d12/StagingDescriptorAllocatorD3D12.cpp", + "d3d12/StagingDescriptorAllocatorD3D12.h", + "d3d12/SwapChainD3D12.cpp", + "d3d12/SwapChainD3D12.h", + "d3d12/TextureCopySplitter.cpp", + "d3d12/TextureCopySplitter.h", + "d3d12/TextureD3D12.cpp", + "d3d12/TextureD3D12.h", + "d3d12/UtilsD3D12.cpp", + "d3d12/UtilsD3D12.h", + "d3d12/d3d12_platform.h", + ] + } + + if (dawn_enable_metal) { + frameworks = [ + "Cocoa.framework", + "IOKit.framework", + "IOSurface.framework", + "QuartzCore.framework", + ] + sources += [ + "Surface_metal.mm", + "metal/BackendMTL.h", + "metal/BackendMTL.mm", + "metal/BindGroupLayoutMTL.h", + "metal/BindGroupLayoutMTL.mm", + "metal/BindGroupMTL.h", + "metal/BindGroupMTL.mm", + "metal/BufferMTL.h", + "metal/BufferMTL.mm", + "metal/CommandBufferMTL.h", + "metal/CommandBufferMTL.mm", + "metal/CommandRecordingContext.h", + "metal/CommandRecordingContext.mm", + "metal/ComputePipelineMTL.h", + "metal/ComputePipelineMTL.mm", + "metal/DeviceMTL.h", + "metal/DeviceMTL.mm", + "metal/Forward.h", + "metal/PipelineLayoutMTL.h", + "metal/PipelineLayoutMTL.mm", + "metal/QueueMTL.h", + "metal/QueueMTL.mm", + "metal/RenderPipelineMTL.h", + "metal/RenderPipelineMTL.mm", + "metal/SamplerMTL.h", + "metal/SamplerMTL.mm", + "metal/ShaderModuleMTL.h", + "metal/ShaderModuleMTL.mm", + "metal/StagingBufferMTL.h", + "metal/StagingBufferMTL.mm", + "metal/SwapChainMTL.h", + "metal/SwapChainMTL.mm", + "metal/TextureMTL.h", + "metal/TextureMTL.mm", + "metal/UtilsMetal.h", + "metal/UtilsMetal.mm", + ] + } + + if (dawn_enable_null) { + sources += [ + "null/DeviceNull.cpp", + "null/DeviceNull.h", + ] + } + + if (dawn_enable_opengl) { + public_deps += [ + ":dawn_native_opengl_loader_gen", + "${dawn_root}/third_party/khronos:khronos_platform", + ] + sources += get_target_outputs(":dawn_native_opengl_loader_gen") + sources += [ + "opengl/BackendGL.cpp", + "opengl/BackendGL.h", + "opengl/BindGroupGL.cpp", + "opengl/BindGroupGL.h", + "opengl/BindGroupLayoutGL.cpp", + "opengl/BindGroupLayoutGL.h", + "opengl/BufferGL.cpp", + "opengl/BufferGL.h", + "opengl/CommandBufferGL.cpp", + "opengl/CommandBufferGL.h", + "opengl/ComputePipelineGL.cpp", + "opengl/ComputePipelineGL.h", + "opengl/DeviceGL.cpp", + "opengl/DeviceGL.h", + "opengl/Forward.h", + "opengl/GLFormat.cpp", + "opengl/GLFormat.h", + "opengl/NativeSwapChainImplGL.cpp", + "opengl/NativeSwapChainImplGL.h", + "opengl/OpenGLFunctions.cpp", + "opengl/OpenGLFunctions.h", + "opengl/PersistentPipelineStateGL.cpp", + "opengl/PersistentPipelineStateGL.h", + "opengl/PipelineGL.cpp", + "opengl/PipelineGL.h", + "opengl/PipelineLayoutGL.cpp", + "opengl/PipelineLayoutGL.h", + "opengl/QuerySetGL.cpp", + "opengl/QuerySetGL.h", + "opengl/QueueGL.cpp", + "opengl/QueueGL.h", + "opengl/RenderPipelineGL.cpp", + "opengl/RenderPipelineGL.h", + "opengl/SamplerGL.cpp", + "opengl/SamplerGL.h", + "opengl/ShaderModuleGL.cpp", + "opengl/ShaderModuleGL.h", + "opengl/SwapChainGL.cpp", + "opengl/SwapChainGL.h", + "opengl/TextureGL.cpp", + "opengl/TextureGL.h", + "opengl/UtilsGL.cpp", + "opengl/UtilsGL.h", + "opengl/opengl_platform.h", + ] + } + + if (dawn_enable_vulkan) { + public_deps += [ "${dawn_root}/third_party/khronos:vulkan_headers" ] + sources += [ + "vulkan/AdapterVk.cpp", + "vulkan/AdapterVk.h", + "vulkan/BackendVk.cpp", + "vulkan/BackendVk.h", + "vulkan/BindGroupLayoutVk.cpp", + "vulkan/BindGroupLayoutVk.h", + "vulkan/BindGroupVk.cpp", + "vulkan/BindGroupVk.h", + "vulkan/BufferVk.cpp", + "vulkan/BufferVk.h", + "vulkan/CommandBufferVk.cpp", + "vulkan/CommandBufferVk.h", + "vulkan/CommandRecordingContext.h", + "vulkan/ComputePipelineVk.cpp", + "vulkan/ComputePipelineVk.h", + "vulkan/DescriptorSetAllocation.h", + "vulkan/DescriptorSetAllocator.cpp", + "vulkan/DescriptorSetAllocator.h", + "vulkan/DeviceVk.cpp", + "vulkan/DeviceVk.h", + "vulkan/ExternalHandle.h", + "vulkan/FencedDeleter.cpp", + "vulkan/FencedDeleter.h", + "vulkan/Forward.h", + "vulkan/NativeSwapChainImplVk.cpp", + "vulkan/NativeSwapChainImplVk.h", + "vulkan/PipelineLayoutVk.cpp", + "vulkan/PipelineLayoutVk.h", + "vulkan/QueueVk.cpp", + "vulkan/QueueVk.h", + "vulkan/RenderPassCache.cpp", + "vulkan/RenderPassCache.h", + "vulkan/RenderPipelineVk.cpp", + "vulkan/RenderPipelineVk.h", + "vulkan/ResourceHeapVk.cpp", + "vulkan/ResourceHeapVk.h", + "vulkan/ResourceMemoryAllocatorVk.cpp", + "vulkan/ResourceMemoryAllocatorVk.h", + "vulkan/SamplerVk.cpp", + "vulkan/SamplerVk.h", + "vulkan/ShaderModuleVk.cpp", + "vulkan/ShaderModuleVk.h", + "vulkan/StagingBufferVk.cpp", + "vulkan/StagingBufferVk.h", + "vulkan/SwapChainVk.cpp", + "vulkan/SwapChainVk.h", + "vulkan/TextureVk.cpp", + "vulkan/TextureVk.h", + "vulkan/UtilsVulkan.cpp", + "vulkan/UtilsVulkan.h", + "vulkan/VulkanError.cpp", + "vulkan/VulkanError.h", + "vulkan/VulkanExtensions.cpp", + "vulkan/VulkanExtensions.h", + "vulkan/VulkanFunctions.cpp", + "vulkan/VulkanFunctions.h", + "vulkan/VulkanInfo.cpp", + "vulkan/VulkanInfo.h", + "vulkan/external_memory/MemoryService.h", + "vulkan/external_semaphore/SemaphoreService.h", + ] + + if (is_chromeos) { + sources += [ + "vulkan/external_memory/MemoryServiceDmaBuf.cpp", + "vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp", + ] + } else if (is_linux) { + sources += [ + "vulkan/external_memory/MemoryServiceOpaqueFD.cpp", + "vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp", + ] + } else if (is_fuchsia) { + sources += [ + "vulkan/external_memory/MemoryServiceZirconHandle.cpp", + "vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp", + ] + } else { + sources += [ + "vulkan/external_memory/MemoryServiceNull.cpp", + "vulkan/external_semaphore/SemaphoreServiceNull.cpp", + ] + } + if (build_with_chromium && is_fuchsia) { + # Necessary to ensure that the Vulkan libraries will be in the + # final Fuchsia package. + data_deps = [ + "//third_party/fuchsia-sdk:vulkan_base", + "//third_party/fuchsia-sdk:vulkan_validation", + + # NOTE: The line below is a work around for http://crbug.com/1001081 + "//third_party/fuchsia-sdk/sdk:trace_engine", + ] + } + if (enable_vulkan_validation_layers) { + defines += [ + "DAWN_ENABLE_VULKAN_VALIDATION_LAYERS", + "DAWN_VK_DATA_DIR=\"$vulkan_data_subdir\"", + ] + } + if (enable_vulkan_loader) { + data_deps += [ "${dawn_vulkan_loader_dir}:libvulkan" ] + } + if (use_swiftshader) { + data_deps += [ + "${dawn_swiftshader_dir}/src/Vulkan:icd_file", + "${dawn_swiftshader_dir}/src/Vulkan:swiftshader_libvulkan", + ] + defines += [ + "DAWN_ENABLE_SWIFTSHADER", + "DAWN_SWIFTSHADER_VK_ICD_JSON=\"${swiftshader_icd_file_name}\"", + ] + } + } + + if (dawn_enable_wgsl) { + deps += [ "${dawn_tint_dir}:libtint" ] + defines += [ "DAWN_ENABLE_WGSL" ] + } +} + +# The static and shared libraries for dawn_native. Most of the files are +# already compiled in dawn_native_sources, but we still need to compile +# files defining exported symbols. +dawn_component("dawn_native") { + DEFINE_PREFIX = "DAWN_NATIVE" + + #Make headers publically visible + public_deps = [ ":dawn_native_headers" ] + + deps = [ + ":dawn_native_sources", + "${dawn_root}/src/common", + ] + sources = [ "DawnNative.cpp" ] + configs = [ ":dawn_native_internal" ] + public_configs = [ ":dawn_native_weak_framework" ] + + if (dawn_enable_d3d12) { + sources += [ "d3d12/D3D12Backend.cpp" ] + } + if (dawn_enable_metal) { + sources += [ "metal/MetalBackend.mm" ] + } + if (dawn_enable_null) { + sources += [ "null/NullBackend.cpp" ] + } + if (dawn_enable_opengl) { + sources += [ "opengl/OpenGLBackend.cpp" ] + } + if (dawn_enable_vulkan) { + sources += [ "vulkan/VulkanBackend.cpp" ] + + if (enable_vulkan_validation_layers) { + data_deps = + [ "${dawn_vulkan_validation_layers_dir}:vulkan_validation_layers" ] + if (!is_android) { + data_deps += + [ "${dawn_vulkan_validation_layers_dir}:vulkan_gen_json_files" ] + } + } + } +} diff --git a/third_party/dawn/src/dawn_native/BackendConnection.cpp b/third_party/dawn/src/dawn_native/BackendConnection.cpp index 5a9d4b169f1..09ef4ef76c7 100644 --- a/third_party/dawn/src/dawn_native/BackendConnection.cpp +++ b/third_party/dawn/src/dawn_native/BackendConnection.cpp @@ -16,11 +16,11 @@ namespace dawn_native { - BackendConnection::BackendConnection(InstanceBase* instance, BackendType type) + BackendConnection::BackendConnection(InstanceBase* instance, wgpu::BackendType type) : mInstance(instance), mType(type) { } - BackendType BackendConnection::GetType() const { + wgpu::BackendType BackendConnection::GetType() const { return mType; } diff --git a/third_party/dawn/src/dawn_native/BackendConnection.h b/third_party/dawn/src/dawn_native/BackendConnection.h index e0e56994eca..f17108ec585 100644 --- a/third_party/dawn/src/dawn_native/BackendConnection.h +++ b/third_party/dawn/src/dawn_native/BackendConnection.h @@ -26,10 +26,10 @@ namespace dawn_native { // backend. class BackendConnection { public: - BackendConnection(InstanceBase* instance, BackendType type); + BackendConnection(InstanceBase* instance, wgpu::BackendType type); virtual ~BackendConnection() = default; - BackendType GetType() const; + wgpu::BackendType GetType() const; InstanceBase* GetInstance() const; // Returns all the adapters for the system that can be created by the backend, without extra @@ -42,7 +42,7 @@ namespace dawn_native { private: InstanceBase* mInstance = nullptr; - BackendType mType; + wgpu::BackendType mType; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/BindGroup.cpp b/third_party/dawn/src/dawn_native/BindGroup.cpp index 93af9bf80bb..71e8a28d78d 100644 --- a/third_party/dawn/src/dawn_native/BindGroup.cpp +++ b/third_party/dawn/src/dawn_native/BindGroup.cpp @@ -16,6 +16,7 @@ #include "common/Assert.h" #include "common/Math.h" +#include "common/ityp_bitset.h" #include "dawn_native/BindGroupLayout.h" #include "dawn_native/Buffer.h" #include "dawn_native/Device.h" @@ -29,60 +30,135 @@ namespace dawn_native { // Helper functions to perform binding-type specific validation MaybeError ValidateBufferBinding(const DeviceBase* device, - const BindGroupBinding& binding, - dawn::BufferUsageBit requiredUsage) { - if (binding.buffer == nullptr || binding.sampler != nullptr || - binding.textureView != nullptr) { + const BindGroupEntry& entry, + wgpu::BufferUsage requiredUsage, + const BindingInfo& bindingInfo, + const uint64_t maxBindingSize) { + if (entry.buffer == nullptr || entry.sampler != nullptr || + entry.textureView != nullptr) { return DAWN_VALIDATION_ERROR("expected buffer binding"); } - DAWN_TRY(device->ValidateObject(binding.buffer)); + DAWN_TRY(device->ValidateObject(entry.buffer)); - uint64_t bufferSize = binding.buffer->GetSize(); - if (binding.size > bufferSize) { + uint64_t bufferSize = entry.buffer->GetSize(); + + // Handle wgpu::WholeSize, avoiding overflows. + if (entry.offset > bufferSize) { + return DAWN_VALIDATION_ERROR("Buffer binding doesn't fit in the buffer"); + } + uint64_t bindingSize = + (entry.size == wgpu::kWholeSize) ? bufferSize - entry.offset : entry.size; + + if (bindingSize > bufferSize) { return DAWN_VALIDATION_ERROR("Buffer binding size larger than the buffer"); } + if (bindingSize == 0) { + return DAWN_VALIDATION_ERROR("Buffer binding size cannot be zero."); + } + // Note that no overflow can happen because we already checked that - // bufferSize >= binding.size - if (binding.offset > bufferSize - binding.size) { + // bufferSize >= bindingSize + if (entry.offset > bufferSize - bindingSize) { return DAWN_VALIDATION_ERROR("Buffer binding doesn't fit in the buffer"); } - if (!IsAligned(binding.offset, 256)) { + if (!IsAligned(entry.offset, 256)) { return DAWN_VALIDATION_ERROR( "Buffer offset for bind group needs to be 256-byte aligned"); } - if (!(binding.buffer->GetUsage() & requiredUsage)) { + if (!(entry.buffer->GetUsage() & requiredUsage)) { return DAWN_VALIDATION_ERROR("buffer binding usage mismatch"); } + if (bindingSize < bindingInfo.minBufferBindingSize) { + return DAWN_VALIDATION_ERROR( + "Binding size smaller than minimum buffer size: binding " + + std::to_string(entry.binding) + " given " + std::to_string(bindingSize) + + " bytes, required " + std::to_string(bindingInfo.minBufferBindingSize) + + " bytes"); + } + + if (bindingSize > maxBindingSize) { + return DAWN_VALIDATION_ERROR( + "Binding size bigger than maximum uniform buffer binding size: binding " + + std::to_string(entry.binding) + " given " + std::to_string(bindingSize) + + " bytes, maximum is " + std::to_string(kMaxUniformBufferBindingSize) + + " bytes"); + } + return {}; } MaybeError ValidateTextureBinding(const DeviceBase* device, - const BindGroupBinding& binding, - dawn::TextureUsageBit requiredUsage) { - if (binding.textureView == nullptr || binding.sampler != nullptr || - binding.buffer != nullptr) { + const BindGroupEntry& entry, + wgpu::TextureUsage requiredUsage, + const BindingInfo& bindingInfo) { + if (entry.textureView == nullptr || entry.sampler != nullptr || + entry.buffer != nullptr) { return DAWN_VALIDATION_ERROR("expected texture binding"); } - DAWN_TRY(device->ValidateObject(binding.textureView)); + DAWN_TRY(device->ValidateObject(entry.textureView)); + + TextureBase* texture = entry.textureView->GetTexture(); - if (!(binding.textureView->GetTexture()->GetUsage() & requiredUsage)) { + if (!(texture->GetUsage() & requiredUsage)) { return DAWN_VALIDATION_ERROR("texture binding usage mismatch"); } + if (texture->IsMultisampledTexture() != bindingInfo.multisampled) { + return DAWN_VALIDATION_ERROR("texture multisampling mismatch"); + } + + switch (requiredUsage) { + case wgpu::TextureUsage::Sampled: { + if (!texture->GetFormat().HasComponentType(bindingInfo.textureComponentType)) { + return DAWN_VALIDATION_ERROR("texture component type usage mismatch"); + } + break; + } + case wgpu::TextureUsage::Storage: { + if (texture->GetFormat().format != bindingInfo.storageTextureFormat) { + return DAWN_VALIDATION_ERROR("storage texture format mismatch"); + } + break; + } + default: + UNREACHABLE(); + break; + } + + if (entry.textureView->GetDimension() != bindingInfo.viewDimension) { + return DAWN_VALIDATION_ERROR("texture view dimension mismatch"); + } + return {}; } MaybeError ValidateSamplerBinding(const DeviceBase* device, - const BindGroupBinding& binding) { - if (binding.sampler == nullptr || binding.textureView != nullptr || - binding.buffer != nullptr) { + const BindGroupEntry& entry, + wgpu::BindingType bindingType) { + if (entry.sampler == nullptr || entry.textureView != nullptr || + entry.buffer != nullptr) { return DAWN_VALIDATION_ERROR("expected sampler binding"); } - DAWN_TRY(device->ValidateObject(binding.sampler)); + DAWN_TRY(device->ValidateObject(entry.sampler)); + + switch (bindingType) { + case wgpu::BindingType::Sampler: + if (entry.sampler->HasCompareFunction()) { + return DAWN_VALIDATION_ERROR("Did not expect comparison sampler"); + } + break; + case wgpu::BindingType::ComparisonSampler: + if (!entry.sampler->HasCompareFunction()) { + return DAWN_VALIDATION_ERROR("Expected comparison sampler"); + } + break; + default: + UNREACHABLE(); + } return {}; } @@ -97,48 +173,58 @@ namespace dawn_native { DAWN_TRY(device->ValidateObject(descriptor->layout)); - const BindGroupLayoutBase::LayoutBindingInfo& layoutInfo = - descriptor->layout->GetBindingInfo(); - - if (descriptor->bindingCount != layoutInfo.mask.count()) { + if (BindingIndex(descriptor->entryCount) != descriptor->layout->GetBindingCount()) { return DAWN_VALIDATION_ERROR("numBindings mismatch"); } - std::bitset bindingsSet; - for (uint32_t i = 0; i < descriptor->bindingCount; ++i) { - const BindGroupBinding& binding = descriptor->bindings[i]; - uint32_t bindingIndex = binding.binding; + const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap(); + ASSERT(bindingMap.size() <= kMaxBindingsPerPipelineLayout); - // Check that we can set this binding. - if (bindingIndex >= kMaxBindingsPerGroup) { - return DAWN_VALIDATION_ERROR("binding index too high"); - } + ityp::bitset bindingsSet; + for (uint32_t i = 0; i < descriptor->entryCount; ++i) { + const BindGroupEntry& entry = descriptor->entries[i]; - if (!layoutInfo.mask[bindingIndex]) { + const auto& it = bindingMap.find(BindingNumber(entry.binding)); + if (it == bindingMap.end()) { return DAWN_VALIDATION_ERROR("setting non-existent binding"); } + BindingIndex bindingIndex = it->second; + ASSERT(bindingIndex < descriptor->layout->GetBindingCount()); if (bindingsSet[bindingIndex]) { return DAWN_VALIDATION_ERROR("binding set twice"); } bindingsSet.set(bindingIndex); + const BindingInfo& bindingInfo = descriptor->layout->GetBindingInfo(bindingIndex); + // Perform binding-type specific validation. - switch (layoutInfo.types[bindingIndex]) { - case dawn::BindingType::UniformBuffer: - case dawn::BindingType::DynamicUniformBuffer: - DAWN_TRY(ValidateBufferBinding(device, binding, dawn::BufferUsageBit::Uniform)); + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: + DAWN_TRY(ValidateBufferBinding(device, entry, wgpu::BufferUsage::Uniform, + bindingInfo, kMaxUniformBufferBindingSize)); + break; + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + DAWN_TRY(ValidateBufferBinding(device, entry, wgpu::BufferUsage::Storage, + bindingInfo, + std::numeric_limits::max())); break; - case dawn::BindingType::StorageBuffer: - case dawn::BindingType::DynamicStorageBuffer: - DAWN_TRY(ValidateBufferBinding(device, binding, dawn::BufferUsageBit::Storage)); + case wgpu::BindingType::SampledTexture: + DAWN_TRY(ValidateTextureBinding(device, entry, wgpu::TextureUsage::Sampled, + bindingInfo)); break; - case dawn::BindingType::SampledTexture: - DAWN_TRY( - ValidateTextureBinding(device, binding, dawn::TextureUsageBit::Sampled)); + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + DAWN_TRY(ValidateSamplerBinding(device, entry, bindingInfo.type)); break; - case dawn::BindingType::Sampler: - DAWN_TRY(ValidateSamplerBinding(device, binding)); + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + DAWN_TRY(ValidateTextureBinding(device, entry, wgpu::TextureUsage::Storage, + bindingInfo)); + break; + case wgpu::BindingType::StorageTexture: + UNREACHABLE(); break; } } @@ -148,48 +234,89 @@ namespace dawn_native { // - Each binding must be set at most once // // We don't validate the equality because it wouldn't be possible to cover it with a test. - ASSERT(bindingsSet == layoutInfo.mask); + ASSERT(bindingsSet.count() == bindingMap.size()); return {}; - } + } // anonymous namespace // BindGroup - BindGroupBase::BindGroupBase(DeviceBase* device, const BindGroupDescriptor* descriptor) - : ObjectBase(device), mLayout(descriptor->layout) { - for (uint32_t i = 0; i < descriptor->bindingCount; ++i) { - const BindGroupBinding& binding = descriptor->bindings[i]; + BindGroupBase::BindGroupBase(DeviceBase* device, + const BindGroupDescriptor* descriptor, + void* bindingDataStart) + : ObjectBase(device), + mLayout(descriptor->layout), + mBindingData(mLayout->ComputeBindingDataPointers(bindingDataStart)) { + for (BindingIndex i{0}; i < mLayout->GetBindingCount(); ++i) { + // TODO(enga): Shouldn't be needed when bindings are tightly packed. + // This is to fill Ref holes with nullptrs. + new (&mBindingData.bindings[i]) Ref(); + } + + for (uint32_t i = 0; i < descriptor->entryCount; ++i) { + const BindGroupEntry& entry = descriptor->entries[i]; - uint32_t bindingIndex = binding.binding; - ASSERT(bindingIndex < kMaxBindingsPerGroup); + BindingIndex bindingIndex = + descriptor->layout->GetBindingIndex(BindingNumber(entry.binding)); + ASSERT(bindingIndex < mLayout->GetBindingCount()); // Only a single binding type should be set, so once we found it we can skip to the // next loop iteration. - if (binding.buffer != nullptr) { - ASSERT(mBindings[bindingIndex].Get() == nullptr); - mBindings[bindingIndex] = binding.buffer; - mOffsets[bindingIndex] = binding.offset; - mSizes[bindingIndex] = binding.size; + if (entry.buffer != nullptr) { + ASSERT(mBindingData.bindings[bindingIndex].Get() == nullptr); + mBindingData.bindings[bindingIndex] = entry.buffer; + mBindingData.bufferData[bindingIndex].offset = entry.offset; + uint64_t bufferSize = (entry.size == wgpu::kWholeSize) + ? entry.buffer->GetSize() - entry.offset + : entry.size; + mBindingData.bufferData[bindingIndex].size = bufferSize; continue; } - if (binding.textureView != nullptr) { - ASSERT(mBindings[bindingIndex].Get() == nullptr); - mBindings[bindingIndex] = binding.textureView; + if (entry.textureView != nullptr) { + ASSERT(mBindingData.bindings[bindingIndex].Get() == nullptr); + mBindingData.bindings[bindingIndex] = entry.textureView; continue; } - if (binding.sampler != nullptr) { - ASSERT(mBindings[bindingIndex].Get() == nullptr); - mBindings[bindingIndex] = binding.sampler; + if (entry.sampler != nullptr) { + ASSERT(mBindingData.bindings[bindingIndex].Get() == nullptr); + mBindingData.bindings[bindingIndex] = entry.sampler; continue; } } + + uint32_t packedIdx = 0; + for (BindingIndex bindingIndex{0}; bindingIndex < descriptor->layout->GetBufferCount(); + ++bindingIndex) { + if (descriptor->layout->GetBindingInfo(bindingIndex).minBufferBindingSize == 0) { + mBindingData.unverifiedBufferSizes[packedIdx] = + mBindingData.bufferData[bindingIndex].size; + ++packedIdx; + } + } + } + + BindGroupBase::~BindGroupBase() { + if (mLayout) { + ASSERT(!IsError()); + for (BindingIndex i{0}; i < mLayout->GetBindingCount(); ++i) { + mBindingData.bindings[i].~Ref(); + } + } + } + + void BindGroupBase::DeleteThis() { + // Add another ref to the layout so that if this is the last ref, the layout + // is destroyed after the bind group. The bind group is slab-allocated inside + // memory owned by the layout (except for the null backend). + Ref layout = mLayout; + RefCounted::DeleteThis(); } BindGroupBase::BindGroupBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag) { + : ObjectBase(device, tag), mBindingData() { } // static @@ -197,38 +324,50 @@ namespace dawn_native { return new BindGroupBase(device, ObjectBase::kError); } + BindGroupLayoutBase* BindGroupBase::GetLayout() { + ASSERT(!IsError()); + return mLayout.Get(); + } + const BindGroupLayoutBase* BindGroupBase::GetLayout() const { ASSERT(!IsError()); return mLayout.Get(); } - BufferBinding BindGroupBase::GetBindingAsBufferBinding(size_t binding) { + const ityp::span& BindGroupBase::GetUnverifiedBufferSizes() const { + ASSERT(!IsError()); + return mBindingData.unverifiedBufferSizes; + } + + BufferBinding BindGroupBase::GetBindingAsBufferBinding(BindingIndex bindingIndex) { ASSERT(!IsError()); - ASSERT(binding < kMaxBindingsPerGroup); - ASSERT(mLayout->GetBindingInfo().mask[binding]); - ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::UniformBuffer || - mLayout->GetBindingInfo().types[binding] == dawn::BindingType::StorageBuffer || - mLayout->GetBindingInfo().types[binding] == - dawn::BindingType::DynamicUniformBuffer || - mLayout->GetBindingInfo().types[binding] == dawn::BindingType::DynamicStorageBuffer); - BufferBase* buffer = static_cast(mBindings[binding].Get()); - return {buffer, mOffsets[binding], mSizes[binding]}; + ASSERT(bindingIndex < mLayout->GetBindingCount()); + ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::UniformBuffer || + mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::StorageBuffer || + mLayout->GetBindingInfo(bindingIndex).type == + wgpu::BindingType::ReadonlyStorageBuffer); + BufferBase* buffer = static_cast(mBindingData.bindings[bindingIndex].Get()); + return {buffer, mBindingData.bufferData[bindingIndex].offset, + mBindingData.bufferData[bindingIndex].size}; } - SamplerBase* BindGroupBase::GetBindingAsSampler(size_t binding) { + SamplerBase* BindGroupBase::GetBindingAsSampler(BindingIndex bindingIndex) const { ASSERT(!IsError()); - ASSERT(binding < kMaxBindingsPerGroup); - ASSERT(mLayout->GetBindingInfo().mask[binding]); - ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::Sampler); - return static_cast(mBindings[binding].Get()); + ASSERT(bindingIndex < mLayout->GetBindingCount()); + ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::Sampler || + mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::ComparisonSampler); + return static_cast(mBindingData.bindings[bindingIndex].Get()); } - TextureViewBase* BindGroupBase::GetBindingAsTextureView(size_t binding) { + TextureViewBase* BindGroupBase::GetBindingAsTextureView(BindingIndex bindingIndex) { ASSERT(!IsError()); - ASSERT(binding < kMaxBindingsPerGroup); - ASSERT(mLayout->GetBindingInfo().mask[binding]); - ASSERT(mLayout->GetBindingInfo().types[binding] == dawn::BindingType::SampledTexture); - return static_cast(mBindings[binding].Get()); + ASSERT(bindingIndex < mLayout->GetBindingCount()); + ASSERT(mLayout->GetBindingInfo(bindingIndex).type == wgpu::BindingType::SampledTexture || + mLayout->GetBindingInfo(bindingIndex).type == + wgpu::BindingType::ReadonlyStorageTexture || + mLayout->GetBindingInfo(bindingIndex).type == + wgpu::BindingType::WriteonlyStorageTexture); + return static_cast(mBindingData.bindings[bindingIndex].Get()); } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/BindGroup.h b/third_party/dawn/src/dawn_native/BindGroup.h index d31fff4f83f..c29bbeb3aa9 100644 --- a/third_party/dawn/src/dawn_native/BindGroup.h +++ b/third_party/dawn/src/dawn_native/BindGroup.h @@ -16,6 +16,7 @@ #define DAWNNATIVE_BINDGROUP_H_ #include "common/Constants.h" +#include "common/Math.h" #include "dawn_native/BindGroupLayout.h" #include "dawn_native/Error.h" #include "dawn_native/Forward.h" @@ -40,22 +41,44 @@ namespace dawn_native { class BindGroupBase : public ObjectBase { public: - BindGroupBase(DeviceBase* device, const BindGroupDescriptor* descriptor); - static BindGroupBase* MakeError(DeviceBase* device); + BindGroupLayoutBase* GetLayout(); const BindGroupLayoutBase* GetLayout() const; - BufferBinding GetBindingAsBufferBinding(size_t binding); - SamplerBase* GetBindingAsSampler(size_t binding); - TextureViewBase* GetBindingAsTextureView(size_t binding); + BufferBinding GetBindingAsBufferBinding(BindingIndex bindingIndex); + SamplerBase* GetBindingAsSampler(BindingIndex bindingIndex) const; + TextureViewBase* GetBindingAsTextureView(BindingIndex bindingIndex); + const ityp::span& GetUnverifiedBufferSizes() const; + + protected: + // To save memory, the size of a bind group is dynamically determined and the bind group is + // placement-allocated into memory big enough to hold the bind group with its + // dynamically-sized bindings after it. The pointer of the memory of the beginning of the + // binding data should be passed as |bindingDataStart|. + BindGroupBase(DeviceBase* device, + const BindGroupDescriptor* descriptor, + void* bindingDataStart); + + // Helper to instantiate BindGroupBase. We pass in |derived| because BindGroupBase may not + // be first in the allocation. The binding data is stored after the Derived class. + template + BindGroupBase(Derived* derived, DeviceBase* device, const BindGroupDescriptor* descriptor) + : BindGroupBase(device, + descriptor, + AlignPtr(reinterpret_cast(derived) + sizeof(Derived), + descriptor->layout->GetBindingDataAlignment())) { + static_assert(std::is_base_of::value, ""); + } + + protected: + ~BindGroupBase() override; private: BindGroupBase(DeviceBase* device, ObjectBase::ErrorTag tag); + void DeleteThis() override; Ref mLayout; - std::array, kMaxBindingsPerGroup> mBindings; - std::array mOffsets; - std::array mSizes; + BindGroupLayoutBase::BindingDataPointers mBindingData; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/BindGroupAndStorageBarrierTracker.h b/third_party/dawn/src/dawn_native/BindGroupAndStorageBarrierTracker.h new file mode 100644 index 00000000000..e34a16511a2 --- /dev/null +++ b/third_party/dawn/src/dawn_native/BindGroupAndStorageBarrierTracker.h @@ -0,0 +1,112 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_ +#define DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_ + +#include "common/ityp_bitset.h" +#include "common/ityp_stack_vec.h" +#include "dawn_native/BindGroup.h" +#include "dawn_native/BindGroupTracker.h" +#include "dawn_native/Buffer.h" +#include "dawn_native/Texture.h" + +namespace dawn_native { + + // Extends BindGroupTrackerBase to also keep track of resources that need a usage transition. + template + class BindGroupAndStorageBarrierTrackerBase + : public BindGroupTrackerBase { + using Base = BindGroupTrackerBase; + + public: + BindGroupAndStorageBarrierTrackerBase() = default; + + void OnSetBindGroup(BindGroupIndex index, + BindGroupBase* bindGroup, + uint32_t dynamicOffsetCount, + uint32_t* dynamicOffsets) { + ASSERT(index < kMaxBindGroupsTyped); + + if (this->mBindGroups[index] != bindGroup) { + const BindGroupLayoutBase* layout = bindGroup->GetLayout(); + + mBindings[index].resize(layout->GetBindingCount()); + mBindingTypes[index].resize(layout->GetBindingCount()); + mBindingsNeedingBarrier[index] = {}; + + for (BindingIndex bindingIndex{0}; bindingIndex < layout->GetBindingCount(); + ++bindingIndex) { + const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex); + + if ((bindingInfo.visibility & wgpu::ShaderStage::Compute) == 0) { + continue; + } + + mBindingTypes[index][bindingIndex] = bindingInfo.type; + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::SampledTexture: + // Don't require barriers. + break; + + case wgpu::BindingType::StorageBuffer: + mBindingsNeedingBarrier[index].set(bindingIndex); + mBindings[index][bindingIndex] = static_cast( + bindGroup->GetBindingAsBufferBinding(bindingIndex).buffer); + break; + + // Read-only and write-only storage textures must use general layout + // because load and store operations on storage images can only be done on + // the images in VK_IMAGE_LAYOUT_GENERAL layout. + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + mBindingsNeedingBarrier[index].set(bindingIndex); + mBindings[index][bindingIndex] = static_cast( + bindGroup->GetBindingAsTextureView(bindingIndex)); + break; + + case wgpu::BindingType::StorageTexture: + // Not implemented. + default: + UNREACHABLE(); + break; + } + } + } + + Base::OnSetBindGroup(index, bindGroup, dynamicOffsetCount, dynamicOffsets); + } + + protected: + ityp::array, + kMaxBindGroups> + mBindingsNeedingBarrier = {}; + ityp::array, + kMaxBindGroups> + mBindingTypes = {}; + ityp::array, + kMaxBindGroups> + mBindings = {}; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_BINDGROUPANDSTORAGEBARRIERTRACKER_H_ diff --git a/third_party/dawn/src/dawn_native/BindGroupLayout.cpp b/third_party/dawn/src/dawn_native/BindGroupLayout.cpp index 4e94e9f7d33..9c5c8dca1fc 100644 --- a/third_party/dawn/src/dawn_native/BindGroupLayout.cpp +++ b/third_party/dawn/src/dawn_native/BindGroupLayout.cpp @@ -17,93 +17,410 @@ #include "common/BitSetIterator.h" #include "common/HashUtils.h" #include "dawn_native/Device.h" +#include "dawn_native/PerStage.h" #include "dawn_native/ValidationUtils_autogen.h" +#include #include +#include namespace dawn_native { - MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase*, + MaybeError ValidateBindingTypeWithShaderStageVisibility( + wgpu::BindingType bindingType, + wgpu::ShaderStage shaderStageVisibility) { + // TODO(jiawei.shao@intel.com): support read-write storage textures. + switch (bindingType) { + case wgpu::BindingType::StorageBuffer: { + if ((shaderStageVisibility & wgpu::ShaderStage::Vertex) != 0) { + return DAWN_VALIDATION_ERROR( + "storage buffer binding is not supported in vertex shader"); + } + break; + } + + case wgpu::BindingType::WriteonlyStorageTexture: { + if ((shaderStageVisibility & wgpu::ShaderStage::Vertex) != 0) { + return DAWN_VALIDATION_ERROR( + "write-only storage texture binding is not supported in vertex shader"); + } + break; + } + + case wgpu::BindingType::StorageTexture: { + return DAWN_VALIDATION_ERROR("Read-write storage texture binding is not supported"); + } + + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + break; + } + + return {}; + } + + MaybeError ValidateStorageTextureFormat(DeviceBase* device, + wgpu::BindingType bindingType, + wgpu::TextureFormat storageTextureFormat) { + switch (bindingType) { + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + if (storageTextureFormat == wgpu::TextureFormat::Undefined) { + return DAWN_VALIDATION_ERROR("Storage texture format is missing"); + } + DAWN_TRY(ValidateTextureFormat(storageTextureFormat)); + + const Format* format = nullptr; + DAWN_TRY_ASSIGN(format, device->GetInternalFormat(storageTextureFormat)); + ASSERT(format != nullptr); + if (!format->supportsStorageUsage) { + return DAWN_VALIDATION_ERROR("The storage texture format is not supported"); + } + break; + } + + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::SampledTexture: + break; + default: + UNREACHABLE(); + break; + } + + return {}; + } + + MaybeError ValidateStorageTextureViewDimension(wgpu::BindingType bindingType, + wgpu::TextureViewDimension dimension) { + switch (bindingType) { + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + break; + } + + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::SampledTexture: + return {}; + + case wgpu::BindingType::StorageTexture: + default: + UNREACHABLE(); + return {}; + } + + switch (dimension) { + case wgpu::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::CubeArray: + return DAWN_VALIDATION_ERROR( + "Cube map and cube map texture views cannot be used as storage textures"); + + case wgpu::TextureViewDimension::e1D: + case wgpu::TextureViewDimension::e2D: + case wgpu::TextureViewDimension::e2DArray: + case wgpu::TextureViewDimension::e3D: + return {}; + + case wgpu::TextureViewDimension::Undefined: + default: + UNREACHABLE(); + return {}; + } + } + + MaybeError ValidateBindingCanBeMultisampled(wgpu::BindingType bindingType, + wgpu::TextureViewDimension viewDimension) { + switch (bindingType) { + case wgpu::BindingType::SampledTexture: + break; + + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + return DAWN_VALIDATION_ERROR("Storage texture bindings may not be multisampled"); + + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + return DAWN_VALIDATION_ERROR("Buffer bindings may not be multisampled"); + + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + return DAWN_VALIDATION_ERROR("Sampler bindings may not be multisampled"); + + case wgpu::BindingType::StorageTexture: + default: + UNREACHABLE(); + return {}; + } + + switch (viewDimension) { + case wgpu::TextureViewDimension::e2D: + break; + + case wgpu::TextureViewDimension::e2DArray: + return DAWN_VALIDATION_ERROR("2D array texture bindings may not be multisampled"); + + case wgpu::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::CubeArray: + return DAWN_VALIDATION_ERROR("Cube texture bindings may not be multisampled"); + + case wgpu::TextureViewDimension::e3D: + return DAWN_VALIDATION_ERROR("3D texture bindings may not be multisampled"); + + case wgpu::TextureViewDimension::e1D: + return DAWN_VALIDATION_ERROR("1D texture bindings may not be multisampled"); + + case wgpu::TextureViewDimension::Undefined: + default: + UNREACHABLE(); + return {}; + } + + return {}; + } + + MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor) { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - std::bitset bindingsSet; - for (uint32_t i = 0; i < descriptor->bindingCount; ++i) { - auto& binding = descriptor->bindings[i]; - DAWN_TRY(ValidateShaderStageBit(binding.visibility)); - DAWN_TRY(ValidateBindingType(binding.type)); + std::set bindingsSet; + BindingCounts bindingCounts = {}; + for (uint32_t i = 0; i < descriptor->entryCount; ++i) { + const BindGroupLayoutEntry& entry = descriptor->entries[i]; + BindingNumber bindingNumber = BindingNumber(entry.binding); + + DAWN_TRY(ValidateShaderStage(entry.visibility)); + DAWN_TRY(ValidateBindingType(entry.type)); + DAWN_TRY(ValidateTextureComponentType(entry.textureComponentType)); - if (binding.binding >= kMaxBindingsPerGroup) { - return DAWN_VALIDATION_ERROR("some binding index exceeds the maximum value"); + wgpu::TextureViewDimension viewDimension = wgpu::TextureViewDimension::e2D; + if (entry.viewDimension != wgpu::TextureViewDimension::Undefined) { + DAWN_TRY(ValidateTextureViewDimension(entry.viewDimension)); + viewDimension = entry.viewDimension; } - if (bindingsSet[binding.binding]) { + + if (bindingsSet.count(bindingNumber) != 0) { return DAWN_VALIDATION_ERROR("some binding index was specified more than once"); } - bindingsSet.set(binding.binding); + + DAWN_TRY(ValidateBindingTypeWithShaderStageVisibility(entry.type, entry.visibility)); + + DAWN_TRY(ValidateStorageTextureFormat(device, entry.type, entry.storageTextureFormat)); + + DAWN_TRY(ValidateStorageTextureViewDimension(entry.type, viewDimension)); + + if (entry.multisampled) { + DAWN_TRY(ValidateBindingCanBeMultisampled(entry.type, viewDimension)); + } + + switch (entry.type) { + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + break; + case wgpu::BindingType::SampledTexture: + if (entry.hasDynamicOffset) { + return DAWN_VALIDATION_ERROR("Sampled textures cannot be dynamic"); + } + break; + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + if (entry.hasDynamicOffset) { + return DAWN_VALIDATION_ERROR("Samplers cannot be dynamic"); + } + break; + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + if (entry.hasDynamicOffset) { + return DAWN_VALIDATION_ERROR("Storage textures cannot be dynamic"); + } + break; + case wgpu::BindingType::StorageTexture: + return DAWN_VALIDATION_ERROR("storage textures aren't supported (yet)"); + default: + UNREACHABLE(); + break; + } + + IncrementBindingCounts(&bindingCounts, entry); + + bindingsSet.insert(bindingNumber); } + + DAWN_TRY(ValidateBindingCounts(bindingCounts)); + return {}; } namespace { - size_t HashBindingInfo(const BindGroupLayoutBase::LayoutBindingInfo& info) { - size_t hash = Hash(info.mask); - for (uint32_t binding : IterateBitSet(info.mask)) { - HashCombine(&hash, info.visibilities[binding], info.types[binding]); - } + void HashCombineBindingInfo(size_t* hash, const BindingInfo& info) { + HashCombine(hash, info.hasDynamicOffset, info.multisampled, info.visibility, info.type, + info.textureComponentType, info.viewDimension, info.storageTextureFormat, + info.minBufferBindingSize); + } - return hash; + bool operator!=(const BindingInfo& a, const BindingInfo& b) { + return a.hasDynamicOffset != b.hasDynamicOffset || // + a.multisampled != b.multisampled || // + a.visibility != b.visibility || // + a.type != b.type || // + a.textureComponentType != b.textureComponentType || // + a.viewDimension != b.viewDimension || // + a.storageTextureFormat != b.storageTextureFormat || // + a.minBufferBindingSize != b.minBufferBindingSize; } - bool operator==(const BindGroupLayoutBase::LayoutBindingInfo& a, - const BindGroupLayoutBase::LayoutBindingInfo& b) { - if (a.mask != b.mask) { - return false; + bool IsBufferBinding(wgpu::BindingType bindingType) { + switch (bindingType) { + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + return true; + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::StorageTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + return false; + default: + UNREACHABLE(); + return false; } + } - for (uint32_t binding : IterateBitSet(a.mask)) { - if ((a.visibilities[binding] != b.visibilities[binding]) || - (a.types[binding] != b.types[binding])) { - return false; + bool SortBindingsCompare(const BindGroupLayoutEntry& a, const BindGroupLayoutEntry& b) { + const bool aIsBuffer = IsBufferBinding(a.type); + const bool bIsBuffer = IsBufferBinding(b.type); + if (aIsBuffer != bIsBuffer) { + // Always place buffers first. + return aIsBuffer; + } else { + if (aIsBuffer) { + ASSERT(bIsBuffer); + if (a.hasDynamicOffset != b.hasDynamicOffset) { + // Buffers with dynamic offsets should come before those without. + // This makes it easy to iterate over the dynamic buffer bindings + // [0, dynamicBufferCount) during validation. + return a.hasDynamicOffset; + } + if (a.hasDynamicOffset) { + ASSERT(b.hasDynamicOffset); + ASSERT(a.binding != b.binding); + // Above, we ensured that dynamic buffers are first. Now, ensure that + // dynamic buffer bindings are in increasing order. This is because dynamic + // buffer offsets are applied in increasing order of binding number. + return a.binding < b.binding; + } + } + // Otherwise, sort by type. + if (a.type != b.type) { + return a.type < b.type; + } + } + if (a.visibility != b.visibility) { + return a.visibility < b.visibility; + } + if (a.multisampled != b.multisampled) { + return a.multisampled < b.multisampled; + } + if (a.viewDimension != b.viewDimension) { + return a.viewDimension < b.viewDimension; + } + if (a.textureComponentType != b.textureComponentType) { + return a.textureComponentType < b.textureComponentType; + } + if (a.storageTextureFormat != b.storageTextureFormat) { + return a.storageTextureFormat < b.storageTextureFormat; + } + if (a.minBufferBindingSize != b.minBufferBindingSize) { + return a.minBufferBindingSize < b.minBufferBindingSize; + } + return false; + } + + // This is a utility function to help ASSERT that the BGL-binding comparator places buffers + // first. + bool CheckBufferBindingsFirst(ityp::span bindings) { + BindingIndex lastBufferIndex{0}; + BindingIndex firstNonBufferIndex = std::numeric_limits::max(); + for (BindingIndex i{0}; i < bindings.size(); ++i) { + if (IsBufferBinding(bindings[i].type)) { + lastBufferIndex = std::max(i, lastBufferIndex); + } else { + firstNonBufferIndex = std::min(i, firstNonBufferIndex); } } - return true; + // If there are no buffers, then |lastBufferIndex| is initialized to 0 and + // |firstNonBufferIndex| gets set to 0. + return firstNonBufferIndex >= lastBufferIndex; } + } // namespace // BindGroupLayoutBase BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, - const BindGroupLayoutDescriptor* descriptor, - bool blueprint) - : ObjectBase(device), mIsBlueprint(blueprint) { - for (uint32_t i = 0; i < descriptor->bindingCount; ++i) { - auto& binding = descriptor->bindings[i]; + const BindGroupLayoutDescriptor* descriptor) + : CachedObject(device), mBindingInfo(BindingIndex(descriptor->entryCount)) { + std::vector sortedBindings( + descriptor->entries, descriptor->entries + descriptor->entryCount); + std::sort(sortedBindings.begin(), sortedBindings.end(), SortBindingsCompare); + + for (BindingIndex i{0}; i < mBindingInfo.size(); ++i) { + const BindGroupLayoutEntry& binding = sortedBindings[static_cast(i)]; + mBindingInfo[i].binding = BindingNumber(binding.binding); + mBindingInfo[i].type = binding.type; + mBindingInfo[i].visibility = binding.visibility; + mBindingInfo[i].textureComponentType = + Format::TextureComponentTypeToFormatType(binding.textureComponentType); + mBindingInfo[i].storageTextureFormat = binding.storageTextureFormat; + mBindingInfo[i].minBufferBindingSize = binding.minBufferBindingSize; + + if (binding.viewDimension == wgpu::TextureViewDimension::Undefined) { + mBindingInfo[i].viewDimension = wgpu::TextureViewDimension::e2D; + } else { + mBindingInfo[i].viewDimension = binding.viewDimension; + } - uint32_t index = binding.binding; - mBindingInfo.visibilities[index] = binding.visibility; - mBindingInfo.types[index] = binding.type; + mBindingInfo[i].multisampled = binding.multisampled; + mBindingInfo[i].hasDynamicOffset = binding.hasDynamicOffset; - if (binding.type == dawn::BindingType::DynamicUniformBuffer || - binding.type == dawn::BindingType::DynamicStorageBuffer) { - mDynamicBufferCount++; + if (IsBufferBinding(binding.type)) { + // Buffers must be contiguously packed at the start of the binding info. + ASSERT(GetBufferCount() == i); } + IncrementBindingCounts(&mBindingCounts, binding); - ASSERT(!mBindingInfo.mask[index]); - mBindingInfo.mask.set(index); + const auto& it = mBindingMap.emplace(BindingNumber(binding.binding), i); + ASSERT(it.second); } + ASSERT(CheckBufferBindingsFirst({mBindingInfo.data(), GetBindingCount()})); + ASSERT(mBindingInfo.size() <= kMaxBindingsPerPipelineLayoutTyped); } BindGroupLayoutBase::BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag), mIsBlueprint(true) { + : CachedObject(device, tag) { } BindGroupLayoutBase::~BindGroupLayoutBase() { // Do not uncache the actual cached object if we are a blueprint - if (!mIsBlueprint && !IsError()) { + if (IsCachedReference()) { GetDevice()->UncacheBindGroupLayout(this); } } @@ -113,22 +430,94 @@ namespace dawn_native { return new BindGroupLayoutBase(device, ObjectBase::kError); } - const BindGroupLayoutBase::LayoutBindingInfo& BindGroupLayoutBase::GetBindingInfo() const { + const BindGroupLayoutBase::BindingMap& BindGroupLayoutBase::GetBindingMap() const { + ASSERT(!IsError()); + return mBindingMap; + } + + BindingIndex BindGroupLayoutBase::GetBindingIndex(BindingNumber bindingNumber) const { ASSERT(!IsError()); - return mBindingInfo; + const auto& it = mBindingMap.find(bindingNumber); + ASSERT(it != mBindingMap.end()); + return it->second; } size_t BindGroupLayoutBase::HashFunc::operator()(const BindGroupLayoutBase* bgl) const { - return HashBindingInfo(bgl->mBindingInfo); + size_t hash = 0; + // std::map is sorted by key, so two BGLs constructed in different orders + // will still hash the same. + for (const auto& it : bgl->mBindingMap) { + HashCombine(&hash, it.first, it.second); + HashCombineBindingInfo(&hash, bgl->mBindingInfo[it.second]); + } + return hash; } bool BindGroupLayoutBase::EqualityFunc::operator()(const BindGroupLayoutBase* a, const BindGroupLayoutBase* b) const { - return a->mBindingInfo == b->mBindingInfo; + if (a->GetBindingCount() != b->GetBindingCount()) { + return false; + } + for (BindingIndex i{0}; i < a->GetBindingCount(); ++i) { + if (a->mBindingInfo[i] != b->mBindingInfo[i]) { + return false; + } + } + return a->mBindingMap == b->mBindingMap; + } + + BindingIndex BindGroupLayoutBase::GetBindingCount() const { + return mBindingInfo.size(); + } + + BindingIndex BindGroupLayoutBase::GetBufferCount() const { + return BindingIndex(mBindingCounts.bufferCount); + } + + BindingIndex BindGroupLayoutBase::GetDynamicBufferCount() const { + // This is a binding index because dynamic buffers are packed at the front of the binding + // info. + return static_cast(mBindingCounts.dynamicStorageBufferCount + + mBindingCounts.dynamicUniformBufferCount); + } + + uint32_t BindGroupLayoutBase::GetUnverifiedBufferCount() const { + return mBindingCounts.unverifiedBufferCount; } - uint32_t BindGroupLayoutBase::GetDynamicBufferCount() const { - return mDynamicBufferCount; + const BindingCounts& BindGroupLayoutBase::GetBindingCountInfo() const { + return mBindingCounts; + } + + size_t BindGroupLayoutBase::GetBindingDataSize() const { + // | ------ buffer-specific ----------| ------------ object pointers -------------| + // | --- offsets + sizes -------------| --------------- Ref ----------| + // Followed by: + // |---------buffer size array--------| + // |-uint64_t[mUnverifiedBufferCount]-| + size_t objectPointerStart = mBindingCounts.bufferCount * sizeof(BufferBindingData); + ASSERT(IsAligned(objectPointerStart, alignof(Ref))); + size_t bufferSizeArrayStart = + Align(objectPointerStart + mBindingCounts.totalCount * sizeof(Ref), + sizeof(uint64_t)); + ASSERT(IsAligned(bufferSizeArrayStart, alignof(uint64_t))); + return bufferSizeArrayStart + mBindingCounts.unverifiedBufferCount * sizeof(uint64_t); + } + + BindGroupLayoutBase::BindingDataPointers BindGroupLayoutBase::ComputeBindingDataPointers( + void* dataStart) const { + BufferBindingData* bufferData = reinterpret_cast(dataStart); + auto bindings = reinterpret_cast*>(bufferData + mBindingCounts.bufferCount); + uint64_t* unverifiedBufferSizes = AlignPtr( + reinterpret_cast(bindings + mBindingCounts.totalCount), sizeof(uint64_t)); + + ASSERT(IsPtrAligned(bufferData, alignof(BufferBindingData))); + ASSERT(IsPtrAligned(bindings, alignof(Ref))); + ASSERT(IsPtrAligned(unverifiedBufferSizes, alignof(uint64_t))); + + return {{bufferData, GetBufferCount()}, + {bindings, GetBindingCount()}, + {unverifiedBufferSizes, mBindingCounts.unverifiedBufferCount}}; } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/BindGroupLayout.h b/third_party/dawn/src/dawn_native/BindGroupLayout.h index decc68f7da3..958abf6f9fe 100644 --- a/third_party/dawn/src/dawn_native/BindGroupLayout.h +++ b/third_party/dawn/src/dawn_native/BindGroupLayout.h @@ -16,35 +16,59 @@ #define DAWNNATIVE_BINDGROUPLAYOUT_H_ #include "common/Constants.h" +#include "common/Math.h" +#include "common/SlabAllocator.h" +#include "common/ityp_span.h" +#include "common/ityp_vector.h" +#include "dawn_native/BindingInfo.h" +#include "dawn_native/CachedObject.h" #include "dawn_native/Error.h" #include "dawn_native/Forward.h" -#include "dawn_native/ObjectBase.h" #include "dawn_native/dawn_platform.h" -#include #include +#include namespace dawn_native { MaybeError ValidateBindGroupLayoutDescriptor(DeviceBase*, const BindGroupLayoutDescriptor* descriptor); - class BindGroupLayoutBase : public ObjectBase { + MaybeError ValidateBindingTypeWithShaderStageVisibility( + wgpu::BindingType bindingType, + wgpu::ShaderStage shaderStageVisibility); + + MaybeError ValidateStorageTextureFormat(DeviceBase* device, + wgpu::BindingType bindingType, + wgpu::TextureFormat storageTextureFormat); + + MaybeError ValidateStorageTextureViewDimension(wgpu::BindingType bindingType, + wgpu::TextureViewDimension dimension); + + MaybeError ValidateBindingCanBeMultisampled(wgpu::BindingType bindingType, + wgpu::TextureViewDimension viewDimension); + + // Bindings are specified as a |BindingNumber| in the BindGroupLayoutDescriptor. + // These numbers may be arbitrary and sparse. Internally, Dawn packs these numbers + // into a packed range of |BindingIndex| integers. + class BindGroupLayoutBase : public CachedObject { public: - BindGroupLayoutBase(DeviceBase* device, - const BindGroupLayoutDescriptor* descriptor, - bool blueprint = false); + BindGroupLayoutBase(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor); ~BindGroupLayoutBase() override; static BindGroupLayoutBase* MakeError(DeviceBase* device); - struct LayoutBindingInfo { - std::array visibilities; - std::array types; - std::bitset mask; - }; - const LayoutBindingInfo& GetBindingInfo() const; + // A map from the BindingNumber to its packed BindingIndex. + using BindingMap = std::map; + + const BindingInfo& GetBindingInfo(BindingIndex bindingIndex) const { + ASSERT(!IsError()); + ASSERT(bindingIndex < mBindingInfo.size()); + return mBindingInfo[bindingIndex]; + } + const BindingMap& GetBindingMap() const; + BindingIndex GetBindingIndex(BindingNumber bindingNumber) const; // Functors necessary for the unordered_set-based cache. struct HashFunc { @@ -54,14 +78,56 @@ namespace dawn_native { bool operator()(const BindGroupLayoutBase* a, const BindGroupLayoutBase* b) const; }; - uint32_t GetDynamicBufferCount() const; + BindingIndex GetBindingCount() const; + // Returns |BindingIndex| because buffers are packed at the front. + BindingIndex GetBufferCount() const; + // Returns |BindingIndex| because dynamic buffers are packed at the front. + BindingIndex GetDynamicBufferCount() const; + uint32_t GetUnverifiedBufferCount() const; + + // Used to get counts and validate them in pipeline layout creation. Other getters + // should be used to get typed integer counts. + const BindingCounts& GetBindingCountInfo() const; + + struct BufferBindingData { + uint64_t offset; + uint64_t size; + }; + + struct BindingDataPointers { + ityp::span const bufferData = {}; + ityp::span> const bindings = {}; + ityp::span const unverifiedBufferSizes = {}; + }; + + // Compute the amount of space / alignment required to store bindings for a bind group of + // this layout. + size_t GetBindingDataSize() const; + static constexpr size_t GetBindingDataAlignment() { + static_assert(alignof(Ref) <= alignof(BufferBindingData), ""); + return alignof(BufferBindingData); + } + + BindingDataPointers ComputeBindingDataPointers(void* dataStart) const; + + protected: + template + SlabAllocator MakeFrontendBindGroupAllocator(size_t size) { + return SlabAllocator( + size, // bytes + Align(sizeof(BindGroup), GetBindingDataAlignment()) + GetBindingDataSize(), // size + std::max(alignof(BindGroup), GetBindingDataAlignment()) // alignment + ); + } private: BindGroupLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag); - LayoutBindingInfo mBindingInfo; - bool mIsBlueprint = false; - uint32_t mDynamicBufferCount = 0; + BindingCounts mBindingCounts = {}; + ityp::vector mBindingInfo; + + // Map from BindGroupLayoutEntry.binding to packed indices. + BindingMap mBindingMap; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/BindGroupTracker.h b/third_party/dawn/src/dawn_native/BindGroupTracker.h new file mode 100644 index 00000000000..a3addb27b19 --- /dev/null +++ b/third_party/dawn/src/dawn_native/BindGroupTracker.h @@ -0,0 +1,137 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_BINDGROUPTRACKER_H_ +#define DAWNNATIVE_BINDGROUPTRACKER_H_ + +#include "common/Constants.h" +#include "dawn_native/BindGroupLayout.h" +#include "dawn_native/Pipeline.h" +#include "dawn_native/PipelineLayout.h" + +#include +#include + +namespace dawn_native { + + // Keeps track of the dirty bind groups so they can be lazily applied when we know the + // pipeline state or it changes. + // |DynamicOffset| is a template parameter because offsets in Vulkan are uint32_t but uint64_t + // in other backends. + template + class BindGroupTrackerBase { + public: + void OnSetBindGroup(BindGroupIndex index, + BindGroupBase* bindGroup, + uint32_t dynamicOffsetCount, + uint32_t* dynamicOffsets) { + ASSERT(index < kMaxBindGroupsTyped); + + if (mBindGroupLayoutsMask[index]) { + // It is okay to only dirty bind groups that are used by the current pipeline + // layout. If the pipeline layout changes, then the bind groups it uses will + // become dirty. + + if (mBindGroups[index] != bindGroup) { + mDirtyBindGroups.set(index); + mDirtyBindGroupsObjectChangedOrIsDynamic.set(index); + } + + if (dynamicOffsetCount > 0) { + mDirtyBindGroupsObjectChangedOrIsDynamic.set(index); + } + } + + mBindGroups[index] = bindGroup; + mDynamicOffsetCounts[index] = dynamicOffsetCount; + SetDynamicOffsets(mDynamicOffsets[index].data(), dynamicOffsetCount, dynamicOffsets); + } + + void OnSetPipeline(PipelineBase* pipeline) { + mPipelineLayout = pipeline->GetLayout(); + if (mLastAppliedPipelineLayout == mPipelineLayout) { + return; + } + + // Keep track of the bind group layout mask to avoid marking unused bind groups as + // dirty. This also allows us to avoid computing the intersection of the dirty bind + // groups and bind group layout mask in Draw or Dispatch which is very hot code. + mBindGroupLayoutsMask = mPipelineLayout->GetBindGroupLayoutsMask(); + + // Changing the pipeline layout sets bind groups as dirty. If CanInheritBindGroups, + // the first |k| matching bind groups may be inherited. + if (CanInheritBindGroups && mLastAppliedPipelineLayout != nullptr) { + // Dirty bind groups that cannot be inherited. + BindGroupLayoutMask dirtiedGroups = + ~mPipelineLayout->InheritedGroupsMask(mLastAppliedPipelineLayout); + + mDirtyBindGroups |= dirtiedGroups; + mDirtyBindGroupsObjectChangedOrIsDynamic |= dirtiedGroups; + + // Clear any bind groups not in the mask. + mDirtyBindGroups &= mBindGroupLayoutsMask; + mDirtyBindGroupsObjectChangedOrIsDynamic &= mBindGroupLayoutsMask; + } else { + mDirtyBindGroups = mBindGroupLayoutsMask; + mDirtyBindGroupsObjectChangedOrIsDynamic = mBindGroupLayoutsMask; + } + } + + protected: + // The Derived class should call this when it applies bind groups. + void DidApply() { + // Reset all dirty bind groups. Dirty bind groups not in the bind group layout mask + // will be dirtied again by the next pipeline change. + mDirtyBindGroups.reset(); + mDirtyBindGroupsObjectChangedOrIsDynamic.reset(); + mLastAppliedPipelineLayout = mPipelineLayout; + } + + BindGroupLayoutMask mDirtyBindGroups = 0; + BindGroupLayoutMask mDirtyBindGroupsObjectChangedOrIsDynamic = 0; + BindGroupLayoutMask mBindGroupLayoutsMask = 0; + ityp::array mBindGroups = {}; + ityp::array mDynamicOffsetCounts = {}; + ityp::array, + kMaxBindGroups> + mDynamicOffsets = {}; + + // |mPipelineLayout| is the current pipeline layout set on the command buffer. + // |mLastAppliedPipelineLayout| is the last pipeline layout for which we applied changes + // to the bind group bindings. + PipelineLayoutBase* mPipelineLayout = nullptr; + PipelineLayoutBase* mLastAppliedPipelineLayout = nullptr; + + private: + // We have two overloads here because offsets in Vulkan are uint32_t but uint64_t + // in other backends. + static void SetDynamicOffsets(uint64_t* data, + uint32_t dynamicOffsetCount, + uint32_t* dynamicOffsets) { + for (uint32_t i = 0; i < dynamicOffsetCount; ++i) { + data[i] = static_cast(dynamicOffsets[i]); + } + } + + static void SetDynamicOffsets(uint32_t* data, + uint32_t dynamicOffsetCount, + uint32_t* dynamicOffsets) { + memcpy(data, dynamicOffsets, sizeof(uint32_t) * dynamicOffsetCount); + } + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_BINDGROUPTRACKER_H_ diff --git a/third_party/dawn/src/dawn_native/BindingInfo.cpp b/third_party/dawn/src/dawn_native/BindingInfo.cpp new file mode 100644 index 00000000000..6ade32bb6b2 --- /dev/null +++ b/third_party/dawn/src/dawn_native/BindingInfo.cpp @@ -0,0 +1,137 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/BindingInfo.h" + +namespace dawn_native { + + void IncrementBindingCounts(BindingCounts* bindingCounts, const BindGroupLayoutEntry& entry) { + bindingCounts->totalCount += 1; + + uint32_t PerStageBindingCounts::*perStageBindingCountMember = nullptr; + switch (entry.type) { + case wgpu::BindingType::UniformBuffer: + ++bindingCounts->bufferCount; + if (entry.hasDynamicOffset) { + ++bindingCounts->dynamicUniformBufferCount; + } + if (entry.minBufferBindingSize == 0) { + ++bindingCounts->unverifiedBufferCount; + } + perStageBindingCountMember = &PerStageBindingCounts::uniformBufferCount; + break; + + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + ++bindingCounts->bufferCount; + if (entry.hasDynamicOffset) { + ++bindingCounts->dynamicStorageBufferCount; + } + if (entry.minBufferBindingSize == 0) { + ++bindingCounts->unverifiedBufferCount; + } + perStageBindingCountMember = &PerStageBindingCounts::storageBufferCount; + break; + + case wgpu::BindingType::SampledTexture: + perStageBindingCountMember = &PerStageBindingCounts::sampledTextureCount; + break; + + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + perStageBindingCountMember = &PerStageBindingCounts::samplerCount; + break; + + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + perStageBindingCountMember = &PerStageBindingCounts::storageTextureCount; + break; + + case wgpu::BindingType::StorageTexture: + default: + UNREACHABLE(); + break; + } + + ASSERT(perStageBindingCountMember != nullptr); + for (SingleShaderStage stage : IterateStages(entry.visibility)) { + ++(bindingCounts->perStage[stage].*perStageBindingCountMember); + } + } + + void AccumulateBindingCounts(BindingCounts* bindingCounts, const BindingCounts& rhs) { + bindingCounts->totalCount += rhs.totalCount; + bindingCounts->bufferCount += rhs.bufferCount; + bindingCounts->unverifiedBufferCount += rhs.unverifiedBufferCount; + bindingCounts->dynamicUniformBufferCount += rhs.dynamicUniformBufferCount; + bindingCounts->dynamicStorageBufferCount += rhs.dynamicStorageBufferCount; + + for (SingleShaderStage stage : IterateStages(kAllStages)) { + bindingCounts->perStage[stage].sampledTextureCount += + rhs.perStage[stage].sampledTextureCount; + bindingCounts->perStage[stage].samplerCount += rhs.perStage[stage].samplerCount; + bindingCounts->perStage[stage].storageBufferCount += + rhs.perStage[stage].storageBufferCount; + bindingCounts->perStage[stage].storageTextureCount += + rhs.perStage[stage].storageTextureCount; + bindingCounts->perStage[stage].uniformBufferCount += + rhs.perStage[stage].uniformBufferCount; + } + } + + MaybeError ValidateBindingCounts(const BindingCounts& bindingCounts) { + if (bindingCounts.dynamicUniformBufferCount > kMaxDynamicUniformBuffersPerPipelineLayout) { + return DAWN_VALIDATION_ERROR( + "The number of dynamic uniform buffers exceeds the maximum per-pipeline-layout " + "limit"); + } + + if (bindingCounts.dynamicStorageBufferCount > kMaxDynamicStorageBuffersPerPipelineLayout) { + return DAWN_VALIDATION_ERROR( + "The number of dynamic storage buffers exceeds the maximum per-pipeline-layout " + "limit"); + } + + for (SingleShaderStage stage : IterateStages(kAllStages)) { + if (bindingCounts.perStage[stage].sampledTextureCount > + kMaxSampledTexturesPerShaderStage) { + return DAWN_VALIDATION_ERROR( + "The number of sampled textures exceeds the maximum " + "per-stage limit."); + } + if (bindingCounts.perStage[stage].samplerCount > kMaxSamplersPerShaderStage) { + return DAWN_VALIDATION_ERROR( + "The number of samplers exceeds the maximum per-stage limit."); + } + if (bindingCounts.perStage[stage].storageBufferCount > + kMaxStorageBuffersPerShaderStage) { + return DAWN_VALIDATION_ERROR( + "The number of storage buffers exceeds the maximum per-stage limit."); + } + if (bindingCounts.perStage[stage].storageTextureCount > + kMaxStorageTexturesPerShaderStage) { + return DAWN_VALIDATION_ERROR( + "The number of storage textures exceeds the maximum per-stage limit."); + } + if (bindingCounts.perStage[stage].uniformBufferCount > + kMaxUniformBuffersPerShaderStage) { + return DAWN_VALIDATION_ERROR( + "The number of uniform buffers exceeds the maximum per-stage limit."); + } + } + + return {}; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/BindingInfo.h b/third_party/dawn/src/dawn_native/BindingInfo.h new file mode 100644 index 00000000000..b6cfad15618 --- /dev/null +++ b/third_party/dawn/src/dawn_native/BindingInfo.h @@ -0,0 +1,99 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_BINDINGINFO_H_ +#define DAWNNATIVE_BINDINGINFO_H_ + +#include "common/Constants.h" +#include "common/TypedInteger.h" +#include "common/ityp_array.h" +#include "dawn_native/Error.h" +#include "dawn_native/Format.h" +#include "dawn_native/PerStage.h" + +#include "dawn_native/dawn_platform.h" + +#include + +namespace dawn_native { + + // Binding numbers in the shader and BindGroup/BindGroupLayoutDescriptors + using BindingNumber = TypedInteger; + + // Binding numbers get mapped to a packed range of indices + using BindingIndex = TypedInteger; + + using BindGroupIndex = TypedInteger; + + static constexpr BindGroupIndex kMaxBindGroupsTyped = BindGroupIndex(kMaxBindGroups); + + // Not a real WebGPU limit, but the sum of the two limits is useful for internal optimizations. + static constexpr uint32_t kMaxDynamicBuffersPerPipelineLayout = + kMaxDynamicUniformBuffersPerPipelineLayout + kMaxDynamicStorageBuffersPerPipelineLayout; + + static constexpr BindingIndex kMaxDynamicBuffersPerPipelineLayoutTyped = + BindingIndex(kMaxDynamicBuffersPerPipelineLayout); + + // Not a real WebGPU limit, but used to optimize parts of Dawn which expect valid usage of the + // API. There should never be more bindings than the max per stage, for each stage. + static constexpr uint32_t kMaxBindingsPerPipelineLayout = + 3 * (kMaxSampledTexturesPerShaderStage + kMaxSamplersPerShaderStage + + kMaxStorageBuffersPerShaderStage + kMaxStorageTexturesPerShaderStage + + kMaxUniformBuffersPerShaderStage); + + static constexpr BindingIndex kMaxBindingsPerPipelineLayoutTyped = + BindingIndex(kMaxBindingsPerPipelineLayout); + + // TODO(enga): Figure out a good number for this. + static constexpr uint32_t kMaxOptimalBindingsPerGroup = 32; + + struct BindingInfo { + BindingNumber binding; + wgpu::ShaderStage visibility; + wgpu::BindingType type; + Format::Type textureComponentType = Format::Type::Float; + wgpu::TextureViewDimension viewDimension = wgpu::TextureViewDimension::Undefined; + wgpu::TextureFormat storageTextureFormat = wgpu::TextureFormat::Undefined; + bool hasDynamicOffset = false; + bool multisampled = false; + uint64_t minBufferBindingSize = 0; + }; + + struct PerStageBindingCounts { + uint32_t sampledTextureCount; + uint32_t samplerCount; + uint32_t storageBufferCount; + uint32_t storageTextureCount; + uint32_t uniformBufferCount; + }; + + struct BindingCounts { + uint32_t totalCount; + uint32_t bufferCount; + uint32_t unverifiedBufferCount; // Buffers with minimum buffer size unspecified + uint32_t dynamicUniformBufferCount; + uint32_t dynamicStorageBufferCount; + PerStage perStage; + }; + + void IncrementBindingCounts(BindingCounts* bindingCounts, const BindGroupLayoutEntry& entry); + void AccumulateBindingCounts(BindingCounts* bindingCounts, const BindingCounts& rhs); + MaybeError ValidateBindingCounts(const BindingCounts& bindingCounts); + + // For buffer size validation + using RequiredBufferSizes = ityp::array, kMaxBindGroups>; + +} // namespace dawn_native + +#endif // DAWNNATIVE_BINDINGINFO_H_ diff --git a/third_party/dawn/src/dawn_native/BuddyAllocator.cpp b/third_party/dawn/src/dawn_native/BuddyAllocator.cpp new file mode 100644 index 00000000000..60a8b9b446a --- /dev/null +++ b/third_party/dawn/src/dawn_native/BuddyAllocator.cpp @@ -0,0 +1,264 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/BuddyAllocator.h" + +#include "common/Assert.h" +#include "common/Math.h" + +namespace dawn_native { + + BuddyAllocator::BuddyAllocator(uint64_t maxSize) : mMaxBlockSize(maxSize) { + ASSERT(IsPowerOfTwo(maxSize)); + + mFreeLists.resize(Log2(mMaxBlockSize) + 1); + + // Insert the level0 free block. + mRoot = new BuddyBlock(maxSize, /*offset*/ 0); + mFreeLists[0] = {mRoot}; + } + + BuddyAllocator::~BuddyAllocator() { + if (mRoot) { + DeleteBlock(mRoot); + } + } + + uint64_t BuddyAllocator::ComputeTotalNumOfFreeBlocksForTesting() const { + return ComputeNumOfFreeBlocks(mRoot); + } + + uint64_t BuddyAllocator::ComputeNumOfFreeBlocks(BuddyBlock* block) const { + if (block->mState == BlockState::Free) { + return 1; + } else if (block->mState == BlockState::Split) { + return ComputeNumOfFreeBlocks(block->split.pLeft) + + ComputeNumOfFreeBlocks(block->split.pLeft->pBuddy); + } + return 0; + } + + uint32_t BuddyAllocator::ComputeLevelFromBlockSize(uint64_t blockSize) const { + // Every level in the buddy system can be indexed by order-n where n = log2(blockSize). + // However, mFreeList zero-indexed by level. + // For example, blockSize=4 is Level1 if MAX_BLOCK is 8. + return Log2(mMaxBlockSize) - Log2(blockSize); + } + + uint64_t BuddyAllocator::GetNextFreeAlignedBlock(size_t allocationBlockLevel, + uint64_t alignment) const { + ASSERT(IsPowerOfTwo(alignment)); + // The current level is the level that corresponds to the allocation size. The free list may + // not contain a block at that level until a larger one gets allocated (and splits). + // Continue to go up the tree until such a larger block exists. + // + // Even if the block exists at the level, it cannot be used if it's offset is unaligned. + // When the alignment is also a power-of-two, we simply use the next free block whose size + // is greater than or equal to the alignment value. + // + // After one 8-byte allocation: + // + // Level -------------------------------- + // 0 32 | S | + // -------------------------------- + // 1 16 | S | F2 | S - split + // -------------------------------- F - free + // 2 8 | Aa | F1 | | A - allocated + // -------------------------------- + // + // Allocate(size=8, alignment=8) will be satisfied by using F1. + // Allocate(size=8, alignment=4) will be satified by using F1. + // Allocate(size=8, alignment=16) will be satisified by using F2. + // + for (size_t ii = 0; ii <= allocationBlockLevel; ++ii) { + size_t currLevel = allocationBlockLevel - ii; + BuddyBlock* freeBlock = mFreeLists[currLevel].head; + if (freeBlock && (freeBlock->mOffset % alignment == 0)) { + return currLevel; + } + } + return kInvalidOffset; // No free block exists at any level. + } + + // Inserts existing free block into the free-list. + // Called by allocate upon splitting to insert a child block into a free-list. + // Note: Always insert into the head of the free-list. As when a larger free block at a lower + // level was split, there were no smaller free blocks at a higher level to allocate. + void BuddyAllocator::InsertFreeBlock(BuddyBlock* block, size_t level) { + ASSERT(block->mState == BlockState::Free); + + // Inserted block is now the front (no prev). + block->free.pPrev = nullptr; + + // Old head is now the inserted block's next. + block->free.pNext = mFreeLists[level].head; + + // Block already in HEAD position (ex. right child was inserted first). + if (mFreeLists[level].head != nullptr) { + // Old head's previous is the inserted block. + mFreeLists[level].head->free.pPrev = block; + } + + mFreeLists[level].head = block; + } + + void BuddyAllocator::RemoveFreeBlock(BuddyBlock* block, size_t level) { + ASSERT(block->mState == BlockState::Free); + + if (mFreeLists[level].head == block) { + // Block is in HEAD position. + mFreeLists[level].head = mFreeLists[level].head->free.pNext; + } else { + // Block is after HEAD position. + BuddyBlock* pPrev = block->free.pPrev; + BuddyBlock* pNext = block->free.pNext; + + ASSERT(pPrev != nullptr); + ASSERT(pPrev->mState == BlockState::Free); + + pPrev->free.pNext = pNext; + + if (pNext != nullptr) { + ASSERT(pNext->mState == BlockState::Free); + pNext->free.pPrev = pPrev; + } + } + } + + uint64_t BuddyAllocator::Allocate(uint64_t allocationSize, uint64_t alignment) { + if (allocationSize == 0 || allocationSize > mMaxBlockSize) { + return kInvalidOffset; + } + + // Compute the level + const uint32_t allocationSizeToLevel = ComputeLevelFromBlockSize(allocationSize); + + ASSERT(allocationSizeToLevel < mFreeLists.size()); + + uint64_t currBlockLevel = GetNextFreeAlignedBlock(allocationSizeToLevel, alignment); + + // Error when no free blocks exist (allocator is full) + if (currBlockLevel == kInvalidOffset) { + return kInvalidOffset; + } + + // Split free blocks level-by-level. + // Terminate when the current block level is equal to the computed level of the requested + // allocation. + BuddyBlock* currBlock = mFreeLists[currBlockLevel].head; + + for (; currBlockLevel < allocationSizeToLevel; currBlockLevel++) { + ASSERT(currBlock->mState == BlockState::Free); + + // Remove curr block (about to be split). + RemoveFreeBlock(currBlock, currBlockLevel); + + // Create two free child blocks (the buddies). + const uint64_t nextLevelSize = currBlock->mSize / 2; + BuddyBlock* leftChildBlock = new BuddyBlock(nextLevelSize, currBlock->mOffset); + BuddyBlock* rightChildBlock = + new BuddyBlock(nextLevelSize, currBlock->mOffset + nextLevelSize); + + // Remember the parent to merge these back upon de-allocation. + rightChildBlock->pParent = currBlock; + leftChildBlock->pParent = currBlock; + + // Make them buddies. + leftChildBlock->pBuddy = rightChildBlock; + rightChildBlock->pBuddy = leftChildBlock; + + // Insert the children back into the free list into the next level. + // The free list does not require a specific order. However, an order is specified as + // it's ideal to allocate lower addresses first by having the leftmost child in HEAD. + InsertFreeBlock(rightChildBlock, currBlockLevel + 1); + InsertFreeBlock(leftChildBlock, currBlockLevel + 1); + + // Curr block is now split. + currBlock->mState = BlockState::Split; + currBlock->split.pLeft = leftChildBlock; + + // Decend down into the next level. + currBlock = leftChildBlock; + } + + // Remove curr block from free-list (now allocated). + RemoveFreeBlock(currBlock, currBlockLevel); + currBlock->mState = BlockState::Allocated; + + return currBlock->mOffset; + } + + void BuddyAllocator::Deallocate(uint64_t offset) { + BuddyBlock* curr = mRoot; + + // TODO(bryan.bernhart@intel.com): Optimize de-allocation. + // Passing allocationSize directly will avoid the following level-by-level search; + // however, it requires the size information to be stored outside the allocator. + + // Search for the free block node that corresponds to the block offset. + size_t currBlockLevel = 0; + while (curr->mState == BlockState::Split) { + if (offset < curr->split.pLeft->pBuddy->mOffset) { + curr = curr->split.pLeft; + } else { + curr = curr->split.pLeft->pBuddy; + } + + currBlockLevel++; + } + + ASSERT(curr->mState == BlockState::Allocated); + + // Ensure the block is at the correct level + ASSERT(currBlockLevel == ComputeLevelFromBlockSize(curr->mSize)); + + // Mark curr free so we can merge. + curr->mState = BlockState::Free; + + // Merge the buddies (LevelN-to-Level0). + while (currBlockLevel > 0 && curr->pBuddy->mState == BlockState::Free) { + // Remove the buddy. + RemoveFreeBlock(curr->pBuddy, currBlockLevel); + + BuddyBlock* parent = curr->pParent; + + // The buddies were inserted in a specific order but + // could be deleted in any order. + DeleteBlock(curr->pBuddy); + DeleteBlock(curr); + + // Parent is now free. + parent->mState = BlockState::Free; + + // Ascend up to the next level (parent block). + curr = parent; + currBlockLevel--; + } + + InsertFreeBlock(curr, currBlockLevel); + } + + // Helper which deletes a block in the tree recursively (post-order). + void BuddyAllocator::DeleteBlock(BuddyBlock* block) { + ASSERT(block != nullptr); + + if (block->mState == BlockState::Split) { + // Delete the pair in same order we inserted. + DeleteBlock(block->split.pLeft->pBuddy); + DeleteBlock(block->split.pLeft); + } + delete block; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/BuddyAllocator.h b/third_party/dawn/src/dawn_native/BuddyAllocator.h new file mode 100644 index 00000000000..84041588715 --- /dev/null +++ b/third_party/dawn/src/dawn_native/BuddyAllocator.h @@ -0,0 +1,117 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_BUDDYALLOCATOR_H_ +#define DAWNNATIVE_BUDDYALLOCATOR_H_ + +#include +#include +#include +#include + +namespace dawn_native { + + // Buddy allocator uses the buddy memory allocation technique to satisfy an allocation request. + // Memory is split into halves until just large enough to fit to the request. This + // requires the allocation size to be a power-of-two value. The allocator "allocates" a block by + // returning the starting offset whose size is guaranteed to be greater than or equal to the + // allocation size. To deallocate, the same offset is used to find the corresponding block. + // + // Internally, it manages a free list to track free blocks in a full binary tree. + // Every index in the free list corresponds to a level in the tree. That level also determines + // the size of the block to be used to satisfy the request. The first level (index=0) represents + // the root whose size is also called the max block size. + // + class BuddyAllocator { + public: + BuddyAllocator(uint64_t maxSize); + ~BuddyAllocator(); + + // Required methods. + uint64_t Allocate(uint64_t allocationSize, uint64_t alignment = 1); + void Deallocate(uint64_t offset); + + // For testing purposes only. + uint64_t ComputeTotalNumOfFreeBlocksForTesting() const; + + static constexpr uint64_t kInvalidOffset = std::numeric_limits::max(); + + private: + uint32_t ComputeLevelFromBlockSize(uint64_t blockSize) const; + uint64_t GetNextFreeAlignedBlock(size_t allocationBlockLevel, uint64_t alignment) const; + + enum class BlockState { Free, Split, Allocated }; + + struct BuddyBlock { + BuddyBlock(uint64_t size, uint64_t offset) + : mOffset(offset), mSize(size), mState(BlockState::Free) { + free.pPrev = nullptr; + free.pNext = nullptr; + } + + uint64_t mOffset; + uint64_t mSize; + + // Pointer to this block's buddy, iff parent is split. + // Used to quickly merge buddy blocks upon de-allocate. + BuddyBlock* pBuddy = nullptr; + BuddyBlock* pParent = nullptr; + + // Track whether this block has been split or not. + BlockState mState; + + struct FreeLinks { + BuddyBlock* pPrev; + BuddyBlock* pNext; + }; + + struct SplitLink { + BuddyBlock* pLeft; + }; + + union { + // Used upon allocation. + // Avoids searching for the next free block. + FreeLinks free; + + // Used upon de-allocation. + // Had this block split upon allocation, it and it's buddy is to be deleted. + SplitLink split; + }; + }; + + void InsertFreeBlock(BuddyBlock* block, size_t level); + void RemoveFreeBlock(BuddyBlock* block, size_t level); + void DeleteBlock(BuddyBlock* block); + + uint64_t ComputeNumOfFreeBlocks(BuddyBlock* block) const; + + // Keep track the head and tail (for faster insertion/removal). + struct BlockList { + BuddyBlock* head = nullptr; // First free block in level. + // TODO(bryan.bernhart@intel.com): Track the tail. + }; + + BuddyBlock* mRoot = nullptr; // Used to deallocate non-free blocks. + + uint64_t mMaxBlockSize = 0; + + // List of linked-lists of free blocks where the index is a level that + // corresponds to a power-of-two sized block. + std::vector mFreeLists; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_BUDDYALLOCATOR_H_ diff --git a/third_party/dawn/src/dawn_native/BuddyMemoryAllocator.cpp b/third_party/dawn/src/dawn_native/BuddyMemoryAllocator.cpp new file mode 100644 index 00000000000..eb7320c56a2 --- /dev/null +++ b/third_party/dawn/src/dawn_native/BuddyMemoryAllocator.cpp @@ -0,0 +1,120 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/BuddyMemoryAllocator.h" + +#include "common/Math.h" +#include "dawn_native/ResourceHeapAllocator.h" + +namespace dawn_native { + + BuddyMemoryAllocator::BuddyMemoryAllocator(uint64_t maxSystemSize, + uint64_t memoryBlockSize, + ResourceHeapAllocator* heapAllocator) + : mMemoryBlockSize(memoryBlockSize), + mBuddyBlockAllocator(maxSystemSize), + mHeapAllocator(heapAllocator) { + ASSERT(memoryBlockSize <= maxSystemSize); + ASSERT(IsPowerOfTwo(mMemoryBlockSize)); + ASSERT(maxSystemSize % mMemoryBlockSize == 0); + + mTrackedSubAllocations.resize(maxSystemSize / mMemoryBlockSize); + } + + uint64_t BuddyMemoryAllocator::GetMemoryIndex(uint64_t offset) const { + ASSERT(offset != BuddyAllocator::kInvalidOffset); + return offset / mMemoryBlockSize; + } + + ResultOrError BuddyMemoryAllocator::Allocate(uint64_t allocationSize, + uint64_t alignment) { + ResourceMemoryAllocation invalidAllocation = ResourceMemoryAllocation{}; + + if (allocationSize == 0) { + return std::move(invalidAllocation); + } + + // Check the unaligned size to avoid overflowing NextPowerOfTwo. + if (allocationSize > mMemoryBlockSize) { + return std::move(invalidAllocation); + } + + // Round allocation size to nearest power-of-two. + allocationSize = NextPowerOfTwo(allocationSize); + + // Allocation cannot exceed the memory size. + if (allocationSize > mMemoryBlockSize) { + return std::move(invalidAllocation); + } + + // Attempt to sub-allocate a block of the requested size. + const uint64_t blockOffset = mBuddyBlockAllocator.Allocate(allocationSize, alignment); + if (blockOffset == BuddyAllocator::kInvalidOffset) { + return std::move(invalidAllocation); + } + + const uint64_t memoryIndex = GetMemoryIndex(blockOffset); + if (mTrackedSubAllocations[memoryIndex].refcount == 0) { + // Transfer ownership to this allocator + std::unique_ptr memory; + DAWN_TRY_ASSIGN(memory, mHeapAllocator->AllocateResourceHeap(mMemoryBlockSize)); + mTrackedSubAllocations[memoryIndex] = {/*refcount*/ 0, std::move(memory)}; + } + + mTrackedSubAllocations[memoryIndex].refcount++; + + AllocationInfo info; + info.mBlockOffset = blockOffset; + info.mMethod = AllocationMethod::kSubAllocated; + + // Allocation offset is always local to the memory. + const uint64_t memoryOffset = blockOffset % mMemoryBlockSize; + + return ResourceMemoryAllocation{ + info, memoryOffset, mTrackedSubAllocations[memoryIndex].mMemoryAllocation.get()}; + } + + void BuddyMemoryAllocator::Deallocate(const ResourceMemoryAllocation& allocation) { + const AllocationInfo info = allocation.GetInfo(); + + ASSERT(info.mMethod == AllocationMethod::kSubAllocated); + + const uint64_t memoryIndex = GetMemoryIndex(info.mBlockOffset); + + ASSERT(mTrackedSubAllocations[memoryIndex].refcount > 0); + mTrackedSubAllocations[memoryIndex].refcount--; + + if (mTrackedSubAllocations[memoryIndex].refcount == 0) { + mHeapAllocator->DeallocateResourceHeap( + std::move(mTrackedSubAllocations[memoryIndex].mMemoryAllocation)); + } + + mBuddyBlockAllocator.Deallocate(info.mBlockOffset); + } + + uint64_t BuddyMemoryAllocator::GetMemoryBlockSize() const { + return mMemoryBlockSize; + } + + uint64_t BuddyMemoryAllocator::ComputeTotalNumOfHeapsForTesting() const { + uint64_t count = 0; + for (const TrackedSubAllocations& allocation : mTrackedSubAllocations) { + if (allocation.refcount > 0) { + count++; + } + } + return count; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/BuddyMemoryAllocator.h b/third_party/dawn/src/dawn_native/BuddyMemoryAllocator.h new file mode 100644 index 00000000000..c057f748223 --- /dev/null +++ b/third_party/dawn/src/dawn_native/BuddyMemoryAllocator.h @@ -0,0 +1,74 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_BUDDYMEMORYALLOCATOR_H_ +#define DAWNNATIVE_BUDDYMEMORYALLOCATOR_H_ + +#include "dawn_native/BuddyAllocator.h" +#include "dawn_native/Error.h" +#include "dawn_native/ResourceMemoryAllocation.h" + +#include +#include + +namespace dawn_native { + + class ResourceHeapAllocator; + + // BuddyMemoryAllocator uses the buddy allocator to sub-allocate blocks of device + // memory created by MemoryAllocator clients. It creates a very large buddy system + // where backing device memory blocks equal a specified level in the system. + // + // Upon sub-allocating, the offset gets mapped to device memory by computing the corresponding + // memory index and should the memory not exist, it is created. If two sub-allocations share the + // same memory index, the memory refcount is incremented to ensure de-allocating one doesn't + // release the other prematurely. + // + // The MemoryAllocator should return ResourceHeaps that are all compatible with each other. + // It should also outlive all the resources that are in the buddy allocator. + class BuddyMemoryAllocator { + public: + BuddyMemoryAllocator(uint64_t maxSystemSize, + uint64_t memoryBlockSize, + ResourceHeapAllocator* heapAllocator); + ~BuddyMemoryAllocator() = default; + + ResultOrError Allocate(uint64_t allocationSize, + uint64_t alignment); + void Deallocate(const ResourceMemoryAllocation& allocation); + + uint64_t GetMemoryBlockSize() const; + + // For testing purposes. + uint64_t ComputeTotalNumOfHeapsForTesting() const; + + private: + uint64_t GetMemoryIndex(uint64_t offset) const; + + uint64_t mMemoryBlockSize = 0; + + BuddyAllocator mBuddyBlockAllocator; + ResourceHeapAllocator* mHeapAllocator; + + struct TrackedSubAllocations { + size_t refcount = 0; + std::unique_ptr mMemoryAllocation; + }; + + std::vector mTrackedSubAllocations; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_BUDDYMEMORYALLOCATOR_H_ diff --git a/third_party/dawn/src/dawn_native/Buffer.cpp b/third_party/dawn/src/dawn_native/Buffer.cpp index dd568f5aa89..0cd6eae1756 100644 --- a/third_party/dawn/src/dawn_native/Buffer.cpp +++ b/third_party/dawn/src/dawn_native/Buffer.cpp @@ -15,33 +15,39 @@ #include "dawn_native/Buffer.h" #include "common/Assert.h" +#include "dawn_native/Commands.h" #include "dawn_native/Device.h" #include "dawn_native/DynamicUploader.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/MapRequestTracker.h" +#include "dawn_native/Queue.h" #include "dawn_native/ValidationUtils_autogen.h" #include +#include #include namespace dawn_native { namespace { - class ErrorBuffer : public BufferBase { + class ErrorBuffer final : public BufferBase { public: - ErrorBuffer(DeviceBase* device) : BufferBase(device, ObjectBase::kError) { - } - - static ErrorBuffer* MakeMapped(DeviceBase* device, - uint64_t size, - uint8_t** mappedPointer) { - ASSERT(mappedPointer != nullptr); - - ErrorBuffer* buffer = new ErrorBuffer(device); - buffer->mFakeMappedData = - std::unique_ptr(new (std::nothrow) uint8_t[size]); - *mappedPointer = buffer->mFakeMappedData.get(); - - return buffer; + ErrorBuffer(DeviceBase* device, const BufferDescriptor* descriptor) + : BufferBase(device, descriptor, ObjectBase::kError) { + if (descriptor->mappedAtCreation) { + // Check that the size can be used to allocate an mFakeMappedData. A malloc(0) + // is invalid, and on 32bit systems we should avoid a narrowing conversion that + // would make size = 1 << 32 + 1 allocate one byte. + bool isValidSize = + descriptor->size != 0 && + descriptor->size < uint64_t(std::numeric_limits::max()); + + if (isValidSize) { + mFakeMappedData = std::unique_ptr(new (std::nothrow) + uint8_t[descriptor->size]); + } + } } void ClearMappedData() { @@ -49,25 +55,30 @@ namespace dawn_native { } private: - bool IsMapWritable() const override { + bool IsMappableAtCreation() const override { UNREACHABLE(); return false; } - MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override { + MaybeError MapAtCreationImpl() override { UNREACHABLE(); return {}; } - MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const void* data) override { + MaybeError MapReadAsyncImpl() override { UNREACHABLE(); return {}; } - void MapReadAsyncImpl(uint32_t serial) override { + MaybeError MapWriteAsyncImpl() override { UNREACHABLE(); + return {}; } - void MapWriteAsyncImpl(uint32_t serial) override { + MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override { UNREACHABLE(); + return {}; + } + void* GetMappedPointerImpl() override { + return mFakeMappedData.get(); } void UnmapImpl() override { UNREACHABLE(); @@ -86,20 +97,24 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - DAWN_TRY(ValidateBufferUsageBit(descriptor->usage)); + DAWN_TRY(ValidateBufferUsage(descriptor->usage)); - dawn::BufferUsageBit usage = descriptor->usage; + wgpu::BufferUsage usage = descriptor->usage; - const dawn::BufferUsageBit kMapWriteAllowedUsages = - dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - if (usage & dawn::BufferUsageBit::MapWrite && (usage & kMapWriteAllowedUsages) != usage) { - return DAWN_VALIDATION_ERROR("Only TransferSrc is allowed with MapWrite"); + const wgpu::BufferUsage kMapWriteAllowedUsages = + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + if (usage & wgpu::BufferUsage::MapWrite && (usage & kMapWriteAllowedUsages) != usage) { + return DAWN_VALIDATION_ERROR("Only CopySrc is allowed with MapWrite"); } - const dawn::BufferUsageBit kMapReadAllowedUsages = - dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferDst; - if (usage & dawn::BufferUsageBit::MapRead && (usage & kMapReadAllowedUsages) != usage) { - return DAWN_VALIDATION_ERROR("Only TransferDst is allowed with MapRead"); + const wgpu::BufferUsage kMapReadAllowedUsages = + wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + if (usage & wgpu::BufferUsage::MapRead && (usage & kMapReadAllowedUsages) != usage) { + return DAWN_VALIDATION_ERROR("Only CopyDst is allowed with MapRead"); + } + + if (descriptor->mappedAtCreation && descriptor->size % 4 != 0) { + return DAWN_VALIDATION_ERROR("size must be aligned to 4 when mappedAtCreation is true"); } return {}; @@ -112,30 +127,36 @@ namespace dawn_native { mSize(descriptor->size), mUsage(descriptor->usage), mState(BufferState::Unmapped) { + // Add readonly storage usage if the buffer has a storage usage. The validation rules in + // ValidatePassResourceUsage will make sure we don't use both at the same + // time. + if (mUsage & wgpu::BufferUsage::Storage) { + mUsage |= kReadOnlyStorageBuffer; + } } - BufferBase::BufferBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag), mState(BufferState::Unmapped) { + BufferBase::BufferBase(DeviceBase* device, + const BufferDescriptor* descriptor, + ObjectBase::ErrorTag tag) + : ObjectBase(device, tag), mSize(descriptor->size), mState(BufferState::Unmapped) { + if (descriptor->mappedAtCreation) { + mState = BufferState::MappedAtCreation; + mMapOffset = 0; + mMapSize = mSize; + } } BufferBase::~BufferBase() { if (mState == BufferState::Mapped) { ASSERT(!IsError()); - CallMapReadCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u); - CallMapWriteCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u); + CallMapReadCallback(mMapSerial, WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u); + CallMapWriteCallback(mMapSerial, WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u); } } // static - BufferBase* BufferBase::MakeError(DeviceBase* device) { - return new ErrorBuffer(device); - } - - // static - BufferBase* BufferBase::MakeErrorMapped(DeviceBase* device, - uint64_t size, - uint8_t** mappedPointer) { - return ErrorBuffer::MakeMapped(device, size, mappedPointer); + BufferBase* BufferBase::MakeError(DeviceBase* device, const BufferDescriptor* descriptor) { + return new ErrorBuffer(device, descriptor); } uint64_t BufferBase::GetSize() const { @@ -143,20 +164,26 @@ namespace dawn_native { return mSize; } - dawn::BufferUsageBit BufferBase::GetUsage() const { + wgpu::BufferUsage BufferBase::GetUsage() const { ASSERT(!IsError()); return mUsage; } - MaybeError BufferBase::MapAtCreation(uint8_t** mappedPointer) { + MaybeError BufferBase::MapAtCreation() { ASSERT(!IsError()); - ASSERT(mappedPointer != nullptr); + mState = BufferState::MappedAtCreation; + mMapOffset = 0; + mMapSize = mSize; - mState = BufferState::Mapped; + // 0-sized buffers are not supposed to be written to, Return back any non-null pointer. + // Handle 0-sized buffers first so we don't try to map them in the backend. + if (mSize == 0) { + return {}; + } - if (IsMapWritable()) { - DAWN_TRY(MapAtCreationImpl(mappedPointer)); - ASSERT(*mappedPointer != nullptr); + // Mappable buffers don't use a staging buffer and are just as if mapped through MapAsync. + if (IsMappableAtCreation()) { + DAWN_TRY(MapAtCreationImpl()); return {}; } @@ -164,73 +191,103 @@ namespace dawn_native { // error buffer. // TODO(enga): Suballocate and reuse memory from a larger staging buffer so we don't create // many small buffers. - DynamicUploader* uploader = nullptr; - DAWN_TRY_ASSIGN(uploader, GetDevice()->GetDynamicUploader()); - DAWN_TRY_ASSIGN(mStagingBuffer, uploader->CreateStagingBuffer(GetSize())); - - ASSERT(mStagingBuffer->GetMappedPointer() != nullptr); - *mappedPointer = reinterpret_cast(mStagingBuffer->GetMappedPointer()); + DAWN_TRY_ASSIGN(mStagingBuffer, GetDevice()->CreateStagingBuffer(GetSize())); return {}; } - MaybeError BufferBase::ValidateCanUseInSubmitNow() const { + MaybeError BufferBase::ValidateCanUseOnQueueNow() const { ASSERT(!IsError()); switch (mState) { case BufferState::Destroyed: return DAWN_VALIDATION_ERROR("Destroyed buffer used in a submit"); case BufferState::Mapped: + case BufferState::MappedAtCreation: return DAWN_VALIDATION_ERROR("Buffer used in a submit while mapped"); case BufferState::Unmapped: return {}; + default: + UNREACHABLE(); } } void BufferBase::CallMapReadCallback(uint32_t serial, - DawnBufferMapAsyncStatus status, + WGPUBufferMapAsyncStatus status, const void* pointer, - uint32_t dataLength) { + uint64_t dataLength) { ASSERT(!IsError()); if (mMapReadCallback != nullptr && serial == mMapSerial) { ASSERT(mMapWriteCallback == nullptr); + // Tag the callback as fired before firing it, otherwise it could fire a second time if // for example buffer.Unmap() is called inside the application-provided callback. - DawnBufferMapReadCallback callback = mMapReadCallback; + WGPUBufferMapReadCallback callback = mMapReadCallback; mMapReadCallback = nullptr; - callback(status, pointer, dataLength, mMapUserdata); + + if (GetDevice()->IsLost()) { + callback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, mMapUserdata); + } else { + callback(status, pointer, dataLength, mMapUserdata); + } } } void BufferBase::CallMapWriteCallback(uint32_t serial, - DawnBufferMapAsyncStatus status, + WGPUBufferMapAsyncStatus status, void* pointer, - uint32_t dataLength) { + uint64_t dataLength) { ASSERT(!IsError()); if (mMapWriteCallback != nullptr && serial == mMapSerial) { ASSERT(mMapReadCallback == nullptr); + // Tag the callback as fired before firing it, otherwise it could fire a second time if // for example buffer.Unmap() is called inside the application-provided callback. - DawnBufferMapWriteCallback callback = mMapWriteCallback; + WGPUBufferMapWriteCallback callback = mMapWriteCallback; mMapWriteCallback = nullptr; - callback(status, pointer, dataLength, mMapUserdata); + + if (GetDevice()->IsLost()) { + callback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, mMapUserdata); + } else { + callback(status, pointer, dataLength, mMapUserdata); + } } } - void BufferBase::SetSubData(uint32_t start, uint32_t count, const void* data) { - if (GetDevice()->ConsumedError(ValidateSetSubData(start, count))) { - return; - } + void BufferBase::CallMapCallback(uint32_t serial, WGPUBufferMapAsyncStatus status) { ASSERT(!IsError()); + if (mMapCallback != nullptr && serial == mMapSerial) { + // Tag the callback as fired before firing it, otherwise it could fire a second time if + // for example buffer.Unmap() is called inside the application-provided callback. + WGPUBufferMapCallback callback = mMapCallback; + mMapCallback = nullptr; - if (GetDevice()->ConsumedError(SetSubDataImpl(start, count, data))) { - return; + if (GetDevice()->IsLost()) { + callback(WGPUBufferMapAsyncStatus_DeviceLost, mMapUserdata); + } else { + callback(status, mMapUserdata); + } + } + } + + void BufferBase::SetSubData(uint64_t start, uint64_t count, const void* data) { + if (count > uint64_t(std::numeric_limits::max())) { + GetDevice()->HandleError(InternalErrorType::Validation, "count too big"); } + + Ref queue = AcquireRef(GetDevice()->GetDefaultQueue()); + GetDevice()->EmitDeprecationWarning( + "Buffer::SetSubData is deprecate. Use Queue::WriteBuffer instead"); + queue->WriteBuffer(this, start, data, static_cast(count)); } - void BufferBase::MapReadAsync(DawnBufferMapReadCallback callback, void* userdata) { - if (GetDevice()->ConsumedError(ValidateMap(dawn::BufferUsageBit::MapRead))) { - callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, userdata); + void BufferBase::MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata) { + GetDevice()->EmitDeprecationWarning( + "Buffer::MapReadAsync is deprecated. Use Buffer::MapAsync instead"); + + WGPUBufferMapAsyncStatus status; + if (GetDevice()->ConsumedError(ValidateMap(wgpu::BufferUsage::MapRead, &status))) { + callback(status, nullptr, 0, userdata); return; } ASSERT(!IsError()); @@ -241,54 +298,118 @@ namespace dawn_native { mMapSerial++; mMapReadCallback = callback; mMapUserdata = userdata; + mMapOffset = 0; + mMapSize = mSize; mState = BufferState::Mapped; - MapReadAsyncImpl(mMapSerial); + if (GetDevice()->ConsumedError(MapReadAsyncImpl())) { + CallMapReadCallback(mMapSerial, WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0); + return; + } + + MapRequestTracker* tracker = GetDevice()->GetMapRequestTracker(); + tracker->Track(this, mMapSerial, MapType::Read); } - MaybeError BufferBase::SetSubDataImpl(uint32_t start, uint32_t count, const void* data) { - DynamicUploader* uploader = nullptr; - DAWN_TRY_ASSIGN(uploader, GetDevice()->GetDynamicUploader()); + void BufferBase::MapWriteAsync(WGPUBufferMapWriteCallback callback, void* userdata) { + GetDevice()->EmitDeprecationWarning( + "Buffer::MapReadAsync is deprecated. Use Buffer::MapAsync instead"); - // TODO(bryan.bernhart@intel.com): Remove once alignment constraint is added to validation - // (dawn:73). D3D12 does not specify so we assume 4-byte alignment to be safe. - static constexpr size_t kDefaultAlignment = 4; + WGPUBufferMapAsyncStatus status; + if (GetDevice()->ConsumedError(ValidateMap(wgpu::BufferUsage::MapWrite, &status))) { + callback(status, nullptr, 0, userdata); + return; + } + ASSERT(!IsError()); - UploadHandle uploadHandle; - DAWN_TRY_ASSIGN(uploadHandle, uploader->Allocate(count, kDefaultAlignment)); - ASSERT(uploadHandle.mappedBuffer != nullptr); + ASSERT(mMapReadCallback == nullptr); - memcpy(uploadHandle.mappedBuffer, data, count); + // TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes. + mMapSerial++; + mMapWriteCallback = callback; + mMapUserdata = userdata; + mMapOffset = 0; + mMapSize = mSize; + mState = BufferState::Mapped; - DAWN_TRY(GetDevice()->CopyFromStagingToBuffer( - uploadHandle.stagingBuffer, uploadHandle.startOffset, this, start, count)); + if (GetDevice()->ConsumedError(MapWriteAsyncImpl())) { + CallMapWriteCallback(mMapSerial, WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0); + return; + } - return {}; + MapRequestTracker* tracker = GetDevice()->GetMapRequestTracker(); + tracker->Track(this, mMapSerial, MapType::Write); } - void BufferBase::MapWriteAsync(DawnBufferMapWriteCallback callback, void* userdata) { - if (GetDevice()->ConsumedError(ValidateMap(dawn::BufferUsageBit::MapWrite))) { - callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, userdata); + void BufferBase::MapAsync(wgpu::MapMode mode, + size_t offset, + size_t size, + WGPUBufferMapCallback callback, + void* userdata) { + // Handle the defaulting of size required by WebGPU, even if in webgpu_cpp.h it is not + // possible to default the function argument (because there is the callback later in the + // argument list) + if (size == 0 && offset < mSize) { + size = mSize - offset; + } + + WGPUBufferMapAsyncStatus status; + if (GetDevice()->ConsumedError(ValidateMapAsync(mode, offset, size, &status))) { + if (callback) { + callback(status, userdata); + } return; } ASSERT(!IsError()); - ASSERT(mMapReadCallback == nullptr); - // TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes. mMapSerial++; - mMapWriteCallback = callback; + mMapMode = mode; + mMapOffset = offset; + mMapSize = size; + mMapCallback = callback; mMapUserdata = userdata; mState = BufferState::Mapped; - MapWriteAsyncImpl(mMapSerial); + if (GetDevice()->ConsumedError(MapAsyncImpl(mode, offset, size))) { + CallMapCallback(mMapSerial, WGPUBufferMapAsyncStatus_DeviceLost); + return; + } + + MapRequestTracker* tracker = GetDevice()->GetMapRequestTracker(); + tracker->Track(this, mMapSerial, MapType::Async); + } + + void* BufferBase::GetMappedRange(size_t offset, size_t size) { + return GetMappedRangeInternal(true, offset, size); + } + + const void* BufferBase::GetConstMappedRange(size_t offset, size_t size) { + return GetMappedRangeInternal(false, offset, size); + } + + // TODO(dawn:445): When CreateBufferMapped is removed, make GetMappedRangeInternal also take + // care of the validation of GetMappedRange. + void* BufferBase::GetMappedRangeInternal(bool writable, size_t offset, size_t size) { + if (!CanGetMappedRange(writable, offset, size)) { + return nullptr; + } + + if (mStagingBuffer != nullptr) { + return static_cast(mStagingBuffer->GetMappedPointer()) + offset; + } + if (mSize == 0) { + return reinterpret_cast(intptr_t(0xCAFED00D)); + } + return static_cast(GetMappedPointerImpl()) + offset; } void BufferBase::Destroy() { if (IsError()) { // It is an error to call Destroy() on an ErrorBuffer, but we still need to reclaim the // fake mapped staging data. - reinterpret_cast(this)->ClearMappedData(); + static_cast(this)->ClearMappedData(); + mState = BufferState::Destroyed; } if (GetDevice()->ConsumedError(ValidateDestroy())) { return; @@ -296,20 +417,28 @@ namespace dawn_native { ASSERT(!IsError()); if (mState == BufferState::Mapped) { - if (mStagingBuffer == nullptr) { + Unmap(); + } else if (mState == BufferState::MappedAtCreation) { + if (mStagingBuffer != nullptr) { + mStagingBuffer.reset(); + } else if (mSize != 0) { + ASSERT(IsMappableAtCreation()); Unmap(); } - mStagingBuffer.reset(); } + DestroyInternal(); } MaybeError BufferBase::CopyFromStagingBuffer() { ASSERT(mStagingBuffer); + if (GetSize() == 0) { + return {}; + } + DAWN_TRY(GetDevice()->CopyFromStagingToBuffer(mStagingBuffer.get(), 0, this, 0, GetSize())); - DynamicUploader* uploader = nullptr; - DAWN_TRY_ASSIGN(uploader, GetDevice()->GetDynamicUploader()); + DynamicUploader* uploader = GetDevice()->GetDynamicUploader(); uploader->ReleaseStagingBuffer(std::move(mStagingBuffer)); return {}; @@ -319,103 +448,178 @@ namespace dawn_native { if (IsError()) { // It is an error to call Unmap() on an ErrorBuffer, but we still need to reclaim the // fake mapped staging data. - reinterpret_cast(this)->ClearMappedData(); + static_cast(this)->ClearMappedData(); + mState = BufferState::Unmapped; } if (GetDevice()->ConsumedError(ValidateUnmap())) { return; } ASSERT(!IsError()); - if (mStagingBuffer != nullptr) { - GetDevice()->ConsumedError(CopyFromStagingBuffer()); - } else { + if (mState == BufferState::Mapped) { // A map request can only be called once, so this will fire only if the request wasn't // completed before the Unmap. // Callbacks are not fired if there is no callback registered, so this is correct for // CreateBufferMapped. - CallMapReadCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u); - CallMapWriteCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u); + CallMapReadCallback(mMapSerial, WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u); + CallMapWriteCallback(mMapSerial, WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u); + CallMapCallback(mMapSerial, WGPUBufferMapAsyncStatus_Unknown); UnmapImpl(); + + mMapReadCallback = nullptr; + mMapWriteCallback = nullptr; + mMapUserdata = 0; + + } else if (mState == BufferState::MappedAtCreation) { + if (mStagingBuffer != nullptr) { + GetDevice()->ConsumedError(CopyFromStagingBuffer()); + } else if (mSize != 0) { + ASSERT(IsMappableAtCreation()); + UnmapImpl(); + } } + mState = BufferState::Unmapped; - mMapReadCallback = nullptr; - mMapWriteCallback = nullptr; - mMapUserdata = 0; } - MaybeError BufferBase::ValidateSetSubData(uint32_t start, uint32_t count) const { + MaybeError BufferBase::ValidateMap(wgpu::BufferUsage requiredUsage, + WGPUBufferMapAsyncStatus* status) const { + *status = WGPUBufferMapAsyncStatus_DeviceLost; + DAWN_TRY(GetDevice()->ValidateIsAlive()); + + *status = WGPUBufferMapAsyncStatus_Error; DAWN_TRY(GetDevice()->ValidateObject(this)); switch (mState) { case BufferState::Mapped: - return DAWN_VALIDATION_ERROR("Buffer is mapped"); + case BufferState::MappedAtCreation: + return DAWN_VALIDATION_ERROR("Buffer is already mapped"); case BufferState::Destroyed: return DAWN_VALIDATION_ERROR("Buffer is destroyed"); case BufferState::Unmapped: break; } - if (count > GetSize()) { - return DAWN_VALIDATION_ERROR("Buffer subdata with too much data"); + if (!(mUsage & requiredUsage)) { + return DAWN_VALIDATION_ERROR("Buffer needs the correct map usage bit"); } - // Metal requests buffer to buffer copy size must be a multiple of 4 bytes on macOS - if (count % 4 != 0) { - return DAWN_VALIDATION_ERROR("Buffer subdata size must be a multiple of 4 bytes"); - } + *status = WGPUBufferMapAsyncStatus_Success; + return {}; + } - // Metal requests offset of buffer to buffer copy must be a multiple of 4 bytes on macOS - if (start % 4 != 0) { - return DAWN_VALIDATION_ERROR("Start position must be a multiple of 4 bytes"); - } + MaybeError BufferBase::ValidateMapAsync(wgpu::MapMode mode, + size_t offset, + size_t size, + WGPUBufferMapAsyncStatus* status) const { + *status = WGPUBufferMapAsyncStatus_DeviceLost; + DAWN_TRY(GetDevice()->ValidateIsAlive()); - // Note that no overflow can happen because we already checked for GetSize() >= count - if (start > GetSize() - count) { - return DAWN_VALIDATION_ERROR("Buffer subdata out of range"); - } + *status = WGPUBufferMapAsyncStatus_Error; + DAWN_TRY(GetDevice()->ValidateObject(this)); - if (!(mUsage & dawn::BufferUsageBit::TransferDst)) { - return DAWN_VALIDATION_ERROR("Buffer needs the transfer dst usage bit"); + if (offset % 4 != 0) { + return DAWN_VALIDATION_ERROR("offset must be a multiple of 4"); } - return {}; - } + if (size % 4 != 0) { + return DAWN_VALIDATION_ERROR("size must be a multiple of 4"); + } - MaybeError BufferBase::ValidateMap(dawn::BufferUsageBit requiredUsage) const { - DAWN_TRY(GetDevice()->ValidateObject(this)); + if (uint64_t(offset) > mSize || uint64_t(size) > mSize - uint64_t(offset)) { + return DAWN_VALIDATION_ERROR("size + offset must fit in the buffer"); + } switch (mState) { case BufferState::Mapped: - return DAWN_VALIDATION_ERROR("Buffer already mapped"); + case BufferState::MappedAtCreation: + return DAWN_VALIDATION_ERROR("Buffer is already mapped"); case BufferState::Destroyed: return DAWN_VALIDATION_ERROR("Buffer is destroyed"); case BufferState::Unmapped: break; } - if (!(mUsage & requiredUsage)) { - return DAWN_VALIDATION_ERROR("Buffer needs the correct map usage bit"); + bool isReadMode = mode & wgpu::MapMode::Read; + bool isWriteMode = mode & wgpu::MapMode::Write; + if (!(isReadMode ^ isWriteMode)) { + return DAWN_VALIDATION_ERROR("Exactly one of Read or Write mode must be set"); } + if (mode & wgpu::MapMode::Read) { + if (!(mUsage & wgpu::BufferUsage::MapRead)) { + return DAWN_VALIDATION_ERROR("The buffer must have the MapRead usage"); + } + } else { + ASSERT(mode & wgpu::MapMode::Write); + + if (!(mUsage & wgpu::BufferUsage::MapWrite)) { + return DAWN_VALIDATION_ERROR("The buffer must have the MapWrite usage"); + } + } + + *status = WGPUBufferMapAsyncStatus_Success; return {}; } + bool BufferBase::CanGetMappedRange(bool writable, size_t offset, size_t size) const { + if (size > mMapSize || offset < mMapOffset) { + return false; + } + + size_t offsetInMappedRange = offset - mMapOffset; + if (offsetInMappedRange > mMapSize - size) { + return false; + } + + // Note that: + // + // - We don't check that the device is alive because the application can ask for the + // mapped pointer before it knows, and even Dawn knows, that the device was lost, and + // still needs to work properly. + // - We don't check that the object is alive because we need to return mapped pointers + // for error buffers too. + + switch (mState) { + // Writeable Buffer::GetMappedRange is always allowed when mapped at creation. + case BufferState::MappedAtCreation: + return true; + + case BufferState::Mapped: + // TODO(dawn:445): When mapRead/WriteAsync is removed, check against mMapMode + // instead of mUsage + ASSERT(bool(mUsage & wgpu::BufferUsage::MapRead) ^ + bool(mUsage & wgpu::BufferUsage::MapWrite)); + return !writable || (mUsage & wgpu::BufferUsage::MapWrite); + + case BufferState::Unmapped: + case BufferState::Destroyed: + return false; + + default: + UNREACHABLE(); + } + } + MaybeError BufferBase::ValidateUnmap() const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); DAWN_TRY(GetDevice()->ValidateObject(this)); switch (mState) { case BufferState::Mapped: + case BufferState::MappedAtCreation: // A buffer may be in the Mapped state if it was created with CreateBufferMapped // even if it did not have a mappable usage. return {}; case BufferState::Unmapped: - if ((mUsage & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) == - 0) { + if ((mUsage & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) == 0) { return DAWN_VALIDATION_ERROR("Buffer does not have map usage"); } return {}; case BufferState::Destroyed: return DAWN_VALIDATION_ERROR("Buffer is destroyed"); + default: + UNREACHABLE(); } } @@ -431,4 +635,31 @@ namespace dawn_native { mState = BufferState::Destroyed; } + void BufferBase::OnMapCommandSerialFinished(uint32_t mapSerial, MapType type) { + switch (type) { + case MapType::Read: + CallMapReadCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, + GetMappedRangeInternal(false, 0, mSize), GetSize()); + break; + case MapType::Write: + CallMapWriteCallback(mapSerial, WGPUBufferMapAsyncStatus_Success, + GetMappedRangeInternal(true, 0, mSize), GetSize()); + break; + case MapType::Async: + CallMapCallback(mapSerial, WGPUBufferMapAsyncStatus_Success); + break; + } + } + + bool BufferBase::IsDataInitialized() const { + return mIsDataInitialized; + } + + void BufferBase::SetIsDataInitialized() { + mIsDataInitialized = true; + } + + bool BufferBase::IsFullBufferRange(uint64_t offset, uint64_t size) const { + return offset == 0 && size == GetSize(); + } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Buffer.h b/third_party/dawn/src/dawn_native/Buffer.h index 0e2ceb2fd44..12aaadeff4a 100644 --- a/third_party/dawn/src/dawn_native/Buffer.h +++ b/third_party/dawn/src/dawn_native/Buffer.h @@ -21,89 +21,116 @@ #include "dawn_native/dawn_platform.h" +#include + namespace dawn_native { - MaybeError ValidateBufferDescriptor(DeviceBase* device, const BufferDescriptor* descriptor); + struct CopyTextureToBufferCmd; + + enum class MapType : uint32_t; - static constexpr dawn::BufferUsageBit kReadOnlyBufferUsages = - dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferSrc | - dawn::BufferUsageBit::Index | dawn::BufferUsageBit::Vertex | dawn::BufferUsageBit::Uniform; + MaybeError ValidateBufferDescriptor(DeviceBase* device, const BufferDescriptor* descriptor); - static constexpr dawn::BufferUsageBit kWritableBufferUsages = - dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferDst | - dawn::BufferUsageBit::Storage; + static constexpr wgpu::BufferUsage kReadOnlyBufferUsages = + wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Index | + wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Uniform | kReadOnlyStorageBuffer; class BufferBase : public ObjectBase { enum class BufferState { Unmapped, Mapped, + MappedAtCreation, Destroyed, }; public: BufferBase(DeviceBase* device, const BufferDescriptor* descriptor); - ~BufferBase(); - static BufferBase* MakeError(DeviceBase* device); - static BufferBase* MakeErrorMapped(DeviceBase* device, - uint64_t size, - uint8_t** mappedPointer); + static BufferBase* MakeError(DeviceBase* device, const BufferDescriptor* descriptor); uint64_t GetSize() const; - dawn::BufferUsageBit GetUsage() const; + wgpu::BufferUsage GetUsage() const; + + MaybeError MapAtCreation(); + void OnMapCommandSerialFinished(uint32_t mapSerial, MapType type); - MaybeError MapAtCreation(uint8_t** mappedPointer); + MaybeError ValidateCanUseOnQueueNow() const; - MaybeError ValidateCanUseInSubmitNow() const; + bool IsFullBufferRange(uint64_t offset, uint64_t size) const; + bool IsDataInitialized() const; + void SetIsDataInitialized(); // Dawn API - void SetSubData(uint32_t start, uint32_t count, const void* data); - void MapReadAsync(DawnBufferMapReadCallback callback, void* userdata); - void MapWriteAsync(DawnBufferMapWriteCallback callback, void* userdata); + void SetSubData(uint64_t start, uint64_t count, const void* data); + void MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata); + void MapWriteAsync(WGPUBufferMapWriteCallback callback, void* userdata); + void MapAsync(wgpu::MapMode mode, + size_t offset, + size_t size, + WGPUBufferMapCallback callback, + void* userdata); + void* GetMappedRange(size_t offset, size_t size); + const void* GetConstMappedRange(size_t offset, size_t size); void Unmap(); void Destroy(); protected: - BufferBase(DeviceBase* device, ObjectBase::ErrorTag tag); - - void CallMapReadCallback(uint32_t serial, - DawnBufferMapAsyncStatus status, - const void* pointer, - uint32_t dataLength); - void CallMapWriteCallback(uint32_t serial, - DawnBufferMapAsyncStatus status, - void* pointer, - uint32_t dataLength); + BufferBase(DeviceBase* device, + const BufferDescriptor* descriptor, + ObjectBase::ErrorTag tag); + ~BufferBase() override; void DestroyInternal(); + bool IsMapped() const; + private: - virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) = 0; - virtual MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const void* data); - virtual void MapReadAsyncImpl(uint32_t serial) = 0; - virtual void MapWriteAsyncImpl(uint32_t serial) = 0; + virtual MaybeError MapAtCreationImpl() = 0; + virtual MaybeError MapReadAsyncImpl() = 0; + virtual MaybeError MapWriteAsyncImpl() = 0; + virtual MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) = 0; virtual void UnmapImpl() = 0; virtual void DestroyImpl() = 0; + virtual void* GetMappedPointerImpl() = 0; - virtual bool IsMapWritable() const = 0; + virtual bool IsMappableAtCreation() const = 0; MaybeError CopyFromStagingBuffer(); - - MaybeError ValidateSetSubData(uint32_t start, uint32_t count) const; - MaybeError ValidateMap(dawn::BufferUsageBit requiredUsage) const; + void* GetMappedRangeInternal(bool writable, size_t offset, size_t size); + void CallMapReadCallback(uint32_t serial, + WGPUBufferMapAsyncStatus status, + const void* pointer, + uint64_t dataLength); + void CallMapWriteCallback(uint32_t serial, + WGPUBufferMapAsyncStatus status, + void* pointer, + uint64_t dataLength); + void CallMapCallback(uint32_t serial, WGPUBufferMapAsyncStatus status); + + MaybeError ValidateMap(wgpu::BufferUsage requiredUsage, + WGPUBufferMapAsyncStatus* status) const; + MaybeError ValidateMapAsync(wgpu::MapMode mode, + size_t offset, + size_t size, + WGPUBufferMapAsyncStatus* status) const; MaybeError ValidateUnmap() const; MaybeError ValidateDestroy() const; + bool CanGetMappedRange(bool writable, size_t offset, size_t size) const; uint64_t mSize = 0; - dawn::BufferUsageBit mUsage = dawn::BufferUsageBit::None; - - DawnBufferMapReadCallback mMapReadCallback = nullptr; - DawnBufferMapWriteCallback mMapWriteCallback = nullptr; - void* mMapUserdata = 0; - uint32_t mMapSerial = 0; + wgpu::BufferUsage mUsage = wgpu::BufferUsage::None; + BufferState mState; + bool mIsDataInitialized = false; std::unique_ptr mStagingBuffer; - BufferState mState; + WGPUBufferMapReadCallback mMapReadCallback = nullptr; + WGPUBufferMapWriteCallback mMapWriteCallback = nullptr; + WGPUBufferMapCallback mMapCallback = nullptr; + void* mMapUserdata = 0; + uint32_t mMapSerial = 0; + wgpu::MapMode mMapMode = wgpu::MapMode::None; + size_t mMapOffset = 0; + size_t mMapSize = 0; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/CMakeLists.txt b/third_party/dawn/src/dawn_native/CMakeLists.txt new file mode 100644 index 00000000000..c6022355412 --- /dev/null +++ b/third_party/dawn/src/dawn_native/CMakeLists.txt @@ -0,0 +1,459 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +DawnJSONGenerator( + TARGET "dawn_native_utils" + PRINT_NAME "Dawn native utilities" + RESULT_VARIABLE "DAWN_NATIVE_UTILS_GEN_SOURCES" +) + +add_library(dawn_native STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawn_native PRIVATE + "${DAWN_INCLUDE_DIR}/dawn_native/DawnNative.h" + "${DAWN_INCLUDE_DIR}/dawn_native/dawn_native_export.h" + ${DAWN_NATIVE_UTILS_GEN_SOURCES} + "Adapter.cpp" + "Adapter.h" + "AttachmentState.cpp" + "AttachmentState.h" + "BackendConnection.cpp" + "BackendConnection.h" + "BindGroup.cpp" + "BindGroup.h" + "BindGroupAndStorageBarrierTracker.h" + "BindGroupLayout.cpp" + "BindGroupLayout.h" + "BindGroupTracker.h" + "BindingInfo.cpp" + "BindingInfo.h" + "BuddyAllocator.cpp" + "BuddyAllocator.h" + "BuddyMemoryAllocator.cpp" + "BuddyMemoryAllocator.h" + "Buffer.cpp" + "Buffer.h" + "CachedObject.cpp" + "CachedObject.h" + "CommandAllocator.cpp" + "CommandAllocator.h" + "CommandBuffer.cpp" + "CommandBuffer.h" + "CommandBufferStateTracker.cpp" + "CommandBufferStateTracker.h" + "CommandEncoder.cpp" + "CommandEncoder.h" + "CommandValidation.cpp" + "CommandValidation.h" + "Commands.cpp" + "Commands.h" + "ComputePassEncoder.cpp" + "ComputePassEncoder.h" + "ComputePipeline.cpp" + "ComputePipeline.h" + "Device.cpp" + "Device.h" + "DynamicUploader.cpp" + "DynamicUploader.h" + "EncodingContext.cpp" + "EncodingContext.h" + "Error.cpp" + "Error.h" + "ErrorData.cpp" + "ErrorData.h" + "ErrorInjector.cpp" + "ErrorInjector.h" + "ErrorScope.cpp" + "ErrorScope.h" + "ErrorScopeTracker.cpp" + "ErrorScopeTracker.h" + "Extensions.cpp" + "Extensions.h" + "Fence.cpp" + "Fence.h" + "FenceSignalTracker.cpp" + "FenceSignalTracker.h" + "Format.cpp" + "Format.h" + "Forward.h" + "Instance.cpp" + "Instance.h" + "MapRequestTracker.cpp" + "MapRequestTracker.h" + "ObjectBase.cpp" + "ObjectBase.h" + "PassResourceUsage.h" + "PassResourceUsageTracker.cpp" + "PassResourceUsageTracker.h" + "PerStage.cpp" + "PerStage.h" + "Pipeline.cpp" + "Pipeline.h" + "PipelineLayout.cpp" + "PipelineLayout.h" + "ProgrammablePassEncoder.cpp" + "ProgrammablePassEncoder.h" + "QuerySet.cpp" + "QuerySet.h" + "Queue.cpp" + "Queue.h" + "RenderBundle.cpp" + "RenderBundle.h" + "RenderBundleEncoder.cpp" + "RenderBundleEncoder.h" + "RenderEncoderBase.cpp" + "RenderEncoderBase.h" + "RenderPassEncoder.cpp" + "RenderPassEncoder.h" + "RenderPipeline.cpp" + "RenderPipeline.h" + "ResourceHeap.h" + "ResourceHeapAllocator.h" + "ResourceMemoryAllocation.cpp" + "ResourceMemoryAllocation.h" + "RingBufferAllocator.cpp" + "RingBufferAllocator.h" + "Sampler.cpp" + "Sampler.h" + "ShaderModule.cpp" + "ShaderModule.h" + "StagingBuffer.cpp" + "StagingBuffer.h" + "Surface.cpp" + "Surface.h" + "SwapChain.cpp" + "SwapChain.h" + "Texture.cpp" + "Texture.h" + "ToBackend.h" + "Toggles.cpp" + "Toggles.h" + "dawn_platform.h" +) +target_link_libraries(dawn_native + PUBLIC dawncpp_headers + PRIVATE dawn_common + dawn_platform + dawn_internal_config + shaderc_spvc + spirv-cross-core +) + +if (DAWN_USE_X11) + find_package(X11 REQUIRED) + target_link_libraries(dawn_native PRIVATE ${X11_LIBRARIES}) +endif() + +if (WIN32) + target_link_libraries(dawn_native PRIVATE user32.lib) +endif() + +if (DAWN_ENABLE_D3D12) + target_sources(dawn_native PRIVATE + "${DAWN_INCLUDE_DIR}/dawn_native/D3D12Backend.h" + "d3d12/AdapterD3D12.cpp" + "d3d12/AdapterD3D12.h" + "d3d12/BackendD3D12.cpp" + "d3d12/BackendD3D12.h" + "d3d12/BindGroupD3D12.cpp" + "d3d12/BindGroupD3D12.h" + "d3d12/BindGroupLayoutD3D12.cpp" + "d3d12/BindGroupLayoutD3D12.h" + "d3d12/BufferD3D12.cpp" + "d3d12/BufferD3D12.h" + "d3d12/CPUDescriptorHeapAllocationD3D12.cpp" + "d3d12/CPUDescriptorHeapAllocationD3D12.h" + "d3d12/CommandAllocatorManager.cpp" + "d3d12/CommandAllocatorManager.h" + "d3d12/CommandBufferD3D12.cpp" + "d3d12/CommandBufferD3D12.h" + "d3d12/CommandRecordingContext.cpp" + "d3d12/CommandRecordingContext.h" + "d3d12/ComputePipelineD3D12.cpp" + "d3d12/ComputePipelineD3D12.h" + "d3d12/D3D12Error.cpp" + "d3d12/D3D12Error.h" + "d3d12/D3D12Info.cpp" + "d3d12/D3D12Info.h" + "d3d12/DeviceD3D12.cpp" + "d3d12/DeviceD3D12.h" + "d3d12/Forward.h" + "d3d12/GPUDescriptorHeapAllocationD3D12.cpp" + "d3d12/GPUDescriptorHeapAllocationD3D12.h" + "d3d12/HeapAllocatorD3D12.cpp" + "d3d12/HeapAllocatorD3D12.h" + "d3d12/HeapD3D12.cpp" + "d3d12/HeapD3D12.h" + "d3d12/NativeSwapChainImplD3D12.cpp" + "d3d12/NativeSwapChainImplD3D12.h" + "d3d12/PageableD3D12.cpp" + "d3d12/PageableD3D12.h" + "d3d12/PipelineLayoutD3D12.cpp" + "d3d12/PipelineLayoutD3D12.h" + "d3d12/PlatformFunctions.cpp" + "d3d12/PlatformFunctions.h" + "d3d12/QuerySetD3D12.cpp" + "d3d12/QuerySetD3D12.h" + "d3d12/QueueD3D12.cpp" + "d3d12/QueueD3D12.h" + "d3d12/RenderPassBuilderD3D12.cpp" + "d3d12/RenderPassBuilderD3D12.h" + "d3d12/RenderPipelineD3D12.cpp" + "d3d12/RenderPipelineD3D12.h" + "d3d12/ResidencyManagerD3D12.cpp" + "d3d12/ResidencyManagerD3D12.h" + "d3d12/ResourceAllocatorManagerD3D12.cpp" + "d3d12/ResourceAllocatorManagerD3D12.h" + "d3d12/ResourceHeapAllocationD3D12.cpp" + "d3d12/ResourceHeapAllocationD3D12.h" + "d3d12/SamplerD3D12.cpp" + "d3d12/SamplerD3D12.h" + "d3d12/SamplerHeapCacheD3D12.cpp" + "d3d12/SamplerHeapCacheD3D12.h" + "d3d12/ShaderModuleD3D12.cpp" + "d3d12/ShaderModuleD3D12.h" + "d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp" + "d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" + "d3d12/StagingBufferD3D12.cpp" + "d3d12/StagingBufferD3D12.h" + "d3d12/StagingDescriptorAllocatorD3D12.cpp" + "d3d12/StagingDescriptorAllocatorD3D12.h" + "d3d12/SwapChainD3D12.cpp" + "d3d12/SwapChainD3D12.h" + "d3d12/TextureCopySplitter.cpp" + "d3d12/TextureCopySplitter.h" + "d3d12/TextureD3D12.cpp" + "d3d12/TextureD3D12.h" + "d3d12/UtilsD3D12.cpp" + "d3d12/UtilsD3D12.h" + "d3d12/d3d12_platform.h" + ) + target_link_libraries(dawn_native PRIVATE dxguid.lib) +endif() + +if (DAWN_ENABLE_METAL) + target_sources(dawn_native PRIVATE + "${DAWN_INCLUDE_DIR}/dawn_native/MetalBackend.h" + "Surface_metal.mm" + "metal/BackendMTL.h" + "metal/BackendMTL.mm" + "metal/BindGroupLayoutMTL.h" + "metal/BindGroupLayoutMTL.mm" + "metal/BindGroupMTL.h" + "metal/BindGroupMTL.mm" + "metal/BufferMTL.h" + "metal/BufferMTL.mm" + "metal/CommandBufferMTL.h" + "metal/CommandBufferMTL.mm" + "metal/CommandRecordingContext.h" + "metal/CommandRecordingContext.mm" + "metal/ComputePipelineMTL.h" + "metal/ComputePipelineMTL.mm" + "metal/DeviceMTL.h" + "metal/DeviceMTL.mm" + "metal/Forward.h" + "metal/PipelineLayoutMTL.h" + "metal/PipelineLayoutMTL.mm" + "metal/QueueMTL.h" + "metal/QueueMTL.mm" + "metal/RenderPipelineMTL.h" + "metal/RenderPipelineMTL.mm" + "metal/SamplerMTL.h" + "metal/SamplerMTL.mm" + "metal/ShaderModuleMTL.h" + "metal/ShaderModuleMTL.mm" + "metal/StagingBufferMTL.h" + "metal/StagingBufferMTL.mm" + "metal/SwapChainMTL.h" + "metal/SwapChainMTL.mm" + "metal/TextureMTL.h" + "metal/TextureMTL.mm" + "metal/UtilsMetal.h" + "metal/UtilsMetal.mm" + ) + target_link_libraries(dawn_native PRIVATE + "-framework Cocoa" + "-framework IOKit" + "-framework IOSurface" + "-framework QuartzCore" + ) +endif() + +if (DAWN_ENABLE_NULL) + target_sources(dawn_native PRIVATE + "${DAWN_INCLUDE_DIR}/dawn_native/NullBackend.h" + "null/DeviceNull.cpp" + "null/DeviceNull.h" + ) +endif() + +if (DAWN_ENABLE_OPENGL) + DawnGenerator( + SCRIPT "${Dawn_SOURCE_DIR}/generator/opengl_loader_generator.py" + PRINT_NAME "OpenGL function loader" + ARGS "--gl-xml" + "${Dawn_SOURCE_DIR}/third_party/khronos/gl.xml" + "--supported-extensions" + "${Dawn_SOURCE_DIR}/src/dawn_native/opengl/supported_extensions.json" + RESULT_VARIABLE "DAWN_NATIVE_OPENGL_AUTOGEN_SOURCES" + ) + + target_sources(dawn_native PRIVATE + "${DAWN_INCLUDE_DIR}/dawn_native/OpenGLBackend.h" + ${DAWN_NATIVE_OPENGL_AUTOGEN_SOURCES} + "opengl/BackendGL.cpp" + "opengl/BackendGL.h" + "opengl/BindGroupGL.cpp" + "opengl/BindGroupGL.h" + "opengl/BindGroupLayoutGL.cpp" + "opengl/BindGroupLayoutGL.h" + "opengl/BufferGL.cpp" + "opengl/BufferGL.h" + "opengl/CommandBufferGL.cpp" + "opengl/CommandBufferGL.h" + "opengl/ComputePipelineGL.cpp" + "opengl/ComputePipelineGL.h" + "opengl/DeviceGL.cpp" + "opengl/DeviceGL.h" + "opengl/Forward.h" + "opengl/GLFormat.cpp" + "opengl/GLFormat.h" + "opengl/NativeSwapChainImplGL.cpp" + "opengl/NativeSwapChainImplGL.h" + "opengl/OpenGLFunctions.cpp" + "opengl/OpenGLFunctions.h" + "opengl/PersistentPipelineStateGL.cpp" + "opengl/PersistentPipelineStateGL.h" + "opengl/PipelineGL.cpp" + "opengl/PipelineGL.h" + "opengl/PipelineLayoutGL.cpp" + "opengl/PipelineLayoutGL.h" + "opengl/QuerySetGL.cpp" + "opengl/QuerySetGL.h" + "opengl/QueueGL.cpp" + "opengl/QueueGL.h" + "opengl/RenderPipelineGL.cpp" + "opengl/RenderPipelineGL.h" + "opengl/SamplerGL.cpp" + "opengl/SamplerGL.h" + "opengl/ShaderModuleGL.cpp" + "opengl/ShaderModuleGL.h" + "opengl/SwapChainGL.cpp" + "opengl/SwapChainGL.h" + "opengl/TextureGL.cpp" + "opengl/TextureGL.h" + "opengl/UtilsGL.cpp" + "opengl/UtilsGL.h" + "opengl/opengl_platform.h" + ) + + target_link_libraries(dawn_native PRIVATE dawn_khronos_platform) +endif() + +if (DAWN_ENABLE_VULKAN) + target_sources(dawn_native PRIVATE + "${DAWN_INCLUDE_DIR}/dawn_native/VulkanBackend.h" + "vulkan/AdapterVk.cpp" + "vulkan/AdapterVk.h" + "vulkan/BackendVk.cpp" + "vulkan/BackendVk.h" + "vulkan/BindGroupLayoutVk.cpp" + "vulkan/BindGroupLayoutVk.h" + "vulkan/BindGroupVk.cpp" + "vulkan/BindGroupVk.h" + "vulkan/BufferVk.cpp" + "vulkan/BufferVk.h" + "vulkan/CommandBufferVk.cpp" + "vulkan/CommandBufferVk.h" + "vulkan/CommandRecordingContext.h" + "vulkan/ComputePipelineVk.cpp" + "vulkan/ComputePipelineVk.h" + "vulkan/DescriptorSetAllocation.h" + "vulkan/DescriptorSetAllocator.cpp" + "vulkan/DescriptorSetAllocator.h" + "vulkan/DeviceVk.cpp" + "vulkan/DeviceVk.h" + "vulkan/ExternalHandle.h" + "vulkan/FencedDeleter.cpp" + "vulkan/FencedDeleter.h" + "vulkan/Forward.h" + "vulkan/NativeSwapChainImplVk.cpp" + "vulkan/NativeSwapChainImplVk.h" + "vulkan/PipelineLayoutVk.cpp" + "vulkan/PipelineLayoutVk.h" + "vulkan/QueueVk.cpp" + "vulkan/QueueVk.h" + "vulkan/RenderPassCache.cpp" + "vulkan/RenderPassCache.h" + "vulkan/RenderPipelineVk.cpp" + "vulkan/RenderPipelineVk.h" + "vulkan/ResourceHeapVk.cpp" + "vulkan/ResourceHeapVk.h" + "vulkan/ResourceMemoryAllocatorVk.cpp" + "vulkan/ResourceMemoryAllocatorVk.h" + "vulkan/SamplerVk.cpp" + "vulkan/SamplerVk.h" + "vulkan/ShaderModuleVk.cpp" + "vulkan/ShaderModuleVk.h" + "vulkan/StagingBufferVk.cpp" + "vulkan/StagingBufferVk.h" + "vulkan/SwapChainVk.cpp" + "vulkan/SwapChainVk.h" + "vulkan/TextureVk.cpp" + "vulkan/TextureVk.h" + "vulkan/UtilsVulkan.cpp" + "vulkan/UtilsVulkan.h" + "vulkan/VulkanError.cpp" + "vulkan/VulkanError.h" + "vulkan/VulkanExtensions.cpp" + "vulkan/VulkanExtensions.h" + "vulkan/VulkanFunctions.cpp" + "vulkan/VulkanFunctions.h" + "vulkan/VulkanInfo.cpp" + "vulkan/VulkanInfo.h" + "vulkan/external_memory/MemoryService.h" + "vulkan/external_semaphore/SemaphoreService.h" + ) + + target_link_libraries(dawn_native PUBLIC dawn_vulkan_headers) + + if (UNIX AND NOT APPLE) + target_sources(dawn_native PRIVATE + "vulkan/external_memory/MemoryServiceOpaqueFD.cpp" + "vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp" + ) + else() + target_sources(dawn_native PRIVATE + "vulkan/external_memory/MemoryServiceNull.cpp" + "vulkan/external_semaphore/SemaphoreServiceNull.cpp" + ) + endif() +endif() + +# TODO how to do the component build in CMake? +target_sources(dawn_native PRIVATE "DawnNative.cpp") +if (DAWN_ENABLE_D3D12) + target_sources(dawn_native PRIVATE "d3d12/D3D12Backend.cpp") +endif() +if (DAWN_ENABLE_METAL) + target_sources(dawn_native PRIVATE "metal/MetalBackend.mm") +endif() +if (DAWN_ENABLE_NULL) + target_sources(dawn_native PRIVATE "null/NullBackend.cpp") +endif() +if (DAWN_ENABLE_OPENGL) + target_sources(dawn_native PRIVATE "opengl/OpenGLBackend.cpp") +endif() +if (DAWN_ENABLE_VULKAN) + target_sources(dawn_native PRIVATE "vulkan/VulkanBackend.cpp") +endif() diff --git a/third_party/dawn/src/dawn_native/CachedObject.cpp b/third_party/dawn/src/dawn_native/CachedObject.cpp new file mode 100644 index 00000000000..b91baed1675 --- /dev/null +++ b/third_party/dawn/src/dawn_native/CachedObject.cpp @@ -0,0 +1,27 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/CachedObject.h" + +namespace dawn_native { + + bool CachedObject::IsCachedReference() const { + return mIsCachedReference; + } + + void CachedObject::SetIsCachedReference() { + mIsCachedReference = true; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/CachedObject.h b/third_party/dawn/src/dawn_native/CachedObject.h new file mode 100644 index 00000000000..b498b917713 --- /dev/null +++ b/third_party/dawn/src/dawn_native/CachedObject.h @@ -0,0 +1,41 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_CACHED_OBJECT_H_ +#define DAWNNATIVE_CACHED_OBJECT_H_ + +#include "dawn_native/ObjectBase.h" + +namespace dawn_native { + + // Some objects are cached so that instead of creating new duplicate objects, + // we increase the refcount of an existing object. + // When an object is successfully created, the device should call + // SetIsCachedReference() and insert the object into the cache. + class CachedObject : public ObjectBase { + public: + using ObjectBase::ObjectBase; + + bool IsCachedReference() const; + + private: + friend class DeviceBase; + void SetIsCachedReference(); + + bool mIsCachedReference = false; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_CACHED_OBJECT_H_ diff --git a/third_party/dawn/src/dawn_native/CommandAllocator.cpp b/third_party/dawn/src/dawn_native/CommandAllocator.cpp index 990c1c59909..553f8896577 100644 --- a/third_party/dawn/src/dawn_native/CommandAllocator.cpp +++ b/third_party/dawn/src/dawn_native/CommandAllocator.cpp @@ -23,12 +23,9 @@ namespace dawn_native { - constexpr uint32_t EndOfBlock = UINT_MAX; // std::numeric_limits::max(); - constexpr uint32_t AdditionalData = UINT_MAX - 1; // std::numeric_limits::max() - 1; - // TODO(cwallez@chromium.org): figure out a way to have more type safety for the iterator - CommandIterator::CommandIterator() : mEndOfBlock(EndOfBlock) { + CommandIterator::CommandIterator() { Reset(); } @@ -42,7 +39,7 @@ namespace dawn_native { } } - CommandIterator::CommandIterator(CommandIterator&& other) : mEndOfBlock(EndOfBlock) { + CommandIterator::CommandIterator(CommandIterator&& other) { if (!other.IsEmpty()) { mBlocks = std::move(other.mBlocks); other.Reset(); @@ -64,7 +61,7 @@ namespace dawn_native { } CommandIterator::CommandIterator(CommandAllocator&& allocator) - : mBlocks(allocator.AcquireBlocks()), mEndOfBlock(EndOfBlock) { + : mBlocks(allocator.AcquireBlocks()) { Reset(); } @@ -74,6 +71,17 @@ namespace dawn_native { return *this; } + bool CommandIterator::NextCommandIdInNewBlock(uint32_t* commandId) { + mCurrentBlock++; + if (mCurrentBlock >= mBlocks.size()) { + Reset(); + *commandId = detail::kEndOfBlock; + return false; + } + mCurrentPtr = AlignPtr(mBlocks[mCurrentBlock].block, alignof(uint32_t)); + return NextCommandId(commandId); + } + void CommandIterator::Reset() { mCurrentBlock = 0; @@ -97,47 +105,6 @@ namespace dawn_native { return mBlocks[0].block == reinterpret_cast(&mEndOfBlock); } - bool CommandIterator::NextCommandId(uint32_t* commandId) { - uint8_t* idPtr = AlignPtr(mCurrentPtr, alignof(uint32_t)); - ASSERT(idPtr + sizeof(uint32_t) <= - mBlocks[mCurrentBlock].block + mBlocks[mCurrentBlock].size); - - uint32_t id = *reinterpret_cast(idPtr); - - if (id == EndOfBlock) { - mCurrentBlock++; - if (mCurrentBlock >= mBlocks.size()) { - Reset(); - *commandId = EndOfBlock; - return false; - } - mCurrentPtr = AlignPtr(mBlocks[mCurrentBlock].block, alignof(uint32_t)); - return NextCommandId(commandId); - } - - mCurrentPtr = idPtr + sizeof(uint32_t); - *commandId = id; - return true; - } - - void* CommandIterator::NextCommand(size_t commandSize, size_t commandAlignment) { - uint8_t* commandPtr = AlignPtr(mCurrentPtr, commandAlignment); - ASSERT(commandPtr + sizeof(commandSize) <= - mBlocks[mCurrentBlock].block + mBlocks[mCurrentBlock].size); - - mCurrentPtr = commandPtr + commandSize; - return commandPtr; - } - - void* CommandIterator::NextData(size_t dataSize, size_t dataAlignment) { - uint32_t id; - bool hasId = NextCommandId(&id); - ASSERT(hasId); - ASSERT(id == AdditionalData); - - return NextCommand(dataSize, dataAlignment); - } - // Potential TODO(cwallez@chromium.org): // - Host the size and pointer to next block in the block itself to avoid having an allocation // in the vector @@ -161,60 +128,23 @@ namespace dawn_native { ASSERT(mCurrentPtr != nullptr && mEndPtr != nullptr); ASSERT(IsPtrAligned(mCurrentPtr, alignof(uint32_t))); ASSERT(mCurrentPtr + sizeof(uint32_t) <= mEndPtr); - *reinterpret_cast(mCurrentPtr) = EndOfBlock; + *reinterpret_cast(mCurrentPtr) = detail::kEndOfBlock; mCurrentPtr = nullptr; mEndPtr = nullptr; return std::move(mBlocks); } - uint8_t* CommandAllocator::Allocate(uint32_t commandId, - size_t commandSize, - size_t commandAlignment) { - ASSERT(mCurrentPtr != nullptr); - ASSERT(mEndPtr != nullptr); - ASSERT(commandId != EndOfBlock); - - // It should always be possible to allocate one id, for EndOfBlock tagging, - ASSERT(IsPtrAligned(mCurrentPtr, alignof(uint32_t))); - ASSERT(mEndPtr >= mCurrentPtr); - ASSERT(static_cast(mEndPtr - mCurrentPtr) >= sizeof(uint32_t)); - - // The memory after the ID will contain the following: - // - the current ID - // - padding to align the command, maximum kMaxSupportedAlignment - // - the command of size commandSize - // - padding to align the next ID, maximum alignof(uint32_t) - // - the next ID of size sizeof(uint32_t) - // - // To avoid checking for overflows at every step of the computations we compute an upper - // bound of the space that will be needed in addition to the command data. - static constexpr size_t kWorstCaseAdditionalSize = - sizeof(uint32_t) + kMaxSupportedAlignment + alignof(uint32_t) + sizeof(uint32_t); - - // This can't overflow because by construction mCurrentPtr always has space for the next ID. - size_t remainingSize = static_cast(mEndPtr - mCurrentPtr); - - // The good case were we have enough space for the command data and upper bound of the - // extra required space. - if ((remainingSize >= kWorstCaseAdditionalSize) && - (remainingSize - kWorstCaseAdditionalSize >= commandSize)) { - uint32_t* idAlloc = reinterpret_cast(mCurrentPtr); - *idAlloc = commandId; - - uint8_t* commandAlloc = AlignPtr(mCurrentPtr + sizeof(uint32_t), commandAlignment); - mCurrentPtr = AlignPtr(commandAlloc + commandSize, alignof(uint32_t)); - - return commandAlloc; - } - - // When there is not enough space, we signal the EndOfBlock, so that the iterator knows to - // move to the next one. EndOfBlock on the last block means the end of the commands. + uint8_t* CommandAllocator::AllocateInNewBlock(uint32_t commandId, + size_t commandSize, + size_t commandAlignment) { + // When there is not enough space, we signal the kEndOfBlock, so that the iterator knows + // to move to the next one. kEndOfBlock on the last block means the end of the commands. uint32_t* idAlloc = reinterpret_cast(mCurrentPtr); - *idAlloc = EndOfBlock; + *idAlloc = detail::kEndOfBlock; // We'll request a block that can contain at least the command ID, the command and an - // additional ID to contain the EndOfBlock tag. + // additional ID to contain the kEndOfBlock tag. size_t requestedBlockSize = commandSize + kWorstCaseAdditionalSize; // The computation of the request could overflow. @@ -228,10 +158,6 @@ namespace dawn_native { return Allocate(commandId, commandSize, commandAlignment); } - uint8_t* CommandAllocator::AllocateData(size_t commandSize, size_t commandAlignment) { - return Allocate(AdditionalData, commandSize, commandAlignment); - } - bool CommandAllocator::GetNewBlock(size_t minimumSize) { // Allocate blocks doubling sizes each time, to a maximum of 16k (or at least minimumSize). mLastAllocationSize = diff --git a/third_party/dawn/src/dawn_native/CommandAllocator.h b/third_party/dawn/src/dawn_native/CommandAllocator.h index 504ba7a6a32..82de05c1a45 100644 --- a/third_party/dawn/src/dawn_native/CommandAllocator.h +++ b/third_party/dawn/src/dawn_native/CommandAllocator.h @@ -15,6 +15,9 @@ #ifndef DAWNNATIVE_COMMAND_ALLOCATOR_H_ #define DAWNNATIVE_COMMAND_ALLOCATOR_H_ +#include "common/Assert.h" +#include "common/Math.h" + #include #include #include @@ -56,6 +59,11 @@ namespace dawn_native { }; using CommandBlocks = std::vector; + namespace detail { + constexpr uint32_t kEndOfBlock = std::numeric_limits::max(); + constexpr uint32_t kAdditionalData = std::numeric_limits::max() - 1; + } // namespace detail + class CommandAllocator; // TODO(cwallez@chromium.org): prevent copy for both iterator and allocator @@ -91,15 +99,46 @@ namespace dawn_native { private: bool IsEmpty() const; - bool NextCommandId(uint32_t* commandId); - void* NextCommand(size_t commandSize, size_t commandAlignment); - void* NextData(size_t dataSize, size_t dataAlignment); + DAWN_FORCE_INLINE bool NextCommandId(uint32_t* commandId) { + uint8_t* idPtr = AlignPtr(mCurrentPtr, alignof(uint32_t)); + ASSERT(idPtr + sizeof(uint32_t) <= + mBlocks[mCurrentBlock].block + mBlocks[mCurrentBlock].size); + + uint32_t id = *reinterpret_cast(idPtr); + + if (id != detail::kEndOfBlock) { + mCurrentPtr = idPtr + sizeof(uint32_t); + *commandId = id; + return true; + } + return NextCommandIdInNewBlock(commandId); + } + + bool NextCommandIdInNewBlock(uint32_t* commandId); + + DAWN_FORCE_INLINE void* NextCommand(size_t commandSize, size_t commandAlignment) { + uint8_t* commandPtr = AlignPtr(mCurrentPtr, commandAlignment); + ASSERT(commandPtr + sizeof(commandSize) <= + mBlocks[mCurrentBlock].block + mBlocks[mCurrentBlock].size); + + mCurrentPtr = commandPtr + commandSize; + return commandPtr; + } + + DAWN_FORCE_INLINE void* NextData(size_t dataSize, size_t dataAlignment) { + uint32_t id; + bool hasId = NextCommandId(&id); + ASSERT(hasId); + ASSERT(id == detail::kAdditionalData); + + return NextCommand(dataSize, dataAlignment); + } CommandBlocks mBlocks; uint8_t* mCurrentPtr = nullptr; size_t mCurrentBlock = 0; // Used to avoid a special case for empty iterators. - uint32_t mEndOfBlock; + uint32_t mEndOfBlock = detail::kEndOfBlock; bool mDataWasDestroyed = false; }; @@ -140,18 +179,67 @@ namespace dawn_native { // using the CommandAllocator passes the static_asserts. static constexpr size_t kMaxSupportedAlignment = 8; + // To avoid checking for overflows at every step of the computations we compute an upper + // bound of the space that will be needed in addition to the command data. + static constexpr size_t kWorstCaseAdditionalSize = + sizeof(uint32_t) + kMaxSupportedAlignment + alignof(uint32_t) + sizeof(uint32_t); + friend CommandIterator; CommandBlocks&& AcquireBlocks(); - uint8_t* Allocate(uint32_t commandId, size_t commandSize, size_t commandAlignment); - uint8_t* AllocateData(size_t dataSize, size_t dataAlignment); + DAWN_FORCE_INLINE uint8_t* Allocate(uint32_t commandId, + size_t commandSize, + size_t commandAlignment) { + ASSERT(mCurrentPtr != nullptr); + ASSERT(mEndPtr != nullptr); + ASSERT(commandId != detail::kEndOfBlock); + + // It should always be possible to allocate one id, for kEndOfBlock tagging, + ASSERT(IsPtrAligned(mCurrentPtr, alignof(uint32_t))); + ASSERT(mEndPtr >= mCurrentPtr); + ASSERT(static_cast(mEndPtr - mCurrentPtr) >= sizeof(uint32_t)); + + // The memory after the ID will contain the following: + // - the current ID + // - padding to align the command, maximum kMaxSupportedAlignment + // - the command of size commandSize + // - padding to align the next ID, maximum alignof(uint32_t) + // - the next ID of size sizeof(uint32_t) + + // This can't overflow because by construction mCurrentPtr always has space for the next + // ID. + size_t remainingSize = static_cast(mEndPtr - mCurrentPtr); + + // The good case were we have enough space for the command data and upper bound of the + // extra required space. + if ((remainingSize >= kWorstCaseAdditionalSize) && + (remainingSize - kWorstCaseAdditionalSize >= commandSize)) { + uint32_t* idAlloc = reinterpret_cast(mCurrentPtr); + *idAlloc = commandId; + + uint8_t* commandAlloc = AlignPtr(mCurrentPtr + sizeof(uint32_t), commandAlignment); + mCurrentPtr = AlignPtr(commandAlloc + commandSize, alignof(uint32_t)); + + return commandAlloc; + } + return AllocateInNewBlock(commandId, commandSize, commandAlignment); + } + + uint8_t* AllocateInNewBlock(uint32_t commandId, + size_t commandSize, + size_t commandAlignment); + + DAWN_FORCE_INLINE uint8_t* AllocateData(size_t commandSize, size_t commandAlignment) { + return Allocate(detail::kAdditionalData, commandSize, commandAlignment); + } + bool GetNewBlock(size_t minimumSize); CommandBlocks mBlocks; size_t mLastAllocationSize = 2048; // Pointers to the current range of allocation in the block. Guaranteed to allow for at - // least one uint32_t if not nullptr, so that the special EndOfBlock command id can always + // least one uint32_t if not nullptr, so that the special kEndOfBlock command id can always // be written. Nullptr iff the blocks were moved out. uint8_t* mCurrentPtr = nullptr; uint8_t* mEndPtr = nullptr; diff --git a/third_party/dawn/src/dawn_native/CommandBuffer.cpp b/third_party/dawn/src/dawn_native/CommandBuffer.cpp index 529fe653bf5..e1446ab7561 100644 --- a/third_party/dawn/src/dawn_native/CommandBuffer.cpp +++ b/third_party/dawn/src/dawn_native/CommandBuffer.cpp @@ -14,13 +14,17 @@ #include "dawn_native/CommandBuffer.h" +#include "common/BitSetIterator.h" +#include "dawn_native/Buffer.h" #include "dawn_native/CommandEncoder.h" +#include "dawn_native/Commands.h" +#include "dawn_native/Format.h" #include "dawn_native/Texture.h" namespace dawn_native { - CommandBufferBase::CommandBufferBase(DeviceBase* device, CommandEncoderBase* encoder) - : ObjectBase(device), mResourceUsages(encoder->AcquireResourceUsages()) { + CommandBufferBase::CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor*) + : ObjectBase(encoder->GetDevice()), mResourceUsages(encoder->AcquireResourceUsages()) { } CommandBufferBase::CommandBufferBase(DeviceBase* device, ObjectBase::ErrorTag tag) @@ -39,11 +43,136 @@ namespace dawn_native { bool IsCompleteSubresourceCopiedTo(const TextureBase* texture, const Extent3D copySize, const uint32_t mipLevel) { - if (texture->GetSize().depth == copySize.depth && - (texture->GetSize().width >> mipLevel) == copySize.width && - (texture->GetSize().height >> mipLevel) == copySize.height) { + Extent3D extent = texture->GetMipLevelPhysicalSize(mipLevel); + + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + if (extent.width == copySize.width && extent.height == copySize.height) { return true; } return false; } + + SubresourceRange GetSubresourcesAffectedByCopy(const TextureCopy& copy, + const Extent3D& copySize) { + switch (copy.texture->GetDimension()) { + case wgpu::TextureDimension::e2D: + return {copy.mipLevel, 1, copy.origin.z, copySize.depth}; + default: + UNREACHABLE(); + return {}; + } + } + + void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass) { + for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { + auto& attachmentInfo = renderPass->colorAttachments[i]; + TextureViewBase* view = attachmentInfo.view.Get(); + bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr; + + ASSERT(view->GetLayerCount() == 1); + ASSERT(view->GetLevelCount() == 1); + SubresourceRange range = view->GetSubresourceRange(); + + // If the loadOp is Load, but the subresource is not initialized, use Clear instead. + if (attachmentInfo.loadOp == wgpu::LoadOp::Load && + !view->GetTexture()->IsSubresourceContentInitialized(range)) { + attachmentInfo.loadOp = wgpu::LoadOp::Clear; + attachmentInfo.clearColor = {0.f, 0.f, 0.f, 0.f}; + } + + if (hasResolveTarget) { + // We need to set the resolve target to initialized so that it does not get + // cleared later in the pipeline. The texture will be resolved from the + // source color attachment, which will be correctly initialized. + TextureViewBase* resolveView = attachmentInfo.resolveTarget.Get(); + ASSERT(resolveView->GetLayerCount() == 1); + ASSERT(resolveView->GetLevelCount() == 1); + resolveView->GetTexture()->SetIsSubresourceContentInitialized( + true, resolveView->GetSubresourceRange()); + } + + switch (attachmentInfo.storeOp) { + case wgpu::StoreOp::Store: + view->GetTexture()->SetIsSubresourceContentInitialized(true, range); + break; + + case wgpu::StoreOp::Clear: + view->GetTexture()->SetIsSubresourceContentInitialized(false, range); + break; + + default: + UNREACHABLE(); + break; + } + } + + if (renderPass->attachmentState->HasDepthStencilAttachment()) { + auto& attachmentInfo = renderPass->depthStencilAttachment; + TextureViewBase* view = attachmentInfo.view.Get(); + ASSERT(view->GetLayerCount() == 1); + ASSERT(view->GetLevelCount() == 1); + SubresourceRange range = view->GetSubresourceRange(); + + // If the depth stencil texture has not been initialized, we want to use loadop + // clear to init the contents to 0's + if (!view->GetTexture()->IsSubresourceContentInitialized(range)) { + if (view->GetTexture()->GetFormat().HasDepth() && + attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) { + attachmentInfo.clearDepth = 0.0f; + attachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; + } + if (view->GetTexture()->GetFormat().HasStencil() && + attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) { + attachmentInfo.clearStencil = 0u; + attachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; + } + } + + // If these have different store ops, make them both Store because we can't track + // initialized state separately yet. TODO(crbug.com/dawn/145) + if (attachmentInfo.depthStoreOp != attachmentInfo.stencilStoreOp) { + attachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + attachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + } + + if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Store && + attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store) { + view->GetTexture()->SetIsSubresourceContentInitialized(true, range); + } else { + ASSERT(attachmentInfo.depthStoreOp == wgpu::StoreOp::Clear && + attachmentInfo.stencilStoreOp == wgpu::StoreOp::Clear); + view->GetTexture()->SetIsSubresourceContentInitialized(false, range); + } + } + } + + // TODO(jiawei.shao@intel.com): support copying with depth stencil textures + bool IsFullBufferOverwrittenInTextureToBufferCopy(const CopyTextureToBufferCmd* copy) { + ASSERT(copy != nullptr); + + if (copy->destination.offset > 0) { + return false; + } + + if (copy->destination.rowsPerImage > copy->copySize.height) { + return false; + } + + const TextureBase* texture = copy->source.texture.Get(); + const uint64_t copyTextureDataSizePerRow = copy->copySize.width / + texture->GetFormat().blockWidth * + texture->GetFormat().blockByteSize; + if (copy->destination.bytesPerRow > copyTextureDataSizePerRow) { + return false; + } + + const uint64_t overwrittenRangeSize = + copyTextureDataSizePerRow * (copy->copySize.height / texture->GetFormat().blockHeight) * + copy->copySize.depth; + if (copy->destination.buffer->GetSize() > overwrittenRangeSize) { + return false; + } + + return true; + } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/CommandBuffer.h b/third_party/dawn/src/dawn_native/CommandBuffer.h index 691290206ca..375f1028108 100644 --- a/third_party/dawn/src/dawn_native/CommandBuffer.h +++ b/third_party/dawn/src/dawn_native/CommandBuffer.h @@ -20,12 +20,17 @@ #include "dawn_native/Forward.h" #include "dawn_native/ObjectBase.h" #include "dawn_native/PassResourceUsage.h" +#include "dawn_native/Texture.h" namespace dawn_native { + struct BeginRenderPassCmd; + struct CopyTextureToBufferCmd; + struct TextureCopy; + class CommandBufferBase : public ObjectBase { public: - CommandBufferBase(DeviceBase* device, CommandEncoderBase* encoder); + CommandBufferBase(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor); static CommandBufferBase* MakeError(DeviceBase* device); const CommandBufferResourceUsage& GetResourceUsages() const; @@ -35,9 +40,16 @@ namespace dawn_native { CommandBufferResourceUsage mResourceUsages; }; + bool IsCompleteSubresourceCopiedTo(const TextureBase* texture, const Extent3D copySize, const uint32_t mipLevel); + SubresourceRange GetSubresourcesAffectedByCopy(const TextureCopy& copy, + const Extent3D& copySize); + + void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass); + + bool IsFullBufferOverwrittenInTextureToBufferCopy(const CopyTextureToBufferCmd* copy); } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/CommandBufferStateTracker.cpp b/third_party/dawn/src/dawn_native/CommandBufferStateTracker.cpp index 26970410939..7c4a327acaa 100644 --- a/third_party/dawn/src/dawn_native/CommandBufferStateTracker.cpp +++ b/third_party/dawn/src/dawn_native/CommandBufferStateTracker.cpp @@ -24,6 +24,21 @@ namespace dawn_native { + namespace { + bool BufferSizesAtLeastAsBig(const ityp::span unverifiedBufferSizes, + const std::vector& pipelineMinimumBufferSizes) { + ASSERT(unverifiedBufferSizes.size() == pipelineMinimumBufferSizes.size()); + + for (uint32_t i = 0; i < unverifiedBufferSizes.size(); ++i) { + if (unverifiedBufferSizes[i] < pipelineMinimumBufferSizes[i]) { + return false; + } + } + + return true; + } + } // namespace + enum ValidationAspect { VALIDATION_ASPECT_PIPELINE, VALIDATION_ASPECT_BIND_GROUPS, @@ -69,16 +84,11 @@ namespace dawn_native { // Generate an error immediately if a non-lazy aspect is missing as computing lazy aspects // requires the pipeline to be set. - if ((missingAspects & ~kLazyAspects).any()) { - return GenerateAspectError(missingAspects); - } + DAWN_TRY(CheckMissingAspects(missingAspects & ~kLazyAspects)); RecomputeLazyAspects(missingAspects); - missingAspects = requiredAspects & ~mAspects; - if (missingAspects.any()) { - return GenerateAspectError(missingAspects); - } + DAWN_TRY(CheckMissingAspects(requiredAspects & ~mAspects)); return {}; } @@ -90,9 +100,11 @@ namespace dawn_native { if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) { bool matches = true; - for (uint32_t i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) { + for (BindGroupIndex i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) { if (mBindgroups[i] == nullptr || - mLastPipelineLayout->GetBindGroupLayout(i) != mBindgroups[i]->GetLayout()) { + mLastPipelineLayout->GetBindGroupLayout(i) != mBindgroups[i]->GetLayout() || + !BufferSizesAtLeastAsBig(mBindgroups[i]->GetUnverifiedBufferSizes(), + (*mMinimumBufferSizes)[i])) { matches = false; break; } @@ -106,15 +118,18 @@ namespace dawn_native { if (aspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) { ASSERT(mLastRenderPipeline != nullptr); - auto requiredInputs = mLastRenderPipeline->GetInputsSetMask(); - if ((mInputsSet & requiredInputs) == requiredInputs) { + const std::bitset& requiredVertexBuffers = + mLastRenderPipeline->GetVertexBufferSlotsUsed(); + if ((mVertexBufferSlotsUsed & requiredVertexBuffers) == requiredVertexBuffers) { mAspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS); } } } - MaybeError CommandBufferStateTracker::GenerateAspectError(ValidationAspects aspects) { - ASSERT(aspects.any()); + MaybeError CommandBufferStateTracker::CheckMissingAspects(ValidationAspects aspects) { + if (!aspects.any()) { + return {}; + } if (aspects[VALIDATION_ASPECT_INDEX_BUFFER]) { return DAWN_VALIDATION_ERROR("Missing index buffer"); @@ -125,7 +140,28 @@ namespace dawn_native { } if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) { - return DAWN_VALIDATION_ERROR("Missing bind group"); + for (BindGroupIndex i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) { + if (mBindgroups[i] == nullptr) { + return DAWN_VALIDATION_ERROR("Missing bind group " + + std::to_string(static_cast(i))); + } else if (mLastPipelineLayout->GetBindGroupLayout(i) != + mBindgroups[i]->GetLayout()) { + return DAWN_VALIDATION_ERROR( + "Pipeline and bind group layout doesn't match for bind group " + + std::to_string(static_cast(i))); + } else if (!BufferSizesAtLeastAsBig(mBindgroups[i]->GetUnverifiedBufferSizes(), + (*mMinimumBufferSizes)[i])) { + return DAWN_VALIDATION_ERROR("Binding sizes too small for bind group " + + std::to_string(static_cast(i))); + } + } + + // The chunk of code above should be similar to the one in |RecomputeLazyAspects|. + // It returns the first invalid state found. We shouldn't be able to reach this line + // because to have invalid aspects one of the above conditions must have failed earlier. + // If this is reached, make sure lazy aspects and the error checks above are consistent. + UNREACHABLE(); + return DAWN_VALIDATION_ERROR("Bind groups invalid"); } if (aspects[VALIDATION_ASPECT_PIPELINE]) { @@ -144,22 +180,22 @@ namespace dawn_native { SetPipelineCommon(pipeline); } - void CommandBufferStateTracker::SetBindGroup(uint32_t index, BindGroupBase* bindgroup) { + void CommandBufferStateTracker::SetBindGroup(BindGroupIndex index, BindGroupBase* bindgroup) { mBindgroups[index] = bindgroup; + mAspects.reset(VALIDATION_ASPECT_BIND_GROUPS); } void CommandBufferStateTracker::SetIndexBuffer() { mAspects.set(VALIDATION_ASPECT_INDEX_BUFFER); } - void CommandBufferStateTracker::SetVertexBuffer(uint32_t start, uint32_t count) { - for (uint32_t i = 0; i < count; ++i) { - mInputsSet.set(start + i); - } + void CommandBufferStateTracker::SetVertexBuffer(uint32_t slot) { + mVertexBufferSlotsUsed.set(slot); } void CommandBufferStateTracker::SetPipelineCommon(PipelineBase* pipeline) { mLastPipelineLayout = pipeline->GetLayout(); + mMinimumBufferSizes = &pipeline->GetMinimumBufferSizes(); mAspects.set(VALIDATION_ASPECT_PIPELINE); diff --git a/third_party/dawn/src/dawn_native/CommandBufferStateTracker.h b/third_party/dawn/src/dawn_native/CommandBufferStateTracker.h index 2009dd73408..39d32fde9d4 100644 --- a/third_party/dawn/src/dawn_native/CommandBufferStateTracker.h +++ b/third_party/dawn/src/dawn_native/CommandBufferStateTracker.h @@ -16,10 +16,11 @@ #define DAWNNATIVE_COMMANDBUFFERSTATETRACKER_H #include "common/Constants.h" +#include "common/ityp_array.h" +#include "dawn_native/BindingInfo.h" #include "dawn_native/Error.h" #include "dawn_native/Forward.h" -#include #include #include #include @@ -36,9 +37,9 @@ namespace dawn_native { // State-modifying methods void SetComputePipeline(ComputePipelineBase* pipeline); void SetRenderPipeline(RenderPipelineBase* pipeline); - void SetBindGroup(uint32_t index, BindGroupBase* bindgroup); + void SetBindGroup(BindGroupIndex index, BindGroupBase* bindgroup); void SetIndexBuffer(); - void SetVertexBuffer(uint32_t start, uint32_t count); + void SetVertexBuffer(uint32_t slot); static constexpr size_t kNumAspects = 4; using ValidationAspects = std::bitset; @@ -46,17 +47,19 @@ namespace dawn_native { private: MaybeError ValidateOperation(ValidationAspects requiredAspects); void RecomputeLazyAspects(ValidationAspects aspects); - MaybeError GenerateAspectError(ValidationAspects aspects); + MaybeError CheckMissingAspects(ValidationAspects aspects); void SetPipelineCommon(PipelineBase* pipeline); ValidationAspects mAspects; - std::array mBindgroups = {}; - std::bitset mInputsSet; + ityp::array mBindgroups = {}; + std::bitset mVertexBufferSlotsUsed; PipelineLayoutBase* mLastPipelineLayout = nullptr; RenderPipelineBase* mLastRenderPipeline = nullptr; + + const RequiredBufferSizes* mMinimumBufferSizes = nullptr; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/CommandEncoder.cpp b/third_party/dawn/src/dawn_native/CommandEncoder.cpp index 3fa880d7212..1a387b7e4f7 100644 --- a/third_party/dawn/src/dawn_native/CommandEncoder.cpp +++ b/third_party/dawn/src/dawn_native/CommandEncoder.cpp @@ -19,68 +19,28 @@ #include "dawn_native/Buffer.h" #include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandBufferStateTracker.h" +#include "dawn_native/CommandValidation.h" #include "dawn_native/Commands.h" #include "dawn_native/ComputePassEncoder.h" #include "dawn_native/Device.h" #include "dawn_native/ErrorData.h" +#include "dawn_native/QuerySet.h" #include "dawn_native/RenderPassEncoder.h" #include "dawn_native/RenderPipeline.h" +#include "dawn_native/ValidationUtils_autogen.h" +#include "dawn_platform/DawnPlatform.h" +#include "dawn_platform/tracing/TraceEvent.h" +#include #include namespace dawn_native { namespace { - MaybeError ValidateCopySizeFitsInTexture(const TextureCopy& textureCopy, - const Extent3D& copySize) { - const TextureBase* texture = textureCopy.texture.Get(); - if (textureCopy.level >= texture->GetNumMipLevels()) { - return DAWN_VALIDATION_ERROR("Copy mip-level out of range"); - } - - if (textureCopy.slice >= texture->GetArrayLayers()) { - return DAWN_VALIDATION_ERROR("Copy array-layer out of range"); - } - - // All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid - // overflows. - uint64_t level = textureCopy.level; - if (uint64_t(textureCopy.origin.x) + uint64_t(copySize.width) > - (static_cast(texture->GetSize().width) >> level) || - uint64_t(textureCopy.origin.y) + uint64_t(copySize.height) > - (static_cast(texture->GetSize().height) >> level)) { - return DAWN_VALIDATION_ERROR("Copy would touch outside of the texture"); - } - - // TODO(cwallez@chromium.org): Check the depth bound differently for 2D arrays and 3D - // textures - if (textureCopy.origin.z != 0 || copySize.depth > 1) { - return DAWN_VALIDATION_ERROR("No support for z != 0 and depth > 1 for now"); - } - - return {}; - } - - MaybeError ValidateCopySizeFitsInBuffer(const Ref& buffer, - uint64_t offset, - uint64_t size) { - uint64_t bufferSize = buffer->GetSize(); - bool fitsInBuffer = offset <= bufferSize && (size <= (bufferSize - offset)); - if (!fitsInBuffer) { - return DAWN_VALIDATION_ERROR("Copy would overflow the buffer"); - } - - return {}; - } - - MaybeError ValidateCopySizeFitsInBuffer(const BufferCopy& bufferCopy, uint64_t dataSize) { - return ValidateCopySizeFitsInBuffer(bufferCopy.buffer, bufferCopy.offset, dataSize); - } - - MaybeError ValidateB2BCopySizeAlignment(uint64_t dataSize, - uint64_t srcOffset, - uint64_t dstOffset) { + MaybeError ValidateB2BCopyAlignment(uint64_t dataSize, + uint64_t srcOffset, + uint64_t dstOffset) { // Copy size must be a multiple of 4 bytes on macOS. if (dataSize % 4 != 0) { return DAWN_VALIDATION_ERROR("Copy size must be a multiple of 4 bytes"); @@ -95,47 +55,6 @@ namespace dawn_native { return {}; } - MaybeError ValidateTexelBufferOffset(TextureBase* texture, const BufferCopy& bufferCopy) { - uint32_t texelSize = - static_cast(TextureFormatPixelSize(texture->GetFormat())); - if (bufferCopy.offset % texelSize != 0) { - return DAWN_VALIDATION_ERROR("Buffer offset must be a multiple of the texel size"); - } - - return {}; - } - - MaybeError ValidateImageHeight(uint32_t imageHeight, uint32_t copyHeight) { - if (imageHeight < copyHeight) { - return DAWN_VALIDATION_ERROR("Image height must not be less than the copy height."); - } - - return {}; - } - - inline MaybeError PushDebugMarkerStack(unsigned int* counter) { - *counter += 1; - return {}; - } - - inline MaybeError PopDebugMarkerStack(unsigned int* counter) { - if (*counter == 0) { - return DAWN_VALIDATION_ERROR("Pop must be balanced by a corresponding Push."); - } else { - *counter -= 1; - } - - return {}; - } - - inline MaybeError ValidateDebugGroups(const unsigned int counter) { - if (counter != 0) { - return DAWN_VALIDATION_ERROR("Each Push must be balanced by a corresponding Pop."); - } - - return {}; - } - MaybeError ValidateTextureSampleCountInCopyCommands(const TextureBase* texture) { if (texture->GetSampleCount() > 1) { return DAWN_VALIDATION_ERROR("The sample count of textures must be 1"); @@ -144,14 +63,15 @@ namespace dawn_native { return {}; } - MaybeError ValidateEntireSubresourceCopied(const TextureCopy& src, - const TextureCopy& dst, + MaybeError ValidateEntireSubresourceCopied(const TextureCopyView& src, + const TextureCopyView& dst, const Extent3D& copySize) { - Extent3D srcSize = src.texture.Get()->GetSize(); + Extent3D srcSize = src.texture->GetSize(); - if (dst.origin.x != 0 || dst.origin.y != 0 || dst.origin.z != 0 || - srcSize.width != copySize.width || srcSize.height != copySize.height || - srcSize.depth != copySize.depth) { + ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D && + dst.texture->GetDimension() == wgpu::TextureDimension::e2D); + if (dst.origin.x != 0 || dst.origin.y != 0 || srcSize.width != copySize.width || + srcSize.height != copySize.height) { return DAWN_VALIDATION_ERROR( "The entire subresource must be copied when using a depth/stencil texture or " "when samples are greater than 1."); @@ -160,11 +80,11 @@ namespace dawn_native { return {}; } - MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopy& src, - const TextureCopy& dst, + MaybeError ValidateTextureToTextureCopyRestrictions(const TextureCopyView& src, + const TextureCopyView& dst, const Extent3D& copySize) { - const uint32_t srcSamples = src.texture.Get()->GetSampleCount(); - const uint32_t dstSamples = dst.texture.Get()->GetSampleCount(); + const uint32_t srcSamples = src.texture->GetSampleCount(); + const uint32_t dstSamples = dst.texture->GetSampleCount(); if (srcSamples != dstSamples) { return DAWN_VALIDATION_ERROR( @@ -175,57 +95,32 @@ namespace dawn_native { DAWN_TRY(ValidateEntireSubresourceCopied(src, dst, copySize)); } - if (src.texture.Get()->GetFormat() != dst.texture.Get()->GetFormat()) { + if (src.texture->GetFormat().format != dst.texture->GetFormat().format) { // Metal requires texture-to-texture copies be the same format return DAWN_VALIDATION_ERROR("Source and destination texture formats must match."); - } else if (TextureFormatHasDepthOrStencil(src.texture.Get()->GetFormat())) { + } + + if (src.texture->GetFormat().HasDepthOrStencil()) { // D3D12 requires entire subresource to be copied when using CopyTextureRegion is // used with depth/stencil. DAWN_TRY(ValidateEntireSubresourceCopied(src, dst, copySize)); } - return {}; - } - - MaybeError ComputeTextureCopyBufferSize(dawn::TextureFormat textureFormat, - const Extent3D& copySize, - uint32_t rowPitch, - uint32_t imageHeight, - uint32_t* bufferSize) { - DAWN_TRY(ValidateImageHeight(imageHeight, copySize.height)); - - // TODO(cwallez@chromium.org): check for overflows - uint32_t slicePitch = rowPitch * imageHeight; - uint32_t sliceSize = rowPitch * (copySize.height - 1) + - copySize.width * TextureFormatPixelSize(textureFormat); - *bufferSize = (slicePitch * (copySize.depth - 1)) + sliceSize; - - return {}; - } - - uint32_t ComputeDefaultRowPitch(TextureBase* texture, uint32_t width) { - uint32_t texelSize = TextureFormatPixelSize(texture->GetFormat()); - return texelSize * width; - } - - MaybeError ValidateRowPitch(dawn::TextureFormat format, - const Extent3D& copySize, - uint32_t rowPitch) { - if (rowPitch % kTextureRowPitchAlignment != 0) { - return DAWN_VALIDATION_ERROR("Row pitch must be a multiple of 256"); - } - - uint32_t texelSize = TextureFormatPixelSize(format); - if (rowPitch < copySize.width * texelSize) { - return DAWN_VALIDATION_ERROR( - "Row pitch must not be less than the number of bytes per row"); + if (src.texture == dst.texture && src.mipLevel == dst.mipLevel) { + ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D && + dst.texture->GetDimension() == wgpu::TextureDimension::e2D); + if (IsRangeOverlapped(src.origin.z, dst.origin.z, copySize.depth)) { + return DAWN_VALIDATION_ERROR( + "Copy subresources cannot be overlapped when copying within the same " + "texture."); + } } return {}; } - MaybeError ValidateCanUseAs(BufferBase* buffer, dawn::BufferUsageBit usage) { - ASSERT(HasZeroOrOneBits(usage)); + MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) { + ASSERT(wgpu::HasZeroOrOneBits(usage)); if (!(buffer->GetUsage() & usage)) { return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage."); } @@ -233,8 +128,8 @@ namespace dawn_native { return {}; } - MaybeError ValidateCanUseAs(TextureBase* texture, dawn::TextureUsageBit usage) { - ASSERT(HasZeroOrOneBits(usage)); + MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage) { + ASSERT(wgpu::HasZeroOrOneBits(usage)); if (!(texture->GetUsage() & usage)) { return DAWN_VALIDATION_ERROR("texture doesn't have the required usage."); } @@ -292,39 +187,40 @@ namespace dawn_native { MaybeError ValidateResolveTarget( const DeviceBase* device, - const RenderPassColorAttachmentDescriptor* colorAttachment) { - if (colorAttachment->resolveTarget == nullptr) { + const RenderPassColorAttachmentDescriptor& colorAttachment) { + if (colorAttachment.resolveTarget == nullptr) { return {}; } - DAWN_TRY(device->ValidateObject(colorAttachment->resolveTarget)); + const TextureViewBase* resolveTarget = colorAttachment.resolveTarget; + const TextureViewBase* attachment = colorAttachment.attachment; + DAWN_TRY(device->ValidateObject(colorAttachment.resolveTarget)); - if (!colorAttachment->attachment->GetTexture()->IsMultisampledTexture()) { + if (!attachment->GetTexture()->IsMultisampledTexture()) { return DAWN_VALIDATION_ERROR( "Cannot set resolve target when the sample count of the color attachment is 1"); } - if (colorAttachment->resolveTarget->GetTexture()->IsMultisampledTexture()) { + if (resolveTarget->GetTexture()->IsMultisampledTexture()) { return DAWN_VALIDATION_ERROR("Cannot use multisampled texture as resolve target"); } - if (colorAttachment->resolveTarget->GetLayerCount() > 1) { + if (resolveTarget->GetLayerCount() > 1) { return DAWN_VALIDATION_ERROR( "The array layer count of the resolve target must be 1"); } - if (colorAttachment->resolveTarget->GetLevelCount() > 1) { + if (resolveTarget->GetLevelCount() > 1) { return DAWN_VALIDATION_ERROR("The mip level count of the resolve target must be 1"); } - uint32_t colorAttachmentBaseMipLevel = colorAttachment->attachment->GetBaseMipLevel(); - const Extent3D& colorTextureSize = colorAttachment->attachment->GetTexture()->GetSize(); + uint32_t colorAttachmentBaseMipLevel = attachment->GetBaseMipLevel(); + const Extent3D& colorTextureSize = attachment->GetTexture()->GetSize(); uint32_t colorAttachmentWidth = colorTextureSize.width >> colorAttachmentBaseMipLevel; uint32_t colorAttachmentHeight = colorTextureSize.height >> colorAttachmentBaseMipLevel; - uint32_t resolveTargetBaseMipLevel = colorAttachment->resolveTarget->GetBaseMipLevel(); - const Extent3D& resolveTextureSize = - colorAttachment->resolveTarget->GetTexture()->GetSize(); + uint32_t resolveTargetBaseMipLevel = resolveTarget->GetBaseMipLevel(); + const Extent3D& resolveTextureSize = resolveTarget->GetTexture()->GetSize(); uint32_t resolveTargetWidth = resolveTextureSize.width >> resolveTargetBaseMipLevel; uint32_t resolveTargetHeight = resolveTextureSize.height >> resolveTargetBaseMipLevel; if (colorAttachmentWidth != resolveTargetWidth || @@ -333,8 +229,8 @@ namespace dawn_native { "The size of the resolve target must be the same as the color attachment"); } - dawn::TextureFormat resolveTargetFormat = colorAttachment->resolveTarget->GetFormat(); - if (resolveTargetFormat != colorAttachment->attachment->GetFormat()) { + wgpu::TextureFormat resolveTargetFormat = resolveTarget->GetFormat().format; + if (resolveTargetFormat != attachment->GetFormat().format) { return DAWN_VALIDATION_ERROR( "The format of the resolve target must be the same as the color attachment"); } @@ -344,21 +240,31 @@ namespace dawn_native { MaybeError ValidateRenderPassColorAttachment( const DeviceBase* device, - const RenderPassColorAttachmentDescriptor* colorAttachment, + const RenderPassColorAttachmentDescriptor& colorAttachment, uint32_t* width, uint32_t* height, uint32_t* sampleCount) { - DAWN_ASSERT(colorAttachment != nullptr); + DAWN_TRY(device->ValidateObject(colorAttachment.attachment)); - DAWN_TRY(device->ValidateObject(colorAttachment->attachment)); - - const TextureViewBase* attachment = colorAttachment->attachment; - if (!IsColorRenderableTextureFormat(attachment->GetFormat())) { + const TextureViewBase* attachment = colorAttachment.attachment; + if (!attachment->GetFormat().IsColor() || !attachment->GetFormat().isRenderable) { return DAWN_VALIDATION_ERROR( "The format of the texture view used as color attachment is not color " "renderable"); } + DAWN_TRY(ValidateLoadOp(colorAttachment.loadOp)); + DAWN_TRY(ValidateStoreOp(colorAttachment.storeOp)); + + if (colorAttachment.loadOp == wgpu::LoadOp::Clear) { + if (std::isnan(colorAttachment.clearColor.r) || + std::isnan(colorAttachment.clearColor.g) || + std::isnan(colorAttachment.clearColor.b) || + std::isnan(colorAttachment.clearColor.a)) { + return DAWN_VALIDATION_ERROR("Color clear value cannot contain NaN"); + } + } + DAWN_TRY(ValidateOrSetColorAttachmentSampleCount(attachment, sampleCount)); DAWN_TRY(ValidateResolveTarget(device, colorAttachment)); @@ -380,12 +286,54 @@ namespace dawn_native { DAWN_TRY(device->ValidateObject(depthStencilAttachment->attachment)); const TextureViewBase* attachment = depthStencilAttachment->attachment; - if (!TextureFormatHasDepthOrStencil(attachment->GetFormat())) { + if (!attachment->GetFormat().HasDepthOrStencil() || + !attachment->GetFormat().isRenderable) { return DAWN_VALIDATION_ERROR( "The format of the texture view used as depth stencil attachment is not a " "depth stencil format"); } + DAWN_TRY(ValidateLoadOp(depthStencilAttachment->depthLoadOp)); + DAWN_TRY(ValidateLoadOp(depthStencilAttachment->stencilLoadOp)); + DAWN_TRY(ValidateStoreOp(depthStencilAttachment->depthStoreOp)); + DAWN_TRY(ValidateStoreOp(depthStencilAttachment->stencilStoreOp)); + + if (attachment->GetAspect() == wgpu::TextureAspect::All && + attachment->GetFormat().HasStencil() && + depthStencilAttachment->depthReadOnly != depthStencilAttachment->stencilReadOnly) { + return DAWN_VALIDATION_ERROR( + "depthReadOnly and stencilReadOnly must be the same when texture aspect is " + "'all'"); + } + + if (depthStencilAttachment->depthReadOnly && + (depthStencilAttachment->depthLoadOp != wgpu::LoadOp::Load || + depthStencilAttachment->depthStoreOp != wgpu::StoreOp::Store)) { + return DAWN_VALIDATION_ERROR( + "depthLoadOp must be load and depthStoreOp must be store when depthReadOnly " + "is true."); + } + + if (depthStencilAttachment->stencilReadOnly && + (depthStencilAttachment->stencilLoadOp != wgpu::LoadOp::Load || + depthStencilAttachment->stencilStoreOp != wgpu::StoreOp::Store)) { + return DAWN_VALIDATION_ERROR( + "stencilLoadOp must be load and stencilStoreOp must be store when " + "stencilReadOnly " + "is true."); + } + + if (depthStencilAttachment->depthLoadOp == wgpu::LoadOp::Clear && + std::isnan(depthStencilAttachment->clearDepth)) { + return DAWN_VALIDATION_ERROR("Depth clear value cannot be NaN"); + } + + // This validates that the depth storeOp and stencil storeOps are the same + if (depthStencilAttachment->depthStoreOp != depthStencilAttachment->stencilStoreOp) { + return DAWN_VALIDATION_ERROR( + "The depth storeOp and stencil storeOp are not the same"); + } + // *sampleCount == 0 must only happen when there is no color attachment. In that case we // do not need to validate the sample count of the depth stencil attachment. const uint32_t depthStencilSampleCount = attachment->GetTexture()->GetSampleCount(); @@ -404,792 +352,631 @@ namespace dawn_native { } MaybeError ValidateRenderPassDescriptor(const DeviceBase* device, - const RenderPassDescriptor* renderPass, + const RenderPassDescriptor* descriptor, uint32_t* width, uint32_t* height, uint32_t* sampleCount) { - if (renderPass->colorAttachmentCount > kMaxColorAttachments) { + if (descriptor->colorAttachmentCount > kMaxColorAttachments) { return DAWN_VALIDATION_ERROR("Setting color attachments out of bounds"); } - for (uint32_t i = 0; i < renderPass->colorAttachmentCount; ++i) { - DAWN_TRY(ValidateRenderPassColorAttachment(device, renderPass->colorAttachments[i], + for (uint32_t i = 0; i < descriptor->colorAttachmentCount; ++i) { + DAWN_TRY(ValidateRenderPassColorAttachment(device, descriptor->colorAttachments[i], width, height, sampleCount)); } - if (renderPass->depthStencilAttachment != nullptr) { + if (descriptor->depthStencilAttachment != nullptr) { DAWN_TRY(ValidateRenderPassDepthStencilAttachment( - device, renderPass->depthStencilAttachment, width, height, sampleCount)); + device, descriptor->depthStencilAttachment, width, height, sampleCount)); + } + + if (descriptor->occlusionQuerySet != nullptr) { + return DAWN_VALIDATION_ERROR("occlusionQuerySet not implemented"); } - if (renderPass->colorAttachmentCount == 0 && - renderPass->depthStencilAttachment == nullptr) { + if (descriptor->colorAttachmentCount == 0 && + descriptor->depthStencilAttachment == nullptr) { return DAWN_VALIDATION_ERROR("Cannot use render pass with no attachments."); } return {}; } - enum class PassType { - Render, - Compute, - }; - - // Helper class to encapsulate the logic of tracking per-resource usage during the - // validation of command buffer passes. It is used both to know if there are validation - // errors, and to get a list of resources used per pass for backends that need the - // information. - class PassResourceUsageTracker { - public: - void BufferUsedAs(BufferBase* buffer, dawn::BufferUsageBit usage) { - // std::map's operator[] will create the key and return 0 if the key didn't exist - // before. - dawn::BufferUsageBit& storedUsage = mBufferUsages[buffer]; - - if (usage == dawn::BufferUsageBit::Storage && - storedUsage & dawn::BufferUsageBit::Storage) { - mStorageUsedMultipleTimes = true; - } - - storedUsage |= usage; - } + MaybeError ValidateComputePassDescriptor(const DeviceBase* device, + const ComputePassDescriptor* descriptor) { + return {}; + } - void TextureUsedAs(TextureBase* texture, dawn::TextureUsageBit usage) { - // std::map's operator[] will create the key and return 0 if the key didn't exist - // before. - dawn::TextureUsageBit& storedUsage = mTextureUsages[texture]; + ResultOrError FixTextureCopyView(DeviceBase* device, + const TextureCopyView* view) { + TextureCopyView fixedView = *view; - if (usage == dawn::TextureUsageBit::Storage && - storedUsage & dawn::TextureUsageBit::Storage) { - mStorageUsedMultipleTimes = true; + if (view->arrayLayer != 0) { + if (view->origin.z != 0) { + return DAWN_VALIDATION_ERROR("arrayLayer and origin.z cannot both be != 0"); + } else { + fixedView.origin.z = fixedView.arrayLayer; + fixedView.arrayLayer = 1; + device->EmitDeprecationWarning( + "wgpu::TextureCopyView::arrayLayer is deprecated in favor of " + "::origin::z"); } - - storedUsage |= usage; } - // Performs the per-pass usage validation checks - MaybeError ValidateUsages(PassType pass) const { - // Storage resources cannot be used twice in the same compute pass - if (pass == PassType::Compute && mStorageUsedMultipleTimes) { - return DAWN_VALIDATION_ERROR( - "Storage resource used multiple times in compute pass"); - } - - // Buffers can only be used as single-write or multiple read. - for (auto& it : mBufferUsages) { - BufferBase* buffer = it.first; - dawn::BufferUsageBit usage = it.second; - - if (usage & ~buffer->GetUsage()) { - return DAWN_VALIDATION_ERROR("Buffer missing usage for the pass"); - } + return fixedView; + } - bool readOnly = (usage & kReadOnlyBufferUsages) == usage; - bool singleUse = dawn::HasZeroOrOneBits(usage); + ResultOrError FixBufferCopyView(DeviceBase* device, + const BufferCopyView* view) { + BufferCopyView fixedView = *view; - if (!readOnly && !singleUse) { - return DAWN_VALIDATION_ERROR( - "Buffer used as writable usage and another usage in pass"); - } + TextureDataLayout& layout = fixedView.layout; + if (layout.offset != 0 || layout.bytesPerRow != 0 || layout.rowsPerImage != 0) { + // Using non-deprecated path + if (fixedView.offset != 0 || fixedView.bytesPerRow != 0 || + fixedView.rowsPerImage != 0) { + return DAWN_VALIDATION_ERROR( + "WGPUBufferCopyView.offset/bytesPerRow/rowsPerImage is deprecated; use " + "only WGPUBufferCopyView.layout"); } + } else if (fixedView.offset != 0 || fixedView.bytesPerRow != 0 || + fixedView.rowsPerImage != 0) { + device->EmitDeprecationWarning( + "WGPUBufferCopyView.offset/bytesPerRow/rowsPerImage is deprecated; use " + "WGPUBufferCopyView.layout"); - // Textures can only be used as single-write or multiple read. - // TODO(cwallez@chromium.org): implement per-subresource tracking - for (auto& it : mTextureUsages) { - TextureBase* texture = it.first; - dawn::TextureUsageBit usage = it.second; - - if (usage & ~texture->GetUsage()) { - return DAWN_VALIDATION_ERROR("Texture missing usage for the pass"); - } - - // For textures the only read-only usage in a pass is Sampled, so checking the - // usage constraint simplifies to checking a single usage bit is set. - if (!dawn::HasZeroOrOneBits(it.second)) { - return DAWN_VALIDATION_ERROR( - "Texture used with more than one usage in pass"); - } - } + layout.offset = fixedView.offset; + layout.bytesPerRow = fixedView.bytesPerRow; + layout.rowsPerImage = fixedView.rowsPerImage; + fixedView.offset = 0; + fixedView.bytesPerRow = 0; + fixedView.rowsPerImage = 0; + } + return fixedView; + } - return {}; + MaybeError ValidateQuerySetResolve(const QuerySetBase* querySet, + uint32_t firstQuery, + uint32_t queryCount, + const BufferBase* destination, + uint64_t destinationOffset) { + if (firstQuery >= querySet->GetQueryCount()) { + return DAWN_VALIDATION_ERROR("Query index out of bounds"); } - // Returns the per-pass usage for use by backends for APIs with explicit barriers. - PassResourceUsage AcquireResourceUsage() { - PassResourceUsage result; - result.buffers.reserve(mBufferUsages.size()); - result.bufferUsages.reserve(mBufferUsages.size()); - result.textures.reserve(mTextureUsages.size()); - result.textureUsages.reserve(mTextureUsages.size()); - - for (auto& it : mBufferUsages) { - result.buffers.push_back(it.first); - result.bufferUsages.push_back(it.second); - } + if (queryCount > querySet->GetQueryCount() - firstQuery) { + return DAWN_VALIDATION_ERROR( + "The sum of firstQuery and queryCount exceeds the number of queries in query " + "set"); + } - for (auto& it : mTextureUsages) { - result.textures.push_back(it.first); - result.textureUsages.push_back(it.second); - } + // TODO(hao.x.li@intel.com): Validate that the queries between [firstQuery, firstQuery + + // queryCount - 1] must be available(written by query operations). - return result; + // The destinationOffset must be a multiple of 8 bytes on D3D12 and Vulkan + if (destinationOffset % 8 != 0) { + return DAWN_VALIDATION_ERROR( + "The alignment offset into the destination buffer must be a multiple of 8 " + "bytes"); } - private: - std::map mBufferUsages; - std::map mTextureUsages; - bool mStorageUsedMultipleTimes = false; - }; - - void TrackBindGroupResourceUsage(BindGroupBase* group, PassResourceUsageTracker* tracker) { - const auto& layoutInfo = group->GetLayout()->GetBindingInfo(); - - for (uint32_t i : IterateBitSet(layoutInfo.mask)) { - dawn::BindingType type = layoutInfo.types[i]; - - switch (type) { - case dawn::BindingType::UniformBuffer: - case dawn::BindingType::DynamicUniformBuffer: { - BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer; - tracker->BufferUsedAs(buffer, dawn::BufferUsageBit::Uniform); - } break; - - case dawn::BindingType::StorageBuffer: - case dawn::BindingType::DynamicStorageBuffer: { - BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer; - tracker->BufferUsedAs(buffer, dawn::BufferUsageBit::Storage); - } break; - - case dawn::BindingType::SampledTexture: { - TextureBase* texture = group->GetBindingAsTextureView(i)->GetTexture(); - tracker->TextureUsedAs(texture, dawn::TextureUsageBit::Sampled); - } break; - - case dawn::BindingType::Sampler: - break; - } + uint64_t bufferSize = destination->GetSize(); + // The destination buffer must have enough storage, from destination offset, to contain + // the result of resolved queries + bool fitsInBuffer = destinationOffset <= bufferSize && + (static_cast(queryCount) * sizeof(uint64_t) <= + (bufferSize - destinationOffset)); + if (!fitsInBuffer) { + return DAWN_VALIDATION_ERROR("The resolved query data would overflow the buffer"); } + + return {}; } } // namespace - enum class CommandEncoderBase::EncodingState : uint8_t { - TopLevel, - ComputePass, - RenderPass, - Finished - }; - - CommandEncoderBase::CommandEncoderBase(DeviceBase* device) - : ObjectBase(device), mEncodingState(EncodingState::TopLevel) { - } - - CommandEncoderBase::~CommandEncoderBase() { - if (!mWereCommandsAcquired) { - MoveToIterator(); - FreeCommands(&mIterator); - } + CommandEncoder::CommandEncoder(DeviceBase* device, const CommandEncoderDescriptor*) + : ObjectBase(device), mEncodingContext(device, this) { } - CommandIterator CommandEncoderBase::AcquireCommands() { - ASSERT(!mWereCommandsAcquired); - mWereCommandsAcquired = true; - return std::move(mIterator); + CommandBufferResourceUsage CommandEncoder::AcquireResourceUsages() { + return CommandBufferResourceUsage{mEncodingContext.AcquirePassUsages(), + std::move(mTopLevelBuffers), std::move(mTopLevelTextures), + std::move(mUsedQuerySets)}; } - CommandBufferResourceUsage CommandEncoderBase::AcquireResourceUsages() { - ASSERT(!mWereResourceUsagesAcquired); - mWereResourceUsagesAcquired = true; - return std::move(mResourceUsages); + CommandIterator CommandEncoder::AcquireCommands() { + return mEncodingContext.AcquireCommands(); } - void CommandEncoderBase::MoveToIterator() { - if (!mWasMovedToIterator) { - mIterator = std::move(mAllocator); - mWasMovedToIterator = true; - } + void CommandEncoder::TrackUsedQuerySet(QuerySetBase* querySet) { + mUsedQuerySets.insert(querySet); } // Implementation of the API's command recording methods - ComputePassEncoderBase* CommandEncoderBase::BeginComputePass() { + ComputePassEncoder* CommandEncoder::BeginComputePass(const ComputePassDescriptor* descriptor) { DeviceBase* device = GetDevice(); - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return ComputePassEncoderBase::MakeError(device, this); - } - mAllocator.Allocate(Command::BeginComputePass); + bool success = + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(ValidateComputePassDescriptor(device, descriptor)); - mEncodingState = EncodingState::ComputePass; - return new ComputePassEncoderBase(device, this, &mAllocator); - } - - RenderPassEncoderBase* CommandEncoderBase::BeginRenderPass(const RenderPassDescriptor* info) { - DeviceBase* device = GetDevice(); - - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return RenderPassEncoderBase::MakeError(device, this); - } - - uint32_t width = 0; - uint32_t height = 0; - uint32_t sampleCount = 0; - if (ConsumedError( - ValidateRenderPassDescriptor(device, info, &width, &height, &sampleCount))) { - return RenderPassEncoderBase::MakeError(device, this); - } - - ASSERT(width > 0 && height > 0 && sampleCount > 0); + allocator->Allocate(Command::BeginComputePass); - mEncodingState = EncodingState::RenderPass; - - BeginRenderPassCmd* cmd = mAllocator.Allocate(Command::BeginRenderPass); - - for (uint32_t i = 0; i < info->colorAttachmentCount; ++i) { - if (info->colorAttachments[i] != nullptr) { - cmd->colorAttachmentsSet.set(i); - cmd->colorAttachments[i].view = info->colorAttachments[i]->attachment; - cmd->colorAttachments[i].resolveTarget = info->colorAttachments[i]->resolveTarget; - cmd->colorAttachments[i].loadOp = info->colorAttachments[i]->loadOp; - cmd->colorAttachments[i].storeOp = info->colorAttachments[i]->storeOp; - cmd->colorAttachments[i].clearColor = info->colorAttachments[i]->clearColor; - } - } + return {}; + }); - cmd->hasDepthStencilAttachment = info->depthStencilAttachment != nullptr; - if (cmd->hasDepthStencilAttachment) { - cmd->hasDepthStencilAttachment = true; - cmd->depthStencilAttachment.view = info->depthStencilAttachment->attachment; - cmd->depthStencilAttachment.clearDepth = info->depthStencilAttachment->clearDepth; - cmd->depthStencilAttachment.clearStencil = info->depthStencilAttachment->clearStencil; - cmd->depthStencilAttachment.depthLoadOp = info->depthStencilAttachment->depthLoadOp; - cmd->depthStencilAttachment.depthStoreOp = info->depthStencilAttachment->depthStoreOp; - cmd->depthStencilAttachment.stencilLoadOp = info->depthStencilAttachment->stencilLoadOp; - cmd->depthStencilAttachment.stencilStoreOp = - info->depthStencilAttachment->stencilStoreOp; + if (success) { + ComputePassEncoder* passEncoder = + new ComputePassEncoder(device, this, &mEncodingContext); + mEncodingContext.EnterPass(passEncoder); + return passEncoder; } - cmd->width = width; - cmd->height = height; - cmd->sampleCount = sampleCount; - - return new RenderPassEncoderBase(device, this, &mAllocator); + return ComputePassEncoder::MakeError(device, this, &mEncodingContext); } - void CommandEncoderBase::CopyBufferToBuffer(BufferBase* source, - uint64_t sourceOffset, - BufferBase* destination, - uint64_t destinationOffset, - uint64_t size) { - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return; - } - - if (ConsumedError(GetDevice()->ValidateObject(source))) { - return; - } + RenderPassEncoder* CommandEncoder::BeginRenderPass(const RenderPassDescriptor* descriptor) { + DeviceBase* device = GetDevice(); - if (ConsumedError(GetDevice()->ValidateObject(destination))) { - return; - } + PassResourceUsageTracker usageTracker(PassType::Render); + bool success = + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + uint32_t width = 0; + uint32_t height = 0; + uint32_t sampleCount = 0; - CopyBufferToBufferCmd* copy = - mAllocator.Allocate(Command::CopyBufferToBuffer); - copy->source = source; - copy->sourceOffset = sourceOffset; - copy->destination = destination; - copy->destinationOffset = destinationOffset; - copy->size = size; - } + DAWN_TRY(ValidateRenderPassDescriptor(device, descriptor, &width, &height, + &sampleCount)); - void CommandEncoderBase::CopyBufferToTexture(const BufferCopyView* source, - const TextureCopyView* destination, - const Extent3D* copySize) { - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return; - } + ASSERT(width > 0 && height > 0 && sampleCount > 0); - if (ConsumedError(GetDevice()->ValidateObject(source->buffer))) { - return; - } + BeginRenderPassCmd* cmd = + allocator->Allocate(Command::BeginRenderPass); - if (ConsumedError(GetDevice()->ValidateObject(destination->texture))) { - return; - } + cmd->attachmentState = device->GetOrCreateAttachmentState(descriptor); - CopyBufferToTextureCmd* copy = - mAllocator.Allocate(Command::CopyBufferToTexture); - copy->source.buffer = source->buffer; - copy->source.offset = source->offset; - copy->destination.texture = destination->texture; - copy->destination.origin = destination->origin; - copy->copySize = *copySize; - copy->destination.level = destination->level; - copy->destination.slice = destination->slice; - if (source->rowPitch == 0) { - copy->source.rowPitch = ComputeDefaultRowPitch(destination->texture, copySize->width); - } else { - copy->source.rowPitch = source->rowPitch; - } - if (source->imageHeight == 0) { - copy->source.imageHeight = copySize->height; - } else { - copy->source.imageHeight = source->imageHeight; - } - } + for (uint32_t i : IterateBitSet(cmd->attachmentState->GetColorAttachmentsMask())) { + TextureViewBase* view = descriptor->colorAttachments[i].attachment; + TextureViewBase* resolveTarget = descriptor->colorAttachments[i].resolveTarget; - void CommandEncoderBase::CopyTextureToBuffer(const TextureCopyView* source, - const BufferCopyView* destination, - const Extent3D* copySize) { - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return; - } + cmd->colorAttachments[i].view = view; + cmd->colorAttachments[i].resolveTarget = resolveTarget; + cmd->colorAttachments[i].loadOp = descriptor->colorAttachments[i].loadOp; + cmd->colorAttachments[i].storeOp = descriptor->colorAttachments[i].storeOp; + cmd->colorAttachments[i].clearColor = + descriptor->colorAttachments[i].clearColor; - if (ConsumedError(GetDevice()->ValidateObject(source->texture))) { - return; - } + usageTracker.TextureViewUsedAs(view, wgpu::TextureUsage::OutputAttachment); - if (ConsumedError(GetDevice()->ValidateObject(destination->buffer))) { - return; - } + if (resolveTarget != nullptr) { + usageTracker.TextureViewUsedAs(resolveTarget, + wgpu::TextureUsage::OutputAttachment); + } + } - CopyTextureToBufferCmd* copy = - mAllocator.Allocate(Command::CopyTextureToBuffer); - copy->source.texture = source->texture; - copy->source.origin = source->origin; - copy->copySize = *copySize; - copy->source.level = source->level; - copy->source.slice = source->slice; - copy->destination.buffer = destination->buffer; - copy->destination.offset = destination->offset; - if (destination->rowPitch == 0) { - copy->destination.rowPitch = ComputeDefaultRowPitch(source->texture, copySize->width); - } else { - copy->destination.rowPitch = destination->rowPitch; - } - if (destination->imageHeight == 0) { - copy->destination.imageHeight = copySize->height; - } else { - copy->destination.imageHeight = destination->imageHeight; - } - } + if (cmd->attachmentState->HasDepthStencilAttachment()) { + TextureViewBase* view = descriptor->depthStencilAttachment->attachment; + + cmd->depthStencilAttachment.view = view; + cmd->depthStencilAttachment.clearDepth = + descriptor->depthStencilAttachment->clearDepth; + cmd->depthStencilAttachment.clearStencil = + descriptor->depthStencilAttachment->clearStencil; + cmd->depthStencilAttachment.depthLoadOp = + descriptor->depthStencilAttachment->depthLoadOp; + cmd->depthStencilAttachment.depthStoreOp = + descriptor->depthStencilAttachment->depthStoreOp; + cmd->depthStencilAttachment.stencilLoadOp = + descriptor->depthStencilAttachment->stencilLoadOp; + cmd->depthStencilAttachment.stencilStoreOp = + descriptor->depthStencilAttachment->stencilStoreOp; + + usageTracker.TextureViewUsedAs(view, wgpu::TextureUsage::OutputAttachment); + } - void CommandEncoderBase::CopyTextureToTexture(const TextureCopyView* source, - const TextureCopyView* destination, - const Extent3D* copySize) { - if (ConsumedError(ValidateCanRecordTopLevelCommands())) { - return; - } + cmd->width = width; + cmd->height = height; - if (ConsumedError(GetDevice()->ValidateObject(source->texture))) { - return; - } + return {}; + }); - if (ConsumedError(GetDevice()->ValidateObject(destination->texture))) { - return; + if (success) { + RenderPassEncoder* passEncoder = + new RenderPassEncoder(device, this, &mEncodingContext, std::move(usageTracker)); + mEncodingContext.EnterPass(passEncoder); + return passEncoder; } - CopyTextureToTextureCmd* copy = - mAllocator.Allocate(Command::CopyTextureToTexture); - copy->source.texture = source->texture; - copy->source.origin = source->origin; - copy->source.level = source->level; - copy->source.slice = source->slice; - copy->destination.texture = destination->texture; - copy->destination.origin = destination->origin; - copy->destination.level = destination->level; - copy->destination.slice = destination->slice; - copy->copySize = *copySize; + return RenderPassEncoder::MakeError(device, this, &mEncodingContext); } - CommandBufferBase* CommandEncoderBase::Finish() { - if (GetDevice()->ConsumedError(ValidateFinish())) { - // Even if finish validation fails, it is now invalid to call any encoding commands on - // this object, so we set its state to finished. - mEncodingState = EncodingState::Finished; - return CommandBufferBase::MakeError(GetDevice()); - } - ASSERT(!IsError()); + void CommandEncoder::CopyBufferToBuffer(BufferBase* source, + uint64_t sourceOffset, + BufferBase* destination, + uint64_t destinationOffset, + uint64_t size) { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(GetDevice()->ValidateObject(source)); + DAWN_TRY(GetDevice()->ValidateObject(destination)); + + if (source == destination) { + return DAWN_VALIDATION_ERROR( + "Source and destination cannot be the same buffer."); + } - mEncodingState = EncodingState::Finished; + DAWN_TRY(ValidateCopySizeFitsInBuffer(source, sourceOffset, size)); + DAWN_TRY(ValidateCopySizeFitsInBuffer(destination, destinationOffset, size)); + DAWN_TRY(ValidateB2BCopyAlignment(size, sourceOffset, destinationOffset)); - MoveToIterator(); - return GetDevice()->CreateCommandBuffer(this); - } + DAWN_TRY(ValidateCanUseAs(source, wgpu::BufferUsage::CopySrc)); + DAWN_TRY(ValidateCanUseAs(destination, wgpu::BufferUsage::CopyDst)); - // Implementation of functions to interact with sub-encoders + mTopLevelBuffers.insert(source); + mTopLevelBuffers.insert(destination); + } - void CommandEncoderBase::HandleError(const char* message) { - if (mEncodingState != EncodingState::Finished) { - if (!mGotError) { - mGotError = true; - mErrorMessage = message; + // Skip noop copies. Some backends validation rules disallow them. + if (size != 0) { + CopyBufferToBufferCmd* copy = + allocator->Allocate(Command::CopyBufferToBuffer); + copy->source = source; + copy->sourceOffset = sourceOffset; + copy->destination = destination; + copy->destinationOffset = destinationOffset; + copy->size = size; } - } else { - GetDevice()->HandleError(message); - } - } - void CommandEncoderBase::ConsumeError(ErrorData* error) { - HandleError(error->GetMessage().c_str()); - delete error; + return {}; + }); } - void CommandEncoderBase::PassEnded() { - // This function may still be called when the command encoder is finished, just do nothing. - if (mEncodingState == EncodingState::Finished) { - return; - } + void CommandEncoder::CopyBufferToTexture(const BufferCopyView* source, + const TextureCopyView* destination, + const Extent3D* copySize) { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + // TODO(crbug.com/dawn/22): Remove once migration from GPUTextureCopyView.arrayLayer to + // GPUTextureCopyView.origin.z is done. + TextureCopyView fixedDest; + DAWN_TRY_ASSIGN(fixedDest, FixTextureCopyView(GetDevice(), destination)); + destination = &fixedDest; + + // TODO(crbug.com/dawn/22): Remove once migration to .layout is done. + BufferCopyView fixedSource; + DAWN_TRY_ASSIGN(fixedSource, FixBufferCopyView(GetDevice(), source)); + source = &fixedSource; + + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(ValidateBufferCopyView(GetDevice(), *source)); + DAWN_TRY(ValidateCanUseAs(source->buffer, wgpu::BufferUsage::CopySrc)); + + DAWN_TRY(ValidateTextureCopyView(GetDevice(), *destination)); + DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst)); + DAWN_TRY(ValidateTextureSampleCountInCopyCommands(destination->texture)); + + // We validate texture copy range before validating linear texture data, + // because in the latter we divide copyExtent.width by blockWidth and + // copyExtent.height by blockHeight while the divisibility conditions are + // checked in validating texture copy range. + DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize)); + DAWN_TRY(ValidateLinearTextureData(source->layout, source->buffer->GetSize(), + destination->texture->GetFormat(), *copySize)); + + mTopLevelBuffers.insert(source->buffer); + mTopLevelTextures.insert(destination->texture); + } + + // Compute default value for rowsPerImage + uint32_t defaultedRowsPerImage = source->layout.rowsPerImage; + if (defaultedRowsPerImage == 0) { + defaultedRowsPerImage = copySize->height; + } + + // Record the copy command. + CopyBufferToTextureCmd* copy = + allocator->Allocate(Command::CopyBufferToTexture); + copy->source.buffer = source->buffer; + copy->source.offset = source->layout.offset; + copy->source.bytesPerRow = source->layout.bytesPerRow; + copy->source.rowsPerImage = defaultedRowsPerImage; + copy->destination.texture = destination->texture; + copy->destination.origin = destination->origin; + copy->destination.mipLevel = destination->mipLevel; + copy->copySize = *copySize; - if (mEncodingState == EncodingState::ComputePass) { - mAllocator.Allocate(Command::EndComputePass); - } else { - ASSERT(mEncodingState == EncodingState::RenderPass); - mAllocator.Allocate(Command::EndRenderPass); - } - mEncodingState = EncodingState::TopLevel; + return {}; + }); } - // Implementation of the command buffer validation that can be precomputed before submit - - MaybeError CommandEncoderBase::ValidateFinish() { - DAWN_TRY(GetDevice()->ValidateObject(this)); - - if (mGotError) { - return DAWN_VALIDATION_ERROR(mErrorMessage); - } - - if (mEncodingState != EncodingState::TopLevel) { - return DAWN_VALIDATION_ERROR("Command buffer recording ended mid-pass"); - } - - MoveToIterator(); - mIterator.Reset(); - - Command type; - while (mIterator.NextCommandId(&type)) { - switch (type) { - case Command::BeginComputePass: { - mIterator.NextCommand(); - DAWN_TRY(ValidateComputePass()); - } break; - - case Command::BeginRenderPass: { - BeginRenderPassCmd* cmd = mIterator.NextCommand(); - DAWN_TRY(ValidateRenderPass(cmd)); - } break; - - case Command::CopyBufferToBuffer: { - CopyBufferToBufferCmd* copy = mIterator.NextCommand(); - - DAWN_TRY( - ValidateCopySizeFitsInBuffer(copy->source, copy->sourceOffset, copy->size)); - DAWN_TRY(ValidateCopySizeFitsInBuffer(copy->destination, - copy->destinationOffset, copy->size)); - DAWN_TRY(ValidateB2BCopySizeAlignment(copy->size, copy->sourceOffset, - copy->destinationOffset)); + void CommandEncoder::CopyTextureToBuffer(const TextureCopyView* source, + const BufferCopyView* destination, + const Extent3D* copySize) { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + // TODO(crbug.com/dawn/22): Remove once migration from GPUTextureCopyView.arrayLayer to + // GPUTextureCopyView.origin.z is done. + TextureCopyView fixedSrc; + DAWN_TRY_ASSIGN(fixedSrc, FixTextureCopyView(GetDevice(), source)); + source = &fixedSrc; + + // TODO(crbug.com/dawn/22): Remove once migration to .layout is done. + BufferCopyView fixedDst; + DAWN_TRY_ASSIGN(fixedDst, FixBufferCopyView(GetDevice(), destination)); + destination = &fixedDst; + + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(ValidateTextureCopyView(GetDevice(), *source)); + DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc)); + DAWN_TRY(ValidateTextureSampleCountInCopyCommands(source->texture)); + + DAWN_TRY(ValidateBufferCopyView(GetDevice(), *destination)); + DAWN_TRY(ValidateCanUseAs(destination->buffer, wgpu::BufferUsage::CopyDst)); + + // We validate texture copy range before validating linear texture data, + // because in the latter we divide copyExtent.width by blockWidth and + // copyExtent.height by blockHeight while the divisibility conditions are + // checked in validating texture copy range. + DAWN_TRY(ValidateTextureCopyRange(*source, *copySize)); + DAWN_TRY(ValidateLinearTextureData(destination->layout, + destination->buffer->GetSize(), + source->texture->GetFormat(), *copySize)); + + mTopLevelTextures.insert(source->texture); + mTopLevelBuffers.insert(destination->buffer); + } + + // Compute default value for rowsPerImage + uint32_t defaultedRowsPerImage = destination->layout.rowsPerImage; + if (defaultedRowsPerImage == 0) { + defaultedRowsPerImage = copySize->height; + } + + // Record the copy command. + CopyTextureToBufferCmd* copy = + allocator->Allocate(Command::CopyTextureToBuffer); + copy->source.texture = source->texture; + copy->source.origin = source->origin; + copy->source.mipLevel = source->mipLevel; + copy->destination.buffer = destination->buffer; + copy->destination.offset = destination->layout.offset; + copy->destination.bytesPerRow = destination->layout.bytesPerRow; + copy->destination.rowsPerImage = defaultedRowsPerImage; + copy->copySize = *copySize; - DAWN_TRY( - ValidateCanUseAs(copy->source.Get(), dawn::BufferUsageBit::TransferSrc)); - DAWN_TRY(ValidateCanUseAs(copy->destination.Get(), - dawn::BufferUsageBit::TransferDst)); - - mResourceUsages.topLevelBuffers.insert(copy->source.Get()); - mResourceUsages.topLevelBuffers.insert(copy->destination.Get()); - } break; - - case Command::CopyBufferToTexture: { - CopyBufferToTextureCmd* copy = mIterator.NextCommand(); - - DAWN_TRY( - ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get())); - - uint32_t bufferCopySize = 0; - DAWN_TRY(ValidateRowPitch(copy->destination.texture->GetFormat(), - copy->copySize, copy->source.rowPitch)); - - DAWN_TRY(ComputeTextureCopyBufferSize( - copy->destination.texture->GetFormat(), copy->copySize, - copy->source.rowPitch, copy->source.imageHeight, &bufferCopySize)); - - DAWN_TRY(ValidateCopySizeFitsInTexture(copy->destination, copy->copySize)); - DAWN_TRY(ValidateCopySizeFitsInBuffer(copy->source, bufferCopySize)); - DAWN_TRY( - ValidateTexelBufferOffset(copy->destination.texture.Get(), copy->source)); - - DAWN_TRY(ValidateCanUseAs(copy->source.buffer.Get(), - dawn::BufferUsageBit::TransferSrc)); - DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(), - dawn::TextureUsageBit::TransferDst)); - - mResourceUsages.topLevelBuffers.insert(copy->source.buffer.Get()); - mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get()); - } break; - - case Command::CopyTextureToBuffer: { - CopyTextureToBufferCmd* copy = mIterator.NextCommand(); - - DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get())); + return {}; + }); + } - uint32_t bufferCopySize = 0; - DAWN_TRY(ValidateRowPitch(copy->source.texture->GetFormat(), copy->copySize, - copy->destination.rowPitch)); - DAWN_TRY(ComputeTextureCopyBufferSize( - copy->source.texture->GetFormat(), copy->copySize, - copy->destination.rowPitch, copy->destination.imageHeight, - &bufferCopySize)); + void CommandEncoder::CopyTextureToTexture(const TextureCopyView* source, + const TextureCopyView* destination, + const Extent3D* copySize) { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + // TODO(crbug.com/dawn/22): Remove once migration from GPUTextureCopyView.arrayLayer to + // GPUTextureCopyView.origin.z is done. + TextureCopyView fixedSrc; + DAWN_TRY_ASSIGN(fixedSrc, FixTextureCopyView(GetDevice(), source)); + source = &fixedSrc; + TextureCopyView fixedDest; + DAWN_TRY_ASSIGN(fixedDest, FixTextureCopyView(GetDevice(), destination)); + destination = &fixedDest; + + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(GetDevice()->ValidateObject(source->texture)); + DAWN_TRY(GetDevice()->ValidateObject(destination->texture)); + + DAWN_TRY( + ValidateTextureToTextureCopyRestrictions(*source, *destination, *copySize)); + + DAWN_TRY(ValidateTextureCopyRange(*source, *copySize)); + DAWN_TRY(ValidateTextureCopyRange(*destination, *copySize)); + + DAWN_TRY(ValidateTextureCopyView(GetDevice(), *source)); + DAWN_TRY(ValidateTextureCopyView(GetDevice(), *destination)); + + DAWN_TRY(ValidateCanUseAs(source->texture, wgpu::TextureUsage::CopySrc)); + DAWN_TRY(ValidateCanUseAs(destination->texture, wgpu::TextureUsage::CopyDst)); + + mTopLevelTextures.insert(source->texture); + mTopLevelTextures.insert(destination->texture); + } + + CopyTextureToTextureCmd* copy = + allocator->Allocate(Command::CopyTextureToTexture); + copy->source.texture = source->texture; + copy->source.origin = source->origin; + copy->source.mipLevel = source->mipLevel; + copy->destination.texture = destination->texture; + copy->destination.origin = destination->origin; + copy->destination.mipLevel = destination->mipLevel; + copy->copySize = *copySize; - DAWN_TRY(ValidateCopySizeFitsInTexture(copy->source, copy->copySize)); - DAWN_TRY(ValidateCopySizeFitsInBuffer(copy->destination, bufferCopySize)); - DAWN_TRY( - ValidateTexelBufferOffset(copy->source.texture.Get(), copy->destination)); + return {}; + }); + } - DAWN_TRY(ValidateCanUseAs(copy->source.texture.Get(), - dawn::TextureUsageBit::TransferSrc)); - DAWN_TRY(ValidateCanUseAs(copy->destination.buffer.Get(), - dawn::BufferUsageBit::TransferDst)); + void CommandEncoder::InsertDebugMarker(const char* groupLabel) { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + InsertDebugMarkerCmd* cmd = + allocator->Allocate(Command::InsertDebugMarker); + cmd->length = strlen(groupLabel); - mResourceUsages.topLevelTextures.insert(copy->source.texture.Get()); - mResourceUsages.topLevelBuffers.insert(copy->destination.buffer.Get()); - } break; + char* label = allocator->AllocateData(cmd->length + 1); + memcpy(label, groupLabel, cmd->length + 1); - case Command::CopyTextureToTexture: { - CopyTextureToTextureCmd* copy = - mIterator.NextCommand(); - - DAWN_TRY(ValidateTextureToTextureCopyRestrictions( - copy->source, copy->destination, copy->copySize)); + return {}; + }); + } - DAWN_TRY(ValidateCopySizeFitsInTexture(copy->source, copy->copySize)); - DAWN_TRY(ValidateCopySizeFitsInTexture(copy->destination, copy->copySize)); + void CommandEncoder::PopDebugGroup() { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + allocator->Allocate(Command::PopDebugGroup); - DAWN_TRY(ValidateCanUseAs(copy->source.texture.Get(), - dawn::TextureUsageBit::TransferSrc)); - DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(), - dawn::TextureUsageBit::TransferDst)); + return {}; + }); + } - mResourceUsages.topLevelTextures.insert(copy->source.texture.Get()); - mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get()); - } break; + void CommandEncoder::PushDebugGroup(const char* groupLabel) { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + PushDebugGroupCmd* cmd = + allocator->Allocate(Command::PushDebugGroup); + cmd->length = strlen(groupLabel); - default: - return DAWN_VALIDATION_ERROR("Command disallowed outside of a pass"); - } - } + char* label = allocator->AllocateData(cmd->length + 1); + memcpy(label, groupLabel, cmd->length + 1); - return {}; + return {}; + }); } - MaybeError CommandEncoderBase::ValidateComputePass() { - PassResourceUsageTracker usageTracker; - CommandBufferStateTracker persistentState; - - Command type; - while (mIterator.NextCommandId(&type)) { - switch (type) { - case Command::EndComputePass: { - mIterator.NextCommand(); + void CommandEncoder::ResolveQuerySet(QuerySetBase* querySet, + uint32_t firstQuery, + uint32_t queryCount, + BufferBase* destination, + uint64_t destinationOffset) { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(GetDevice()->ValidateObject(querySet)); + DAWN_TRY(GetDevice()->ValidateObject(destination)); - DAWN_TRY(ValidateDebugGroups(mDebugGroupStackSize)); + DAWN_TRY(ValidateQuerySetResolve(querySet, firstQuery, queryCount, destination, + destinationOffset)); - DAWN_TRY(usageTracker.ValidateUsages(PassType::Compute)); - mResourceUsages.perPass.push_back(usageTracker.AcquireResourceUsage()); - return {}; - } break; + DAWN_TRY(ValidateCanUseAs(destination, wgpu::BufferUsage::QueryResolve)); - case Command::Dispatch: { - mIterator.NextCommand(); - DAWN_TRY(persistentState.ValidateCanDispatch()); - } break; + TrackUsedQuerySet(querySet); + mTopLevelBuffers.insert(destination); + } - case Command::DispatchIndirect: { - DispatchIndirectCmd* cmd = mIterator.NextCommand(); - DAWN_TRY(persistentState.ValidateCanDispatch()); - usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(), - dawn::BufferUsageBit::Indirect); - } break; + ResolveQuerySetCmd* cmd = + allocator->Allocate(Command::ResolveQuerySet); + cmd->querySet = querySet; + cmd->firstQuery = firstQuery; + cmd->queryCount = queryCount; + cmd->destination = destination; + cmd->destinationOffset = destinationOffset; - case Command::InsertDebugMarker: { - InsertDebugMarkerCmd* cmd = mIterator.NextCommand(); - mIterator.NextData(cmd->length + 1); - } break; + return {}; + }); + } - case Command::PopDebugGroup: { - mIterator.NextCommand(); - DAWN_TRY(PopDebugMarkerStack(&mDebugGroupStackSize)); - } break; + void CommandEncoder::WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) { + mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(GetDevice()->ValidateObject(querySet)); + DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex)); + TrackUsedQuerySet(querySet); + } - case Command::PushDebugGroup: { - PushDebugGroupCmd* cmd = mIterator.NextCommand(); - mIterator.NextData(cmd->length + 1); - DAWN_TRY(PushDebugMarkerStack(&mDebugGroupStackSize)); - } break; - - case Command::SetComputePipeline: { - SetComputePipelineCmd* cmd = mIterator.NextCommand(); - ComputePipelineBase* pipeline = cmd->pipeline.Get(); - persistentState.SetComputePipeline(pipeline); - } break; - - case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mIterator.NextCommand(); - if (cmd->dynamicOffsetCount > 0) { - mIterator.NextData(cmd->dynamicOffsetCount); - } + WriteTimestampCmd* cmd = + allocator->Allocate(Command::WriteTimestamp); + cmd->querySet = querySet; + cmd->queryIndex = queryIndex; - TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker); - persistentState.SetBindGroup(cmd->index, cmd->group.Get()); - } break; + return {}; + }); + } - default: - return DAWN_VALIDATION_ERROR("Command disallowed inside a compute pass"); - } + CommandBufferBase* CommandEncoder::Finish(const CommandBufferDescriptor* descriptor) { + DeviceBase* device = GetDevice(); + // Even if mEncodingContext.Finish() validation fails, calling it will mutate the internal + // state of the encoding context. The internal state is set to finished, and subsequent + // calls to encode commands will generate errors. + if (device->ConsumedError(mEncodingContext.Finish()) || + device->ConsumedError(device->ValidateIsAlive()) || + (device->IsValidationEnabled() && + device->ConsumedError(ValidateFinish(mEncodingContext.GetIterator(), + mEncodingContext.GetPassUsages())))) { + return CommandBufferBase::MakeError(device); } - - UNREACHABLE(); - return DAWN_VALIDATION_ERROR("Unfinished compute pass"); + ASSERT(!IsError()); + return device->CreateCommandBuffer(this, descriptor); } - MaybeError CommandEncoderBase::ValidateRenderPass(BeginRenderPassCmd* renderPass) { - PassResourceUsageTracker usageTracker; - CommandBufferStateTracker persistentState; - - // Track usage of the render pass attachments - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { - RenderPassColorAttachmentInfo* colorAttachment = &renderPass->colorAttachments[i]; - TextureBase* texture = colorAttachment->view->GetTexture(); - usageTracker.TextureUsedAs(texture, dawn::TextureUsageBit::OutputAttachment); + // Implementation of the command buffer validation that can be precomputed before submit + MaybeError CommandEncoder::ValidateFinish(CommandIterator* commands, + const PerPassUsages& perPassUsages) const { + TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "CommandEncoder::ValidateFinish"); + DAWN_TRY(GetDevice()->ValidateObject(this)); - TextureViewBase* resolveTarget = colorAttachment->resolveTarget.Get(); - if (resolveTarget != nullptr) { - usageTracker.TextureUsedAs(resolveTarget->GetTexture(), - dawn::TextureUsageBit::OutputAttachment); - } + for (const PassResourceUsage& passUsage : perPassUsages) { + DAWN_TRY(ValidatePassResourceUsage(passUsage)); } - if (renderPass->hasDepthStencilAttachment) { - TextureBase* texture = renderPass->depthStencilAttachment.view->GetTexture(); - usageTracker.TextureUsedAs(texture, dawn::TextureUsageBit::OutputAttachment); - } + uint64_t debugGroupStackSize = 0; + commands->Reset(); Command type; - while (mIterator.NextCommandId(&type)) { + while (commands->NextCommandId(&type)) { switch (type) { - case Command::EndRenderPass: { - mIterator.NextCommand(); - - DAWN_TRY(ValidateDebugGroups(mDebugGroupStackSize)); - - DAWN_TRY(usageTracker.ValidateUsages(PassType::Render)); - mResourceUsages.perPass.push_back(usageTracker.AcquireResourceUsage()); - return {}; - } break; - - case Command::Draw: { - mIterator.NextCommand(); - DAWN_TRY(persistentState.ValidateCanDraw()); - } break; - - case Command::DrawIndexed: { - mIterator.NextCommand(); - DAWN_TRY(persistentState.ValidateCanDrawIndexed()); - } break; - - case Command::DrawIndirect: { - DrawIndirectCmd* cmd = mIterator.NextCommand(); - DAWN_TRY(persistentState.ValidateCanDraw()); - usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(), - dawn::BufferUsageBit::Indirect); - } break; - - case Command::DrawIndexedIndirect: { - DrawIndexedIndirectCmd* cmd = mIterator.NextCommand(); - DAWN_TRY(persistentState.ValidateCanDrawIndexed()); - usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(), - dawn::BufferUsageBit::Indirect); - } break; - - case Command::InsertDebugMarker: { - InsertDebugMarkerCmd* cmd = mIterator.NextCommand(); - mIterator.NextData(cmd->length + 1); - } break; - - case Command::PopDebugGroup: { - mIterator.NextCommand(); - DAWN_TRY(PopDebugMarkerStack(&mDebugGroupStackSize)); - } break; - - case Command::PushDebugGroup: { - PushDebugGroupCmd* cmd = mIterator.NextCommand(); - mIterator.NextData(cmd->length + 1); - DAWN_TRY(PushDebugMarkerStack(&mDebugGroupStackSize)); - } break; - - case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mIterator.NextCommand(); - RenderPipelineBase* pipeline = cmd->pipeline.Get(); - - if (!pipeline->IsCompatibleWith(renderPass)) { - return DAWN_VALIDATION_ERROR( - "Pipeline is incompatible with this render pass"); - } - - persistentState.SetRenderPipeline(pipeline); - } break; + case Command::BeginComputePass: { + commands->NextCommand(); + DAWN_TRY(ValidateComputePass(commands)); + break; + } - case Command::SetStencilReference: { - mIterator.NextCommand(); - } break; + case Command::BeginRenderPass: { + const BeginRenderPassCmd* cmd = commands->NextCommand(); + DAWN_TRY(ValidateRenderPass(commands, cmd)); + break; + } - case Command::SetBlendColor: { - mIterator.NextCommand(); - } break; + case Command::CopyBufferToBuffer: { + commands->NextCommand(); + break; + } - case Command::SetScissorRect: { - mIterator.NextCommand(); - } break; + case Command::CopyBufferToTexture: { + commands->NextCommand(); + break; + } - case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mIterator.NextCommand(); - if (cmd->dynamicOffsetCount > 0) { - mIterator.NextData(cmd->dynamicOffsetCount); - } + case Command::CopyTextureToBuffer: { + commands->NextCommand(); + break; + } - TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker); - persistentState.SetBindGroup(cmd->index, cmd->group.Get()); - } break; + case Command::CopyTextureToTexture: { + commands->NextCommand(); + break; + } - case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mIterator.NextCommand(); + case Command::InsertDebugMarker: { + const InsertDebugMarkerCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); + break; + } - usageTracker.BufferUsedAs(cmd->buffer.Get(), dawn::BufferUsageBit::Index); - persistentState.SetIndexBuffer(); - } break; + case Command::PopDebugGroup: { + commands->NextCommand(); + DAWN_TRY(ValidateCanPopDebugGroup(debugGroupStackSize)); + debugGroupStackSize--; + break; + } - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mIterator.NextCommand(); - auto buffers = mIterator.NextData>(cmd->count); - mIterator.NextData(cmd->count); + case Command::PushDebugGroup: { + const PushDebugGroupCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); + debugGroupStackSize++; + break; + } - for (uint32_t i = 0; i < cmd->count; ++i) { - usageTracker.BufferUsedAs(buffers[i].Get(), dawn::BufferUsageBit::Vertex); - } - persistentState.SetVertexBuffer(cmd->startSlot, cmd->count); - } break; + case Command::ResolveQuerySet: { + commands->NextCommand(); + break; + } + case Command::WriteTimestamp: { + commands->NextCommand(); + break; + } default: - return DAWN_VALIDATION_ERROR("Command disallowed inside a render pass"); + return DAWN_VALIDATION_ERROR("Command disallowed outside of a pass"); } } - UNREACHABLE(); - return DAWN_VALIDATION_ERROR("Unfinished render pass"); - } + DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize)); - MaybeError CommandEncoderBase::ValidateCanRecordTopLevelCommands() const { - if (mEncodingState != EncodingState::TopLevel) { - return DAWN_VALIDATION_ERROR("Command cannot be recorded inside a pass"); - } return {}; } diff --git a/third_party/dawn/src/dawn_native/CommandEncoder.h b/third_party/dawn/src/dawn_native/CommandEncoder.h index a5e3c9be726..2fe801042ce 100644 --- a/third_party/dawn/src/dawn_native/CommandEncoder.h +++ b/third_party/dawn/src/dawn_native/CommandEncoder.h @@ -17,7 +17,7 @@ #include "dawn_native/dawn_platform.h" -#include "dawn_native/CommandAllocator.h" +#include "dawn_native/EncodingContext.h" #include "dawn_native/Error.h" #include "dawn_native/ObjectBase.h" #include "dawn_native/PassResourceUsage.h" @@ -28,17 +28,19 @@ namespace dawn_native { struct BeginRenderPassCmd; - class CommandEncoderBase : public ObjectBase { + class CommandEncoder final : public ObjectBase { public: - CommandEncoderBase(DeviceBase* device); - ~CommandEncoderBase(); + CommandEncoder(DeviceBase* device, const CommandEncoderDescriptor* descriptor); CommandIterator AcquireCommands(); CommandBufferResourceUsage AcquireResourceUsages(); + void TrackUsedQuerySet(QuerySetBase* querySet); + // Dawn API - ComputePassEncoderBase* BeginComputePass(); - RenderPassEncoderBase* BeginRenderPass(const RenderPassDescriptor* info); + ComputePassEncoder* BeginComputePass(const ComputePassDescriptor* descriptor); + RenderPassEncoder* BeginRenderPass(const RenderPassDescriptor* descriptor); + void CopyBufferToBuffer(BufferBase* source, uint64_t sourceOffset, BufferBase* destination, @@ -53,43 +55,28 @@ namespace dawn_native { void CopyTextureToTexture(const TextureCopyView* source, const TextureCopyView* destination, const Extent3D* copySize); - CommandBufferBase* Finish(); - // Functions to interact with the encoders - void HandleError(const char* message); - void ConsumeError(ErrorData* error); - bool ConsumedError(MaybeError maybeError) { - if (DAWN_UNLIKELY(maybeError.IsError())) { - ConsumeError(maybeError.AcquireError()); - return true; - } - return false; - } + void InsertDebugMarker(const char* groupLabel); + void PopDebugGroup(); + void PushDebugGroup(const char* groupLabel); - void PassEnded(); - - private: - MaybeError ValidateFinish(); - MaybeError ValidateComputePass(); - MaybeError ValidateRenderPass(BeginRenderPassCmd* renderPass); - MaybeError ValidateCanRecordTopLevelCommands() const; + void ResolveQuerySet(QuerySetBase* querySet, + uint32_t firstQuery, + uint32_t queryCount, + BufferBase* destination, + uint64_t destinationOffset); + void WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex); - enum class EncodingState : uint8_t; - EncodingState mEncodingState; + CommandBufferBase* Finish(const CommandBufferDescriptor* descriptor); - void MoveToIterator(); - CommandAllocator mAllocator; - CommandIterator mIterator; - bool mWasMovedToIterator = false; - bool mWereCommandsAcquired = false; - - bool mWereResourceUsagesAcquired = false; - CommandBufferResourceUsage mResourceUsages; - - unsigned int mDebugGroupStackSize = 0; + private: + MaybeError ValidateFinish(CommandIterator* commands, + const PerPassUsages& perPassUsages) const; - bool mGotError = false; - std::string mErrorMessage; + EncodingContext mEncodingContext; + std::set mTopLevelBuffers; + std::set mTopLevelTextures; + std::set mUsedQuerySets; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/CommandValidation.cpp b/third_party/dawn/src/dawn_native/CommandValidation.cpp new file mode 100644 index 00000000000..11a363a1ca5 --- /dev/null +++ b/third_party/dawn/src/dawn_native/CommandValidation.cpp @@ -0,0 +1,526 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/CommandValidation.h" + +#include "common/BitSetIterator.h" +#include "dawn_native/BindGroup.h" +#include "dawn_native/Buffer.h" +#include "dawn_native/CommandBufferStateTracker.h" +#include "dawn_native/Commands.h" +#include "dawn_native/Device.h" +#include "dawn_native/PassResourceUsage.h" +#include "dawn_native/QuerySet.h" +#include "dawn_native/RenderBundle.h" +#include "dawn_native/RenderPipeline.h" + +namespace dawn_native { + + namespace { + + inline MaybeError ValidateRenderBundleCommand(CommandIterator* commands, + Command type, + CommandBufferStateTracker* commandBufferState, + const AttachmentState* attachmentState, + uint64_t* debugGroupStackSize, + const char* disallowedMessage) { + switch (type) { + case Command::Draw: { + commands->NextCommand(); + DAWN_TRY(commandBufferState->ValidateCanDraw()); + break; + } + + case Command::DrawIndexed: { + commands->NextCommand(); + DAWN_TRY(commandBufferState->ValidateCanDrawIndexed()); + break; + } + + case Command::DrawIndirect: { + commands->NextCommand(); + DAWN_TRY(commandBufferState->ValidateCanDraw()); + break; + } + + case Command::DrawIndexedIndirect: { + commands->NextCommand(); + DAWN_TRY(commandBufferState->ValidateCanDrawIndexed()); + break; + } + + case Command::InsertDebugMarker: { + InsertDebugMarkerCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); + break; + } + + case Command::PopDebugGroup: { + commands->NextCommand(); + DAWN_TRY(ValidateCanPopDebugGroup(*debugGroupStackSize)); + *debugGroupStackSize -= 1; + break; + } + + case Command::PushDebugGroup: { + PushDebugGroupCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); + *debugGroupStackSize += 1; + break; + } + + case Command::SetRenderPipeline: { + SetRenderPipelineCmd* cmd = commands->NextCommand(); + RenderPipelineBase* pipeline = cmd->pipeline.Get(); + + if (DAWN_UNLIKELY(pipeline->GetAttachmentState() != attachmentState)) { + return DAWN_VALIDATION_ERROR("Pipeline attachment state is not compatible"); + } + commandBufferState->SetRenderPipeline(pipeline); + break; + } + + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = commands->NextCommand(); + if (cmd->dynamicOffsetCount > 0) { + commands->NextData(cmd->dynamicOffsetCount); + } + + commandBufferState->SetBindGroup(cmd->index, cmd->group.Get()); + break; + } + + case Command::SetIndexBuffer: { + commands->NextCommand(); + commandBufferState->SetIndexBuffer(); + break; + } + + case Command::SetVertexBuffer: { + SetVertexBufferCmd* cmd = commands->NextCommand(); + commandBufferState->SetVertexBuffer(cmd->slot); + break; + } + + default: + return DAWN_VALIDATION_ERROR(disallowedMessage); + } + + return {}; + } + + } // namespace + + MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize) { + if (debugGroupStackSize == 0) { + return DAWN_VALIDATION_ERROR("Pop must be balanced by a corresponding Push."); + } + return {}; + } + + MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize) { + if (debugGroupStackSize != 0) { + return DAWN_VALIDATION_ERROR("Each Push must be balanced by a corresponding Pop."); + } + return {}; + } + + MaybeError ValidateRenderBundle(CommandIterator* commands, + const AttachmentState* attachmentState) { + CommandBufferStateTracker commandBufferState; + uint64_t debugGroupStackSize = 0; + + Command type; + while (commands->NextCommandId(&type)) { + DAWN_TRY(ValidateRenderBundleCommand(commands, type, &commandBufferState, + attachmentState, &debugGroupStackSize, + "Command disallowed inside a render bundle")); + } + + DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize)); + return {}; + } + + MaybeError ValidateRenderPass(CommandIterator* commands, const BeginRenderPassCmd* renderPass) { + CommandBufferStateTracker commandBufferState; + uint64_t debugGroupStackSize = 0; + + Command type; + while (commands->NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + commands->NextCommand(); + DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize)); + return {}; + } + + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = commands->NextCommand(); + auto bundles = commands->NextData>(cmd->count); + for (uint32_t i = 0; i < cmd->count; ++i) { + if (DAWN_UNLIKELY(renderPass->attachmentState.Get() != + bundles[i]->GetAttachmentState())) { + return DAWN_VALIDATION_ERROR( + "Render bundle is not compatible with render pass"); + } + } + + if (cmd->count > 0) { + // Reset state. It is invalidated after render bundle execution. + commandBufferState = CommandBufferStateTracker{}; + } + + break; + } + + case Command::SetStencilReference: { + commands->NextCommand(); + break; + } + + case Command::SetBlendColor: { + commands->NextCommand(); + break; + } + + case Command::SetViewport: { + commands->NextCommand(); + break; + } + + case Command::SetScissorRect: { + commands->NextCommand(); + break; + } + + case Command::WriteTimestamp: { + commands->NextCommand(); + break; + } + + default: + DAWN_TRY(ValidateRenderBundleCommand( + commands, type, &commandBufferState, renderPass->attachmentState.Get(), + &debugGroupStackSize, "Command disallowed inside a render pass")); + } + } + + UNREACHABLE(); + return DAWN_VALIDATION_ERROR("Unfinished render pass"); + } + + MaybeError ValidateComputePass(CommandIterator* commands) { + CommandBufferStateTracker commandBufferState; + uint64_t debugGroupStackSize = 0; + + Command type; + while (commands->NextCommandId(&type)) { + switch (type) { + case Command::EndComputePass: { + commands->NextCommand(); + DAWN_TRY(ValidateFinalDebugGroupStackSize(debugGroupStackSize)); + return {}; + } + + case Command::Dispatch: { + commands->NextCommand(); + DAWN_TRY(commandBufferState.ValidateCanDispatch()); + break; + } + + case Command::DispatchIndirect: { + commands->NextCommand(); + DAWN_TRY(commandBufferState.ValidateCanDispatch()); + break; + } + + case Command::InsertDebugMarker: { + InsertDebugMarkerCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); + break; + } + + case Command::PopDebugGroup: { + commands->NextCommand(); + DAWN_TRY(ValidateCanPopDebugGroup(debugGroupStackSize)); + debugGroupStackSize--; + break; + } + + case Command::PushDebugGroup: { + PushDebugGroupCmd* cmd = commands->NextCommand(); + commands->NextData(cmd->length + 1); + debugGroupStackSize++; + break; + } + + case Command::SetComputePipeline: { + SetComputePipelineCmd* cmd = commands->NextCommand(); + ComputePipelineBase* pipeline = cmd->pipeline.Get(); + commandBufferState.SetComputePipeline(pipeline); + break; + } + + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = commands->NextCommand(); + if (cmd->dynamicOffsetCount > 0) { + commands->NextData(cmd->dynamicOffsetCount); + } + commandBufferState.SetBindGroup(cmd->index, cmd->group.Get()); + break; + } + + case Command::WriteTimestamp: { + commands->NextCommand(); + break; + } + + default: + return DAWN_VALIDATION_ERROR("Command disallowed inside a compute pass"); + } + } + + UNREACHABLE(); + return DAWN_VALIDATION_ERROR("Unfinished compute pass"); + } + + // Performs the per-pass usage validation checks + // This will eventually need to differentiate between render and compute passes. + // It will be valid to use a buffer both as uniform and storage in the same compute pass. + MaybeError ValidatePassResourceUsage(const PassResourceUsage& pass) { + // Buffers can only be used as single-write or multiple read. + for (size_t i = 0; i < pass.buffers.size(); ++i) { + const BufferBase* buffer = pass.buffers[i]; + wgpu::BufferUsage usage = pass.bufferUsages[i]; + + if (usage & ~buffer->GetUsage()) { + return DAWN_VALIDATION_ERROR("Buffer missing usage for the pass"); + } + + bool readOnly = (usage & kReadOnlyBufferUsages) == usage; + bool singleUse = wgpu::HasZeroOrOneBits(usage); + + if (pass.passType == PassType::Render && !readOnly && !singleUse) { + return DAWN_VALIDATION_ERROR( + "Buffer used as writable usage and another usage in pass"); + } + } + + // Textures can only be used as single-write or multiple read. + for (size_t i = 0; i < pass.textures.size(); ++i) { + const TextureBase* texture = pass.textures[i]; + const PassTextureUsage& textureUsage = pass.textureUsages[i]; + wgpu::TextureUsage usage = textureUsage.usage; + + if (usage & ~texture->GetUsage()) { + return DAWN_VALIDATION_ERROR("Texture missing usage for the pass"); + } + + // TODO (yunchao.he@intel.com): add read/write usage tracking for compute + + // The usage variable for the whole texture is a fast path for texture usage tracking. + // Because in most cases a texture (with or without subresources) is used as + // single-write or multiple read, then we can skip iterating the subresources' usages. + bool readOnly = (usage & kReadOnlyTextureUsages) == usage; + bool singleUse = wgpu::HasZeroOrOneBits(usage); + if (pass.passType != PassType::Render || readOnly || singleUse) { + continue; + } + // Inspect the subresources if the usage of the whole texture violates usage validation. + // Every single subresource can only be used as single-write or multiple read. + for (wgpu::TextureUsage subresourceUsage : textureUsage.subresourceUsages) { + bool readOnly = (subresourceUsage & kReadOnlyTextureUsages) == subresourceUsage; + bool singleUse = wgpu::HasZeroOrOneBits(subresourceUsage); + if (!readOnly && !singleUse) { + return DAWN_VALIDATION_ERROR( + "Texture used as writable usage and another usage in render pass"); + } + } + } + return {}; + } + + MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex) { + if (querySet->GetQueryType() != wgpu::QueryType::Timestamp) { + return DAWN_VALIDATION_ERROR("The query type of query set must be Timestamp"); + } + + if (queryIndex >= querySet->GetQueryCount()) { + return DAWN_VALIDATION_ERROR("Query index exceeds the number of queries in query set"); + } + + return {}; + } + + bool IsRangeOverlapped(uint32_t startA, uint32_t startB, uint32_t length) { + uint32_t maxStart = std::max(startA, startB); + uint32_t minStart = std::min(startA, startB); + return static_cast(minStart) + static_cast(length) > + static_cast(maxStart); + } + + uint32_t ComputeRequiredBytesInCopy(const Format& textureFormat, + const Extent3D& copySize, + uint32_t bytesPerRow, + uint32_t rowsPerImage) { + // Default value for rowsPerImage + if (rowsPerImage == 0) { + rowsPerImage = copySize.height; + } + ASSERT(rowsPerImage >= copySize.height); + if (copySize.width == 0 || copySize.height == 0 || copySize.depth == 0) { + return 0; + } + + ASSERT(copySize.height >= 1); + ASSERT(copySize.depth >= 1); + + uint64_t texelBlockRowsPerImage = rowsPerImage / textureFormat.blockHeight; + uint64_t bytesPerImage = bytesPerRow * texelBlockRowsPerImage; + uint64_t bytesInLastSlice = + bytesPerRow * (copySize.height / textureFormat.blockHeight - 1) + + (copySize.width / textureFormat.blockWidth * textureFormat.blockByteSize); + return bytesPerImage * (copySize.depth - 1) + bytesInLastSlice; + } + + MaybeError ValidateCopySizeFitsInBuffer(const Ref& buffer, + uint64_t offset, + uint64_t size) { + uint64_t bufferSize = buffer->GetSize(); + bool fitsInBuffer = offset <= bufferSize && (size <= (bufferSize - offset)); + if (!fitsInBuffer) { + return DAWN_VALIDATION_ERROR("Copy would overflow the buffer"); + } + + return {}; + } + + MaybeError ValidateLinearTextureData(const TextureDataLayout& layout, + uint64_t byteSize, + const Format& format, + const Extent3D& copyExtent) { + // Validation for the texel block alignments: + if (layout.rowsPerImage % format.blockHeight != 0) { + return DAWN_VALIDATION_ERROR( + "rowsPerImage must be a multiple of compressed texture format block height"); + } + + if (layout.offset % format.blockByteSize != 0) { + return DAWN_VALIDATION_ERROR("Offset must be a multiple of the texel or block size"); + } + + // Validation for the copy being in-bounds: + if (layout.rowsPerImage != 0 && layout.rowsPerImage < copyExtent.height) { + return DAWN_VALIDATION_ERROR("rowsPerImage must not be less than the copy height."); + } + + // We compute required bytes in copy after validating texel block alignments + // because the divisibility conditions are necessary for the algorithm to be valid. + // TODO(tommek@google.com): to match the spec this should only be checked when + // copyExtent.depth > 1. + uint32_t requiredBytesInCopy = + ComputeRequiredBytesInCopy(format, copyExtent, layout.bytesPerRow, layout.rowsPerImage); + + bool fitsInData = + layout.offset <= byteSize && (requiredBytesInCopy <= (byteSize - layout.offset)); + if (!fitsInData) { + return DAWN_VALIDATION_ERROR( + "Required size for texture data layout exceeds the given size"); + } + + // Validation for other members in layout: + if (layout.bytesPerRow < copyExtent.width / format.blockWidth * format.blockByteSize) { + return DAWN_VALIDATION_ERROR( + "bytesPerRow must not be less than the number of bytes per row"); + } + + // TODO(tommek@google.com): to match the spec there should be another condition here + // on rowsPerImage >= copyExtent.height if copyExtent.depth > 1. + + return {}; + } + + MaybeError ValidateBufferCopyView(DeviceBase const* device, + const BufferCopyView& bufferCopyView) { + // Should have already been fixed up to not use deprecated fields. + ASSERT(bufferCopyView.offset == 0 && bufferCopyView.bytesPerRow == 0 && + bufferCopyView.rowsPerImage == 0); + + DAWN_TRY(device->ValidateObject(bufferCopyView.buffer)); + if (bufferCopyView.layout.bytesPerRow % kTextureBytesPerRowAlignment != 0) { + return DAWN_VALIDATION_ERROR("bytesPerRow must be a multiple of 256"); + } + + return {}; + } + + MaybeError ValidateTextureCopyView(DeviceBase const* device, + const TextureCopyView& textureCopy) { + DAWN_TRY(device->ValidateObject(textureCopy.texture)); + if (textureCopy.mipLevel >= textureCopy.texture->GetNumMipLevels()) { + return DAWN_VALIDATION_ERROR("mipLevel out of range"); + } + + if (textureCopy.origin.x % textureCopy.texture->GetFormat().blockWidth != 0) { + return DAWN_VALIDATION_ERROR( + "Offset.x must be a multiple of compressed texture format block width"); + } + + if (textureCopy.origin.y % textureCopy.texture->GetFormat().blockHeight != 0) { + return DAWN_VALIDATION_ERROR( + "Offset.y must be a multiple of compressed texture format block height"); + } + + return {}; + } + + MaybeError ValidateTextureCopyRange(const TextureCopyView& textureCopy, + const Extent3D& copySize) { + // TODO(jiawei.shao@intel.com): add validations on the texture-to-texture copies within the + // same texture. + const TextureBase* texture = textureCopy.texture; + + // Validation for the copy being in-bounds: + Extent3D mipSize = texture->GetMipLevelPhysicalSize(textureCopy.mipLevel); + // For 2D textures, include the array layer as depth so it can be checked with other + // dimensions. + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + mipSize.depth = texture->GetArrayLayers(); + + // All texture dimensions are in uint32_t so by doing checks in uint64_t we avoid + // overflows. + if (static_cast(textureCopy.origin.x) + static_cast(copySize.width) > + static_cast(mipSize.width) || + static_cast(textureCopy.origin.y) + static_cast(copySize.height) > + static_cast(mipSize.height) || + static_cast(textureCopy.origin.z) + static_cast(copySize.depth) > + static_cast(mipSize.depth)) { + return DAWN_VALIDATION_ERROR("Touching outside of the texture"); + } + + // Validation for the texel block alignments: + if (copySize.width % textureCopy.texture->GetFormat().blockWidth != 0) { + return DAWN_VALIDATION_ERROR( + "copySize.width must be a multiple of compressed texture format block width"); + } + + if (copySize.height % textureCopy.texture->GetFormat().blockHeight != 0) { + return DAWN_VALIDATION_ERROR( + "copySize.height must be a multiple of compressed texture format block height"); + } + + return {}; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/CommandValidation.h b/third_party/dawn/src/dawn_native/CommandValidation.h new file mode 100644 index 00000000000..ae0464c0acb --- /dev/null +++ b/third_party/dawn/src/dawn_native/CommandValidation.h @@ -0,0 +1,74 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_COMMANDVALIDATION_H_ +#define DAWNNATIVE_COMMANDVALIDATION_H_ + +#include "dawn_native/CommandAllocator.h" +#include "dawn_native/Error.h" +#include "dawn_native/Texture.h" + +#include + +namespace dawn_native { + + class AttachmentState; + class QuerySetBase; + struct BeginRenderPassCmd; + struct PassResourceUsage; + + MaybeError ValidateCanPopDebugGroup(uint64_t debugGroupStackSize); + MaybeError ValidateFinalDebugGroupStackSize(uint64_t debugGroupStackSize); + + MaybeError ValidateRenderBundle(CommandIterator* commands, + const AttachmentState* attachmentState); + MaybeError ValidateRenderPass(CommandIterator* commands, const BeginRenderPassCmd* renderPass); + MaybeError ValidateComputePass(CommandIterator* commands); + + MaybeError ValidatePassResourceUsage(const PassResourceUsage& usage); + + MaybeError ValidateTimestampQuery(QuerySetBase* querySet, uint32_t queryIndex); + + uint32_t ComputeRequiredBytesInCopy(const Format& textureFormat, + const Extent3D& copySize, + uint32_t bytesPerRow, + uint32_t rowsPerImage); + + MaybeError ValidateLinearTextureData(const TextureDataLayout& layout, + uint64_t byteSize, + const Format& format, + const Extent3D& copyExtent); + MaybeError ValidateTextureCopyRange(const TextureCopyView& textureCopyView, + const Extent3D& copySize); + + MaybeError ValidateBufferCopyView(DeviceBase const* device, + const BufferCopyView& bufferCopyView); + MaybeError ValidateTextureCopyView(DeviceBase const* device, + const TextureCopyView& textureCopyView); + + MaybeError ValidateRowsPerImage(const Format& format, + uint32_t rowsPerImage, + uint32_t copyHeight); + MaybeError ValidateBytesPerRow(const Format& format, + const Extent3D& copySize, + uint32_t bytesPerRow); + MaybeError ValidateCopySizeFitsInBuffer(const Ref& buffer, + uint64_t offset, + uint64_t size); + + bool IsRangeOverlapped(uint32_t startA, uint32_t startB, uint32_t length); + +} // namespace dawn_native + +#endif // DAWNNATIVE_COMMANDVALIDATION_H_ diff --git a/third_party/dawn/src/dawn_native/Commands.cpp b/third_party/dawn/src/dawn_native/Commands.cpp index 93f7ddfc978..9528e82ed8f 100644 --- a/third_party/dawn/src/dawn_native/Commands.cpp +++ b/third_party/dawn/src/dawn_native/Commands.cpp @@ -18,6 +18,8 @@ #include "dawn_native/Buffer.h" #include "dawn_native/CommandAllocator.h" #include "dawn_native/ComputePipeline.h" +#include "dawn_native/QuerySet.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/RenderPipeline.h" #include "dawn_native/Texture.h" @@ -32,114 +34,158 @@ namespace dawn_native { case Command::BeginComputePass: { BeginComputePassCmd* begin = commands->NextCommand(); begin->~BeginComputePassCmd(); - } break; + break; + } case Command::BeginRenderPass: { BeginRenderPassCmd* begin = commands->NextCommand(); begin->~BeginRenderPassCmd(); - } break; + break; + } case Command::CopyBufferToBuffer: { CopyBufferToBufferCmd* copy = commands->NextCommand(); copy->~CopyBufferToBufferCmd(); - } break; + break; + } case Command::CopyBufferToTexture: { CopyBufferToTextureCmd* copy = commands->NextCommand(); copy->~CopyBufferToTextureCmd(); - } break; + break; + } case Command::CopyTextureToBuffer: { CopyTextureToBufferCmd* copy = commands->NextCommand(); copy->~CopyTextureToBufferCmd(); - } break; + break; + } case Command::CopyTextureToTexture: { CopyTextureToTextureCmd* copy = commands->NextCommand(); copy->~CopyTextureToTextureCmd(); - } break; + break; + } case Command::Dispatch: { DispatchCmd* dispatch = commands->NextCommand(); dispatch->~DispatchCmd(); - } break; + break; + } case Command::DispatchIndirect: { DispatchIndirectCmd* dispatch = commands->NextCommand(); dispatch->~DispatchIndirectCmd(); - } break; + break; + } case Command::Draw: { DrawCmd* draw = commands->NextCommand(); draw->~DrawCmd(); - } break; + break; + } case Command::DrawIndexed: { DrawIndexedCmd* draw = commands->NextCommand(); draw->~DrawIndexedCmd(); - } break; + break; + } case Command::DrawIndirect: { DrawIndirectCmd* draw = commands->NextCommand(); draw->~DrawIndirectCmd(); - } break; + break; + } case Command::DrawIndexedIndirect: { DrawIndexedIndirectCmd* draw = commands->NextCommand(); draw->~DrawIndexedIndirectCmd(); - } break; + break; + } case Command::EndComputePass: { EndComputePassCmd* cmd = commands->NextCommand(); cmd->~EndComputePassCmd(); - } break; + break; + } case Command::EndRenderPass: { EndRenderPassCmd* cmd = commands->NextCommand(); cmd->~EndRenderPassCmd(); - } break; + break; + } + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = commands->NextCommand(); + auto bundles = commands->NextData>(cmd->count); + for (size_t i = 0; i < cmd->count; ++i) { + (&bundles[i])->~Ref(); + } + cmd->~ExecuteBundlesCmd(); + break; + } case Command::InsertDebugMarker: { InsertDebugMarkerCmd* cmd = commands->NextCommand(); commands->NextData(cmd->length + 1); cmd->~InsertDebugMarkerCmd(); - } break; + break; + } case Command::PopDebugGroup: { PopDebugGroupCmd* cmd = commands->NextCommand(); cmd->~PopDebugGroupCmd(); - } break; + break; + } case Command::PushDebugGroup: { PushDebugGroupCmd* cmd = commands->NextCommand(); commands->NextData(cmd->length + 1); cmd->~PushDebugGroupCmd(); - } break; + break; + } + case Command::ResolveQuerySet: { + ResolveQuerySetCmd* cmd = commands->NextCommand(); + cmd->~ResolveQuerySetCmd(); + break; + } case Command::SetComputePipeline: { SetComputePipelineCmd* cmd = commands->NextCommand(); cmd->~SetComputePipelineCmd(); - } break; + break; + } case Command::SetRenderPipeline: { SetRenderPipelineCmd* cmd = commands->NextCommand(); cmd->~SetRenderPipelineCmd(); - } break; + break; + } case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = commands->NextCommand(); cmd->~SetStencilReferenceCmd(); - } break; + break; + } + case Command::SetViewport: { + SetViewportCmd* cmd = commands->NextCommand(); + cmd->~SetViewportCmd(); + break; + } case Command::SetScissorRect: { SetScissorRectCmd* cmd = commands->NextCommand(); cmd->~SetScissorRectCmd(); - } break; + break; + } case Command::SetBlendColor: { SetBlendColorCmd* cmd = commands->NextCommand(); cmd->~SetBlendColorCmd(); - } break; + break; + } case Command::SetBindGroup: { SetBindGroupCmd* cmd = commands->NextCommand(); if (cmd->dynamicOffsetCount > 0) { - commands->NextData(cmd->dynamicOffsetCount); + commands->NextData(cmd->dynamicOffsetCount); } cmd->~SetBindGroupCmd(); - } break; + break; + } case Command::SetIndexBuffer: { SetIndexBufferCmd* cmd = commands->NextCommand(); cmd->~SetIndexBufferCmd(); - } break; - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = commands->NextCommand(); - auto buffers = commands->NextData>(cmd->count); - for (size_t i = 0; i < cmd->count; ++i) { - (&buffers[i])->~Ref(); - } - commands->NextData(cmd->count); - cmd->~SetVertexBuffersCmd(); - } break; + break; + } + case Command::SetVertexBuffer: { + SetVertexBufferCmd* cmd = commands->NextCommand(); + cmd->~SetVertexBufferCmd(); + break; + } + case Command::WriteTimestamp: { + WriteTimestampCmd* cmd = commands->NextCommand(); + cmd->~WriteTimestampCmd(); + break; + } } } commands->DataWasDestroyed(); @@ -203,10 +249,17 @@ namespace dawn_native { commands->NextCommand(); break; + case Command::ExecuteBundles: { + auto* cmd = commands->NextCommand(); + commands->NextData>(cmd->count); + break; + } + case Command::InsertDebugMarker: { InsertDebugMarkerCmd* cmd = commands->NextCommand(); commands->NextData(cmd->length + 1); - } break; + break; + } case Command::PopDebugGroup: commands->NextCommand(); @@ -215,7 +268,13 @@ namespace dawn_native { case Command::PushDebugGroup: { PushDebugGroupCmd* cmd = commands->NextCommand(); commands->NextData(cmd->length + 1); - } break; + break; + } + + case Command::ResolveQuerySet: { + commands->NextCommand(); + break; + } case Command::SetComputePipeline: commands->NextCommand(); @@ -229,6 +288,10 @@ namespace dawn_native { commands->NextCommand(); break; + case Command::SetViewport: + commands->NextCommand(); + break; + case Command::SetScissorRect: commands->NextCommand(); break; @@ -237,19 +300,27 @@ namespace dawn_native { commands->NextCommand(); break; - case Command::SetBindGroup: - commands->NextCommand(); + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = commands->NextCommand(); + if (cmd->dynamicOffsetCount > 0) { + commands->NextData(cmd->dynamicOffsetCount); + } break; + } case Command::SetIndexBuffer: commands->NextCommand(); break; - case Command::SetVertexBuffers: { - auto* cmd = commands->NextCommand(); - commands->NextData>(cmd->count); - commands->NextData(cmd->count); - } break; + case Command::SetVertexBuffer: { + commands->NextCommand(); + break; + } + + case Command::WriteTimestamp: { + commands->NextCommand(); + break; + } } } diff --git a/third_party/dawn/src/dawn_native/Commands.h b/third_party/dawn/src/dawn_native/Commands.h index 04ae7f58a6b..85e462bc6a3 100644 --- a/third_party/dawn/src/dawn_native/Commands.h +++ b/third_party/dawn/src/dawn_native/Commands.h @@ -17,6 +17,8 @@ #include "common/Constants.h" +#include "dawn_native/AttachmentState.h" +#include "dawn_native/BindingInfo.h" #include "dawn_native/Texture.h" #include "dawn_native/dawn_platform.h" @@ -45,17 +47,21 @@ namespace dawn_native { DrawIndexedIndirect, EndComputePass, EndRenderPass, + ExecuteBundles, InsertDebugMarker, PopDebugGroup, PushDebugGroup, + ResolveQuerySet, SetComputePipeline, SetRenderPipeline, SetStencilReference, + SetViewport, SetScissorRect, SetBlendColor, SetBindGroup, SetIndexBuffer, - SetVertexBuffers, + SetVertexBuffer, + WriteTimestamp, }; struct BeginComputePassCmd {}; @@ -63,45 +69,42 @@ namespace dawn_native { struct RenderPassColorAttachmentInfo { Ref view; Ref resolveTarget; - dawn::LoadOp loadOp; - dawn::StoreOp storeOp; + wgpu::LoadOp loadOp; + wgpu::StoreOp storeOp; dawn_native::Color clearColor; }; struct RenderPassDepthStencilAttachmentInfo { Ref view; - dawn::LoadOp depthLoadOp; - dawn::StoreOp depthStoreOp; - dawn::LoadOp stencilLoadOp; - dawn::StoreOp stencilStoreOp; + wgpu::LoadOp depthLoadOp; + wgpu::StoreOp depthStoreOp; + wgpu::LoadOp stencilLoadOp; + wgpu::StoreOp stencilStoreOp; float clearDepth; uint32_t clearStencil; }; struct BeginRenderPassCmd { - std::bitset colorAttachmentsSet; + Ref attachmentState; RenderPassColorAttachmentInfo colorAttachments[kMaxColorAttachments]; - bool hasDepthStencilAttachment; RenderPassDepthStencilAttachmentInfo depthStencilAttachment; - // Cache the width, height and sample count of all attachments for convenience + // Cache the width and height of all attachments for convenience uint32_t width; uint32_t height; - uint32_t sampleCount; }; struct BufferCopy { Ref buffer; - uint64_t offset; // Bytes - uint32_t rowPitch; // Bytes - uint32_t imageHeight; // Texels + uint64_t offset; + uint32_t bytesPerRow; + uint32_t rowsPerImage; }; struct TextureCopy { Ref texture; - uint32_t level; - uint32_t slice; - Origin3D origin; // Texels + uint32_t mipLevel; + Origin3D origin; // Texels / array layer }; struct CopyBufferToBufferCmd { @@ -170,6 +173,10 @@ namespace dawn_native { struct EndRenderPassCmd {}; + struct ExecuteBundlesCmd { + uint32_t count; + }; + struct InsertDebugMarkerCmd { uint32_t length; }; @@ -180,6 +187,14 @@ namespace dawn_native { uint32_t length; }; + struct ResolveQuerySetCmd { + Ref querySet; + uint32_t firstQuery; + uint32_t queryCount; + Ref destination; + uint64_t destinationOffset; + }; + struct SetComputePipelineCmd { Ref pipeline; }; @@ -192,6 +207,10 @@ namespace dawn_native { uint32_t reference; }; + struct SetViewportCmd { + float x, y, width, height, minDepth, maxDepth; + }; + struct SetScissorRectCmd { uint32_t x, y, width, height; }; @@ -201,7 +220,7 @@ namespace dawn_native { }; struct SetBindGroupCmd { - uint32_t index; + BindGroupIndex index; Ref group; uint32_t dynamicOffsetCount; }; @@ -209,11 +228,19 @@ namespace dawn_native { struct SetIndexBufferCmd { Ref buffer; uint64_t offset; + uint64_t size; }; - struct SetVertexBuffersCmd { - uint32_t startSlot; - uint32_t count; + struct SetVertexBufferCmd { + uint32_t slot; + Ref buffer; + uint64_t offset; + uint64_t size; + }; + + struct WriteTimestampCmd { + Ref querySet; + uint32_t queryIndex; }; // This needs to be called before the CommandIterator is freed so that the Ref<> present in diff --git a/third_party/dawn/src/dawn_native/ComputePassEncoder.cpp b/third_party/dawn/src/dawn_native/ComputePassEncoder.cpp index 708b2cb01b1..2329c0008e2 100644 --- a/third_party/dawn/src/dawn_native/ComputePassEncoder.cpp +++ b/third_party/dawn/src/dawn_native/ComputePassEncoder.cpp @@ -16,68 +16,103 @@ #include "dawn_native/Buffer.h" #include "dawn_native/CommandEncoder.h" +#include "dawn_native/CommandValidation.h" #include "dawn_native/Commands.h" #include "dawn_native/ComputePipeline.h" #include "dawn_native/Device.h" +#include "dawn_native/QuerySet.h" namespace dawn_native { - ComputePassEncoderBase::ComputePassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator) - : ProgrammablePassEncoder(device, topLevelEncoder, allocator) { + ComputePassEncoder::ComputePassEncoder(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext) + : ProgrammablePassEncoder(device, encodingContext, PassType::Compute), + mCommandEncoder(commandEncoder) { } - ComputePassEncoderBase::ComputePassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - ErrorTag errorTag) - : ProgrammablePassEncoder(device, topLevelEncoder, errorTag) { + ComputePassEncoder::ComputePassEncoder(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext, + ErrorTag errorTag) + : ProgrammablePassEncoder(device, encodingContext, errorTag, PassType::Compute), + mCommandEncoder(commandEncoder) { } - ComputePassEncoderBase* ComputePassEncoderBase::MakeError(DeviceBase* device, - CommandEncoderBase* topLevelEncoder) { - return new ComputePassEncoderBase(device, topLevelEncoder, ObjectBase::kError); + ComputePassEncoder* ComputePassEncoder::MakeError(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext) { + return new ComputePassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError); } - void ComputePassEncoderBase::Dispatch(uint32_t x, uint32_t y, uint32_t z) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; + void ComputePassEncoder::EndPass() { + if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + allocator->Allocate(Command::EndComputePass); + + return {}; + })) { + mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage()); } + } - DispatchCmd* dispatch = mAllocator->Allocate(Command::Dispatch); - dispatch->x = x; - dispatch->y = y; - dispatch->z = z; + void ComputePassEncoder::Dispatch(uint32_t x, uint32_t y, uint32_t z) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DispatchCmd* dispatch = allocator->Allocate(Command::Dispatch); + dispatch->x = x; + dispatch->y = y; + dispatch->z = z; + + return {}; + }); } - void ComputePassEncoderBase::DispatchIndirect(BufferBase* indirectBuffer, - uint64_t indirectOffset) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) { - return; - } + void ComputePassEncoder::DispatchIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer)); - if (indirectOffset >= indirectBuffer->GetSize() || - indirectOffset + kDispatchIndirectSize > indirectBuffer->GetSize()) { - mTopLevelEncoder->HandleError("Indirect offset out of bounds"); - return; - } + if (indirectOffset >= indirectBuffer->GetSize() || + indirectOffset + kDispatchIndirectSize > indirectBuffer->GetSize()) { + return DAWN_VALIDATION_ERROR("Indirect offset out of bounds"); + } + + DispatchIndirectCmd* dispatch = + allocator->Allocate(Command::DispatchIndirect); + dispatch->indirectBuffer = indirectBuffer; + dispatch->indirectOffset = indirectOffset; + + mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect); - DispatchIndirectCmd* dispatch = - mAllocator->Allocate(Command::DispatchIndirect); - dispatch->indirectBuffer = indirectBuffer; - dispatch->indirectOffset = indirectOffset; + return {}; + }); } - void ComputePassEncoderBase::SetPipeline(ComputePipelineBase* pipeline) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(pipeline))) { - return; - } + void ComputePassEncoder::SetPipeline(ComputePipelineBase* pipeline) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(pipeline)); + + SetComputePipelineCmd* cmd = + allocator->Allocate(Command::SetComputePipeline); + cmd->pipeline = pipeline; + + return {}; + }); + } + + void ComputePassEncoder::WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(GetDevice()->ValidateObject(querySet)); + DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex)); + mCommandEncoder->TrackUsedQuerySet(querySet); + } + + WriteTimestampCmd* cmd = + allocator->Allocate(Command::WriteTimestamp); + cmd->querySet = querySet; + cmd->queryIndex = queryIndex; - SetComputePipelineCmd* cmd = - mAllocator->Allocate(Command::SetComputePipeline); - cmd->pipeline = pipeline; + return {}; + }); } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ComputePassEncoder.h b/third_party/dawn/src/dawn_native/ComputePassEncoder.h index 36a7e31e646..6ae796a2411 100644 --- a/third_party/dawn/src/dawn_native/ComputePassEncoder.h +++ b/third_party/dawn/src/dawn_native/ComputePassEncoder.h @@ -20,27 +20,34 @@ namespace dawn_native { - // This is called ComputePassEncoderBase to match the code generator expectations. Note that it - // is a pure frontend type to record in its parent CommandEncoder and never has a backend - // implementation. - // TODO(cwallez@chromium.org): Remove that generator limitation and rename to ComputePassEncoder - class ComputePassEncoderBase : public ProgrammablePassEncoder { + class ComputePassEncoder final : public ProgrammablePassEncoder { public: - ComputePassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator); + ComputePassEncoder(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext); - static ComputePassEncoderBase* MakeError(DeviceBase* device, - CommandEncoderBase* topLevelEncoder); + static ComputePassEncoder* MakeError(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext); + + void EndPass(); void Dispatch(uint32_t x, uint32_t y, uint32_t z); void DispatchIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset); void SetPipeline(ComputePipelineBase* pipeline); + void WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex); + protected: - ComputePassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - ErrorTag errorTag); + ComputePassEncoder(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext, + ErrorTag errorTag); + + private: + // For render and compute passes, the encoding context is borrowed from the command encoder. + // Keep a reference to the encoder to make sure the context isn't freed. + Ref mCommandEncoder; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ComputePipeline.cpp b/third_party/dawn/src/dawn_native/ComputePipeline.cpp index 468928b9472..ee49b1151d1 100644 --- a/third_party/dawn/src/dawn_native/ComputePipeline.cpp +++ b/third_party/dawn/src/dawn_native/ComputePipeline.cpp @@ -19,27 +19,38 @@ namespace dawn_native { + namespace { + RequiredBufferSizes ComputeMinBufferSizes(const ComputePipelineDescriptor* descriptor) { + return descriptor->computeStage.module->ComputeRequiredBufferSizesForLayout( + descriptor->layout); + } + } // anonymous namespace + MaybeError ValidateComputePipelineDescriptor(DeviceBase* device, const ComputePipelineDescriptor* descriptor) { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - DAWN_TRY(device->ValidateObject(descriptor->layout)); - DAWN_TRY(ValidatePipelineStageDescriptor(device, descriptor->computeStage, - descriptor->layout, dawn::ShaderStage::Compute)); + if (descriptor->layout != nullptr) { + DAWN_TRY(device->ValidateObject(descriptor->layout)); + } + + DAWN_TRY(ValidateProgrammableStageDescriptor( + device, &descriptor->computeStage, descriptor->layout, SingleShaderStage::Compute)); return {}; } // ComputePipelineBase ComputePipelineBase::ComputePipelineBase(DeviceBase* device, - const ComputePipelineDescriptor* descriptor, - bool blueprint) - : PipelineBase(device, descriptor->layout, dawn::ShaderStageBit::Compute), - mModule(descriptor->computeStage->module), - mEntryPoint(descriptor->computeStage->entryPoint), - mIsBlueprint(blueprint) { + const ComputePipelineDescriptor* descriptor) + : PipelineBase(device, + descriptor->layout, + wgpu::ShaderStage::Compute, + ComputeMinBufferSizes(descriptor)), + mModule(descriptor->computeStage.module), + mEntryPoint(descriptor->computeStage.entryPoint) { } ComputePipelineBase::ComputePipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag) @@ -48,7 +59,7 @@ namespace dawn_native { ComputePipelineBase::~ComputePipelineBase() { // Do not uncache the actual cached object if we are a blueprint - if (!mIsBlueprint && !IsError()) { + if (IsCachedReference()) { GetDevice()->UncacheComputePipeline(this); } } diff --git a/third_party/dawn/src/dawn_native/ComputePipeline.h b/third_party/dawn/src/dawn_native/ComputePipeline.h index 006c469d96a..43d7966568d 100644 --- a/third_party/dawn/src/dawn_native/ComputePipeline.h +++ b/third_party/dawn/src/dawn_native/ComputePipeline.h @@ -26,9 +26,7 @@ namespace dawn_native { class ComputePipelineBase : public PipelineBase { public: - ComputePipelineBase(DeviceBase* device, - const ComputePipelineDescriptor* descriptor, - bool blueprint = false); + ComputePipelineBase(DeviceBase* device, const ComputePipelineDescriptor* descriptor); ~ComputePipelineBase() override; static ComputePipelineBase* MakeError(DeviceBase* device); @@ -47,7 +45,6 @@ namespace dawn_native { // TODO(cwallez@chromium.org): Store a crypto hash of the module instead. Ref mModule; std::string mEntryPoint; - bool mIsBlueprint = false; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/DawnNative.cpp b/third_party/dawn/src/dawn_native/DawnNative.cpp index 90a332f6431..32061152bdf 100644 --- a/third_party/dawn/src/dawn_native/DawnNative.cpp +++ b/third_party/dawn/src/dawn_native/DawnNative.cpp @@ -15,6 +15,8 @@ #include "dawn_native/DawnNative.h" #include "dawn_native/Device.h" #include "dawn_native/Instance.h" +#include "dawn_native/Texture.h" +#include "dawn_platform/DawnPlatform.h" // Contains the entry-points into dawn_native @@ -26,7 +28,7 @@ namespace dawn_native { return GetProcsAutogen(); } - std::vector GetTogglesUsed(DawnDevice device) { + std::vector GetTogglesUsed(WGPUDevice device) { const dawn_native::DeviceBase* deviceBase = reinterpret_cast(device); return deviceBase->GetTogglesUsed(); @@ -43,39 +45,83 @@ namespace dawn_native { mImpl = nullptr; } + void Adapter::GetProperties(wgpu::AdapterProperties* properties) const { + properties->backendType = mImpl->GetBackendType(); + properties->adapterType = mImpl->GetAdapterType(); + properties->deviceID = mImpl->GetPCIInfo().deviceId; + properties->vendorID = mImpl->GetPCIInfo().vendorId; + properties->name = mImpl->GetPCIInfo().name.c_str(); + } + BackendType Adapter::GetBackendType() const { - return mImpl->GetBackendType(); + switch (mImpl->GetBackendType()) { + case wgpu::BackendType::D3D12: + return BackendType::D3D12; + case wgpu::BackendType::Metal: + return BackendType::Metal; + case wgpu::BackendType::Null: + return BackendType::Null; + case wgpu::BackendType::OpenGL: + return BackendType::OpenGL; + case wgpu::BackendType::Vulkan: + return BackendType::Vulkan; + default: + UNREACHABLE(); + } } DeviceType Adapter::GetDeviceType() const { - return mImpl->GetDeviceType(); + switch (mImpl->GetAdapterType()) { + case wgpu::AdapterType::DiscreteGPU: + return DeviceType::DiscreteGPU; + case wgpu::AdapterType::IntegratedGPU: + return DeviceType::IntegratedGPU; + case wgpu::AdapterType::CPU: + return DeviceType::CPU; + case wgpu::AdapterType::Unknown: + return DeviceType::Unknown; + default: + UNREACHABLE(); + } } const PCIInfo& Adapter::GetPCIInfo() const { return mImpl->GetPCIInfo(); } + std::vector Adapter::GetSupportedExtensions() const { + ExtensionsSet supportedExtensionsSet = mImpl->GetSupportedExtensions(); + return supportedExtensionsSet.GetEnabledExtensionNames(); + } + + WGPUDeviceProperties Adapter::GetAdapterProperties() const { + return mImpl->GetAdapterProperties(); + } + Adapter::operator bool() const { return mImpl != nullptr; } - DawnDevice Adapter::CreateDevice(const DeviceDescriptor* deviceDescriptor) { - return reinterpret_cast(mImpl->CreateDevice(deviceDescriptor)); + WGPUDevice Adapter::CreateDevice(const DeviceDescriptor* deviceDescriptor) { + return reinterpret_cast(mImpl->CreateDevice(deviceDescriptor)); } // AdapterDiscoverOptionsBase - AdapterDiscoveryOptionsBase::AdapterDiscoveryOptionsBase(BackendType type) : backendType(type) { + AdapterDiscoveryOptionsBase::AdapterDiscoveryOptionsBase(WGPUBackendType type) + : backendType(type) { } // Instance - Instance::Instance() : mImpl(new InstanceBase()) { + Instance::Instance() : mImpl(InstanceBase::Create()) { } Instance::~Instance() { - delete mImpl; - mImpl = nullptr; + if (mImpl != nullptr) { + mImpl->Release(); + mImpl = nullptr; + } } void Instance::DiscoverDefaultAdapters() { @@ -103,7 +149,47 @@ namespace dawn_native { mImpl->EnableBackendValidation(enableBackendValidation); } - bool Instance::IsBackendValidationEnabled() { - return mImpl->IsBackendValidationEnabled(); + void Instance::EnableBeginCaptureOnStartup(bool beginCaptureOnStartup) { + mImpl->EnableBeginCaptureOnStartup(beginCaptureOnStartup); + } + + void Instance::SetPlatform(dawn_platform::Platform* platform) { + mImpl->SetPlatform(platform); + } + + WGPUInstance Instance::Get() const { + return reinterpret_cast(mImpl); + } + + size_t GetLazyClearCountForTesting(WGPUDevice device) { + dawn_native::DeviceBase* deviceBase = reinterpret_cast(device); + return deviceBase->GetLazyClearCountForTesting(); + } + + size_t GetDeprecationWarningCountForTesting(WGPUDevice device) { + dawn_native::DeviceBase* deviceBase = reinterpret_cast(device); + return deviceBase->GetDeprecationWarningCountForTesting(); + } + + bool IsTextureSubresourceInitialized(WGPUTexture texture, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount) { + dawn_native::TextureBase* textureBase = + reinterpret_cast(texture); + SubresourceRange range = {baseMipLevel, levelCount, baseArrayLayer, layerCount}; + return textureBase->IsSubresourceContentInitialized(range); } + + std::vector GetProcMapNamesForTestingInternal(); + + std::vector GetProcMapNamesForTesting() { + return GetProcMapNamesForTestingInternal(); + } + + ExternalImageDescriptor::ExternalImageDescriptor(ExternalImageDescriptorType type) + : type(type) { + } + } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Device.cpp b/third_party/dawn/src/dawn_native/Device.cpp index 26d0d44e283..533035c3d91 100644 --- a/third_party/dawn/src/dawn_native/Device.cpp +++ b/third_party/dawn/src/dawn_native/Device.cpp @@ -14,7 +14,9 @@ #include "dawn_native/Device.h" +#include "common/Log.h" #include "dawn_native/Adapter.h" +#include "dawn_native/AttachmentState.h" #include "dawn_native/BindGroup.h" #include "dawn_native/BindGroupLayout.h" #include "dawn_native/Buffer.h" @@ -23,22 +25,29 @@ #include "dawn_native/ComputePipeline.h" #include "dawn_native/DynamicUploader.h" #include "dawn_native/ErrorData.h" +#include "dawn_native/ErrorScope.h" +#include "dawn_native/ErrorScopeTracker.h" #include "dawn_native/Fence.h" #include "dawn_native/FenceSignalTracker.h" #include "dawn_native/Instance.h" +#include "dawn_native/MapRequestTracker.h" #include "dawn_native/PipelineLayout.h" +#include "dawn_native/QuerySet.h" #include "dawn_native/Queue.h" +#include "dawn_native/RenderBundleEncoder.h" #include "dawn_native/RenderPipeline.h" #include "dawn_native/Sampler.h" #include "dawn_native/ShaderModule.h" +#include "dawn_native/Surface.h" #include "dawn_native/SwapChain.h" #include "dawn_native/Texture.h" +#include "dawn_native/ValidationUtils_autogen.h" #include namespace dawn_native { - // DeviceBase::Caches + // DeviceBase sub-structures // The caches are unordered_sets of pointers with special hash and compare functions // to compare the value of the objects, instead of the pointers. @@ -47,6 +56,17 @@ namespace dawn_native { std::unordered_set; struct DeviceBase::Caches { + ~Caches() { + ASSERT(attachmentStates.empty()); + ASSERT(bindGroupLayouts.empty()); + ASSERT(computePipelines.empty()); + ASSERT(pipelineLayouts.empty()); + ASSERT(renderPipelines.empty()); + ASSERT(samplers.empty()); + ASSERT(shaderModules.empty()); + } + + ContentLessObjectCache attachmentStates; ContentLessObjectCache bindGroupLayouts; ContentLessObjectCache computePipelines; ContentLessObjectCache pipelineLayouts; @@ -55,33 +75,204 @@ namespace dawn_native { ContentLessObjectCache shaderModules; }; + struct DeviceBase::DeprecationWarnings { + std::unordered_set emitted; + size_t count = 0; + }; + // DeviceBase DeviceBase::DeviceBase(AdapterBase* adapter, const DeviceDescriptor* descriptor) : mAdapter(adapter) { + if (descriptor != nullptr) { + ApplyToggleOverrides(descriptor); + ApplyExtensions(descriptor); + } + + mFormatTable = BuildFormatTable(this); + SetDefaultToggles(); + } + + DeviceBase::~DeviceBase() { + } + + MaybeError DeviceBase::Initialize(QueueBase* defaultQueue) { + mDefaultQueue = AcquireRef(defaultQueue); + mRootErrorScope = AcquireRef(new ErrorScope()); + mCurrentErrorScope = mRootErrorScope.Get(); + mCaches = std::make_unique(); + mErrorScopeTracker = std::make_unique(this); mFenceSignalTracker = std::make_unique(this); + mMapRequestTracker = std::make_unique(this); mDynamicUploader = std::make_unique(this); - SetDefaultToggles(); + mDeprecationWarnings = std::make_unique(); + + // Starting from now the backend can start doing reentrant calls so the device is marked as + // alive. + mState = State::Alive; + + DAWN_TRY_ASSIGN(mEmptyBindGroupLayout, CreateEmptyBindGroupLayout()); + + return {}; } - DeviceBase::~DeviceBase() { - // Devices must explicitly free the uploader - ASSERT(mDynamicUploader == nullptr); + void DeviceBase::ShutDownBase() { + // Disconnect the device, depending on which state we are currently in. + switch (mState) { + case State::BeingCreated: + // The GPU timeline was never started so we don't have to wait. + break; + + case State::Alive: + // Alive is the only state which can have GPU work happening. Wait for all of it to + // complete before proceeding with destruction. + // Ignore errors so that we can continue with destruction + IgnoreErrors(WaitForIdleForDestruction()); + AssumeCommandsComplete(); + break; + + case State::BeingDisconnected: + // Getting disconnected is a transient state happening in a single API call so there + // is always an external reference keeping the Device alive, which means the + // destructor cannot run while BeingDisconnected. + UNREACHABLE(); + break; + + case State::Disconnected: + break; + } + ASSERT(mCompletedSerial == mLastSubmittedSerial); + ASSERT(mFutureCallbackSerial <= mCompletedSerial); + + // Skip handling device facilities if they haven't even been created (or failed doing so) + if (mState != State::BeingCreated) { + // The GPU timeline is finished so all services can be freed immediately. They need to + // be freed before ShutDownImpl() because they might relinquish resources that will be + // freed by backends in the ShutDownImpl() call. Still tick the ones that might have + // pending callbacks. + mErrorScopeTracker->Tick(GetCompletedCommandSerial()); + mFenceSignalTracker->Tick(GetCompletedCommandSerial()); + mMapRequestTracker->Tick(GetCompletedCommandSerial()); + // call TickImpl once last time to clean up resources + // Ignore errors so that we can continue with destruction + IgnoreErrors(TickImpl()); + } + + // At this point GPU operations are always finished, so we are in the disconnected state. + mState = State::Disconnected; + + // mCurrentErrorScope can be null if we failed device initialization. + if (mCurrentErrorScope.Get() != nullptr) { + mCurrentErrorScope->UnlinkForShutdown(); + } + mErrorScopeTracker = nullptr; + mFenceSignalTracker = nullptr; + mDynamicUploader = nullptr; + mMapRequestTracker = nullptr; + + mEmptyBindGroupLayout = nullptr; + + AssumeCommandsComplete(); + // Tell the backend that it can free all the objects now that the GPU timeline is empty. + ShutDownImpl(); + + mCaches = nullptr; + } + + void DeviceBase::HandleError(InternalErrorType type, const char* message) { + // If we receive an internal error, assume the backend can't recover and proceed with + // device destruction. We first wait for all previous commands to be completed so that + // backend objects can be freed immediately, before handling the loss. + if (type == InternalErrorType::Internal) { + // Move away from the Alive state so that the application cannot use this device + // anymore. + // TODO(cwallez@chromium.org): Do we need atomics for this to become visible to other + // threads in a multithreaded scenario? + mState = State::BeingDisconnected; + + // Ignore errors so that we can continue with destruction + // Assume all commands are complete after WaitForIdleForDestruction (because they were) + IgnoreErrors(WaitForIdleForDestruction()); + IgnoreErrors(TickImpl()); + AssumeCommandsComplete(); + ASSERT(mFutureCallbackSerial <= mCompletedSerial); + mState = State::Disconnected; + + // Now everything is as if the device was lost. + type = InternalErrorType::DeviceLost; + } + + // The device was lost, call the application callback. + if (type == InternalErrorType::DeviceLost && mDeviceLostCallback != nullptr) { + mDeviceLostCallback(message, mDeviceLostUserdata); + mDeviceLostCallback = nullptr; + } + + // Still forward device loss and internal errors to the error scopes so they all reject. + mCurrentErrorScope->HandleError(ToWGPUErrorType(type), message); + } + + void DeviceBase::InjectError(wgpu::ErrorType type, const char* message) { + if (ConsumedError(ValidateErrorType(type))) { + return; + } + + // This method should only be used to make error scope reject. For DeviceLost there is the + // LoseForTesting function that can be used instead. + if (type != wgpu::ErrorType::Validation && type != wgpu::ErrorType::OutOfMemory) { + HandleError(InternalErrorType::Validation, + "Invalid injected error, must be Validation or OutOfMemory"); + return; + } + + HandleError(FromWGPUErrorType(type), message); } - void DeviceBase::HandleError(const char* message) { - if (mErrorCallback) { - mErrorCallback(message, mErrorUserdata); + void DeviceBase::ConsumeError(std::unique_ptr error) { + ASSERT(error != nullptr); + std::ostringstream ss; + ss << error->GetMessage(); + for (const auto& callsite : error->GetBacktrace()) { + ss << "\n at " << callsite.function << " (" << callsite.file << ":" << callsite.line + << ")"; } + HandleError(error->GetType(), ss.str().c_str()); + } + + void DeviceBase::SetUncapturedErrorCallback(wgpu::ErrorCallback callback, void* userdata) { + mRootErrorScope->SetCallback(callback, userdata); + } + + void DeviceBase::SetDeviceLostCallback(wgpu::DeviceLostCallback callback, void* userdata) { + mDeviceLostCallback = callback; + mDeviceLostUserdata = userdata; } - void DeviceBase::SetErrorCallback(dawn::DeviceErrorCallback callback, void* userdata) { - mErrorCallback = callback; - mErrorUserdata = userdata; + void DeviceBase::PushErrorScope(wgpu::ErrorFilter filter) { + if (ConsumedError(ValidateErrorFilter(filter))) { + return; + } + mCurrentErrorScope = AcquireRef(new ErrorScope(filter, mCurrentErrorScope.Get())); + } + + bool DeviceBase::PopErrorScope(wgpu::ErrorCallback callback, void* userdata) { + if (DAWN_UNLIKELY(mCurrentErrorScope.Get() == mRootErrorScope.Get())) { + return false; + } + mCurrentErrorScope->SetCallback(callback, userdata); + mCurrentErrorScope = Ref(mCurrentErrorScope->GetParent()); + + return true; + } + + ErrorScope* DeviceBase::GetCurrentErrorScope() { + ASSERT(mCurrentErrorScope.Get() != nullptr); + return mCurrentErrorScope.Get(); } MaybeError DeviceBase::ValidateObject(const ObjectBase* object) const { + ASSERT(object != nullptr); if (DAWN_UNLIKELY(object->GetDevice() != this)) { return DAWN_VALIDATION_ERROR("Object from a different device."); } @@ -91,42 +282,157 @@ namespace dawn_native { return {}; } + MaybeError DeviceBase::ValidateIsAlive() const { + if (DAWN_LIKELY(mState == State::Alive)) { + return {}; + } + return DAWN_DEVICE_LOST_ERROR("Device is lost"); + } + + void DeviceBase::LoseForTesting() { + if (mState != State::Alive) { + return; + } + + HandleError(InternalErrorType::Internal, "Device lost for testing"); + } + + DeviceBase::State DeviceBase::GetState() const { + return mState; + } + + bool DeviceBase::IsLost() const { + ASSERT(mState != State::BeingCreated); + return mState != State::Alive; + } + AdapterBase* DeviceBase::GetAdapter() const { return mAdapter; } - DeviceBase* DeviceBase::GetDevice() { - return this; + dawn_platform::Platform* DeviceBase::GetPlatform() const { + return GetAdapter()->GetInstance()->GetPlatform(); + } + + ErrorScopeTracker* DeviceBase::GetErrorScopeTracker() const { + return mErrorScopeTracker.get(); } FenceSignalTracker* DeviceBase::GetFenceSignalTracker() const { return mFenceSignalTracker.get(); } - ResultOrError DeviceBase::GetOrCreateBindGroupLayout( + MapRequestTracker* DeviceBase::GetMapRequestTracker() const { + return mMapRequestTracker.get(); + } + + Serial DeviceBase::GetCompletedCommandSerial() const { + return mCompletedSerial; + } + + Serial DeviceBase::GetLastSubmittedCommandSerial() const { + return mLastSubmittedSerial; + } + + Serial DeviceBase::GetFutureCallbackSerial() const { + return mFutureCallbackSerial; + } + + void DeviceBase::IncrementLastSubmittedCommandSerial() { + mLastSubmittedSerial++; + } + + void DeviceBase::AssumeCommandsComplete() { + Serial maxSerial = std::max(mLastSubmittedSerial + 1, mFutureCallbackSerial); + mLastSubmittedSerial = maxSerial; + mCompletedSerial = maxSerial; + } + + Serial DeviceBase::GetPendingCommandSerial() const { + return mLastSubmittedSerial + 1; + } + + void DeviceBase::AddFutureCallbackSerial(Serial serial) { + if (serial > mFutureCallbackSerial) { + mFutureCallbackSerial = serial; + } + } + + void DeviceBase::CheckPassedSerials() { + Serial completedSerial = CheckAndUpdateCompletedSerials(); + + ASSERT(completedSerial <= mLastSubmittedSerial); + // completedSerial should not be less than mCompletedSerial unless it is 0. + // It can be 0 when there's no fences to check. + ASSERT(completedSerial >= mCompletedSerial || completedSerial == 0); + + if (completedSerial > mCompletedSerial) { + mCompletedSerial = completedSerial; + } + } + + ResultOrError DeviceBase::GetInternalFormat(wgpu::TextureFormat format) const { + size_t index = ComputeFormatIndex(format); + if (index >= mFormatTable.size()) { + return DAWN_VALIDATION_ERROR("Unknown texture format"); + } + + const Format* internalFormat = &mFormatTable[index]; + if (!internalFormat->isSupported) { + return DAWN_VALIDATION_ERROR("Unsupported texture format"); + } + + return internalFormat; + } + + const Format& DeviceBase::GetValidInternalFormat(wgpu::TextureFormat format) const { + size_t index = ComputeFormatIndex(format); + ASSERT(index < mFormatTable.size()); + ASSERT(mFormatTable[index].isSupported); + return mFormatTable[index]; + } + + ResultOrError> DeviceBase::GetOrCreateBindGroupLayout( const BindGroupLayoutDescriptor* descriptor) { - BindGroupLayoutBase blueprint(this, descriptor, true); + BindGroupLayoutBase blueprint(this, descriptor); + Ref result = nullptr; auto iter = mCaches->bindGroupLayouts.find(&blueprint); if (iter != mCaches->bindGroupLayouts.end()) { - (*iter)->Reference(); - return *iter; + result = *iter; + } else { + BindGroupLayoutBase* backendObj; + DAWN_TRY_ASSIGN(backendObj, CreateBindGroupLayoutImpl(descriptor)); + backendObj->SetIsCachedReference(); + mCaches->bindGroupLayouts.insert(backendObj); + result = AcquireRef(backendObj); } - - BindGroupLayoutBase* backendObj; - DAWN_TRY_ASSIGN(backendObj, CreateBindGroupLayoutImpl(descriptor)); - mCaches->bindGroupLayouts.insert(backendObj); - return backendObj; + return std::move(result); } void DeviceBase::UncacheBindGroupLayout(BindGroupLayoutBase* obj) { + ASSERT(obj->IsCachedReference()); size_t removedCount = mCaches->bindGroupLayouts.erase(obj); ASSERT(removedCount == 1); } + // Private function used at initialization + ResultOrError> DeviceBase::CreateEmptyBindGroupLayout() { + BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 0; + desc.entries = nullptr; + + return GetOrCreateBindGroupLayout(&desc); + } + + BindGroupLayoutBase* DeviceBase::GetEmptyBindGroupLayout() { + ASSERT(mEmptyBindGroupLayout); + return mEmptyBindGroupLayout.Get(); + } + ResultOrError DeviceBase::GetOrCreateComputePipeline( const ComputePipelineDescriptor* descriptor) { - ComputePipelineBase blueprint(this, descriptor, true); + ComputePipelineBase blueprint(this, descriptor); auto iter = mCaches->computePipelines.find(&blueprint); if (iter != mCaches->computePipelines.end()) { @@ -136,18 +442,20 @@ namespace dawn_native { ComputePipelineBase* backendObj; DAWN_TRY_ASSIGN(backendObj, CreateComputePipelineImpl(descriptor)); + backendObj->SetIsCachedReference(); mCaches->computePipelines.insert(backendObj); return backendObj; } void DeviceBase::UncacheComputePipeline(ComputePipelineBase* obj) { + ASSERT(obj->IsCachedReference()); size_t removedCount = mCaches->computePipelines.erase(obj); ASSERT(removedCount == 1); } ResultOrError DeviceBase::GetOrCreatePipelineLayout( const PipelineLayoutDescriptor* descriptor) { - PipelineLayoutBase blueprint(this, descriptor, true); + PipelineLayoutBase blueprint(this, descriptor); auto iter = mCaches->pipelineLayouts.find(&blueprint); if (iter != mCaches->pipelineLayouts.end()) { @@ -157,18 +465,20 @@ namespace dawn_native { PipelineLayoutBase* backendObj; DAWN_TRY_ASSIGN(backendObj, CreatePipelineLayoutImpl(descriptor)); + backendObj->SetIsCachedReference(); mCaches->pipelineLayouts.insert(backendObj); return backendObj; } void DeviceBase::UncachePipelineLayout(PipelineLayoutBase* obj) { + ASSERT(obj->IsCachedReference()); size_t removedCount = mCaches->pipelineLayouts.erase(obj); ASSERT(removedCount == 1); } ResultOrError DeviceBase::GetOrCreateRenderPipeline( const RenderPipelineDescriptor* descriptor) { - RenderPipelineBase blueprint(this, descriptor, true); + RenderPipelineBase blueprint(this, descriptor); auto iter = mCaches->renderPipelines.find(&blueprint); if (iter != mCaches->renderPipelines.end()) { @@ -178,18 +488,20 @@ namespace dawn_native { RenderPipelineBase* backendObj; DAWN_TRY_ASSIGN(backendObj, CreateRenderPipelineImpl(descriptor)); + backendObj->SetIsCachedReference(); mCaches->renderPipelines.insert(backendObj); return backendObj; } void DeviceBase::UncacheRenderPipeline(RenderPipelineBase* obj) { + ASSERT(obj->IsCachedReference()); size_t removedCount = mCaches->renderPipelines.erase(obj); ASSERT(removedCount == 1); } ResultOrError DeviceBase::GetOrCreateSampler( const SamplerDescriptor* descriptor) { - SamplerBase blueprint(this, descriptor, true); + SamplerBase blueprint(this, descriptor); auto iter = mCaches->samplers.find(&blueprint); if (iter != mCaches->samplers.end()) { @@ -199,18 +511,20 @@ namespace dawn_native { SamplerBase* backendObj; DAWN_TRY_ASSIGN(backendObj, CreateSamplerImpl(descriptor)); + backendObj->SetIsCachedReference(); mCaches->samplers.insert(backendObj); return backendObj; } void DeviceBase::UncacheSampler(SamplerBase* obj) { + ASSERT(obj->IsCachedReference()); size_t removedCount = mCaches->samplers.erase(obj); ASSERT(removedCount == 1); } ResultOrError DeviceBase::GetOrCreateShaderModule( const ShaderModuleDescriptor* descriptor) { - ShaderModuleBase blueprint(this, descriptor, true); + ShaderModuleBase blueprint(this, descriptor); auto iter = mCaches->shaderModules.find(&blueprint); if (iter != mCaches->shaderModules.end()) { @@ -220,15 +534,54 @@ namespace dawn_native { ShaderModuleBase* backendObj; DAWN_TRY_ASSIGN(backendObj, CreateShaderModuleImpl(descriptor)); + backendObj->SetIsCachedReference(); mCaches->shaderModules.insert(backendObj); return backendObj; } void DeviceBase::UncacheShaderModule(ShaderModuleBase* obj) { + ASSERT(obj->IsCachedReference()); size_t removedCount = mCaches->shaderModules.erase(obj); ASSERT(removedCount == 1); } + Ref DeviceBase::GetOrCreateAttachmentState( + AttachmentStateBlueprint* blueprint) { + auto iter = mCaches->attachmentStates.find(blueprint); + if (iter != mCaches->attachmentStates.end()) { + return static_cast(*iter); + } + + Ref attachmentState = AcquireRef(new AttachmentState(this, *blueprint)); + attachmentState->SetIsCachedReference(); + mCaches->attachmentStates.insert(attachmentState.Get()); + return attachmentState; + } + + Ref DeviceBase::GetOrCreateAttachmentState( + const RenderBundleEncoderDescriptor* descriptor) { + AttachmentStateBlueprint blueprint(descriptor); + return GetOrCreateAttachmentState(&blueprint); + } + + Ref DeviceBase::GetOrCreateAttachmentState( + const RenderPipelineDescriptor* descriptor) { + AttachmentStateBlueprint blueprint(descriptor); + return GetOrCreateAttachmentState(&blueprint); + } + + Ref DeviceBase::GetOrCreateAttachmentState( + const RenderPassDescriptor* descriptor) { + AttachmentStateBlueprint blueprint(descriptor); + return GetOrCreateAttachmentState(&blueprint); + } + + void DeviceBase::UncacheAttachmentState(AttachmentState* obj) { + ASSERT(obj->IsCachedReference()); + size_t removedCount = mCaches->attachmentStates.erase(obj); + ASSERT(removedCount == 1); + } + // Object creation API methods BindGroupBase* DeviceBase::CreateBindGroup(const BindGroupDescriptor* descriptor) { @@ -251,48 +604,37 @@ namespace dawn_native { return result; } BufferBase* DeviceBase::CreateBuffer(const BufferDescriptor* descriptor) { - BufferBase* result = nullptr; - - if (ConsumedError(CreateBufferInternal(&result, descriptor))) { - return BufferBase::MakeError(this); + Ref result = nullptr; + if (ConsumedError(CreateBufferInternal(descriptor), &result)) { + ASSERT(result.Get() == nullptr); + return BufferBase::MakeError(this, descriptor); } - return result; + return result.Detach(); } - DawnCreateBufferMappedResult DeviceBase::CreateBufferMapped( + WGPUCreateBufferMappedResult DeviceBase::CreateBufferMapped( const BufferDescriptor* descriptor) { - BufferBase* buffer = nullptr; - uint8_t* data = nullptr; - - uint64_t size = descriptor->size; - if (ConsumedError(CreateBufferInternal(&buffer, descriptor)) || - ConsumedError(buffer->MapAtCreation(&data))) { - // Map failed. Replace the buffer with an error buffer. - if (buffer != nullptr) { - delete buffer; - } - buffer = BufferBase::MakeErrorMapped(this, size, &data); - } + EmitDeprecationWarning( + "CreateBufferMapped is deprecated, use wgpu::BufferDescriptor::mappedAtCreation and " + "wgpu::Buffer::GetMappedRange instead"); - ASSERT(buffer != nullptr); - if (data == nullptr) { - // |data| may be nullptr if there was an OOM in MakeErrorMapped. - // Non-zero dataLength and nullptr data is used to indicate there should be - // mapped data but the allocation failed. - ASSERT(buffer->IsError()); - } else { - memset(data, 0, size); - } + BufferDescriptor fixedDesc = *descriptor; + fixedDesc.mappedAtCreation = true; + BufferBase* buffer = CreateBuffer(&fixedDesc); + + WGPUCreateBufferMappedResult result = {}; + result.buffer = reinterpret_cast(buffer); + result.data = buffer->GetMappedRange(0, descriptor->size); + result.dataLength = descriptor->size; - DawnCreateBufferMappedResult result = {}; - result.buffer = reinterpret_cast(buffer); - result.data = data; - result.dataLength = size; + if (result.data != nullptr) { + memset(result.data, 0, result.dataLength); + } return result; } - CommandEncoderBase* DeviceBase::CreateCommandEncoder() { - return new CommandEncoderBase(this); + CommandEncoder* DeviceBase::CreateCommandEncoder(const CommandEncoderDescriptor* descriptor) { + return new CommandEncoder(this, descriptor); } ComputePipelineBase* DeviceBase::CreateComputePipeline( const ComputePipelineDescriptor* descriptor) { @@ -314,18 +656,21 @@ namespace dawn_native { return result; } - QueueBase* DeviceBase::CreateQueue() { - QueueBase* result = nullptr; + QuerySetBase* DeviceBase::CreateQuerySet(const QuerySetDescriptor* descriptor) { + QuerySetBase* result = nullptr; - if (ConsumedError(CreateQueueInternal(&result))) { - // If queue creation failure ever becomes possible, we should implement MakeError and - // friends for them. - UNREACHABLE(); - return nullptr; + if (ConsumedError(CreateQuerySetInternal(&result, descriptor))) { + return QuerySetBase::MakeError(this); } return result; } + QueueBase* DeviceBase::CreateQueue() { + // TODO(dawn:22): Remove this once users use GetDefaultQueue + EmitDeprecationWarning( + "Device::CreateQueue is deprecated, use Device::GetDefaultQueue instead"); + return GetDefaultQueue(); + } SamplerBase* DeviceBase::CreateSampler(const SamplerDescriptor* descriptor) { SamplerBase* result = nullptr; @@ -335,6 +680,16 @@ namespace dawn_native { return result; } + RenderBundleEncoder* DeviceBase::CreateRenderBundleEncoder( + const RenderBundleEncoderDescriptor* descriptor) { + RenderBundleEncoder* result = nullptr; + + if (ConsumedError(CreateRenderBundleEncoderInternal(&result, descriptor))) { + return RenderBundleEncoder::MakeError(this); + } + + return result; + } RenderPipelineBase* DeviceBase::CreateRenderPipeline( const RenderPipelineDescriptor* descriptor) { RenderPipelineBase* result = nullptr; @@ -354,23 +709,24 @@ namespace dawn_native { return result; } - SwapChainBase* DeviceBase::CreateSwapChain(const SwapChainDescriptor* descriptor) { + SwapChainBase* DeviceBase::CreateSwapChain(Surface* surface, + const SwapChainDescriptor* descriptor) { SwapChainBase* result = nullptr; - if (ConsumedError(CreateSwapChainInternal(&result, descriptor))) { + if (ConsumedError(CreateSwapChainInternal(&result, surface, descriptor))) { return SwapChainBase::MakeError(this); } return result; } TextureBase* DeviceBase::CreateTexture(const TextureDescriptor* descriptor) { - TextureBase* result = nullptr; + Ref result; - if (ConsumedError(CreateTextureInternal(&result, descriptor))) { + if (ConsumedError(CreateTextureInternal(descriptor), &result)) { return TextureBase::MakeError(this); } - return result; + return result.Detach(); } TextureViewBase* DeviceBase::CreateTextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) { @@ -383,11 +739,45 @@ namespace dawn_native { return result; } + // For Dawn Wire + + BufferBase* DeviceBase::CreateErrorBuffer() { + BufferDescriptor desc = {}; + return BufferBase::MakeError(this, &desc); + } + // Other Device API methods void DeviceBase::Tick() { - TickImpl(); - mFenceSignalTracker->Tick(GetCompletedCommandSerial()); + if (ConsumedError(ValidateIsAlive())) { + return; + } + // to avoid overly ticking, we only want to tick when: + // 1. the last submitted serial has moved beyond the completed serial + // 2. or the completed serial has not reached the future serial set by the trackers + if (mLastSubmittedSerial > mCompletedSerial || mCompletedSerial < mFutureCallbackSerial) { + CheckPassedSerials(); + + if (ConsumedError(TickImpl())) { + return; + } + + // There is no GPU work in flight, we need to move the serials forward so that + // so that CPU operations waiting on GPU completion can know they don't have to wait. + // AssumeCommandsComplete will assign the max serial we must tick to in order to + // fire the awaiting callbacks. + if (mCompletedSerial == mLastSubmittedSerial) { + AssumeCommandsComplete(); + } + + // TODO(cwallez@chromium.org): decouple TickImpl from updating the serial so that we can + // tick the dynamic uploader before the backend resource allocators. This would allow + // reclaiming resources one tick earlier. + mDynamicUploader->Deallocate(mCompletedSerial); + mErrorScopeTracker->Tick(mCompletedSerial); + mFenceSignalTracker->Tick(mCompletedSerial); + mMapRequestTracker->Tick(mCompletedSerial); + } } void DeviceBase::Reference() { @@ -403,51 +793,66 @@ namespace dawn_native { } } - void DeviceBase::ApplyToggleOverrides(const DeviceDescriptor* deviceDescriptor) { + QueueBase* DeviceBase::GetDefaultQueue() { + // Backends gave the default queue during initialization. + ASSERT(mDefaultQueue.Get() != nullptr); + + // Returns a new reference to the queue. + mDefaultQueue->Reference(); + return mDefaultQueue.Get(); + } + + void DeviceBase::ApplyExtensions(const DeviceDescriptor* deviceDescriptor) { ASSERT(deviceDescriptor); + ASSERT(GetAdapter()->SupportsAllRequestedExtensions(deviceDescriptor->requiredExtensions)); - for (const char* toggleName : deviceDescriptor->forceEnabledToggles) { - Toggle toggle = GetAdapter()->GetInstance()->ToggleNameToEnum(toggleName); - if (toggle != Toggle::InvalidEnum) { - mTogglesSet.SetToggle(toggle, true); - } - } - for (const char* toggleName : deviceDescriptor->forceDisabledToggles) { - Toggle toggle = GetAdapter()->GetInstance()->ToggleNameToEnum(toggleName); - if (toggle != Toggle::InvalidEnum) { - mTogglesSet.SetToggle(toggle, false); - } - } + mEnabledExtensions = GetAdapter()->GetInstance()->ExtensionNamesToExtensionsSet( + deviceDescriptor->requiredExtensions); } - std::vector DeviceBase::GetTogglesUsed() const { - std::vector togglesNameInUse(mTogglesSet.toggleBitset.count()); + std::vector DeviceBase::GetEnabledExtensions() const { + return mEnabledExtensions.GetEnabledExtensionNames(); + } - uint32_t index = 0; - for (uint32_t i : IterateBitSet(mTogglesSet.toggleBitset)) { - const char* toggleName = - GetAdapter()->GetInstance()->ToggleEnumToName(static_cast(i)); - togglesNameInUse[index] = toggleName; - ++index; - } + bool DeviceBase::IsExtensionEnabled(Extension extension) const { + return mEnabledExtensions.IsEnabled(extension); + } - return togglesNameInUse; + bool DeviceBase::IsValidationEnabled() const { + return !IsToggleEnabled(Toggle::SkipValidation); } - bool DeviceBase::IsToggleEnabled(Toggle toggle) const { - return mTogglesSet.IsEnabled(toggle); + bool DeviceBase::IsRobustnessEnabled() const { + return !IsToggleEnabled(Toggle::DisableRobustness); } - void DeviceBase::SetDefaultToggles() { - // Sets the default-enabled toggles - mTogglesSet.SetToggle(Toggle::LazyClearResourceOnFirstUse, true); + size_t DeviceBase::GetLazyClearCountForTesting() { + return mLazyClearCountForTesting; + } + + void DeviceBase::IncrementLazyClearCountForTesting() { + ++mLazyClearCountForTesting; + } + + size_t DeviceBase::GetDeprecationWarningCountForTesting() { + return mDeprecationWarnings->count; + } + + void DeviceBase::EmitDeprecationWarning(const char* warning) { + mDeprecationWarnings->count++; + if (mDeprecationWarnings->emitted.insert(warning).second) { + dawn::WarningLog() << warning; + } } // Implementation details of object creation MaybeError DeviceBase::CreateBindGroupInternal(BindGroupBase** result, const BindGroupDescriptor* descriptor) { - DAWN_TRY(ValidateBindGroupDescriptor(this, descriptor)); + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateBindGroupDescriptor(this, descriptor)); + } DAWN_TRY_ASSIGN(*result, CreateBindGroupImpl(descriptor)); return {}; } @@ -455,100 +860,254 @@ namespace dawn_native { MaybeError DeviceBase::CreateBindGroupLayoutInternal( BindGroupLayoutBase** result, const BindGroupLayoutDescriptor* descriptor) { - DAWN_TRY(ValidateBindGroupLayoutDescriptor(this, descriptor)); - DAWN_TRY_ASSIGN(*result, GetOrCreateBindGroupLayout(descriptor)); + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateBindGroupLayoutDescriptor(this, descriptor)); + } + Ref bgl; + DAWN_TRY_ASSIGN(bgl, GetOrCreateBindGroupLayout(descriptor)); + *result = bgl.Detach(); return {}; } - MaybeError DeviceBase::CreateBufferInternal(BufferBase** result, - const BufferDescriptor* descriptor) { - DAWN_TRY(ValidateBufferDescriptor(this, descriptor)); - DAWN_TRY_ASSIGN(*result, CreateBufferImpl(descriptor)); - return {}; + ResultOrError> DeviceBase::CreateBufferInternal( + const BufferDescriptor* descriptor) { + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateBufferDescriptor(this, descriptor)); + } + + Ref buffer; + DAWN_TRY_ASSIGN(buffer, CreateBufferImpl(descriptor)); + + if (descriptor->mappedAtCreation) { + DAWN_TRY(buffer->MapAtCreation()); + } + + return std::move(buffer); } MaybeError DeviceBase::CreateComputePipelineInternal( ComputePipelineBase** result, const ComputePipelineDescriptor* descriptor) { - DAWN_TRY(ValidateComputePipelineDescriptor(this, descriptor)); - DAWN_TRY_ASSIGN(*result, GetOrCreateComputePipeline(descriptor)); + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateComputePipelineDescriptor(this, descriptor)); + } + + if (descriptor->layout == nullptr) { + ComputePipelineDescriptor descriptorWithDefaultLayout = *descriptor; + + DAWN_TRY_ASSIGN( + descriptorWithDefaultLayout.layout, + PipelineLayoutBase::CreateDefault(this, &descriptor->computeStage.module, 1)); + // Ref will keep the pipeline layout alive until the end of the function where + // the pipeline will take another reference. + Ref layoutRef = AcquireRef(descriptorWithDefaultLayout.layout); + + DAWN_TRY_ASSIGN(*result, GetOrCreateComputePipeline(&descriptorWithDefaultLayout)); + } else { + DAWN_TRY_ASSIGN(*result, GetOrCreateComputePipeline(descriptor)); + } return {}; } MaybeError DeviceBase::CreatePipelineLayoutInternal( PipelineLayoutBase** result, const PipelineLayoutDescriptor* descriptor) { - DAWN_TRY(ValidatePipelineLayoutDescriptor(this, descriptor)); + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidatePipelineLayoutDescriptor(this, descriptor)); + } DAWN_TRY_ASSIGN(*result, GetOrCreatePipelineLayout(descriptor)); return {}; } - MaybeError DeviceBase::CreateQueueInternal(QueueBase** result) { - DAWN_TRY_ASSIGN(*result, CreateQueueImpl()); + MaybeError DeviceBase::CreateQuerySetInternal(QuerySetBase** result, + const QuerySetDescriptor* descriptor) { + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateQuerySetDescriptor(this, descriptor)); + } + DAWN_TRY_ASSIGN(*result, CreateQuerySetImpl(descriptor)); + return {}; + } + + MaybeError DeviceBase::CreateRenderBundleEncoderInternal( + RenderBundleEncoder** result, + const RenderBundleEncoderDescriptor* descriptor) { + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateRenderBundleEncoderDescriptor(this, descriptor)); + } + *result = new RenderBundleEncoder(this, descriptor); return {}; } MaybeError DeviceBase::CreateRenderPipelineInternal( RenderPipelineBase** result, const RenderPipelineDescriptor* descriptor) { - DAWN_TRY(ValidateRenderPipelineDescriptor(this, descriptor)); - DAWN_TRY_ASSIGN(*result, GetOrCreateRenderPipeline(descriptor)); + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateRenderPipelineDescriptor(this, descriptor)); + } + + if (descriptor->layout == nullptr) { + RenderPipelineDescriptor descriptorWithDefaultLayout = *descriptor; + + const ShaderModuleBase* modules[2]; + modules[0] = descriptor->vertexStage.module; + uint32_t count; + if (descriptor->fragmentStage == nullptr) { + count = 1; + } else { + modules[1] = descriptor->fragmentStage->module; + count = 2; + } + + DAWN_TRY_ASSIGN(descriptorWithDefaultLayout.layout, + PipelineLayoutBase::CreateDefault(this, modules, count)); + // Ref will keep the pipeline layout alive until the end of the function where + // the pipeline will take another reference. + Ref layoutRef = AcquireRef(descriptorWithDefaultLayout.layout); + + DAWN_TRY_ASSIGN(*result, GetOrCreateRenderPipeline(&descriptorWithDefaultLayout)); + } else { + DAWN_TRY_ASSIGN(*result, GetOrCreateRenderPipeline(descriptor)); + } return {}; } MaybeError DeviceBase::CreateSamplerInternal(SamplerBase** result, const SamplerDescriptor* descriptor) { - DAWN_TRY(ValidateSamplerDescriptor(this, descriptor)); + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateSamplerDescriptor(this, descriptor)); + } DAWN_TRY_ASSIGN(*result, GetOrCreateSampler(descriptor)); return {}; } MaybeError DeviceBase::CreateShaderModuleInternal(ShaderModuleBase** result, const ShaderModuleDescriptor* descriptor) { - DAWN_TRY(ValidateShaderModuleDescriptor(this, descriptor)); + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateShaderModuleDescriptor(this, descriptor)); + } DAWN_TRY_ASSIGN(*result, GetOrCreateShaderModule(descriptor)); return {}; } MaybeError DeviceBase::CreateSwapChainInternal(SwapChainBase** result, + Surface* surface, const SwapChainDescriptor* descriptor) { - DAWN_TRY(ValidateSwapChainDescriptor(this, descriptor)); - DAWN_TRY_ASSIGN(*result, CreateSwapChainImpl(descriptor)); + DAWN_TRY(ValidateIsAlive()); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateSwapChainDescriptor(this, surface, descriptor)); + } + + if (surface == nullptr) { + DAWN_TRY_ASSIGN(*result, CreateSwapChainImpl(descriptor)); + } else { + ASSERT(descriptor->implementation == 0); + + NewSwapChainBase* previousSwapChain = surface->GetAttachedSwapChain(); + NewSwapChainBase* newSwapChain; + DAWN_TRY_ASSIGN(newSwapChain, + CreateSwapChainImpl(surface, previousSwapChain, descriptor)); + + if (previousSwapChain != nullptr) { + ASSERT(!previousSwapChain->IsAttached()); + } + ASSERT(newSwapChain->IsAttached()); + + surface->SetAttachedSwapChain(newSwapChain); + *result = newSwapChain; + } return {}; } - MaybeError DeviceBase::CreateTextureInternal(TextureBase** result, - const TextureDescriptor* descriptor) { - DAWN_TRY(ValidateTextureDescriptor(this, descriptor)); - DAWN_TRY_ASSIGN(*result, CreateTextureImpl(descriptor)); - return {}; + ResultOrError> DeviceBase::CreateTextureInternal( + const TextureDescriptor* descriptor) { + DAWN_TRY(ValidateIsAlive()); + + // TODO(dawn:22): Remove once migration from GPUTextureDescriptor.arrayLayerCount to + // GPUTextureDescriptor.size.depth is done. + TextureDescriptor fixedDescriptor; + DAWN_TRY_ASSIGN(fixedDescriptor, FixTextureDescriptor(this, descriptor)); + descriptor = &fixedDescriptor; + + if (IsValidationEnabled()) { + DAWN_TRY(ValidateTextureDescriptor(this, descriptor)); + } + return CreateTextureImpl(descriptor); } MaybeError DeviceBase::CreateTextureViewInternal(TextureViewBase** result, TextureBase* texture, const TextureViewDescriptor* descriptor) { - DAWN_TRY(ValidateTextureViewDescriptor(this, texture, descriptor)); - DAWN_TRY_ASSIGN(*result, CreateTextureViewImpl(texture, descriptor)); + DAWN_TRY(ValidateIsAlive()); + DAWN_TRY(ValidateObject(texture)); + TextureViewDescriptor desc = GetTextureViewDescriptorWithDefaults(texture, descriptor); + if (IsValidationEnabled()) { + DAWN_TRY(ValidateTextureViewDescriptor(texture, &desc)); + } + DAWN_TRY_ASSIGN(*result, CreateTextureViewImpl(texture, &desc)); return {}; } // Other implementation details - void DeviceBase::ConsumeError(ErrorData* error) { - ASSERT(error != nullptr); - HandleError(error->GetMessage().c_str()); - delete error; + DynamicUploader* DeviceBase::GetDynamicUploader() const { + return mDynamicUploader.get(); } - ResultOrError DeviceBase::GetDynamicUploader() const { - if (mDynamicUploader->IsEmpty()) { - DAWN_TRY(mDynamicUploader->CreateAndAppendBuffer()); - } - return mDynamicUploader.get(); + // The Toggle device facility + + std::vector DeviceBase::GetTogglesUsed() const { + return mEnabledToggles.GetContainedToggleNames(); + } + + bool DeviceBase::IsToggleEnabled(Toggle toggle) const { + return mEnabledToggles.Has(toggle); } void DeviceBase::SetToggle(Toggle toggle, bool isEnabled) { - mTogglesSet.SetToggle(toggle, isEnabled); + if (!mOverridenToggles.Has(toggle)) { + mEnabledToggles.Set(toggle, isEnabled); + } + } + + void DeviceBase::ForceSetToggle(Toggle toggle, bool isEnabled) { + if (!mOverridenToggles.Has(toggle) && mEnabledToggles.Has(toggle) != isEnabled) { + dawn::WarningLog() << "Forcing toggle \"" << ToggleEnumToName(toggle) << "\" to " + << isEnabled << "when it was overriden to be " << !isEnabled; + } + mEnabledToggles.Set(toggle, isEnabled); + } + + void DeviceBase::SetDefaultToggles() { + SetToggle(Toggle::LazyClearResourceOnFirstUse, true); + SetToggle(Toggle::UseSpvc, false); + } + + void DeviceBase::ApplyToggleOverrides(const DeviceDescriptor* deviceDescriptor) { + ASSERT(deviceDescriptor); + + for (const char* toggleName : deviceDescriptor->forceEnabledToggles) { + Toggle toggle = GetAdapter()->GetInstance()->ToggleNameToEnum(toggleName); + if (toggle != Toggle::InvalidEnum) { + mEnabledToggles.Set(toggle, true); + mOverridenToggles.Set(toggle, true); + } + } + for (const char* toggleName : deviceDescriptor->forceDisabledToggles) { + Toggle toggle = GetAdapter()->GetInstance()->ToggleNameToEnum(toggleName); + if (toggle != Toggle::InvalidEnum) { + mEnabledToggles.Set(toggle, false); + mOverridenToggles.Set(toggle, true); + } + } } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Device.h b/third_party/dawn/src/dawn_native/Device.h index a79cdd9bbab..16f076c3343 100644 --- a/third_party/dawn/src/dawn_native/Device.h +++ b/third_party/dawn/src/dawn_native/Device.h @@ -17,6 +17,8 @@ #include "common/Serial.h" #include "dawn_native/Error.h" +#include "dawn_native/Extensions.h" +#include "dawn_native/Format.h" #include "dawn_native/Forward.h" #include "dawn_native/ObjectBase.h" #include "dawn_native/Toggles.h" @@ -27,12 +29,15 @@ #include namespace dawn_native { - - using ErrorCallback = void (*)(const char* errorMessage, void* userData); - class AdapterBase; - class FenceSignalTracker; + class AttachmentState; + class AttachmentStateBlueprint; + class BindGroupLayoutBase; class DynamicUploader; + class ErrorScope; + class ErrorScopeTracker; + class FenceSignalTracker; + class MapRequestTracker; class StagingBufferBase; class DeviceBase { @@ -40,7 +45,7 @@ namespace dawn_native { DeviceBase(AdapterBase* adapter, const DeviceDescriptor* descriptor); virtual ~DeviceBase(); - void HandleError(const char* message); + void HandleError(InternalErrorType type, const char* message); bool ConsumedError(MaybeError maybeError) { if (DAWN_UNLIKELY(maybeError.IsError())) { @@ -50,21 +55,44 @@ namespace dawn_native { return false; } + template + bool ConsumedError(ResultOrError resultOrError, T* result) { + if (DAWN_UNLIKELY(resultOrError.IsError())) { + ConsumeError(resultOrError.AcquireError()); + return true; + } + *result = resultOrError.AcquireSuccess(); + return false; + } + MaybeError ValidateObject(const ObjectBase* object) const; AdapterBase* GetAdapter() const; + dawn_platform::Platform* GetPlatform() const; - // Used by autogenerated code, returns itself - DeviceBase* GetDevice(); - + ErrorScopeTracker* GetErrorScopeTracker() const; FenceSignalTracker* GetFenceSignalTracker() const; + MapRequestTracker* GetMapRequestTracker() const; + + // Returns the Format corresponding to the wgpu::TextureFormat or an error if the format + // isn't a valid wgpu::TextureFormat or isn't supported by this device. + // The pointer returned has the same lifetime as the device. + ResultOrError GetInternalFormat(wgpu::TextureFormat format) const; - virtual CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) = 0; + // Returns the Format corresponding to the wgpu::TextureFormat and assumes the format is + // valid and supported. + // The reference returned has the same lifetime as the device. + const Format& GetValidInternalFormat(wgpu::TextureFormat format) const; - virtual Serial GetCompletedCommandSerial() const = 0; - virtual Serial GetLastSubmittedCommandSerial() const = 0; - virtual Serial GetPendingCommandSerial() const = 0; - virtual void TickImpl() = 0; + virtual CommandBufferBase* CreateCommandBuffer( + CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) = 0; + + Serial GetCompletedCommandSerial() const; + Serial GetLastSubmittedCommandSerial() const; + Serial GetFutureCallbackSerial() const; + Serial GetPendingCommandSerial() const; + virtual MaybeError TickImpl() = 0; // Many Dawn objects are completely immutable once created which means that if two // creations are given the same arguments, they can return the same object. Reusing @@ -80,10 +108,12 @@ namespace dawn_native { // the created object will be, the "blueprint". The blueprint is just a FooBase object // instead of a backend Foo object. If the blueprint doesn't match an object in the // cache, then the descriptor is used to make a new object. - ResultOrError GetOrCreateBindGroupLayout( + ResultOrError> GetOrCreateBindGroupLayout( const BindGroupLayoutDescriptor* descriptor); void UncacheBindGroupLayout(BindGroupLayoutBase* obj); + BindGroupLayoutBase* GetEmptyBindGroupLayout(); + ResultOrError GetOrCreateComputePipeline( const ComputePipelineDescriptor* descriptor); void UncacheComputePipeline(ComputePipelineBase* obj); @@ -103,26 +133,50 @@ namespace dawn_native { const ShaderModuleDescriptor* descriptor); void UncacheShaderModule(ShaderModuleBase* obj); + Ref GetOrCreateAttachmentState(AttachmentStateBlueprint* blueprint); + Ref GetOrCreateAttachmentState( + const RenderBundleEncoderDescriptor* descriptor); + Ref GetOrCreateAttachmentState(const RenderPipelineDescriptor* descriptor); + Ref GetOrCreateAttachmentState(const RenderPassDescriptor* descriptor); + void UncacheAttachmentState(AttachmentState* obj); + // Dawn API BindGroupBase* CreateBindGroup(const BindGroupDescriptor* descriptor); BindGroupLayoutBase* CreateBindGroupLayout(const BindGroupLayoutDescriptor* descriptor); BufferBase* CreateBuffer(const BufferDescriptor* descriptor); - DawnCreateBufferMappedResult CreateBufferMapped(const BufferDescriptor* descriptor); - CommandEncoderBase* CreateCommandEncoder(); + WGPUCreateBufferMappedResult CreateBufferMapped(const BufferDescriptor* descriptor); + CommandEncoder* CreateCommandEncoder(const CommandEncoderDescriptor* descriptor); ComputePipelineBase* CreateComputePipeline(const ComputePipelineDescriptor* descriptor); PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor); + QuerySetBase* CreateQuerySet(const QuerySetDescriptor* descriptor); QueueBase* CreateQueue(); + RenderBundleEncoder* CreateRenderBundleEncoder( + const RenderBundleEncoderDescriptor* descriptor); RenderPipelineBase* CreateRenderPipeline(const RenderPipelineDescriptor* descriptor); SamplerBase* CreateSampler(const SamplerDescriptor* descriptor); ShaderModuleBase* CreateShaderModule(const ShaderModuleDescriptor* descriptor); - SwapChainBase* CreateSwapChain(const SwapChainDescriptor* descriptor); + SwapChainBase* CreateSwapChain(Surface* surface, const SwapChainDescriptor* descriptor); TextureBase* CreateTexture(const TextureDescriptor* descriptor); TextureViewBase* CreateTextureView(TextureBase* texture, const TextureViewDescriptor* descriptor); + // For Dawn Wire + BufferBase* CreateErrorBuffer(); + + QueueBase* GetDefaultQueue(); + + void InjectError(wgpu::ErrorType type, const char* message); void Tick(); - void SetErrorCallback(dawn::DeviceErrorCallback callback, void* userdata); + void SetDeviceLostCallback(wgpu::DeviceLostCallback callback, void* userdata); + void SetUncapturedErrorCallback(wgpu::ErrorCallback callback, void* userdata); + void PushErrorScope(wgpu::ErrorFilter filter); + bool PopErrorScope(wgpu::ErrorCallback callback, void* userdata); + + MaybeError ValidateIsAlive() const; + + ErrorScope* GetCurrentErrorScope(); + void Reference(); void Release(); @@ -134,28 +188,67 @@ namespace dawn_native { uint64_t destinationOffset, uint64_t size) = 0; - ResultOrError GetDynamicUploader() const; + DynamicUploader* GetDynamicUploader() const; + // The device state which is a combination of creation state and loss state. + // + // - BeingCreated: the device didn't finish creation yet and the frontend cannot be used + // (both for the application calling WebGPU, or re-entrant calls). No work exists on + // the GPU timeline. + // - Alive: the device is usable and might have work happening on the GPU timeline. + // - BeingDisconnected: the device is no longer usable because we are waiting for all + // work on the GPU timeline to finish. (this is to make validation prevent the + // application from adding more work during the transition from Available to + // Disconnected) + // - Disconnected: there is no longer work happening on the GPU timeline and the CPU data + // structures can be safely destroyed without additional synchronization. + enum class State { + BeingCreated, + Alive, + BeingDisconnected, + Disconnected, + }; + State GetState() const; + bool IsLost() const; + + std::vector GetEnabledExtensions() const; std::vector GetTogglesUsed() const; + bool IsExtensionEnabled(Extension extension) const; bool IsToggleEnabled(Toggle toggle) const; + bool IsValidationEnabled() const; + bool IsRobustnessEnabled() const; + size_t GetLazyClearCountForTesting(); + void IncrementLazyClearCountForTesting(); + size_t GetDeprecationWarningCountForTesting(); + void EmitDeprecationWarning(const char* warning); + void LoseForTesting(); + void AddFutureCallbackSerial(Serial serial); protected: void SetToggle(Toggle toggle, bool isEnabled); - void ApplyToggleOverrides(const DeviceDescriptor* deviceDescriptor); + void ForceSetToggle(Toggle toggle, bool isEnabled); - std::unique_ptr mDynamicUploader; + MaybeError Initialize(QueueBase* defaultQueue); + void ShutDownBase(); + + // Incrememt mLastSubmittedSerial when we submit the next serial + void IncrementLastSubmittedCommandSerial(); + // Check for passed fences and set the new completed serial + void CheckPassedSerials(); private: virtual ResultOrError CreateBindGroupImpl( const BindGroupDescriptor* descriptor) = 0; virtual ResultOrError CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) = 0; - virtual ResultOrError CreateBufferImpl(const BufferDescriptor* descriptor) = 0; + virtual ResultOrError> CreateBufferImpl( + const BufferDescriptor* descriptor) = 0; virtual ResultOrError CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) = 0; virtual ResultOrError CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) = 0; - virtual ResultOrError CreateQueueImpl() = 0; + virtual ResultOrError CreateQuerySetImpl( + const QuerySetDescriptor* descriptor) = 0; virtual ResultOrError CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) = 0; virtual ResultOrError CreateSamplerImpl( @@ -164,51 +257,116 @@ namespace dawn_native { const ShaderModuleDescriptor* descriptor) = 0; virtual ResultOrError CreateSwapChainImpl( const SwapChainDescriptor* descriptor) = 0; - virtual ResultOrError CreateTextureImpl( + // Note that previousSwapChain may be nullptr, or come from a different backend. + virtual ResultOrError CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) = 0; + virtual ResultOrError> CreateTextureImpl( const TextureDescriptor* descriptor) = 0; virtual ResultOrError CreateTextureViewImpl( TextureBase* texture, const TextureViewDescriptor* descriptor) = 0; + ResultOrError> CreateEmptyBindGroupLayout(); + MaybeError CreateBindGroupInternal(BindGroupBase** result, const BindGroupDescriptor* descriptor); MaybeError CreateBindGroupLayoutInternal(BindGroupLayoutBase** result, const BindGroupLayoutDescriptor* descriptor); - MaybeError CreateBufferInternal(BufferBase** result, const BufferDescriptor* descriptor); + ResultOrError> CreateBufferInternal(const BufferDescriptor* descriptor); MaybeError CreateComputePipelineInternal(ComputePipelineBase** result, const ComputePipelineDescriptor* descriptor); MaybeError CreatePipelineLayoutInternal(PipelineLayoutBase** result, const PipelineLayoutDescriptor* descriptor); - MaybeError CreateQueueInternal(QueueBase** result); + MaybeError CreateQuerySetInternal(QuerySetBase** result, + const QuerySetDescriptor* descriptor); + MaybeError CreateRenderBundleEncoderInternal( + RenderBundleEncoder** result, + const RenderBundleEncoderDescriptor* descriptor); MaybeError CreateRenderPipelineInternal(RenderPipelineBase** result, const RenderPipelineDescriptor* descriptor); MaybeError CreateSamplerInternal(SamplerBase** result, const SamplerDescriptor* descriptor); MaybeError CreateShaderModuleInternal(ShaderModuleBase** result, const ShaderModuleDescriptor* descriptor); MaybeError CreateSwapChainInternal(SwapChainBase** result, + Surface* surface, const SwapChainDescriptor* descriptor); - MaybeError CreateTextureInternal(TextureBase** result, const TextureDescriptor* descriptor); + ResultOrError> CreateTextureInternal(const TextureDescriptor* descriptor); MaybeError CreateTextureViewInternal(TextureViewBase** result, TextureBase* texture, const TextureViewDescriptor* descriptor); - void ConsumeError(ErrorData* error); + void ApplyToggleOverrides(const DeviceDescriptor* deviceDescriptor); + void ApplyExtensions(const DeviceDescriptor* deviceDescriptor); + void SetDefaultToggles(); + void ConsumeError(std::unique_ptr error); + + // Each backend should implement to check their passed fences if there are any and return a + // completed serial. Return 0 should indicate no fences to check. + virtual Serial CheckAndUpdateCompletedSerials() = 0; + // During shut down of device, some operations might have been started since the last submit + // and waiting on a serial that doesn't have a corresponding fence enqueued. Fake serials to + // make all commands look completed. + void AssumeCommandsComplete(); + // mCompletedSerial tracks the last completed command serial that the fence has returned. + // mLastSubmittedSerial tracks the last submitted command serial. + // During device removal, the serials could be artificially incremented + // to make it appear as if commands have been compeleted. They can also be artificially + // incremented when no work is being done in the GPU so CPU operations don't have to wait on + // stale serials. + // mFutureCallbackSerial tracks the largest serial we need to tick to for the callbacks to + // fire + Serial mCompletedSerial = 0; + Serial mLastSubmittedSerial = 0; + Serial mFutureCallbackSerial = 0; + + // ShutDownImpl is used to clean up and release resources used by device, does not wait for + // GPU or check errors. + virtual void ShutDownImpl() = 0; + + // WaitForIdleForDestruction waits for GPU to finish, checks errors and gets ready for + // destruction. This is only used when properly destructing the device. For a real + // device loss, this function doesn't need to be called since the driver already closed all + // resources. + virtual MaybeError WaitForIdleForDestruction() = 0; + + wgpu::DeviceLostCallback mDeviceLostCallback = nullptr; + void* mDeviceLostUserdata = nullptr; + AdapterBase* mAdapter = nullptr; + Ref mRootErrorScope; + Ref mCurrentErrorScope; + // The object caches aren't exposed in the header as they would require a lot of // additional includes. struct Caches; std::unique_ptr mCaches; + Ref mEmptyBindGroupLayout; + + std::unique_ptr mDynamicUploader; + std::unique_ptr mErrorScopeTracker; std::unique_ptr mFenceSignalTracker; + std::unique_ptr mMapRequestTracker; + Ref mDefaultQueue; + + struct DeprecationWarnings; + std::unique_ptr mDeprecationWarnings; - dawn::DeviceErrorCallback mErrorCallback = nullptr; - void* mErrorUserdata = 0; uint32_t mRefCount = 1; + State mState = State::BeingCreated; + + FormatTable mFormatTable; + + TogglesSet mEnabledToggles; + TogglesSet mOverridenToggles; + size_t mLazyClearCountForTesting = 0; - TogglesSet mTogglesSet; + ExtensionsSet mEnabledExtensions; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/DynamicUploader.cpp b/third_party/dawn/src/dawn_native/DynamicUploader.cpp index d5c04504fe9..a732f495ceb 100644 --- a/third_party/dawn/src/dawn_native/DynamicUploader.cpp +++ b/third_party/dawn/src/dawn_native/DynamicUploader.cpp @@ -19,14 +19,8 @@ namespace dawn_native { DynamicUploader::DynamicUploader(DeviceBase* device) : mDevice(device) { - } - - ResultOrError> DynamicUploader::CreateStagingBuffer( - size_t size) { - std::unique_ptr stagingBuffer; - DAWN_TRY_ASSIGN(stagingBuffer, mDevice->CreateStagingBuffer(size)); - DAWN_TRY(stagingBuffer->Initialize()); - return stagingBuffer; + mRingBuffers.emplace_back(std::unique_ptr( + new RingBuffer{nullptr, RingBufferAllocator(kRingBufferSize)})); } void DynamicUploader::ReleaseStagingBuffer(std::unique_ptr stagingBuffer) { @@ -34,63 +28,84 @@ namespace dawn_native { mDevice->GetPendingCommandSerial()); } - MaybeError DynamicUploader::CreateAndAppendBuffer(size_t size) { - std::unique_ptr ringBuffer = std::make_unique(mDevice, size); - DAWN_TRY(ringBuffer->Initialize()); - mRingBuffers.emplace_back(std::move(ringBuffer)); - return {}; - } + ResultOrError DynamicUploader::Allocate(uint64_t allocationSize, Serial serial) { + // Disable further sub-allocation should the request be too large. + if (allocationSize > kRingBufferSize) { + std::unique_ptr stagingBuffer; + DAWN_TRY_ASSIGN(stagingBuffer, mDevice->CreateStagingBuffer(allocationSize)); - ResultOrError DynamicUploader::Allocate(uint32_t size, uint32_t alignment) { - ASSERT(IsPowerOfTwo(alignment)); + UploadHandle uploadHandle; + uploadHandle.mappedBuffer = static_cast(stagingBuffer->GetMappedPointer()); + uploadHandle.stagingBuffer = stagingBuffer.get(); - // Align the requested allocation size - const size_t alignedSize = Align(size, alignment); + ReleaseStagingBuffer(std::move(stagingBuffer)); + return uploadHandle; + } - RingBuffer* largestRingBuffer = GetLargestBuffer(); - UploadHandle uploadHandle = largestRingBuffer->SubAllocate(alignedSize); + // Note: Validation ensures size is already aligned. + // First-fit: find next smallest buffer large enough to satisfy the allocation request. + RingBuffer* targetRingBuffer = mRingBuffers.back().get(); + for (auto& ringBuffer : mRingBuffers) { + const RingBufferAllocator& ringBufferAllocator = ringBuffer->mAllocator; + // Prevent overflow. + ASSERT(ringBufferAllocator.GetSize() >= ringBufferAllocator.GetUsedSize()); + const uint64_t remainingSize = + ringBufferAllocator.GetSize() - ringBufferAllocator.GetUsedSize(); + if (allocationSize <= remainingSize) { + targetRingBuffer = ringBuffer.get(); + break; + } + } + + uint64_t startOffset = RingBufferAllocator::kInvalidOffset; + if (targetRingBuffer != nullptr) { + startOffset = targetRingBuffer->mAllocator.Allocate(allocationSize, serial); + } - // Upon failure, append a newly created (and much larger) ring buffer to fulfill the + // Upon failure, append a newly created ring buffer to fulfill the // request. - if (uploadHandle.mappedBuffer == nullptr) { - // Compute the new max size (in powers of two to preserve alignment). - size_t newMaxSize = largestRingBuffer->GetSize() * 2; - while (newMaxSize < size) { - newMaxSize *= 2; - } + if (startOffset == RingBufferAllocator::kInvalidOffset) { + mRingBuffers.emplace_back(std::unique_ptr( + new RingBuffer{nullptr, RingBufferAllocator(kRingBufferSize)})); - // TODO(bryan.bernhart@intel.com): Fall-back to no sub-allocations should this fail. - DAWN_TRY(CreateAndAppendBuffer(newMaxSize)); - largestRingBuffer = GetLargestBuffer(); - uploadHandle = largestRingBuffer->SubAllocate(alignedSize); + targetRingBuffer = mRingBuffers.back().get(); + startOffset = targetRingBuffer->mAllocator.Allocate(allocationSize, serial); } - uploadHandle.stagingBuffer = largestRingBuffer->GetStagingBuffer(); + ASSERT(startOffset != RingBufferAllocator::kInvalidOffset); + + // Allocate the staging buffer backing the ringbuffer. + // Note: the first ringbuffer will be lazily created. + if (targetRingBuffer->mStagingBuffer == nullptr) { + std::unique_ptr stagingBuffer; + DAWN_TRY_ASSIGN(stagingBuffer, + mDevice->CreateStagingBuffer(targetRingBuffer->mAllocator.GetSize())); + targetRingBuffer->mStagingBuffer = std::move(stagingBuffer); + } + + ASSERT(targetRingBuffer->mStagingBuffer != nullptr); + + UploadHandle uploadHandle; + uploadHandle.stagingBuffer = targetRingBuffer->mStagingBuffer.get(); + uploadHandle.mappedBuffer = + static_cast(uploadHandle.stagingBuffer->GetMappedPointer()) + startOffset; + uploadHandle.startOffset = startOffset; return uploadHandle; } - void DynamicUploader::Tick(Serial lastCompletedSerial) { + void DynamicUploader::Deallocate(Serial lastCompletedSerial) { // Reclaim memory within the ring buffers by ticking (or removing requests no longer // in-flight). for (size_t i = 0; i < mRingBuffers.size(); ++i) { - mRingBuffers[i]->Tick(lastCompletedSerial); + mRingBuffers[i]->mAllocator.Deallocate(lastCompletedSerial); // Never erase the last buffer as to prevent re-creating smaller buffers // again. The last buffer is the largest. - if (mRingBuffers[i]->Empty() && i < mRingBuffers.size() - 1) { + if (mRingBuffers[i]->mAllocator.Empty() && i < mRingBuffers.size() - 1) { mRingBuffers.erase(mRingBuffers.begin() + i); } } mReleasedStagingBuffers.ClearUpTo(lastCompletedSerial); } - - RingBuffer* DynamicUploader::GetLargestBuffer() { - ASSERT(!mRingBuffers.empty()); - return mRingBuffers.back().get(); - } - - bool DynamicUploader::IsEmpty() const { - return mRingBuffers.empty(); - } -} // namespace dawn_native \ No newline at end of file +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/DynamicUploader.h b/third_party/dawn/src/dawn_native/DynamicUploader.h index 5e4d0796023..8210b035b22 100644 --- a/third_party/dawn/src/dawn_native/DynamicUploader.h +++ b/third_party/dawn/src/dawn_native/DynamicUploader.h @@ -16,36 +16,40 @@ #define DAWNNATIVE_DYNAMICUPLOADER_H_ #include "dawn_native/Forward.h" -#include "dawn_native/RingBuffer.h" +#include "dawn_native/RingBufferAllocator.h" +#include "dawn_native/StagingBuffer.h" // DynamicUploader is the front-end implementation used to manage multiple ring buffers for upload // usage. namespace dawn_native { + struct UploadHandle { + uint8_t* mappedBuffer = nullptr; + uint64_t startOffset = 0; + StagingBufferBase* stagingBuffer = nullptr; + }; + class DynamicUploader { public: DynamicUploader(DeviceBase* device); ~DynamicUploader() = default; - // We add functions to Create/Release StagingBuffers to the DynamicUploader as there's + // We add functions to Release StagingBuffers to the DynamicUploader as there's // currently no place to track the allocated staging buffers such that they're freed after - // pending coommands are finished. This should be changed when better resource allocation is + // pending commands are finished. This should be changed when better resource allocation is // implemented. - ResultOrError> CreateStagingBuffer(size_t size); void ReleaseStagingBuffer(std::unique_ptr stagingBuffer); - ResultOrError Allocate(uint32_t requiredSize, uint32_t alignment); - void Tick(Serial lastCompletedSerial); - - RingBuffer* GetLargestBuffer(); - - MaybeError CreateAndAppendBuffer(size_t size = kBaseUploadBufferSize); - - bool IsEmpty() const; + ResultOrError Allocate(uint64_t allocationSize, Serial serial); + void Deallocate(Serial lastCompletedSerial); private: - // TODO(bryan.bernhart@intel.com): Figure out this value. - static constexpr size_t kBaseUploadBufferSize = 64000; + static constexpr uint64_t kRingBufferSize = 4 * 1024 * 1024; + + struct RingBuffer { + std::unique_ptr mStagingBuffer; + RingBufferAllocator mAllocator; + }; std::vector> mRingBuffers; SerialQueue> mReleasedStagingBuffers; diff --git a/third_party/dawn/src/dawn_native/EncodingContext.cpp b/third_party/dawn/src/dawn_native/EncodingContext.cpp new file mode 100644 index 00000000000..8ecf7b26f9b --- /dev/null +++ b/third_party/dawn/src/dawn_native/EncodingContext.cpp @@ -0,0 +1,125 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/EncodingContext.h" + +#include "common/Assert.h" +#include "dawn_native/CommandEncoder.h" +#include "dawn_native/Commands.h" +#include "dawn_native/Device.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/RenderBundleEncoder.h" + +namespace dawn_native { + + EncodingContext::EncodingContext(DeviceBase* device, const ObjectBase* initialEncoder) + : mDevice(device), mTopLevelEncoder(initialEncoder), mCurrentEncoder(initialEncoder) { + } + + EncodingContext::~EncodingContext() { + if (!mWereCommandsAcquired) { + FreeCommands(GetIterator()); + } + } + + CommandIterator EncodingContext::AcquireCommands() { + MoveToIterator(); + ASSERT(!mWereCommandsAcquired); + mWereCommandsAcquired = true; + return std::move(mIterator); + } + + CommandIterator* EncodingContext::GetIterator() { + MoveToIterator(); + ASSERT(!mWereCommandsAcquired); + return &mIterator; + } + + void EncodingContext::MoveToIterator() { + if (!mWasMovedToIterator) { + mIterator = std::move(mAllocator); + mWasMovedToIterator = true; + } + } + + void EncodingContext::HandleError(InternalErrorType type, const char* message) { + if (!IsFinished()) { + // If the encoding context is not finished, errors are deferred until + // Finish() is called. + if (!mGotError) { + mGotError = true; + mErrorMessage = message; + } + } else { + mDevice->HandleError(type, message); + } + } + + void EncodingContext::EnterPass(const ObjectBase* passEncoder) { + // Assert we're at the top level. + ASSERT(mCurrentEncoder == mTopLevelEncoder); + ASSERT(passEncoder != nullptr); + + mCurrentEncoder = passEncoder; + } + + void EncodingContext::ExitPass(const ObjectBase* passEncoder, PassResourceUsage passUsage) { + // Assert we're not at the top level. + ASSERT(mCurrentEncoder != mTopLevelEncoder); + // Assert the pass encoder is current. + ASSERT(mCurrentEncoder == passEncoder); + + mCurrentEncoder = mTopLevelEncoder; + mPassUsages.push_back(std::move(passUsage)); + } + + const PerPassUsages& EncodingContext::GetPassUsages() const { + ASSERT(!mWerePassUsagesAcquired); + return mPassUsages; + } + + PerPassUsages EncodingContext::AcquirePassUsages() { + ASSERT(!mWerePassUsagesAcquired); + mWerePassUsagesAcquired = true; + return std::move(mPassUsages); + } + + MaybeError EncodingContext::Finish() { + if (IsFinished()) { + return DAWN_VALIDATION_ERROR("Command encoding already finished"); + } + + const void* currentEncoder = mCurrentEncoder; + const void* topLevelEncoder = mTopLevelEncoder; + + // Even if finish validation fails, it is now invalid to call any encoding commands, + // so we clear the encoders. Note: mTopLevelEncoder == nullptr is used as a flag for + // if Finish() has been called. + mCurrentEncoder = nullptr; + mTopLevelEncoder = nullptr; + + if (mGotError) { + return DAWN_VALIDATION_ERROR(mErrorMessage); + } + if (currentEncoder != topLevelEncoder) { + return DAWN_VALIDATION_ERROR("Command buffer recording ended mid-pass"); + } + return {}; + } + + bool EncodingContext::IsFinished() const { + return mTopLevelEncoder == nullptr; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/EncodingContext.h b/third_party/dawn/src/dawn_native/EncodingContext.h new file mode 100644 index 00000000000..3142bd092c5 --- /dev/null +++ b/third_party/dawn/src/dawn_native/EncodingContext.h @@ -0,0 +1,111 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_ENCODINGCONTEXT_H_ +#define DAWNNATIVE_ENCODINGCONTEXT_H_ + +#include "dawn_native/CommandAllocator.h" +#include "dawn_native/Error.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/PassResourceUsageTracker.h" +#include "dawn_native/dawn_platform.h" + +#include + +namespace dawn_native { + + class DeviceBase; + class ObjectBase; + + // Base class for allocating/iterating commands. + // It performs error tracking as well as encoding state for render/compute passes. + class EncodingContext { + public: + EncodingContext(DeviceBase* device, const ObjectBase* initialEncoder); + ~EncodingContext(); + + CommandIterator AcquireCommands(); + CommandIterator* GetIterator(); + + // Functions to handle encoder errors + void HandleError(InternalErrorType type, const char* message); + + inline void ConsumeError(std::unique_ptr error) { + HandleError(error->GetType(), error->GetMessage().c_str()); + } + + inline bool ConsumedError(MaybeError maybeError) { + if (DAWN_UNLIKELY(maybeError.IsError())) { + ConsumeError(maybeError.AcquireError()); + return true; + } + return false; + } + + template + inline bool TryEncode(const ObjectBase* encoder, EncodeFunction&& encodeFunction) { + if (DAWN_UNLIKELY(encoder != mCurrentEncoder)) { + if (mCurrentEncoder != mTopLevelEncoder) { + // The top level encoder was used when a pass encoder was current. + HandleError(InternalErrorType::Validation, + "Command cannot be recorded inside a pass"); + } else { + HandleError(InternalErrorType::Validation, + "Recording in an error or already ended pass encoder"); + } + return false; + } + ASSERT(!mWasMovedToIterator); + return !ConsumedError(encodeFunction(&mAllocator)); + } + + // Functions to set current encoder state + void EnterPass(const ObjectBase* passEncoder); + void ExitPass(const ObjectBase* passEncoder, PassResourceUsage passUsages); + MaybeError Finish(); + + const PerPassUsages& GetPassUsages() const; + PerPassUsages AcquirePassUsages(); + + private: + bool IsFinished() const; + void MoveToIterator(); + + DeviceBase* mDevice; + + // There can only be two levels of encoders. Top-level and render/compute pass. + // The top level encoder is the encoder the EncodingContext is created with. + // It doubles as flag to check if encoding has been Finished. + const ObjectBase* mTopLevelEncoder; + // The current encoder must be the same as the encoder provided to TryEncode, + // otherwise an error is produced. It may be nullptr if the EncodingContext is an error. + // The current encoder changes with Enter/ExitPass which should be called by + // CommandEncoder::Begin/EndPass. + const ObjectBase* mCurrentEncoder; + + PerPassUsages mPassUsages; + bool mWerePassUsagesAcquired = false; + + CommandAllocator mAllocator; + CommandIterator mIterator; + bool mWasMovedToIterator = false; + bool mWereCommandsAcquired = false; + + bool mGotError = false; + std::string mErrorMessage; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_ENCODINGCONTEXT_H_ diff --git a/third_party/dawn/src/dawn_native/Error.cpp b/third_party/dawn/src/dawn_native/Error.cpp index 2e09931574e..6dcc3f86c13 100644 --- a/third_party/dawn/src/dawn_native/Error.cpp +++ b/third_party/dawn/src/dawn_native/Error.cpp @@ -15,21 +15,50 @@ #include "dawn_native/Error.h" #include "dawn_native/ErrorData.h" +#include "dawn_native/dawn_platform.h" namespace dawn_native { - ErrorData* MakeError(ErrorType type, - std::string message, - const char* file, - const char* function, - int line) { - ErrorData* error = new ErrorData(type, message); - error->AppendBacktrace(file, function, line); - return error; + void IgnoreErrors(MaybeError maybeError) { + if (maybeError.IsError()) { + std::unique_ptr errorData = maybeError.AcquireError(); + // During shutdown and destruction, device lost errors can be ignored. + // We can also ignore other unexpected internal errors on shut down and treat it as + // device lost so that we can continue with destruction. + ASSERT(errorData->GetType() == InternalErrorType::DeviceLost || + errorData->GetType() == InternalErrorType::Internal); + } } - void AppendBacktrace(ErrorData* error, const char* file, const char* function, int line) { - error->AppendBacktrace(file, function, line); + wgpu::ErrorType ToWGPUErrorType(InternalErrorType type) { + switch (type) { + case InternalErrorType::Validation: + return wgpu::ErrorType::Validation; + case InternalErrorType::OutOfMemory: + return wgpu::ErrorType::OutOfMemory; + + // There is no equivalent of Internal errors in the WebGPU API. Internal errors cause + // the device at the API level to be lost, so treat it like a DeviceLost error. + case InternalErrorType::Internal: + case InternalErrorType::DeviceLost: + return wgpu::ErrorType::DeviceLost; + + default: + return wgpu::ErrorType::Unknown; + } + } + + InternalErrorType FromWGPUErrorType(wgpu::ErrorType type) { + switch (type) { + case wgpu::ErrorType::Validation: + return InternalErrorType::Validation; + case wgpu::ErrorType::OutOfMemory: + return InternalErrorType::OutOfMemory; + case wgpu::ErrorType::DeviceLost: + return InternalErrorType::DeviceLost; + default: + return InternalErrorType::Internal; + } } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Error.h b/third_party/dawn/src/dawn_native/Error.h index 9e073c27b2e..3d5d5c38762 100644 --- a/third_party/dawn/src/dawn_native/Error.h +++ b/third_party/dawn/src/dawn_native/Error.h @@ -16,23 +16,26 @@ #define DAWNNATIVE_ERROR_H_ #include "common/Result.h" +#include "dawn_native/ErrorData.h" #include namespace dawn_native { - // This is the content of an error value for MaybeError or ResultOrError, split off to its own - // file to avoid having all files including headers like and - class ErrorData; - - enum class ErrorType : uint32_t { Validation, ContextLost, Unimplemented }; + enum class InternalErrorType : uint32_t { + Validation, + DeviceLost, + Internal, + Unimplemented, + OutOfMemory + }; // MaybeError and ResultOrError are meant to be used as return value for function that are not // expected to, but might fail. The handling of error is potentially much slower than successes. - using MaybeError = Result; + using MaybeError = Result; template - using ResultOrError = Result; + using ResultOrError = Result; // Returning a success is done like so: // return {}; // for Error @@ -43,11 +46,38 @@ namespace dawn_native { // // but shorthand version for specific error types are preferred: // return DAWN_VALIDATION_ERROR("My error message"); + // + // There are different types of errors that should be used for different purpose: + // + // - Validation: these are errors that show the user did something bad, which causes the + // whole call to be a no-op. It's most commonly found in the frontend but there can be some + // backend specific validation in non-conformant backends too. + // + // - Out of memory: creation of a Buffer or Texture failed because there isn't enough memory. + // This is similar to validation errors in that the call becomes a no-op and returns an + // error object, but is reported separated from validation to the user. + // + // - Device loss: the backend driver reported that the GPU has been lost, which means all + // previous commands magically disappeared and the only thing left to do is clean up. + // Note: Device loss should be used rarely and in most case you want to use Internal + // instead. + // + // - Internal: something happened that the backend didn't expect, and it doesn't know + // how to recover from that situation. This causes the device to be lost, but is separate + // from device loss, because the GPU execution is still happening so we need to clean up + // more gracefully. + // + // - Unimplemented: same as Internal except it puts "unimplemented" in the error message for + // more clarity. + #define DAWN_MAKE_ERROR(TYPE, MESSAGE) \ - ::dawn_native::MakeError(TYPE, MESSAGE, __FILE__, __func__, __LINE__) -#define DAWN_VALIDATION_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::Validation, MESSAGE) -#define DAWN_CONTEXT_LOST_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::ContextLost, MESSAGE) -#define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) DAWN_MAKE_ERROR(ErrorType::Unimplemented, MESSAGE) + ::dawn_native::ErrorData::Create(TYPE, MESSAGE, __FILE__, __func__, __LINE__) +#define DAWN_VALIDATION_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::Validation, MESSAGE) +#define DAWN_DEVICE_LOST_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::DeviceLost, MESSAGE) +#define DAWN_INTERNAL_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::Internal, MESSAGE) +#define DAWN_UNIMPLEMENTED_ERROR(MESSAGE) \ + DAWN_MAKE_ERROR(InternalErrorType::Internal, std::string("Unimplemented: ") + MESSAGE) +#define DAWN_OUT_OF_MEMORY_ERROR(MESSAGE) DAWN_MAKE_ERROR(InternalErrorType::OutOfMemory, MESSAGE) #define DAWN_CONCAT1(x, y) x##y #define DAWN_CONCAT2(x, y) DAWN_CONCAT1(x, y) @@ -56,42 +86,38 @@ namespace dawn_native { // When Errors aren't handled explicitly, calls to functions returning errors should be // wrapped in an DAWN_TRY. It will return the error if any, otherwise keep executing // the current function. -#define DAWN_TRY(EXPR) \ - { \ - auto DAWN_LOCAL_VAR = EXPR; \ - if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \ - ErrorData* error = DAWN_LOCAL_VAR.AcquireError(); \ - ::dawn_native::AppendBacktrace(error, __FILE__, __func__, __LINE__); \ - return {std::move(error)}; \ - } \ - } \ - for (;;) \ +#define DAWN_TRY(EXPR) \ + { \ + auto DAWN_LOCAL_VAR = EXPR; \ + if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \ + std::unique_ptr<::dawn_native::ErrorData> error = DAWN_LOCAL_VAR.AcquireError(); \ + error->AppendBacktrace(__FILE__, __func__, __LINE__); \ + return {std::move(error)}; \ + } \ + } \ + for (;;) \ break // DAWN_TRY_ASSIGN is the same as DAWN_TRY for ResultOrError and assigns the success value, if // any, to VAR. -#define DAWN_TRY_ASSIGN(VAR, EXPR) \ - { \ - auto DAWN_LOCAL_VAR = EXPR; \ - if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \ - ErrorData* error = DAWN_LOCAL_VAR.AcquireError(); \ - ::dawn_native::AppendBacktrace(error, __FILE__, __func__, __LINE__); \ - return {std::move(error)}; \ - } \ - VAR = DAWN_LOCAL_VAR.AcquireSuccess(); \ - } \ - for (;;) \ +#define DAWN_TRY_ASSIGN(VAR, EXPR) \ + { \ + auto DAWN_LOCAL_VAR = EXPR; \ + if (DAWN_UNLIKELY(DAWN_LOCAL_VAR.IsError())) { \ + std::unique_ptr error = DAWN_LOCAL_VAR.AcquireError(); \ + error->AppendBacktrace(__FILE__, __func__, __LINE__); \ + return {std::move(error)}; \ + } \ + VAR = DAWN_LOCAL_VAR.AcquireSuccess(); \ + } \ + for (;;) \ break - // Implementation detail of DAWN_TRY and DAWN_TRY_ASSIGN's adding to the Error's backtrace. - void AppendBacktrace(ErrorData* error, const char* file, const char* function, int line); + // Assert that errors are device loss so that we can continue with destruction + void IgnoreErrors(MaybeError maybeError); - // Implementation detail of DAWN_MAKE_ERROR - ErrorData* MakeError(ErrorType type, - std::string message, - const char* file, - const char* function, - int line); + wgpu::ErrorType ToWGPUErrorType(InternalErrorType type); + InternalErrorType FromWGPUErrorType(wgpu::ErrorType type); } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ErrorData.cpp b/third_party/dawn/src/dawn_native/ErrorData.cpp index 77a0e3f9dc1..41d0c297021 100644 --- a/third_party/dawn/src/dawn_native/ErrorData.cpp +++ b/third_party/dawn/src/dawn_native/ErrorData.cpp @@ -14,11 +14,22 @@ #include "dawn_native/ErrorData.h" +#include "dawn_native/Error.h" +#include "dawn_native/dawn_platform.h" + namespace dawn_native { - ErrorData::ErrorData() = default; + std::unique_ptr ErrorData::Create(InternalErrorType type, + std::string message, + const char* file, + const char* function, + int line) { + std::unique_ptr error = std::make_unique(type, message); + error->AppendBacktrace(file, function, line); + return error; + } - ErrorData::ErrorData(ErrorType type, std::string message) + ErrorData::ErrorData(InternalErrorType type, std::string message) : mType(type), mMessage(std::move(message)) { } @@ -31,7 +42,7 @@ namespace dawn_native { mBacktrace.push_back(std::move(record)); } - ErrorType ErrorData::GetType() const { + InternalErrorType ErrorData::GetType() const { return mType; } diff --git a/third_party/dawn/src/dawn_native/ErrorData.h b/third_party/dawn/src/dawn_native/ErrorData.h index 01520034838..02486020e07 100644 --- a/third_party/dawn/src/dawn_native/ErrorData.h +++ b/third_party/dawn/src/dawn_native/ErrorData.h @@ -15,18 +15,32 @@ #ifndef DAWNNATIVE_ERRORDATA_H_ #define DAWNNATIVE_ERRORDATA_H_ +#include "common/Compiler.h" + #include +#include #include #include -namespace dawn_native { - +namespace wgpu { enum class ErrorType : uint32_t; +} + +namespace dawn { + using ErrorType = wgpu::ErrorType; +} + +namespace dawn_native { + enum class InternalErrorType : uint32_t; - class ErrorData { + class DAWN_NO_DISCARD ErrorData { public: - ErrorData(); - ErrorData(ErrorType type, std::string message); + static DAWN_NO_DISCARD std::unique_ptr Create(InternalErrorType type, + std::string message, + const char* file, + const char* function, + int line); + ErrorData(InternalErrorType type, std::string message); struct BacktraceRecord { const char* file; @@ -35,12 +49,12 @@ namespace dawn_native { }; void AppendBacktrace(const char* file, const char* function, int line); - ErrorType GetType() const; + InternalErrorType GetType() const; const std::string& GetMessage() const; const std::vector& GetBacktrace() const; private: - ErrorType mType; + InternalErrorType mType; std::string mMessage; std::vector mBacktrace; }; diff --git a/third_party/dawn/src/dawn_native/ErrorInjector.cpp b/third_party/dawn/src/dawn_native/ErrorInjector.cpp new file mode 100644 index 00000000000..836ef1ecab6 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ErrorInjector.cpp @@ -0,0 +1,70 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/ErrorInjector.h" + +#include "common/Assert.h" +#include "dawn_native/DawnNative.h" + +namespace dawn_native { + + namespace { + + bool sIsEnabled = false; + uint64_t sNextIndex = 0; + uint64_t sInjectedFailureIndex = 0; + bool sHasPendingInjectedError = false; + + } // anonymous namespace + + void EnableErrorInjector() { + sIsEnabled = true; + } + + void DisableErrorInjector() { + sIsEnabled = false; + } + + void ClearErrorInjector() { + sNextIndex = 0; + sHasPendingInjectedError = false; + } + + bool ErrorInjectorEnabled() { + return sIsEnabled; + } + + uint64_t AcquireErrorInjectorCallCount() { + uint64_t count = sNextIndex; + ClearErrorInjector(); + return count; + } + + bool ShouldInjectError() { + uint64_t index = sNextIndex++; + if (sHasPendingInjectedError && index == sInjectedFailureIndex) { + sHasPendingInjectedError = false; + return true; + } + return false; + } + + void InjectErrorAt(uint64_t index) { + // Only one error can be injected at a time. + ASSERT(!sHasPendingInjectedError); + sInjectedFailureIndex = index; + sHasPendingInjectedError = true; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ErrorInjector.h b/third_party/dawn/src/dawn_native/ErrorInjector.h new file mode 100644 index 00000000000..4d7d2b8a2b6 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ErrorInjector.h @@ -0,0 +1,68 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_ERRORINJECTOR_H_ +#define DAWNNATIVE_ERRORINJECTOR_H_ + +#include +#include + +namespace dawn_native { + + template + struct InjectedErrorResult { + ErrorType error; + bool injected; + }; + + bool ErrorInjectorEnabled(); + + bool ShouldInjectError(); + + template + InjectedErrorResult MaybeInjectError(ErrorType errorType) { + return InjectedErrorResult{errorType, ShouldInjectError()}; + } + + template + InjectedErrorResult MaybeInjectError(ErrorType errorType, ErrorTypes... errorTypes) { + if (ShouldInjectError()) { + return InjectedErrorResult{errorType, true}; + } + return MaybeInjectError(errorTypes...); + } + +} // namespace dawn_native + +#if defined(DAWN_ENABLE_ERROR_INJECTION) + +# define INJECT_ERROR_OR_RUN(stmt, ...) \ + [&]() { \ + if (DAWN_UNLIKELY(::dawn_native::ErrorInjectorEnabled())) { \ + /* Only used for testing and fuzzing, so it's okay if this is deoptimized */ \ + auto injectedError = ::dawn_native::MaybeInjectError(__VA_ARGS__); \ + if (injectedError.injected) { \ + return injectedError.error; \ + } \ + } \ + return (stmt); \ + }() + +#else + +# define INJECT_ERROR_OR_RUN(stmt, ...) stmt + +#endif + +#endif // DAWNNATIVE_ERRORINJECTOR_H_ diff --git a/third_party/dawn/src/dawn_native/ErrorScope.cpp b/third_party/dawn/src/dawn_native/ErrorScope.cpp new file mode 100644 index 00000000000..daa0e48f2a6 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ErrorScope.cpp @@ -0,0 +1,143 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/ErrorScope.h" + +#include "common/Assert.h" + +namespace dawn_native { + + ErrorScope::ErrorScope() : mIsRoot(true) { + } + + ErrorScope::ErrorScope(wgpu::ErrorFilter errorFilter, ErrorScope* parent) + : RefCounted(), mErrorFilter(errorFilter), mParent(parent), mIsRoot(false) { + ASSERT(mParent.Get() != nullptr); + } + + ErrorScope::~ErrorScope() { + if (!IsRoot()) { + RunNonRootCallback(); + } + } + + void ErrorScope::SetCallback(wgpu::ErrorCallback callback, void* userdata) { + mCallback = callback; + mUserdata = userdata; + } + + ErrorScope* ErrorScope::GetParent() { + return mParent.Get(); + } + + bool ErrorScope::IsRoot() const { + return mIsRoot; + } + + void ErrorScope::RunNonRootCallback() { + ASSERT(!IsRoot()); + + if (mCallback != nullptr) { + // For non-root error scopes, the callback can run at most once. + mCallback(static_cast(mErrorType), mErrorMessage.c_str(), mUserdata); + mCallback = nullptr; + } + } + + void ErrorScope::HandleError(wgpu::ErrorType type, const char* message) { + HandleErrorImpl(this, type, message); + } + + void ErrorScope::UnlinkForShutdown() { + UnlinkForShutdownImpl(this); + } + + // static + void ErrorScope::HandleErrorImpl(ErrorScope* scope, wgpu::ErrorType type, const char* message) { + ErrorScope* currentScope = scope; + for (; !currentScope->IsRoot(); currentScope = currentScope->GetParent()) { + ASSERT(currentScope != nullptr); + + bool consumed = false; + switch (type) { + case wgpu::ErrorType::Validation: + if (currentScope->mErrorFilter != wgpu::ErrorFilter::Validation) { + // Error filter does not match. Move on to the next scope. + continue; + } + consumed = true; + break; + + case wgpu::ErrorType::OutOfMemory: + if (currentScope->mErrorFilter != wgpu::ErrorFilter::OutOfMemory) { + // Error filter does not match. Move on to the next scope. + continue; + } + consumed = true; + break; + + // Unknown and DeviceLost are fatal. All error scopes capture them. + // |consumed| is false because these should bubble to all scopes. + case wgpu::ErrorType::Unknown: + case wgpu::ErrorType::DeviceLost: + consumed = false; + break; + + case wgpu::ErrorType::NoError: + default: + UNREACHABLE(); + return; + } + + // Record the error if the scope doesn't have one yet. + if (currentScope->mErrorType == wgpu::ErrorType::NoError) { + currentScope->mErrorType = type; + currentScope->mErrorMessage = message; + } + + if (consumed) { + return; + } + } + + // The root error scope captures all uncaptured errors. + ASSERT(currentScope->IsRoot()); + if (currentScope->mCallback) { + currentScope->mCallback(static_cast(type), message, + currentScope->mUserdata); + } + } + + // static + void ErrorScope::UnlinkForShutdownImpl(ErrorScope* scope) { + Ref currentScope = scope; + Ref parentScope = nullptr; + for (; !currentScope->IsRoot(); currentScope = parentScope.Get()) { + ASSERT(!currentScope->IsRoot()); + ASSERT(currentScope.Get() != nullptr); + parentScope = std::move(currentScope->mParent); + ASSERT(parentScope.Get() != nullptr); + + // On shutdown, error scopes that have yet to have a status get Unknown. + if (currentScope->mErrorType == wgpu::ErrorType::NoError) { + currentScope->mErrorType = wgpu::ErrorType::Unknown; + currentScope->mErrorMessage = "Error scope destroyed"; + } + + // Run the callback if it hasn't run already. + currentScope->RunNonRootCallback(); + } + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ErrorScope.h b/third_party/dawn/src/dawn_native/ErrorScope.h new file mode 100644 index 00000000000..75327d55693 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ErrorScope.h @@ -0,0 +1,70 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_ERRORSCOPE_H_ +#define DAWNNATIVE_ERRORSCOPE_H_ + +#include "dawn_native/dawn_platform.h" + +#include "common/RefCounted.h" + +#include + +namespace dawn_native { + + // Errors can be recorded into an ErrorScope by calling |HandleError|. + // Because an error scope should not resolve until contained + // commands are complete, calling the callback is deferred until it is destructed. + // In-flight commands or asynchronous events should hold a reference to the + // ErrorScope for their duration. + // + // Because parent ErrorScopes should not resolve before child ErrorScopes, + // ErrorScopes hold a reference to their parent. + // + // To simplify ErrorHandling, there is a sentinel root error scope which has + // no parent. All uncaptured errors are handled by the root error scope. Its + // callback is called immediately once it encounters an error. + class ErrorScope final : public RefCounted { + public: + ErrorScope(); // Constructor for the root error scope. + ErrorScope(wgpu::ErrorFilter errorFilter, ErrorScope* parent); + + void SetCallback(wgpu::ErrorCallback callback, void* userdata); + ErrorScope* GetParent(); + + void HandleError(wgpu::ErrorType type, const char* message); + void UnlinkForShutdown(); + + private: + ~ErrorScope() override; + bool IsRoot() const; + void RunNonRootCallback(); + + static void HandleErrorImpl(ErrorScope* scope, wgpu::ErrorType type, const char* message); + static void UnlinkForShutdownImpl(ErrorScope* scope); + + wgpu::ErrorFilter mErrorFilter = wgpu::ErrorFilter::None; + Ref mParent = nullptr; + bool mIsRoot; + + wgpu::ErrorCallback mCallback = nullptr; + void* mUserdata = nullptr; + + wgpu::ErrorType mErrorType = wgpu::ErrorType::NoError; + std::string mErrorMessage = ""; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_ERRORSCOPE_H_ diff --git a/third_party/dawn/src/dawn_native/ErrorScopeTracker.cpp b/third_party/dawn/src/dawn_native/ErrorScopeTracker.cpp new file mode 100644 index 00000000000..b110e97b003 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ErrorScopeTracker.cpp @@ -0,0 +1,47 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/ErrorScopeTracker.h" + +#include "dawn_native/Device.h" +#include "dawn_native/ErrorScope.h" + +#include + +namespace dawn_native { + + ErrorScopeTracker::ErrorScopeTracker(DeviceBase* device) : mDevice(device) { + } + + ErrorScopeTracker::~ErrorScopeTracker() { + // The tracker is destroyed when the Device is destroyed. We need to + // call Destroy on all in-flight error scopes so they resolve their callbacks + // with UNKNOWN. + constexpr Serial maxSerial = std::numeric_limits::max(); + for (Ref& scope : mScopesInFlight.IterateUpTo(maxSerial)) { + scope->UnlinkForShutdown(); + } + Tick(maxSerial); + } + + void ErrorScopeTracker::TrackUntilLastSubmitComplete(ErrorScope* scope) { + mScopesInFlight.Enqueue(scope, mDevice->GetLastSubmittedCommandSerial()); + mDevice->AddFutureCallbackSerial(mDevice->GetPendingCommandSerial()); + } + + void ErrorScopeTracker::Tick(Serial completedSerial) { + mScopesInFlight.ClearUpTo(completedSerial); + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ErrorScopeTracker.h b/third_party/dawn/src/dawn_native/ErrorScopeTracker.h new file mode 100644 index 00000000000..2ca651198f5 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ErrorScopeTracker.h @@ -0,0 +1,42 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_ERRORSCOPETRACKER_H_ +#define DAWNNATIVE_ERRORSCOPETRACKER_H_ + +#include "common/RefCounted.h" +#include "common/SerialQueue.h" + +namespace dawn_native { + + class DeviceBase; + class ErrorScope; + + class ErrorScopeTracker { + public: + ErrorScopeTracker(DeviceBase* device); + ~ErrorScopeTracker(); + + void TrackUntilLastSubmitComplete(ErrorScope* scope); + + void Tick(Serial completedSerial); + + protected: + DeviceBase* mDevice; + SerialQueue> mScopesInFlight; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_ERRORSCOPETRACKER_H_ diff --git a/third_party/dawn/src/dawn_native/Extensions.cpp b/third_party/dawn/src/dawn_native/Extensions.cpp new file mode 100644 index 00000000000..d356616558d --- /dev/null +++ b/third_party/dawn/src/dawn_native/Extensions.cpp @@ -0,0 +1,136 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/Assert.h" +#include "common/BitSetIterator.h" +#include "dawn_native/Extensions.h" + +namespace dawn_native { + namespace { + + struct ExtensionEnumAndInfo { + Extension extension; + ExtensionInfo info; + bool WGPUDeviceProperties::*memberInWGPUDeviceProperties; + }; + + using ExtensionEnumAndInfoList = + std::array(Extension::EnumCount)>; + + static constexpr ExtensionEnumAndInfoList kExtensionNameAndInfoList = { + {{Extension::TextureCompressionBC, + {"texture_compression_bc", "Support Block Compressed (BC) texture formats", + "https://bugs.chromium.org/p/dawn/issues/detail?id=42"}, + &WGPUDeviceProperties::textureCompressionBC}, + {Extension::ShaderFloat16, + {"shader_float16", + "Support 16bit float arithmetic and declarations in uniform and storage buffers", + "https://bugs.chromium.org/p/dawn/issues/detail?id=426"}, + &WGPUDeviceProperties::shaderFloat16}, + {Extension::PipelineStatisticsQuery, + {"pipeline_statistics_query", "Support Pipeline Statistics Query", + "https://bugs.chromium.org/p/dawn/issues/detail?id=434"}, + &WGPUDeviceProperties::pipelineStatisticsQuery}, + {Extension::TimestampQuery, + {"timestamp_query", "Support Timestamp Query", + "https://bugs.chromium.org/p/dawn/issues/detail?id=434"}, + &WGPUDeviceProperties::timestampQuery}}}; + + } // anonymous namespace + + void ExtensionsSet::EnableExtension(Extension extension) { + ASSERT(extension != Extension::InvalidEnum); + const size_t extensionIndex = static_cast(extension); + extensionsBitSet.set(extensionIndex); + } + + bool ExtensionsSet::IsEnabled(Extension extension) const { + ASSERT(extension != Extension::InvalidEnum); + const size_t extensionIndex = static_cast(extension); + return extensionsBitSet[extensionIndex]; + } + + std::vector ExtensionsSet::GetEnabledExtensionNames() const { + std::vector enabledExtensionNames(extensionsBitSet.count()); + + uint32_t index = 0; + for (uint32_t i : IterateBitSet(extensionsBitSet)) { + const char* extensionName = ExtensionEnumToName(static_cast(i)); + enabledExtensionNames[index] = extensionName; + ++index; + } + return enabledExtensionNames; + } + + void ExtensionsSet::InitializeDeviceProperties(WGPUDeviceProperties* properties) const { + ASSERT(properties != nullptr); + + for (uint32_t i : IterateBitSet(extensionsBitSet)) { + properties->*(kExtensionNameAndInfoList[i].memberInWGPUDeviceProperties) = true; + } + } + + const char* ExtensionEnumToName(Extension extension) { + ASSERT(extension != Extension::InvalidEnum); + + const ExtensionEnumAndInfo& extensionNameAndInfo = + kExtensionNameAndInfoList[static_cast(extension)]; + ASSERT(extensionNameAndInfo.extension == extension); + return extensionNameAndInfo.info.name; + } + + ExtensionsInfo::ExtensionsInfo() { + for (size_t index = 0; index < kExtensionNameAndInfoList.size(); ++index) { + const ExtensionEnumAndInfo& extensionNameAndInfo = kExtensionNameAndInfoList[index]; + ASSERT(index == static_cast(extensionNameAndInfo.extension)); + mExtensionNameToEnumMap[extensionNameAndInfo.info.name] = + extensionNameAndInfo.extension; + } + } + + const ExtensionInfo* ExtensionsInfo::GetExtensionInfo(const char* extensionName) const { + ASSERT(extensionName); + + const auto& iter = mExtensionNameToEnumMap.find(extensionName); + if (iter != mExtensionNameToEnumMap.cend()) { + return &kExtensionNameAndInfoList[static_cast(iter->second)].info; + } + return nullptr; + } + + Extension ExtensionsInfo::ExtensionNameToEnum(const char* extensionName) const { + ASSERT(extensionName); + + const auto& iter = mExtensionNameToEnumMap.find(extensionName); + if (iter != mExtensionNameToEnumMap.cend()) { + return kExtensionNameAndInfoList[static_cast(iter->second)].extension; + } + return Extension::InvalidEnum; + } + + ExtensionsSet ExtensionsInfo::ExtensionNamesToExtensionsSet( + const std::vector& requiredExtensions) const { + ExtensionsSet extensionsSet; + + for (const char* extensionName : requiredExtensions) { + Extension extensionEnum = ExtensionNameToEnum(extensionName); + ASSERT(extensionEnum != Extension::InvalidEnum); + extensionsSet.EnableExtension(extensionEnum); + } + return extensionsSet; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Extensions.h b/third_party/dawn/src/dawn_native/Extensions.h new file mode 100644 index 00000000000..ba32ee153e5 --- /dev/null +++ b/third_party/dawn/src/dawn_native/Extensions.h @@ -0,0 +1,67 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_EXTENSIONS_H_ +#define DAWNNATIVE_EXTENSIONS_H_ + +#include +#include +#include + +#include "dawn_native/DawnNative.h" + +namespace dawn_native { + + enum class Extension { + TextureCompressionBC, + ShaderFloat16, + PipelineStatisticsQuery, + TimestampQuery, + + EnumCount, + InvalidEnum = EnumCount, + ExtensionMin = TextureCompressionBC, + }; + + // A wrapper of the bitset to store if an extension is enabled or not. This wrapper provides the + // convenience to convert the enums of enum class Extension to the indices of a bitset. + struct ExtensionsSet { + std::bitset(Extension::EnumCount)> extensionsBitSet; + + void EnableExtension(Extension extension); + bool IsEnabled(Extension extension) const; + std::vector GetEnabledExtensionNames() const; + void InitializeDeviceProperties(WGPUDeviceProperties* properties) const; + }; + + const char* ExtensionEnumToName(Extension extension); + + class ExtensionsInfo { + public: + ExtensionsInfo(); + + // Used to query the details of an extension. Return nullptr if extensionName is not a valid + // name of an extension supported in Dawn + const ExtensionInfo* GetExtensionInfo(const char* extensionName) const; + Extension ExtensionNameToEnum(const char* extensionName) const; + ExtensionsSet ExtensionNamesToExtensionsSet( + const std::vector& requiredExtensions) const; + + private: + std::unordered_map mExtensionNameToEnumMap; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_EXTENSIONS_H_ diff --git a/third_party/dawn/src/dawn_native/Fence.cpp b/third_party/dawn/src/dawn_native/Fence.cpp index 55b6b332c90..f16274e9d3a 100644 --- a/third_party/dawn/src/dawn_native/Fence.cpp +++ b/third_party/dawn/src/dawn_native/Fence.cpp @@ -33,47 +33,48 @@ namespace dawn_native { // Fence - FenceBase::FenceBase(QueueBase* queue, const FenceDescriptor* descriptor) + Fence::Fence(QueueBase* queue, const FenceDescriptor* descriptor) : ObjectBase(queue->GetDevice()), mSignalValue(descriptor->initialValue), mCompletedValue(descriptor->initialValue), mQueue(queue) { } - FenceBase::FenceBase(DeviceBase* device, ObjectBase::ErrorTag tag) : ObjectBase(device, tag) { + Fence::Fence(DeviceBase* device, ObjectBase::ErrorTag tag) : ObjectBase(device, tag) { } - FenceBase::~FenceBase() { + Fence::~Fence() { for (auto& request : mRequests.IterateAll()) { ASSERT(!IsError()); - request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, request.userdata); + request.completionCallback(WGPUFenceCompletionStatus_Unknown, request.userdata); } mRequests.Clear(); } // static - FenceBase* FenceBase::MakeError(DeviceBase* device) { - return new FenceBase(device, ObjectBase::kError); + Fence* Fence::MakeError(DeviceBase* device) { + return new Fence(device, ObjectBase::kError); } - uint64_t FenceBase::GetCompletedValue() const { + uint64_t Fence::GetCompletedValue() const { if (IsError()) { return 0; } return mCompletedValue; } - void FenceBase::OnCompletion(uint64_t value, - dawn::FenceOnCompletionCallback callback, - void* userdata) { - if (GetDevice()->ConsumedError(ValidateOnCompletion(value))) { - callback(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata); + void Fence::OnCompletion(uint64_t value, + wgpu::FenceOnCompletionCallback callback, + void* userdata) { + WGPUFenceCompletionStatus status; + if (GetDevice()->ConsumedError(ValidateOnCompletion(value, &status))) { + callback(status, userdata); return; } ASSERT(!IsError()); if (value <= mCompletedValue) { - callback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata); + callback(WGPUFenceCompletionStatus_Success, userdata); return; } @@ -83,39 +84,51 @@ namespace dawn_native { mRequests.Enqueue(std::move(request), value); } - uint64_t FenceBase::GetSignaledValue() const { + uint64_t Fence::GetSignaledValue() const { ASSERT(!IsError()); return mSignalValue; } - const QueueBase* FenceBase::GetQueue() const { + const QueueBase* Fence::GetQueue() const { ASSERT(!IsError()); return mQueue.Get(); } - void FenceBase::SetSignaledValue(uint64_t signalValue) { + void Fence::SetSignaledValue(uint64_t signalValue) { ASSERT(!IsError()); ASSERT(signalValue > mSignalValue); mSignalValue = signalValue; } - void FenceBase::SetCompletedValue(uint64_t completedValue) { + void Fence::SetCompletedValue(uint64_t completedValue) { ASSERT(!IsError()); ASSERT(completedValue <= mSignalValue); ASSERT(completedValue > mCompletedValue); mCompletedValue = completedValue; for (auto& request : mRequests.IterateUpTo(mCompletedValue)) { - request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, request.userdata); + if (GetDevice()->IsLost()) { + request.completionCallback(WGPUFenceCompletionStatus_DeviceLost, request.userdata); + } else { + request.completionCallback(WGPUFenceCompletionStatus_Success, request.userdata); + } } mRequests.ClearUpTo(mCompletedValue); } - MaybeError FenceBase::ValidateOnCompletion(uint64_t value) const { + MaybeError Fence::ValidateOnCompletion(uint64_t value, + WGPUFenceCompletionStatus* status) const { + *status = WGPUFenceCompletionStatus_DeviceLost; + DAWN_TRY(GetDevice()->ValidateIsAlive()); + + *status = WGPUFenceCompletionStatus_Error; DAWN_TRY(GetDevice()->ValidateObject(this)); + if (value > mSignalValue) { return DAWN_VALIDATION_ERROR("Value greater than fence signaled value"); } + + *status = WGPUFenceCompletionStatus_Success; return {}; } diff --git a/third_party/dawn/src/dawn_native/Fence.h b/third_party/dawn/src/dawn_native/Fence.h index cd20d26a1ba..f5596599fea 100644 --- a/third_party/dawn/src/dawn_native/Fence.h +++ b/third_party/dawn/src/dawn_native/Fence.h @@ -28,19 +28,18 @@ namespace dawn_native { MaybeError ValidateFenceDescriptor(const FenceDescriptor* descriptor); - class FenceBase : public ObjectBase { + class Fence final : public ObjectBase { public: - FenceBase(QueueBase* queue, const FenceDescriptor* descriptor); - ~FenceBase(); + Fence(QueueBase* queue, const FenceDescriptor* descriptor); - static FenceBase* MakeError(DeviceBase* device); + static Fence* MakeError(DeviceBase* device); uint64_t GetSignaledValue() const; const QueueBase* GetQueue() const; // Dawn API uint64_t GetCompletedValue() const; - void OnCompletion(uint64_t value, dawn::FenceOnCompletionCallback callback, void* userdata); + void OnCompletion(uint64_t value, wgpu::FenceOnCompletionCallback callback, void* userdata); protected: friend class QueueBase; @@ -49,12 +48,13 @@ namespace dawn_native { void SetCompletedValue(uint64_t completedValue); private: - FenceBase(DeviceBase* device, ObjectBase::ErrorTag tag); + Fence(DeviceBase* device, ObjectBase::ErrorTag tag); + ~Fence() override; - MaybeError ValidateOnCompletion(uint64_t value) const; + MaybeError ValidateOnCompletion(uint64_t value, WGPUFenceCompletionStatus* status) const; struct OnCompletionData { - dawn::FenceOnCompletionCallback completionCallback = nullptr; + wgpu::FenceOnCompletionCallback completionCallback = nullptr; void* userdata = nullptr; }; diff --git a/third_party/dawn/src/dawn_native/FenceSignalTracker.cpp b/third_party/dawn/src/dawn_native/FenceSignalTracker.cpp index 132ac9c4996..b8243a256ce 100644 --- a/third_party/dawn/src/dawn_native/FenceSignalTracker.cpp +++ b/third_party/dawn/src/dawn_native/FenceSignalTracker.cpp @@ -26,11 +26,12 @@ namespace dawn_native { ASSERT(mFencesInFlight.Empty()); } - void FenceSignalTracker::UpdateFenceOnComplete(FenceBase* fence, uint64_t value) { + void FenceSignalTracker::UpdateFenceOnComplete(Fence* fence, uint64_t value) { // Because we currently only have a single queue, we can simply update // the fence completed value once the last submitted serial has passed. mFencesInFlight.Enqueue(FenceInFlight{fence, value}, mDevice->GetLastSubmittedCommandSerial()); + mDevice->AddFutureCallbackSerial(mDevice->GetPendingCommandSerial()); } void FenceSignalTracker::Tick(Serial finishedSerial) { diff --git a/third_party/dawn/src/dawn_native/FenceSignalTracker.h b/third_party/dawn/src/dawn_native/FenceSignalTracker.h index d689277244e..10026a44efa 100644 --- a/third_party/dawn/src/dawn_native/FenceSignalTracker.h +++ b/third_party/dawn/src/dawn_native/FenceSignalTracker.h @@ -15,17 +15,17 @@ #ifndef DAWNNATIVE_FENCESIGNALTRACKER_H_ #define DAWNNATIVE_FENCESIGNALTRACKER_H_ +#include "common/RefCounted.h" #include "common/SerialQueue.h" -#include "dawn_native/RefCounted.h" namespace dawn_native { class DeviceBase; - class FenceBase; + class Fence; class FenceSignalTracker { struct FenceInFlight { - Ref fence; + Ref fence; uint64_t value; }; @@ -33,7 +33,7 @@ namespace dawn_native { FenceSignalTracker(DeviceBase* device); ~FenceSignalTracker(); - void UpdateFenceOnComplete(FenceBase* fence, uint64_t value); + void UpdateFenceOnComplete(Fence* fence, uint64_t value); void Tick(Serial finishedSerial); diff --git a/third_party/dawn/src/dawn_native/Format.cpp b/third_party/dawn/src/dawn_native/Format.cpp new file mode 100644 index 00000000000..9c7fa58a3eb --- /dev/null +++ b/third_party/dawn/src/dawn_native/Format.cpp @@ -0,0 +1,263 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/Format.h" +#include "dawn_native/Device.h" +#include "dawn_native/Extensions.h" + +#include + +namespace dawn_native { + + // Format + + // static + Format::Type Format::TextureComponentTypeToFormatType( + wgpu::TextureComponentType componentType) { + switch (componentType) { + case wgpu::TextureComponentType::Float: + case wgpu::TextureComponentType::Sint: + case wgpu::TextureComponentType::Uint: + break; + default: + UNREACHABLE(); + } + // Check that Type correctly mirrors TextureComponentType except for "Other". + static_assert(static_cast(wgpu::TextureComponentType::Float) == Type::Float, ""); + static_assert(static_cast(wgpu::TextureComponentType::Sint) == Type::Sint, ""); + static_assert(static_cast(wgpu::TextureComponentType::Uint) == Type::Uint, ""); + return static_cast(componentType); + } + + // static + wgpu::TextureComponentType Format::FormatTypeToTextureComponentType(Type type) { + switch (type) { + case Type::Float: + case Type::Sint: + case Type::Uint: + break; + default: + UNREACHABLE(); + } + // Check that Type correctly mirrors TextureComponentType except for "Other". + static_assert(static_cast(wgpu::TextureComponentType::Float) == Type::Float, ""); + static_assert(static_cast(wgpu::TextureComponentType::Sint) == Type::Sint, ""); + static_assert(static_cast(wgpu::TextureComponentType::Uint) == Type::Uint, ""); + return static_cast(type); + } + + bool Format::IsColor() const { + return aspect == Aspect::Color; + } + + bool Format::HasDepth() const { + return aspect == Depth || aspect == DepthStencil; + } + + bool Format::HasStencil() const { + return aspect == Stencil || aspect == DepthStencil; + } + + bool Format::HasDepthOrStencil() const { + return aspect != Color; + } + + bool Format::HasComponentType(Type componentType) const { + return componentType == type; + } + + size_t Format::GetIndex() const { + return ComputeFormatIndex(format); + } + + // Implementation details of the format table of the DeviceBase + + // For the enum for formats are packed but this might change when we have a broader extension + // mechanism for webgpu.h. Formats start at 1 because 0 is the undefined format. + size_t ComputeFormatIndex(wgpu::TextureFormat format) { + // This takes advantage of overflows to make the index of TextureFormat::Undefined outside + // of the range of the FormatTable. + static_assert(static_cast(wgpu::TextureFormat::Undefined) - 1 > kKnownFormatCount, + ""); + return static_cast(static_cast(format) - 1); + } + + FormatTable BuildFormatTable(const DeviceBase* device) { + FormatTable table; + std::bitset formatsSet; + + using Type = Format::Type; + using Aspect = Format::Aspect; + + auto AddFormat = [&table, &formatsSet](Format format) { + size_t index = ComputeFormatIndex(format.format); + ASSERT(index < table.size()); + + // This checks that each format is set at most once, the first part of checking that all + // formats are set exactly once. + ASSERT(!formatsSet[index]); + + table[index] = format; + formatsSet.set(index); + }; + + auto AddColorFormat = [&AddFormat](wgpu::TextureFormat format, bool renderable, + bool supportsStorageUsage, uint32_t byteSize, + Type type) { + Format internalFormat; + internalFormat.format = format; + internalFormat.isRenderable = renderable; + internalFormat.isCompressed = false; + internalFormat.isSupported = true; + internalFormat.supportsStorageUsage = supportsStorageUsage; + internalFormat.aspect = Aspect::Color; + internalFormat.type = type; + internalFormat.blockByteSize = byteSize; + internalFormat.blockWidth = 1; + internalFormat.blockHeight = 1; + AddFormat(internalFormat); + }; + + auto AddDepthStencilFormat = [&AddFormat](wgpu::TextureFormat format, Format::Aspect aspect, + uint32_t byteSize) { + Format internalFormat; + internalFormat.format = format; + internalFormat.isRenderable = true; + internalFormat.isCompressed = false; + internalFormat.isSupported = true; + internalFormat.supportsStorageUsage = false; + internalFormat.aspect = aspect; + internalFormat.type = Type::Other; + internalFormat.blockByteSize = byteSize; + internalFormat.blockWidth = 1; + internalFormat.blockHeight = 1; + AddFormat(internalFormat); + }; + + auto AddDepthFormat = [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize, + Type type) { + Format internalFormat; + internalFormat.format = format; + internalFormat.isRenderable = true; + internalFormat.isCompressed = false; + internalFormat.isSupported = true; + internalFormat.supportsStorageUsage = false; + internalFormat.aspect = Aspect::Depth; + internalFormat.type = type; + internalFormat.blockByteSize = byteSize; + internalFormat.blockWidth = 1; + internalFormat.blockHeight = 1; + AddFormat(internalFormat); + }; + + auto AddCompressedFormat = [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize, + uint32_t width, uint32_t height, bool isSupported) { + Format internalFormat; + internalFormat.format = format; + internalFormat.isRenderable = false; + internalFormat.isCompressed = true; + internalFormat.isSupported = isSupported; + internalFormat.supportsStorageUsage = false; + internalFormat.aspect = Aspect::Color; + internalFormat.type = Type::Float; + internalFormat.blockByteSize = byteSize; + internalFormat.blockWidth = width; + internalFormat.blockHeight = height; + AddFormat(internalFormat); + }; + + // clang-format off + + // 1 byte color formats + AddColorFormat(wgpu::TextureFormat::R8Unorm, true, false, 1, Type::Float); + AddColorFormat(wgpu::TextureFormat::R8Snorm, false, false, 1, Type::Float); + AddColorFormat(wgpu::TextureFormat::R8Uint, true, false, 1, Type::Uint); + AddColorFormat(wgpu::TextureFormat::R8Sint, true, false, 1, Type::Sint); + + // 2 bytes color formats + AddColorFormat(wgpu::TextureFormat::R16Uint, true, false, 2, Type::Uint); + AddColorFormat(wgpu::TextureFormat::R16Sint, true, false, 2, Type::Sint); + AddColorFormat(wgpu::TextureFormat::R16Float, true, false, 2, Type::Float); + AddColorFormat(wgpu::TextureFormat::RG8Unorm, true, false, 2, Type::Float); + AddColorFormat(wgpu::TextureFormat::RG8Snorm, false, false, 2, Type::Float); + AddColorFormat(wgpu::TextureFormat::RG8Uint, true, false, 2, Type::Uint); + AddColorFormat(wgpu::TextureFormat::RG8Sint, true, false, 2, Type::Sint); + + // 4 bytes color formats + AddColorFormat(wgpu::TextureFormat::R32Uint, true, true, 4, Type::Uint); + AddColorFormat(wgpu::TextureFormat::R32Sint, true, true, 4, Type::Sint); + AddColorFormat(wgpu::TextureFormat::R32Float, true, true, 4, Type::Float); + AddColorFormat(wgpu::TextureFormat::RG16Uint, true, false, 4, Type::Uint); + AddColorFormat(wgpu::TextureFormat::RG16Sint, true, false, 4, Type::Sint); + AddColorFormat(wgpu::TextureFormat::RG16Float, true, false, 4, Type::Float); + AddColorFormat(wgpu::TextureFormat::RGBA8Unorm, true, true, 4, Type::Float); + AddColorFormat(wgpu::TextureFormat::RGBA8UnormSrgb, true, false, 4, Type::Float); + AddColorFormat(wgpu::TextureFormat::RGBA8Snorm, false, true, 4, Type::Float); + AddColorFormat(wgpu::TextureFormat::RGBA8Uint, true, true, 4, Type::Uint); + AddColorFormat(wgpu::TextureFormat::RGBA8Sint, true, true, 4, Type::Sint); + AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, false, 4, Type::Float); + AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, false, 4, Type::Float); + AddColorFormat(wgpu::TextureFormat::RGB10A2Unorm, true, false, 4, Type::Float); + + AddColorFormat(wgpu::TextureFormat::RG11B10Float, false, false, 4, Type::Float); + + // 8 bytes color formats + AddColorFormat(wgpu::TextureFormat::RG32Uint, true, true, 8, Type::Uint); + AddColorFormat(wgpu::TextureFormat::RG32Sint, true, true, 8, Type::Sint); + AddColorFormat(wgpu::TextureFormat::RG32Float, true, true, 8, Type::Float); + AddColorFormat(wgpu::TextureFormat::RGBA16Uint, true, true, 8, Type::Uint); + AddColorFormat(wgpu::TextureFormat::RGBA16Sint, true, true, 8, Type::Sint); + AddColorFormat(wgpu::TextureFormat::RGBA16Float, true, true, 8, Type::Float); + + // 16 bytes color formats + AddColorFormat(wgpu::TextureFormat::RGBA32Uint, true, true, 16, Type::Uint); + AddColorFormat(wgpu::TextureFormat::RGBA32Sint, true, true, 16, Type::Sint); + AddColorFormat(wgpu::TextureFormat::RGBA32Float, true, true, 16, Type::Float); + + // Depth only formats + AddDepthFormat(wgpu::TextureFormat::Depth32Float, 4, Type::Float); + + // Packed depth/depth-stencil formats + AddDepthStencilFormat(wgpu::TextureFormat::Depth24Plus, Aspect::Depth, 4); + // TODO(cwallez@chromium.org): It isn't clear if this format should be copyable + // because its size isn't well defined, is it 4, 5 or 8? + AddDepthStencilFormat(wgpu::TextureFormat::Depth24PlusStencil8, Aspect::DepthStencil, 4); + + // BC compressed formats + bool isBCFormatSupported = device->IsExtensionEnabled(Extension::TextureCompressionBC); + AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnorm, 8, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnormSrgb, 8, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC4RSnorm, 8, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC4RUnorm, 8, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnorm, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnorm, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC5RGSnorm, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC5RGUnorm, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC6HRGBSfloat, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC6HRGBUfloat, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnorm, 16, 4, 4, isBCFormatSupported); + AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported); + + // clang-format on + + // This checks that each format is set at least once, the second part of checking that all + // formats are checked exactly once. + ASSERT(formatsSet.all()); + + return table; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Format.h b/third_party/dawn/src/dawn_native/Format.h new file mode 100644 index 00000000000..82b40d81cce --- /dev/null +++ b/third_party/dawn/src/dawn_native/Format.h @@ -0,0 +1,86 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_FORMAT_H_ +#define DAWNNATIVE_FORMAT_H_ + +#include "dawn_native/dawn_platform.h" + +#include "dawn_native/Error.h" + +#include + +namespace dawn_native { + + class DeviceBase; + + // The number of formats Dawn knows about. Asserts in BuildFormatTable ensure that this is the + // exact number of known format. + static constexpr size_t kKnownFormatCount = 52; + + // A wgpu::TextureFormat along with all the information about it necessary for validation. + struct Format { + enum Aspect { + Color, + Depth, + Stencil, + DepthStencil, + }; + + enum Type { + Float, + Sint, + Uint, + Other, + }; + + wgpu::TextureFormat format; + bool isRenderable; + bool isCompressed; + // A format can be known but not supported because it is part of a disabled extension. + bool isSupported; + bool supportsStorageUsage; + Aspect aspect; + Type type; + + uint32_t blockByteSize; + uint32_t blockWidth; + uint32_t blockHeight; + + static Type TextureComponentTypeToFormatType(wgpu::TextureComponentType componentType); + static wgpu::TextureComponentType FormatTypeToTextureComponentType(Type type); + + bool IsColor() const; + bool HasDepth() const; + bool HasStencil() const; + bool HasDepthOrStencil() const; + bool HasComponentType(Type componentType) const; + + // The index of the format in the list of all known formats: a unique number for each format + // in [0, kKnownFormatCount) + size_t GetIndex() const; + }; + + // Implementation details of the format table in the device. + + using FormatTable = std::array; + + // Returns the index of a format in the FormatTable. + size_t ComputeFormatIndex(wgpu::TextureFormat format); + // Builds the format table with the extensions enabled on the device. + FormatTable BuildFormatTable(const DeviceBase* device); + +} // namespace dawn_native + +#endif // DAWNNATIVE_FORMAT_H_ diff --git a/third_party/dawn/src/dawn_native/Forward.h b/third_party/dawn/src/dawn_native/Forward.h index db539048abe..66e07e42ac6 100644 --- a/third_party/dawn/src/dawn_native/Forward.h +++ b/third_party/dawn/src/dawn_native/Forward.h @@ -17,6 +17,9 @@ #include +template +class Ref; + namespace dawn_native { class AdapterBase; @@ -25,29 +28,35 @@ namespace dawn_native { class BufferBase; class ComputePipelineBase; class CommandBufferBase; - class CommandEncoderBase; - class ComputePassEncoderBase; - class FenceBase; + class CommandEncoder; + class ComputePassEncoder; + class Fence; class InstanceBase; class PipelineBase; class PipelineLayoutBase; + class QuerySetBase; class QueueBase; - class RenderPassEncoderBase; + class RenderBundleBase; + class RenderBundleEncoder; + class RenderPassEncoder; class RenderPipelineBase; + class ResourceHeapBase; class SamplerBase; + class Surface; class ShaderModuleBase; class StagingBufferBase; class SwapChainBase; + class NewSwapChainBase; class TextureBase; class TextureViewBase; class DeviceBase; - template - class Ref; - template class PerStage; + + struct Format; + } // namespace dawn_native #endif // DAWNNATIVE_FORWARD_H_ diff --git a/third_party/dawn/src/dawn_native/Instance.cpp b/third_party/dawn/src/dawn_native/Instance.cpp index b5e2f896d75..58e6cffd24d 100644 --- a/third_party/dawn/src/dawn_native/Instance.cpp +++ b/third_party/dawn/src/dawn_native/Instance.cpp @@ -15,9 +15,9 @@ #include "dawn_native/Instance.h" #include "common/Assert.h" +#include "common/Log.h" #include "dawn_native/ErrorData.h" - -#include +#include "dawn_native/Surface.h" namespace dawn_native { @@ -45,52 +45,24 @@ namespace dawn_native { #endif // defined(DAWN_ENABLE_BACKEND_OPENGL) #if defined(DAWN_ENABLE_BACKEND_VULKAN) namespace vulkan { - BackendConnection* Connect(InstanceBase* instance); + BackendConnection* Connect(InstanceBase* instance, bool useSwiftshader); } #endif // defined(DAWN_ENABLE_BACKEND_VULKAN) - namespace { - - struct ToggleEnumAndInfo { - Toggle toggle; - ToggleInfo info; - }; + // InstanceBase - using ToggleEnumAndInfoList = - std::array(Toggle::EnumCount)>; - - static constexpr ToggleEnumAndInfoList kToggleNameAndInfoList = { - {{Toggle::EmulateStoreAndMSAAResolve, - {"emulate_store_and_msaa_resolve", - "Emulate storing into multisampled color attachments and doing MSAA resolve " - "simultaneously. This workaround is enabled by default on the Metal drivers that do " - "not support MTLStoreActionStoreAndMultisampleResolve. To support StoreOp::Store on " - "those platforms, we should do MSAA resolve in another render pass after ending the " - "previous one.", - "https://bugs.chromium.org/p/dawn/issues/detail?id=56"}}, - {Toggle::NonzeroClearResourcesOnCreationForTesting, - {"nonzero_clear_resources_on_creation_for_testing", - "Clears texture to full 1 bits as soon as they are created, but doesn't update " - "the tracking state of the texture. This way we can test the logic of clearing " - "textures that use recycled memory.", - "https://bugs.chromium.org/p/dawn/issues/detail?id=145"}}, - {Toggle::AlwaysResolveIntoZeroLevelAndLayer, - {"always_resolve_into_zero_level_and_layer", - "When the resolve target is a texture view that is created on the non-zero level or " - "layer of a texture, we first resolve into a temporarily 2D texture with only one " - "mipmap level and one array layer, and copy the result of MSAA resolve into the " - "true resolve target. This workaround is enabled by default on the Metal drivers " - "that have bugs when setting non-zero resolveLevel or resolveSlice.", - "https://bugs.chromium.org/p/dawn/issues/detail?id=56"}}, - {Toggle::LazyClearResourceOnFirstUse, - {"lazy_clear_resource_on_first_use", - "Clears resource to zero on first usage. This initializes the resource " - "so that no dirty bits from recycled memory is present in the new resource.", - "https://bugs.chromium.org/p/dawn/issues/detail?id=145"}}}}; - - } // anonymous namespace + // static + InstanceBase* InstanceBase::Create(const InstanceDescriptor* descriptor) { + Ref instance = AcquireRef(new InstanceBase); + if (!instance->Initialize(descriptor)) { + return nullptr; + } + return instance.Detach(); + } - // InstanceBase + bool InstanceBase::Initialize(const InstanceDescriptor*) { + return true; + } void InstanceBase::DiscoverDefaultAdapters() { EnsureBackendConnections(); @@ -119,51 +91,25 @@ namespace dawn_native { return !ConsumedError(DiscoverAdaptersInternal(options)); } - const char* InstanceBase::ToggleEnumToName(Toggle toggle) { - ASSERT(toggle != Toggle::InvalidEnum); - - const ToggleEnumAndInfo& toggleNameAndInfo = - kToggleNameAndInfoList[static_cast(toggle)]; - ASSERT(toggleNameAndInfo.toggle == toggle); - return toggleNameAndInfo.info.name; - } - const ToggleInfo* InstanceBase::GetToggleInfo(const char* toggleName) { - ASSERT(toggleName); - - EnsureToggleNameToEnumMapInitialized(); - - const auto& iter = mToggleNameToEnumMap.find(toggleName); - if (iter != mToggleNameToEnumMap.cend()) { - return &kToggleNameAndInfoList[static_cast(iter->second)].info; - } - return nullptr; + return mTogglesInfo.GetToggleInfo(toggleName); } Toggle InstanceBase::ToggleNameToEnum(const char* toggleName) { - ASSERT(toggleName); - - EnsureToggleNameToEnumMapInitialized(); - - const auto& iter = mToggleNameToEnumMap.find(toggleName); - if (iter != mToggleNameToEnumMap.cend()) { - return kToggleNameAndInfoList[static_cast(iter->second)].toggle; - } - return Toggle::InvalidEnum; + return mTogglesInfo.ToggleNameToEnum(toggleName); } - void InstanceBase::EnsureToggleNameToEnumMapInitialized() { - if (mToggleNameToEnumMapInitialized) { - return; - } + const ExtensionInfo* InstanceBase::GetExtensionInfo(const char* extensionName) { + return mExtensionsInfo.GetExtensionInfo(extensionName); + } - for (size_t index = 0; index < kToggleNameAndInfoList.size(); ++index) { - const ToggleEnumAndInfo& toggleNameAndInfo = kToggleNameAndInfoList[index]; - ASSERT(index == static_cast(toggleNameAndInfo.toggle)); - mToggleNameToEnumMap[toggleNameAndInfo.info.name] = toggleNameAndInfo.toggle; - } + Extension InstanceBase::ExtensionNameToEnum(const char* extensionName) { + return mExtensionsInfo.ExtensionNameToEnum(extensionName); + } - mToggleNameToEnumMapInitialized = true; + ExtensionsSet InstanceBase::ExtensionNamesToExtensionsSet( + const std::vector& requiredExtensions) { + return mExtensionsInfo.ExtensionNamesToExtensionsSet(requiredExtensions); } const std::vector>& InstanceBase::GetAdapters() const { @@ -175,7 +121,7 @@ namespace dawn_native { return; } - auto Register = [this](BackendConnection* connection, BackendType expectedType) { + auto Register = [this](BackendConnection* connection, wgpu::BackendType expectedType) { if (connection != nullptr) { ASSERT(connection->GetType() == expectedType); ASSERT(connection->GetInstance() == this); @@ -184,59 +130,64 @@ namespace dawn_native { }; #if defined(DAWN_ENABLE_BACKEND_D3D12) - Register(d3d12::Connect(this), BackendType::D3D12); + Register(d3d12::Connect(this), wgpu::BackendType::D3D12); #endif // defined(DAWN_ENABLE_BACKEND_D3D12) #if defined(DAWN_ENABLE_BACKEND_METAL) - Register(metal::Connect(this), BackendType::Metal); + Register(metal::Connect(this), wgpu::BackendType::Metal); #endif // defined(DAWN_ENABLE_BACKEND_METAL) #if defined(DAWN_ENABLE_BACKEND_VULKAN) - Register(vulkan::Connect(this), BackendType::Vulkan); -#endif // defined(DAWN_ENABLE_BACKEND_VULKAN) + // TODO(https://github.com/KhronosGroup/Vulkan-Loader/issues/287): + // When we can load SwiftShader in parallel with the system driver, we should create the + // backend only once and expose SwiftShader as an additional adapter. For now, we create two + // VkInstances, one from SwiftShader, and one from the system. Note: If the Vulkan driver + // *is* SwiftShader, then this would load SwiftShader twice. + Register(vulkan::Connect(this, false), wgpu::BackendType::Vulkan); +# if defined(DAWN_ENABLE_SWIFTSHADER) + Register(vulkan::Connect(this, true), wgpu::BackendType::Vulkan); +# endif // defined(DAWN_ENABLE_SWIFTSHADER) +#endif // defined(DAWN_ENABLE_BACKEND_VULKAN) #if defined(DAWN_ENABLE_BACKEND_OPENGL) - Register(opengl::Connect(this), BackendType::OpenGL); + Register(opengl::Connect(this), wgpu::BackendType::OpenGL); #endif // defined(DAWN_ENABLE_BACKEND_OPENGL) #if defined(DAWN_ENABLE_BACKEND_NULL) - Register(null::Connect(this), BackendType::Null); + Register(null::Connect(this), wgpu::BackendType::Null); #endif // defined(DAWN_ENABLE_BACKEND_NULL) mBackendsConnected = true; } - ResultOrError InstanceBase::FindBackend(BackendType type) { - for (std::unique_ptr& backend : mBackends) { - if (backend->GetType() == type) { - return backend.get(); - } - } - - return DAWN_VALIDATION_ERROR("Backend isn't present."); - } - MaybeError InstanceBase::DiscoverAdaptersInternal(const AdapterDiscoveryOptionsBase* options) { EnsureBackendConnections(); - BackendConnection* backend; - DAWN_TRY_ASSIGN(backend, FindBackend(options->backendType)); + bool foundBackend = false; + for (std::unique_ptr& backend : mBackends) { + if (backend->GetType() != static_cast(options->backendType)) { + continue; + } + foundBackend = true; - std::vector> newAdapters; - DAWN_TRY_ASSIGN(newAdapters, backend->DiscoverAdapters(options)); + std::vector> newAdapters; + DAWN_TRY_ASSIGN(newAdapters, backend->DiscoverAdapters(options)); - for (std::unique_ptr& adapter : newAdapters) { - ASSERT(adapter->GetBackendType() == backend->GetType()); - ASSERT(adapter->GetInstance() == this); - mAdapters.push_back(std::move(adapter)); + for (std::unique_ptr& adapter : newAdapters) { + ASSERT(adapter->GetBackendType() == backend->GetType()); + ASSERT(adapter->GetInstance() == this); + mAdapters.push_back(std::move(adapter)); + } } + if (!foundBackend) { + return DAWN_VALIDATION_ERROR("Backend isn't present."); + } return {}; } bool InstanceBase::ConsumedError(MaybeError maybeError) { if (maybeError.IsError()) { - ErrorData* error = maybeError.AcquireError(); + std::unique_ptr error = maybeError.AcquireError(); ASSERT(error != nullptr); - std::cout << error->GetMessage() << std::endl; - delete error; + dawn::InfoLog() << error->GetMessage(); return true; } @@ -247,8 +198,32 @@ namespace dawn_native { mEnableBackendValidation = enableBackendValidation; } - bool InstanceBase::IsBackendValidationEnabled() { + bool InstanceBase::IsBackendValidationEnabled() const { return mEnableBackendValidation; } + void InstanceBase::EnableBeginCaptureOnStartup(bool beginCaptureOnStartup) { + mBeginCaptureOnStartup = beginCaptureOnStartup; + } + + bool InstanceBase::IsBeginCaptureOnStartupEnabled() const { + return mBeginCaptureOnStartup; + } + + void InstanceBase::SetPlatform(dawn_platform::Platform* platform) { + mPlatform = platform; + } + + dawn_platform::Platform* InstanceBase::GetPlatform() const { + return mPlatform; + } + + Surface* InstanceBase::CreateSurface(const SurfaceDescriptor* descriptor) { + if (ConsumedError(ValidateSurfaceDescriptor(this, descriptor))) { + return nullptr; + } + + return new Surface(this, descriptor); + } + } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Instance.h b/third_party/dawn/src/dawn_native/Instance.h index fba7e01c71d..0ade98b4e60 100644 --- a/third_party/dawn/src/dawn_native/Instance.h +++ b/third_party/dawn/src/dawn_native/Instance.h @@ -15,9 +15,12 @@ #ifndef DAWNNATIVE_INSTANCE_H_ #define DAWNNATIVE_INSTANCE_H_ +#include "common/RefCounted.h" #include "dawn_native/Adapter.h" #include "dawn_native/BackendConnection.h" +#include "dawn_native/Extensions.h" #include "dawn_native/Toggles.h" +#include "dawn_native/dawn_platform.h" #include #include @@ -26,15 +29,13 @@ namespace dawn_native { + class Surface; + // This is called InstanceBase for consistency across the frontend, even if the backends don't // specialize this class. - class InstanceBase final { + class InstanceBase final : public RefCounted { public: - InstanceBase() = default; - ~InstanceBase() = default; - - InstanceBase(const InstanceBase& other) = delete; - InstanceBase& operator=(const InstanceBase& other) = delete; + static InstanceBase* Create(const InstanceDescriptor* descriptor = nullptr); void DiscoverDefaultAdapters(); bool DiscoverAdapters(const AdapterDiscoveryOptionsBase* options); @@ -47,34 +48,54 @@ namespace dawn_native { // Used to query the details of a toggle. Return nullptr if toggleName is not a valid name // of a toggle supported in Dawn. const ToggleInfo* GetToggleInfo(const char* toggleName); - Toggle ToggleNameToEnum(const char* toggleName); - const char* ToggleEnumToName(Toggle toggle); + + // Used to query the details of an extension. Return nullptr if extensionName is not a valid + // name of an extension supported in Dawn. + const ExtensionInfo* GetExtensionInfo(const char* extensionName); + Extension ExtensionNameToEnum(const char* extensionName); + ExtensionsSet ExtensionNamesToExtensionsSet( + const std::vector& requiredExtensions); void EnableBackendValidation(bool enableBackendValidation); - bool IsBackendValidationEnabled(); + bool IsBackendValidationEnabled() const; + + void EnableBeginCaptureOnStartup(bool beginCaptureOnStartup); + bool IsBeginCaptureOnStartupEnabled() const; + + void SetPlatform(dawn_platform::Platform* platform); + dawn_platform::Platform* GetPlatform() const; + + // Dawn API + Surface* CreateSurface(const SurfaceDescriptor* descriptor); private: + InstanceBase() = default; + ~InstanceBase() = default; + + InstanceBase(const InstanceBase& other) = delete; + InstanceBase& operator=(const InstanceBase& other) = delete; + + bool Initialize(const InstanceDescriptor* descriptor); + // Lazily creates connections to all backends that have been compiled. void EnsureBackendConnections(); - // Finds the BackendConnection for `type` or returns an error. - ResultOrError FindBackend(BackendType type); - MaybeError DiscoverAdaptersInternal(const AdapterDiscoveryOptionsBase* options); - void EnsureToggleNameToEnumMapInitialized(); - bool mBackendsConnected = false; bool mDiscoveredDefaultAdapters = false; - bool mToggleNameToEnumMapInitialized = false; bool mEnableBackendValidation = false; + bool mBeginCaptureOnStartup = false; + + dawn_platform::Platform* mPlatform = nullptr; std::vector> mBackends; std::vector> mAdapters; - std::unordered_map mToggleNameToEnumMap; + ExtensionsInfo mExtensionsInfo; + TogglesInfo mTogglesInfo; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/MapRequestTracker.cpp b/third_party/dawn/src/dawn_native/MapRequestTracker.cpp new file mode 100644 index 00000000000..91af10bcece --- /dev/null +++ b/third_party/dawn/src/dawn_native/MapRequestTracker.cpp @@ -0,0 +1,46 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/MapRequestTracker.h" +#include "dawn_native/Buffer.h" +#include "dawn_native/Device.h" + +namespace dawn_native { + struct Request; + class DeviceBase; + + MapRequestTracker::MapRequestTracker(DeviceBase* device) : mDevice(device) { + } + + MapRequestTracker::~MapRequestTracker() { + ASSERT(mInflightRequests.Empty()); + } + + void MapRequestTracker::Track(BufferBase* buffer, uint32_t mapSerial, MapType type) { + Request request; + request.buffer = buffer; + request.mapSerial = mapSerial; + request.type = type; + + mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial()); + mDevice->AddFutureCallbackSerial(mDevice->GetPendingCommandSerial()); + } + + void MapRequestTracker::Tick(Serial finishedSerial) { + for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) { + request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.type); + } + mInflightRequests.ClearUpTo(finishedSerial); + } +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/MapRequestTracker.h b/third_party/dawn/src/dawn_native/MapRequestTracker.h new file mode 100644 index 00000000000..1daf47dad92 --- /dev/null +++ b/third_party/dawn/src/dawn_native/MapRequestTracker.h @@ -0,0 +1,47 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_MAPREQUESTTRACKER_H_ +#define DAWNNATIVE_MAPREQUESTTRACKER_H_ + +#include "common/SerialQueue.h" +#include "dawn_native/Device.h" + +namespace dawn_native { + + // TODO(dawn:22) remove this enum once MapReadAsync/MapWriteAsync are removed. + enum class MapType : uint32_t { Read, Write, Async }; + + class MapRequestTracker { + public: + MapRequestTracker(DeviceBase* device); + ~MapRequestTracker(); + + void Track(BufferBase* buffer, uint32_t mapSerial, MapType type); + void Tick(Serial finishedSerial); + + private: + DeviceBase* mDevice; + + struct Request { + Ref buffer; + uint32_t mapSerial; + MapType type; + }; + SerialQueue mInflightRequests; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_MAPREQUESTTRACKER_H diff --git a/third_party/dawn/src/dawn_native/ObjectBase.cpp b/third_party/dawn/src/dawn_native/ObjectBase.cpp index 3e40af4de69..8b4731f338d 100644 --- a/third_party/dawn/src/dawn_native/ObjectBase.cpp +++ b/third_party/dawn/src/dawn_native/ObjectBase.cpp @@ -16,13 +16,14 @@ namespace dawn_native { - ObjectBase::ObjectBase(DeviceBase* device) : mDevice(device), mIsError(false) { - } + static constexpr uint64_t kErrorPayload = 0; + static constexpr uint64_t kNotErrorPayload = 1; - ObjectBase::ObjectBase(DeviceBase* device, ErrorTag) : mDevice(device), mIsError(true) { + ObjectBase::ObjectBase(DeviceBase* device) : RefCounted(kNotErrorPayload), mDevice(device) { } - ObjectBase::~ObjectBase() { + ObjectBase::ObjectBase(DeviceBase* device, ErrorTag) + : RefCounted(kErrorPayload), mDevice(device) { } DeviceBase* ObjectBase::GetDevice() const { @@ -30,7 +31,7 @@ namespace dawn_native { } bool ObjectBase::IsError() const { - return mIsError; + return GetRefCountPayload() == kErrorPayload; } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ObjectBase.h b/third_party/dawn/src/dawn_native/ObjectBase.h index 02dd7ec6ba1..544ce1a4bb6 100644 --- a/third_party/dawn/src/dawn_native/ObjectBase.h +++ b/third_party/dawn/src/dawn_native/ObjectBase.h @@ -15,7 +15,7 @@ #ifndef DAWNNATIVE_OBJECTBASE_H_ #define DAWNNATIVE_OBJECTBASE_H_ -#include "dawn_native/RefCounted.h" +#include "common/RefCounted.h" namespace dawn_native { @@ -28,17 +28,15 @@ namespace dawn_native { ObjectBase(DeviceBase* device); ObjectBase(DeviceBase* device, ErrorTag tag); - virtual ~ObjectBase(); DeviceBase* GetDevice() const; bool IsError() const; + protected: + ~ObjectBase() override = default; + private: DeviceBase* mDevice; - // TODO(cwallez@chromium.org): This most likely adds 4 bytes to most Dawn objects, see if - // that bit can be hidden in the refcount once it is a single 64bit refcount. - // See https://bugs.chromium.org/p/dawn/issues/detail?id=105 - bool mIsError; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/PassResourceUsage.h b/third_party/dawn/src/dawn_native/PassResourceUsage.h index 9a8c2b07db4..60dcd219a6d 100644 --- a/third_party/dawn/src/dawn_native/PassResourceUsage.h +++ b/third_party/dawn/src/dawn_native/PassResourceUsage.h @@ -23,23 +23,53 @@ namespace dawn_native { class BufferBase; + class QuerySetBase; class TextureBase; + enum class PassType { Render, Compute }; + + // Describe the usage of the whole texture and its subresources. + // - subresourceUsages vector is used to track every subresource's usage within a texture. + // + // - usage variable is used the track the whole texture even though it can be deduced from + // subresources' usages. This is designed deliberately to track texture usage in a fast path + // at frontend. + // + // - sameUsagesAcrossSubresources is used for optimization at backend. If the texture view + // we are using covers all subresources, then the texture's usages of all subresources are + // the same. Otherwise the texture's usages of all subresources are thought as different, + // although we can deliberately design some particular cases in which we have a few texture + // views and all of them have the same usages and they cover all subresources of the texture + // altogether. + + // TODO(yunchao.he@intel.com): if sameUsagesAcrossSubresources is true, we don't need + // the vector to record every single subresource's Usages. The texture usage is enough. And we + // can decompress texture usage to a vector if necessary. + struct PassTextureUsage { + wgpu::TextureUsage usage = wgpu::TextureUsage::None; + bool sameUsagesAcrossSubresources = true; + std::vector subresourceUsages; + }; + // Which resources are used by pass and how they are used. The command buffer validation // pre-computes this information so that backends with explicit barriers don't have to // re-compute it. struct PassResourceUsage { + PassType passType; std::vector buffers; - std::vector bufferUsages; + std::vector bufferUsages; std::vector textures; - std::vector textureUsages; + std::vector textureUsages; }; + using PerPassUsages = std::vector; + struct CommandBufferResourceUsage { - std::vector perPass; + PerPassUsages perPass; std::set topLevelBuffers; std::set topLevelTextures; + std::set usedQuerySets; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/PassResourceUsageTracker.cpp b/third_party/dawn/src/dawn_native/PassResourceUsageTracker.cpp new file mode 100644 index 00000000000..bd176dfcb24 --- /dev/null +++ b/third_party/dawn/src/dawn_native/PassResourceUsageTracker.cpp @@ -0,0 +1,105 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/PassResourceUsageTracker.h" + +#include "dawn_native/Buffer.h" +#include "dawn_native/Texture.h" + +namespace dawn_native { + PassResourceUsageTracker::PassResourceUsageTracker(PassType passType) : mPassType(passType) { + } + + void PassResourceUsageTracker::BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage) { + // std::map's operator[] will create the key and return 0 if the key didn't exist + // before. + mBufferUsages[buffer] |= usage; + } + + void PassResourceUsageTracker::TextureViewUsedAs(TextureViewBase* view, + wgpu::TextureUsage usage) { + TextureBase* texture = view->GetTexture(); + uint32_t baseMipLevel = view->GetBaseMipLevel(); + uint32_t levelCount = view->GetLevelCount(); + uint32_t baseArrayLayer = view->GetBaseArrayLayer(); + uint32_t layerCount = view->GetLayerCount(); + + // std::map's operator[] will create the key and return a PassTextureUsage with usage = 0 + // and an empty vector for subresourceUsages. + // TODO (yunchao.he@intel.com): optimize this + PassTextureUsage& textureUsage = mTextureUsages[texture]; + + // Set parameters for the whole texture + textureUsage.usage |= usage; + uint32_t subresourceCount = texture->GetSubresourceCount(); + textureUsage.sameUsagesAcrossSubresources &= levelCount * layerCount == subresourceCount; + + // Set usages for subresources + if (!textureUsage.subresourceUsages.size()) { + textureUsage.subresourceUsages = + std::vector(subresourceCount, wgpu::TextureUsage::None); + } + for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount; + ++arrayLayer) { + for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; + ++mipLevel) { + uint32_t subresourceIndex = texture->GetSubresourceIndex(mipLevel, arrayLayer); + textureUsage.subresourceUsages[subresourceIndex] |= usage; + } + } + } + + void PassResourceUsageTracker::AddTextureUsage(TextureBase* texture, + const PassTextureUsage& textureUsage) { + PassTextureUsage& passTextureUsage = mTextureUsages[texture]; + passTextureUsage.usage |= textureUsage.usage; + passTextureUsage.sameUsagesAcrossSubresources &= textureUsage.sameUsagesAcrossSubresources; + + uint32_t subresourceCount = texture->GetSubresourceCount(); + ASSERT(textureUsage.subresourceUsages.size() == subresourceCount); + if (!passTextureUsage.subresourceUsages.size()) { + passTextureUsage.subresourceUsages = textureUsage.subresourceUsages; + return; + } + for (uint32_t i = 0; i < subresourceCount; ++i) { + passTextureUsage.subresourceUsages[i] |= textureUsage.subresourceUsages[i]; + } + } + + // Returns the per-pass usage for use by backends for APIs with explicit barriers. + PassResourceUsage PassResourceUsageTracker::AcquireResourceUsage() { + PassResourceUsage result; + result.passType = mPassType; + result.buffers.reserve(mBufferUsages.size()); + result.bufferUsages.reserve(mBufferUsages.size()); + result.textures.reserve(mTextureUsages.size()); + result.textureUsages.reserve(mTextureUsages.size()); + + for (auto& it : mBufferUsages) { + result.buffers.push_back(it.first); + result.bufferUsages.push_back(it.second); + } + + for (auto& it : mTextureUsages) { + result.textures.push_back(it.first); + result.textureUsages.push_back(std::move(it.second)); + } + + mBufferUsages.clear(); + mTextureUsages.clear(); + + return result; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/PassResourceUsageTracker.h b/third_party/dawn/src/dawn_native/PassResourceUsageTracker.h new file mode 100644 index 00000000000..cfcaa225e12 --- /dev/null +++ b/third_party/dawn/src/dawn_native/PassResourceUsageTracker.h @@ -0,0 +1,51 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_ +#define DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_ + +#include "dawn_native/PassResourceUsage.h" + +#include "dawn_native/dawn_platform.h" + +#include + +namespace dawn_native { + + class BufferBase; + class TextureBase; + + // Helper class to encapsulate the logic of tracking per-resource usage during the + // validation of command buffer passes. It is used both to know if there are validation + // errors, and to get a list of resources used per pass for backends that need the + // information. + class PassResourceUsageTracker { + public: + PassResourceUsageTracker(PassType passType); + void BufferUsedAs(BufferBase* buffer, wgpu::BufferUsage usage); + void TextureViewUsedAs(TextureViewBase* texture, wgpu::TextureUsage usage); + void AddTextureUsage(TextureBase* texture, const PassTextureUsage& textureUsage); + + // Returns the per-pass usage for use by backends for APIs with explicit barriers. + PassResourceUsage AcquireResourceUsage(); + + private: + PassType mPassType; + std::map mBufferUsages; + std::map mTextureUsages; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_PASSRESOURCEUSAGETRACKER_H_ diff --git a/third_party/dawn/src/dawn_native/PerStage.cpp b/third_party/dawn/src/dawn_native/PerStage.cpp index b29ede1c33b..198d99dbdc6 100644 --- a/third_party/dawn/src/dawn_native/PerStage.cpp +++ b/third_party/dawn/src/dawn_native/PerStage.cpp @@ -16,14 +16,14 @@ namespace dawn_native { - BitSetIterator IterateStages(dawn::ShaderStageBit stages) { + BitSetIterator IterateStages(wgpu::ShaderStage stages) { std::bitset bits(static_cast(stages)); - return BitSetIterator(bits); + return BitSetIterator(bits); } - dawn::ShaderStageBit StageBit(dawn::ShaderStage stage) { + wgpu::ShaderStage StageBit(SingleShaderStage stage) { ASSERT(static_cast(stage) < kNumStages); - return static_cast(1 << static_cast(stage)); + return static_cast(1 << static_cast(stage)); } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/PerStage.h b/third_party/dawn/src/dawn_native/PerStage.h index b1b67af5fa2..be9f4c459dd 100644 --- a/third_party/dawn/src/dawn_native/PerStage.h +++ b/third_party/dawn/src/dawn_native/PerStage.h @@ -25,25 +25,27 @@ namespace dawn_native { - static_assert(static_cast(dawn::ShaderStage::Vertex) < kNumStages, ""); - static_assert(static_cast(dawn::ShaderStage::Fragment) < kNumStages, ""); - static_assert(static_cast(dawn::ShaderStage::Compute) < kNumStages, ""); + enum class SingleShaderStage { Vertex, Fragment, Compute }; - static_assert(static_cast(dawn::ShaderStageBit::Vertex) == - (1 << static_cast(dawn::ShaderStage::Vertex)), + static_assert(static_cast(SingleShaderStage::Vertex) < kNumStages, ""); + static_assert(static_cast(SingleShaderStage::Fragment) < kNumStages, ""); + static_assert(static_cast(SingleShaderStage::Compute) < kNumStages, ""); + + static_assert(static_cast(wgpu::ShaderStage::Vertex) == + (1 << static_cast(SingleShaderStage::Vertex)), ""); - static_assert(static_cast(dawn::ShaderStageBit::Fragment) == - (1 << static_cast(dawn::ShaderStage::Fragment)), + static_assert(static_cast(wgpu::ShaderStage::Fragment) == + (1 << static_cast(SingleShaderStage::Fragment)), ""); - static_assert(static_cast(dawn::ShaderStageBit::Compute) == - (1 << static_cast(dawn::ShaderStage::Compute)), + static_assert(static_cast(wgpu::ShaderStage::Compute) == + (1 << static_cast(SingleShaderStage::Compute)), ""); - BitSetIterator IterateStages(dawn::ShaderStageBit stages); - dawn::ShaderStageBit StageBit(dawn::ShaderStage stage); + BitSetIterator IterateStages(wgpu::ShaderStage stages); + wgpu::ShaderStage StageBit(SingleShaderStage stage); - static constexpr dawn::ShaderStageBit kAllStages = - static_cast((1 << kNumStages) - 1); + static constexpr wgpu::ShaderStage kAllStages = + static_cast((1 << kNumStages) - 1); template class PerStage { @@ -53,21 +55,21 @@ namespace dawn_native { mData.fill(initialValue); } - T& operator[](dawn::ShaderStage stage) { + T& operator[](SingleShaderStage stage) { DAWN_ASSERT(static_cast(stage) < kNumStages); return mData[static_cast(stage)]; } - const T& operator[](dawn::ShaderStage stage) const { + const T& operator[](SingleShaderStage stage) const { DAWN_ASSERT(static_cast(stage) < kNumStages); return mData[static_cast(stage)]; } - T& operator[](dawn::ShaderStageBit stageBit) { + T& operator[](wgpu::ShaderStage stageBit) { uint32_t bit = static_cast(stageBit); DAWN_ASSERT(bit != 0 && IsPowerOfTwo(bit) && bit <= (1 << kNumStages)); return mData[Log2(bit)]; } - const T& operator[](dawn::ShaderStageBit stageBit) const { + const T& operator[](wgpu::ShaderStage stageBit) const { uint32_t bit = static_cast(stageBit); DAWN_ASSERT(bit != 0 && IsPowerOfTwo(bit) && bit <= (1 << kNumStages)); return mData[Log2(bit)]; diff --git a/third_party/dawn/src/dawn_native/Pipeline.cpp b/third_party/dawn/src/dawn_native/Pipeline.cpp index bc79cf06db3..ae05c02f4b4 100644 --- a/third_party/dawn/src/dawn_native/Pipeline.cpp +++ b/third_party/dawn/src/dawn_native/Pipeline.cpp @@ -14,16 +14,17 @@ #include "dawn_native/Pipeline.h" +#include "dawn_native/BindGroupLayout.h" #include "dawn_native/Device.h" #include "dawn_native/PipelineLayout.h" #include "dawn_native/ShaderModule.h" namespace dawn_native { - MaybeError ValidatePipelineStageDescriptor(DeviceBase* device, - const PipelineStageDescriptor* descriptor, - const PipelineLayoutBase* layout, - dawn::ShaderStage stage) { + MaybeError ValidateProgrammableStageDescriptor(const DeviceBase* device, + const ProgrammableStageDescriptor* descriptor, + const PipelineLayoutBase* layout, + SingleShaderStage stage) { DAWN_TRY(device->ValidateObject(descriptor->module)); if (descriptor->entryPoint != std::string("main")) { @@ -32,8 +33,8 @@ namespace dawn_native { if (descriptor->module->GetExecutionModel() != stage) { return DAWN_VALIDATION_ERROR("Setting module with wrong stages"); } - if (!descriptor->module->IsCompatibleWithPipelineLayout(layout)) { - return DAWN_VALIDATION_ERROR("Stage not compatible with layout"); + if (layout != nullptr) { + DAWN_TRY(descriptor->module->ValidateCompatibilityWithPipelineLayout(layout)); } return {}; } @@ -42,15 +43,19 @@ namespace dawn_native { PipelineBase::PipelineBase(DeviceBase* device, PipelineLayoutBase* layout, - dawn::ShaderStageBit stages) - : ObjectBase(device), mStageMask(stages), mLayout(layout) { + wgpu::ShaderStage stages, + RequiredBufferSizes minimumBufferSizes) + : CachedObject(device), + mStageMask(stages), + mLayout(layout), + mMinimumBufferSizes(std::move(minimumBufferSizes)) { } PipelineBase::PipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag) { + : CachedObject(device, tag) { } - dawn::ShaderStageBit PipelineBase::GetStageMask() const { + wgpu::ShaderStage PipelineBase::GetStageMask() const { ASSERT(!IsError()); return mStageMask; } @@ -65,4 +70,36 @@ namespace dawn_native { return mLayout.Get(); } + const RequiredBufferSizes& PipelineBase::GetMinimumBufferSizes() const { + ASSERT(!IsError()); + return mMinimumBufferSizes; + } + + MaybeError PipelineBase::ValidateGetBindGroupLayout(uint32_t groupIndex) { + DAWN_TRY(GetDevice()->ValidateIsAlive()); + DAWN_TRY(GetDevice()->ValidateObject(this)); + DAWN_TRY(GetDevice()->ValidateObject(mLayout.Get())); + if (groupIndex >= kMaxBindGroups) { + return DAWN_VALIDATION_ERROR("Bind group layout index out of bounds"); + } + return {}; + } + + BindGroupLayoutBase* PipelineBase::GetBindGroupLayout(uint32_t groupIndexIn) { + if (GetDevice()->ConsumedError(ValidateGetBindGroupLayout(groupIndexIn))) { + return BindGroupLayoutBase::MakeError(GetDevice()); + } + + BindGroupIndex groupIndex(groupIndexIn); + + BindGroupLayoutBase* bgl = nullptr; + if (!mLayout->GetBindGroupLayoutsMask()[groupIndex]) { + bgl = GetDevice()->GetEmptyBindGroupLayout(); + } else { + bgl = mLayout->GetBindGroupLayout(groupIndex); + } + bgl->Reference(); + return bgl; + } + } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Pipeline.h b/third_party/dawn/src/dawn_native/Pipeline.h index ceacdf11aeb..bfc846bcde9 100644 --- a/third_party/dawn/src/dawn_native/Pipeline.h +++ b/third_party/dawn/src/dawn_native/Pipeline.h @@ -15,8 +15,8 @@ #ifndef DAWNNATIVE_PIPELINE_H_ #define DAWNNATIVE_PIPELINE_H_ +#include "dawn_native/CachedObject.h" #include "dawn_native/Forward.h" -#include "dawn_native/ObjectBase.h" #include "dawn_native/PerStage.h" #include "dawn_native/PipelineLayout.h" #include "dawn_native/ShaderModule.h" @@ -28,24 +28,32 @@ namespace dawn_native { - MaybeError ValidatePipelineStageDescriptor(DeviceBase* device, - const PipelineStageDescriptor* descriptor, - const PipelineLayoutBase* layout, - dawn::ShaderStage stage); + MaybeError ValidateProgrammableStageDescriptor(const DeviceBase* device, + const ProgrammableStageDescriptor* descriptor, + const PipelineLayoutBase* layout, + SingleShaderStage stage); - class PipelineBase : public ObjectBase { + class PipelineBase : public CachedObject { public: - dawn::ShaderStageBit GetStageMask() const; + wgpu::ShaderStage GetStageMask() const; PipelineLayoutBase* GetLayout(); const PipelineLayoutBase* GetLayout() const; + BindGroupLayoutBase* GetBindGroupLayout(uint32_t groupIndex); + const RequiredBufferSizes& GetMinimumBufferSizes() const; protected: - PipelineBase(DeviceBase* device, PipelineLayoutBase* layout, dawn::ShaderStageBit stages); + PipelineBase(DeviceBase* device, + PipelineLayoutBase* layout, + wgpu::ShaderStage stages, + RequiredBufferSizes bufferSizes); PipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag); private: - dawn::ShaderStageBit mStageMask; + MaybeError ValidateGetBindGroupLayout(uint32_t group); + + wgpu::ShaderStage mStageMask; Ref mLayout; + RequiredBufferSizes mMinimumBufferSizes; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/PipelineLayout.cpp b/third_party/dawn/src/dawn_native/PipelineLayout.cpp index ee3b89ae26b..f34003a5b7a 100644 --- a/third_party/dawn/src/dawn_native/PipelineLayout.cpp +++ b/third_party/dawn/src/dawn_native/PipelineLayout.cpp @@ -17,11 +17,50 @@ #include "common/Assert.h" #include "common/BitSetIterator.h" #include "common/HashUtils.h" +#include "common/ityp_stack_vec.h" #include "dawn_native/BindGroupLayout.h" #include "dawn_native/Device.h" +#include "dawn_native/ShaderModule.h" namespace dawn_native { + namespace { + + bool InferredBindGroupLayoutEntriesCompatible(const BindGroupLayoutEntry& lhs, + const BindGroupLayoutEntry& rhs) { + // Minimum buffer binding size excluded because we take the maximum seen across stages + return lhs.binding == rhs.binding && lhs.visibility == rhs.visibility && + lhs.type == rhs.type && lhs.hasDynamicOffset == rhs.hasDynamicOffset && + lhs.multisampled == rhs.multisampled && lhs.viewDimension == rhs.viewDimension && + lhs.textureComponentType == rhs.textureComponentType; + } + + wgpu::ShaderStage GetShaderStageVisibilityWithBindingType(wgpu::BindingType bindingType) { + // TODO(jiawei.shao@intel.com): support read-only and read-write storage textures. + switch (bindingType) { + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::WriteonlyStorageTexture: + return wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute; + + case wgpu::BindingType::StorageTexture: + UNREACHABLE(); + return wgpu::ShaderStage::None; + + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + return wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment | + wgpu::ShaderStage::Compute; + } + + return {}; + } + + } // anonymous namespace + MaybeError ValidatePipelineLayoutDescriptor(DeviceBase* device, const PipelineLayoutDescriptor* descriptor) { if (descriptor->nextInChain != nullptr) { @@ -32,32 +71,37 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("too many bind group layouts"); } + BindingCounts bindingCounts = {}; for (uint32_t i = 0; i < descriptor->bindGroupLayoutCount; ++i) { DAWN_TRY(device->ValidateObject(descriptor->bindGroupLayouts[i])); + AccumulateBindingCounts(&bindingCounts, + descriptor->bindGroupLayouts[i]->GetBindingCountInfo()); } + + DAWN_TRY(ValidateBindingCounts(bindingCounts)); return {}; } // PipelineLayoutBase PipelineLayoutBase::PipelineLayoutBase(DeviceBase* device, - const PipelineLayoutDescriptor* descriptor, - bool blueprint) - : ObjectBase(device), mIsBlueprint(blueprint) { + const PipelineLayoutDescriptor* descriptor) + : CachedObject(device) { ASSERT(descriptor->bindGroupLayoutCount <= kMaxBindGroups); - for (uint32_t group = 0; group < descriptor->bindGroupLayoutCount; ++group) { - mBindGroupLayouts[group] = descriptor->bindGroupLayouts[group]; + for (BindGroupIndex group(0); group < BindGroupIndex(descriptor->bindGroupLayoutCount); + ++group) { + mBindGroupLayouts[group] = descriptor->bindGroupLayouts[static_cast(group)]; mMask.set(group); } } PipelineLayoutBase::PipelineLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag) { + : CachedObject(device, tag) { } PipelineLayoutBase::~PipelineLayoutBase() { // Do not uncache the actual cached object if we are a blueprint - if (!mIsBlueprint && !IsError()) { + if (IsCachedReference()) { GetDevice()->UncachePipelineLayout(this); } } @@ -67,39 +111,187 @@ namespace dawn_native { return new PipelineLayoutBase(device, ObjectBase::kError); } - const BindGroupLayoutBase* PipelineLayoutBase::GetBindGroupLayout(size_t group) const { + // static + ResultOrError PipelineLayoutBase::CreateDefault( + DeviceBase* device, + const ShaderModuleBase* const* modules, + uint32_t count) { + ASSERT(count > 0); + + // Data which BindGroupLayoutDescriptor will point to for creation + ityp::array< + BindGroupIndex, + ityp::stack_vec, + kMaxBindGroups> + entryData = {}; + + // A map of bindings to the index in |entryData| + ityp::array, kMaxBindGroups> + usedBindingsMap = {}; + + // A counter of how many bindings we've populated in |entryData| + ityp::array entryCounts = {}; + + BindingCounts bindingCounts = {}; + BindGroupIndex bindGroupLayoutCount(0); + for (uint32_t moduleIndex = 0; moduleIndex < count; ++moduleIndex) { + const ShaderModuleBase* module = modules[moduleIndex]; + const ShaderModuleBase::ModuleBindingInfo& info = module->GetBindingInfo(); + + for (BindGroupIndex group(0); group < info.size(); ++group) { + for (const auto& it : info[group]) { + BindingNumber bindingNumber = it.first; + const ShaderModuleBase::ShaderBindingInfo& bindingInfo = it.second; + + BindGroupLayoutEntry bindingSlot; + bindingSlot.binding = static_cast(bindingNumber); + + DAWN_TRY(ValidateBindingTypeWithShaderStageVisibility( + bindingInfo.type, StageBit(module->GetExecutionModel()))); + DAWN_TRY(ValidateStorageTextureFormat(device, bindingInfo.type, + bindingInfo.storageTextureFormat)); + DAWN_TRY(ValidateStorageTextureViewDimension(bindingInfo.type, + bindingInfo.viewDimension)); + + if (bindingInfo.multisampled) { + DAWN_TRY(ValidateBindingCanBeMultisampled(bindingInfo.type, + bindingInfo.viewDimension)); + } + + bindingSlot.visibility = + GetShaderStageVisibilityWithBindingType(bindingInfo.type); + + bindingSlot.type = bindingInfo.type; + bindingSlot.hasDynamicOffset = false; + bindingSlot.multisampled = bindingInfo.multisampled; + bindingSlot.viewDimension = bindingInfo.viewDimension; + bindingSlot.textureComponentType = + Format::FormatTypeToTextureComponentType(bindingInfo.textureComponentType); + bindingSlot.storageTextureFormat = bindingInfo.storageTextureFormat; + bindingSlot.minBufferBindingSize = bindingInfo.minBufferBindingSize; + + { + const auto& it = usedBindingsMap[group].find(bindingNumber); + if (it != usedBindingsMap[group].end()) { + BindGroupLayoutEntry* existingEntry = &entryData[group][it->second]; + + // Check if any properties are incompatible with existing entry + // If compatible, we will merge some properties + if (!InferredBindGroupLayoutEntriesCompatible(*existingEntry, + bindingSlot)) { + return DAWN_VALIDATION_ERROR( + "Duplicate binding in default pipeline layout initialization " + "not compatible with previous declaration"); + } + + // Use the max |minBufferBindingSize| we find + existingEntry->minBufferBindingSize = + std::max(existingEntry->minBufferBindingSize, + bindingSlot.minBufferBindingSize); + + // Already used slot, continue + continue; + } + } + + IncrementBindingCounts(&bindingCounts, bindingSlot); + BindingIndex currentBindingCount = entryCounts[group]; + entryData[group].resize(currentBindingCount + BindingIndex(1)); + entryData[group][currentBindingCount] = bindingSlot; + + usedBindingsMap[group][bindingNumber] = currentBindingCount; + + entryCounts[group]++; + + bindGroupLayoutCount = + std::max(bindGroupLayoutCount, group + BindGroupIndex(1)); + } + } + } + + DAWN_TRY(ValidateBindingCounts(bindingCounts)); + + ityp::array bindGroupLayouts = {}; + for (BindGroupIndex group(0); group < bindGroupLayoutCount; ++group) { + BindGroupLayoutDescriptor desc = {}; + desc.entries = entryData[group].data(); + desc.entryCount = static_cast(entryCounts[group]); + + // We should never produce a bad descriptor. + ASSERT(!ValidateBindGroupLayoutDescriptor(device, &desc).IsError()); + + Ref bgl; + DAWN_TRY_ASSIGN(bgl, device->GetOrCreateBindGroupLayout(&desc)); + bindGroupLayouts[group] = bgl.Detach(); + } + + PipelineLayoutDescriptor desc = {}; + desc.bindGroupLayouts = bindGroupLayouts.data(); + desc.bindGroupLayoutCount = static_cast(bindGroupLayoutCount); + PipelineLayoutBase* pipelineLayout = device->CreatePipelineLayout(&desc); + ASSERT(!pipelineLayout->IsError()); + + // These bind group layouts are created internally and referenced by the pipeline layout. + // Release the external refcount. + for (BindGroupIndex group(0); group < bindGroupLayoutCount; ++group) { + if (bindGroupLayouts[group] != nullptr) { + bindGroupLayouts[group]->Release(); + } + } + + for (uint32_t moduleIndex = 0; moduleIndex < count; ++moduleIndex) { + ASSERT(modules[moduleIndex] + ->ValidateCompatibilityWithPipelineLayout(pipelineLayout) + .IsSuccess()); + } + + return pipelineLayout; + } + + const BindGroupLayoutBase* PipelineLayoutBase::GetBindGroupLayout(BindGroupIndex group) const { + ASSERT(!IsError()); + ASSERT(group < kMaxBindGroupsTyped); + ASSERT(mMask[group]); + const BindGroupLayoutBase* bgl = mBindGroupLayouts[group].Get(); + ASSERT(bgl != nullptr); + return bgl; + } + + BindGroupLayoutBase* PipelineLayoutBase::GetBindGroupLayout(BindGroupIndex group) { ASSERT(!IsError()); - ASSERT(group < kMaxBindGroups); + ASSERT(group < kMaxBindGroupsTyped); ASSERT(mMask[group]); - return mBindGroupLayouts[group].Get(); + BindGroupLayoutBase* bgl = mBindGroupLayouts[group].Get(); + ASSERT(bgl != nullptr); + return bgl; } - const std::bitset PipelineLayoutBase::GetBindGroupLayoutsMask() const { + const BindGroupLayoutMask& PipelineLayoutBase::GetBindGroupLayoutsMask() const { ASSERT(!IsError()); return mMask; } - std::bitset PipelineLayoutBase::InheritedGroupsMask( + BindGroupLayoutMask PipelineLayoutBase::InheritedGroupsMask( const PipelineLayoutBase* other) const { ASSERT(!IsError()); - return {(1 << GroupsInheritUpTo(other)) - 1u}; + return {(1 << static_cast(GroupsInheritUpTo(other))) - 1u}; } - uint32_t PipelineLayoutBase::GroupsInheritUpTo(const PipelineLayoutBase* other) const { + BindGroupIndex PipelineLayoutBase::GroupsInheritUpTo(const PipelineLayoutBase* other) const { ASSERT(!IsError()); - for (uint32_t i = 0; i < kMaxBindGroups; ++i) { + for (BindGroupIndex i(0); i < kMaxBindGroupsTyped; ++i) { if (!mMask[i] || mBindGroupLayouts[i].Get() != other->mBindGroupLayouts[i].Get()) { return i; } } - return kMaxBindGroups; + return kMaxBindGroupsTyped; } size_t PipelineLayoutBase::HashFunc::operator()(const PipelineLayoutBase* pl) const { size_t hash = Hash(pl->mMask); - for (uint32_t group : IterateBitSet(pl->mMask)) { + for (BindGroupIndex group : IterateBitSet(pl->mMask)) { HashCombine(&hash, pl->GetBindGroupLayout(group)); } @@ -112,7 +304,7 @@ namespace dawn_native { return false; } - for (uint32_t group : IterateBitSet(a->mMask)) { + for (BindGroupIndex group : IterateBitSet(a->mMask)) { if (a->GetBindGroupLayout(group) != b->GetBindGroupLayout(group)) { return false; } diff --git a/third_party/dawn/src/dawn_native/PipelineLayout.h b/third_party/dawn/src/dawn_native/PipelineLayout.h index f10149805c5..862caaf8c75 100644 --- a/third_party/dawn/src/dawn_native/PipelineLayout.h +++ b/third_party/dawn/src/dawn_native/PipelineLayout.h @@ -16,9 +16,12 @@ #define DAWNNATIVE_PIPELINELAYOUT_H_ #include "common/Constants.h" +#include "common/ityp_array.h" +#include "common/ityp_bitset.h" +#include "dawn_native/BindingInfo.h" +#include "dawn_native/CachedObject.h" #include "dawn_native/Error.h" #include "dawn_native/Forward.h" -#include "dawn_native/ObjectBase.h" #include "dawn_native/dawn_platform.h" @@ -30,27 +33,30 @@ namespace dawn_native { MaybeError ValidatePipelineLayoutDescriptor(DeviceBase*, const PipelineLayoutDescriptor* descriptor); - using BindGroupLayoutArray = std::array, kMaxBindGroups>; + using BindGroupLayoutArray = + ityp::array, kMaxBindGroups>; + using BindGroupLayoutMask = ityp::bitset; - class PipelineLayoutBase : public ObjectBase { + class PipelineLayoutBase : public CachedObject { public: - PipelineLayoutBase(DeviceBase* device, - const PipelineLayoutDescriptor* descriptor, - bool blueprint = false); + PipelineLayoutBase(DeviceBase* device, const PipelineLayoutDescriptor* descriptor); ~PipelineLayoutBase() override; static PipelineLayoutBase* MakeError(DeviceBase* device); + static ResultOrError + CreateDefault(DeviceBase* device, const ShaderModuleBase* const* modules, uint32_t count); - const BindGroupLayoutBase* GetBindGroupLayout(size_t group) const; - const std::bitset GetBindGroupLayoutsMask() const; + const BindGroupLayoutBase* GetBindGroupLayout(BindGroupIndex group) const; + BindGroupLayoutBase* GetBindGroupLayout(BindGroupIndex group); + const BindGroupLayoutMask& GetBindGroupLayoutsMask() const; // Utility functions to compute inherited bind groups. // Returns the inherited bind groups as a mask. - std::bitset InheritedGroupsMask(const PipelineLayoutBase* other) const; + BindGroupLayoutMask InheritedGroupsMask(const PipelineLayoutBase* other) const; // Returns the index of the first incompatible bind group in the range - // [1, kMaxBindGroups + 1] - uint32_t GroupsInheritUpTo(const PipelineLayoutBase* other) const; + // [0, kMaxBindGroups] + BindGroupIndex GroupsInheritUpTo(const PipelineLayoutBase* other) const; // Functors necessary for the unordered_set-based cache. struct HashFunc { @@ -64,8 +70,7 @@ namespace dawn_native { PipelineLayoutBase(DeviceBase* device, ObjectBase::ErrorTag tag); BindGroupLayoutArray mBindGroupLayouts; - std::bitset mMask; - bool mIsBlueprint = false; + BindGroupLayoutMask mMask; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ProgrammablePassEncoder.cpp b/third_party/dawn/src/dawn_native/ProgrammablePassEncoder.cpp index 5330b6c990c..83aedad39dd 100644 --- a/third_party/dawn/src/dawn_native/ProgrammablePassEncoder.cpp +++ b/third_party/dawn/src/dawn_native/ProgrammablePassEncoder.cpp @@ -14,122 +14,193 @@ #include "dawn_native/ProgrammablePassEncoder.h" +#include "common/BitSetIterator.h" +#include "common/ityp_array.h" #include "dawn_native/BindGroup.h" +#include "dawn_native/Buffer.h" #include "dawn_native/CommandBuffer.h" #include "dawn_native/Commands.h" #include "dawn_native/Device.h" #include "dawn_native/ValidationUtils_autogen.h" -#include +#include namespace dawn_native { - ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator) - : ObjectBase(device), mTopLevelEncoder(topLevelEncoder), mAllocator(allocator) { - DAWN_ASSERT(allocator != nullptr); - } + namespace { + void TrackBindGroupResourceUsage(PassResourceUsageTracker* usageTracker, + BindGroupBase* group) { + for (BindingIndex bindingIndex{0}; bindingIndex < group->GetLayout()->GetBindingCount(); + ++bindingIndex) { + wgpu::BindingType type = group->GetLayout()->GetBindingInfo(bindingIndex).type; + + switch (type) { + case wgpu::BindingType::UniformBuffer: { + BufferBase* buffer = group->GetBindingAsBufferBinding(bindingIndex).buffer; + usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Uniform); + break; + } + + case wgpu::BindingType::StorageBuffer: { + BufferBase* buffer = group->GetBindingAsBufferBinding(bindingIndex).buffer; + usageTracker->BufferUsedAs(buffer, wgpu::BufferUsage::Storage); + break; + } + + case wgpu::BindingType::SampledTexture: { + TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex); + usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Sampled); + break; + } + + case wgpu::BindingType::ReadonlyStorageBuffer: { + BufferBase* buffer = group->GetBindingAsBufferBinding(bindingIndex).buffer; + usageTracker->BufferUsedAs(buffer, kReadOnlyStorageBuffer); + break; + } + + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + break; + + case wgpu::BindingType::ReadonlyStorageTexture: { + TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex); + usageTracker->TextureViewUsedAs(view, kReadonlyStorageTexture); + break; + } + + case wgpu::BindingType::WriteonlyStorageTexture: { + TextureViewBase* view = group->GetBindingAsTextureView(bindingIndex); + usageTracker->TextureViewUsedAs(view, wgpu::TextureUsage::Storage); + break; + } + + case wgpu::BindingType::StorageTexture: + UNREACHABLE(); + break; + } + } + } + } // namespace ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - ErrorTag errorTag) - : ObjectBase(device, errorTag), mTopLevelEncoder(topLevelEncoder), mAllocator(nullptr) { + EncodingContext* encodingContext, + PassType passType) + : ObjectBase(device), mEncodingContext(encodingContext), mUsageTracker(passType) { } - void ProgrammablePassEncoder::EndPass() { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } - - mTopLevelEncoder->PassEnded(); - mAllocator = nullptr; + ProgrammablePassEncoder::ProgrammablePassEncoder(DeviceBase* device, + EncodingContext* encodingContext, + ErrorTag errorTag, + PassType passType) + : ObjectBase(device, errorTag), mEncodingContext(encodingContext), mUsageTracker(passType) { } void ProgrammablePassEncoder::InsertDebugMarker(const char* groupLabel) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + InsertDebugMarkerCmd* cmd = + allocator->Allocate(Command::InsertDebugMarker); + cmd->length = strlen(groupLabel); - InsertDebugMarkerCmd* cmd = - mAllocator->Allocate(Command::InsertDebugMarker); - cmd->length = strlen(groupLabel); + char* label = allocator->AllocateData(cmd->length + 1); + memcpy(label, groupLabel, cmd->length + 1); - char* label = mAllocator->AllocateData(cmd->length + 1); - memcpy(label, groupLabel, cmd->length + 1); + return {}; + }); } void ProgrammablePassEncoder::PopDebugGroup() { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + allocator->Allocate(Command::PopDebugGroup); - mAllocator->Allocate(Command::PopDebugGroup); + return {}; + }); } void ProgrammablePassEncoder::PushDebugGroup(const char* groupLabel) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + PushDebugGroupCmd* cmd = + allocator->Allocate(Command::PushDebugGroup); + cmd->length = strlen(groupLabel); - PushDebugGroupCmd* cmd = mAllocator->Allocate(Command::PushDebugGroup); - cmd->length = strlen(groupLabel); + char* label = allocator->AllocateData(cmd->length + 1); + memcpy(label, groupLabel, cmd->length + 1); - char* label = mAllocator->AllocateData(cmd->length + 1); - memcpy(label, groupLabel, cmd->length + 1); + return {}; + }); } - void ProgrammablePassEncoder::SetBindGroup(uint32_t groupIndex, + void ProgrammablePassEncoder::SetBindGroup(uint32_t groupIndexIn, BindGroupBase* group, - uint32_t dynamicOffsetCount, - const uint64_t* dynamicOffsets) { - const BindGroupLayoutBase* layout = group->GetLayout(); - - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(group))) { - return; - } - - if (groupIndex >= kMaxBindGroups) { - mTopLevelEncoder->HandleError("Setting bind group over the max"); - return; - } - - // Dynamic offsets count must match the number required by the layout perfectly. - if (layout->GetDynamicBufferCount() != dynamicOffsetCount) { - mTopLevelEncoder->HandleError("dynamicOffset count mismatch"); - } - - for (uint32_t i = 0; i < dynamicOffsetCount; ++i) { - if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) { - mTopLevelEncoder->HandleError("Dynamic Buffer Offset need to be aligned"); - return; + uint32_t dynamicOffsetCountIn, + const uint32_t* dynamicOffsetsIn) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + BindGroupIndex groupIndex(groupIndexIn); + + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(GetDevice()->ValidateObject(group)); + + if (groupIndex >= kMaxBindGroupsTyped) { + return DAWN_VALIDATION_ERROR("Setting bind group over the max"); + } + + ityp::span dynamicOffsets( + dynamicOffsetsIn, BindingIndex(dynamicOffsetCountIn)); + + // Dynamic offsets count must match the number required by the layout perfectly. + const BindGroupLayoutBase* layout = group->GetLayout(); + if (layout->GetDynamicBufferCount() != dynamicOffsets.size()) { + return DAWN_VALIDATION_ERROR("dynamicOffset count mismatch"); + } + + for (BindingIndex i{0}; i < dynamicOffsets.size(); ++i) { + const BindingInfo& bindingInfo = layout->GetBindingInfo(i); + + // BGL creation sorts bindings such that the dynamic buffer bindings are first. + // ASSERT that this true. + ASSERT(bindingInfo.hasDynamicOffset); + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + break; + default: + UNREACHABLE(); + break; + } + + if (dynamicOffsets[i] % kMinDynamicBufferOffsetAlignment != 0) { + return DAWN_VALIDATION_ERROR("Dynamic Buffer Offset need to be aligned"); + } + + BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i); + + // During BindGroup creation, validation ensures binding offset + binding size + // <= buffer size. + ASSERT(bufferBinding.buffer->GetSize() >= bufferBinding.size); + ASSERT(bufferBinding.buffer->GetSize() - bufferBinding.size >= + bufferBinding.offset); + + if ((dynamicOffsets[i] > bufferBinding.buffer->GetSize() - + bufferBinding.offset - bufferBinding.size)) { + return DAWN_VALIDATION_ERROR("dynamic offset out of bounds"); + } + } } - BufferBinding bufferBinding = group->GetBindingAsBufferBinding(i); - - if (dynamicOffsets[i] >= bufferBinding.size - bufferBinding.offset) { - mTopLevelEncoder->HandleError("dynamic offset out of bounds"); - return; + SetBindGroupCmd* cmd = allocator->Allocate(Command::SetBindGroup); + cmd->index = groupIndex; + cmd->group = group; + cmd->dynamicOffsetCount = dynamicOffsetCountIn; + if (dynamicOffsetCountIn > 0) { + uint32_t* offsets = allocator->AllocateData(cmd->dynamicOffsetCount); + memcpy(offsets, dynamicOffsetsIn, dynamicOffsetCountIn * sizeof(uint32_t)); } - } - SetBindGroupCmd* cmd = mAllocator->Allocate(Command::SetBindGroup); - cmd->index = groupIndex; - cmd->group = group; - cmd->dynamicOffsetCount = dynamicOffsetCount; - if (dynamicOffsetCount > 0) { - uint64_t* offsets = mAllocator->AllocateData(cmd->dynamicOffsetCount); - memcpy(offsets, dynamicOffsets, dynamicOffsetCount * sizeof(uint64_t)); - } - } - - MaybeError ProgrammablePassEncoder::ValidateCanRecordCommands() const { - if (mAllocator == nullptr) { - return DAWN_VALIDATION_ERROR("Recording in an error or already ended pass encoder"); - } + TrackBindGroupResourceUsage(&mUsageTracker, group); - return nullptr; + return {}; + }); } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ProgrammablePassEncoder.h b/third_party/dawn/src/dawn_native/ProgrammablePassEncoder.h index 03894475710..b7bd4520c6c 100644 --- a/third_party/dawn/src/dawn_native/ProgrammablePassEncoder.h +++ b/third_party/dawn/src/dawn_native/ProgrammablePassEncoder.h @@ -18,6 +18,7 @@ #include "dawn_native/CommandEncoder.h" #include "dawn_native/Error.h" #include "dawn_native/ObjectBase.h" +#include "dawn_native/PassResourceUsageTracker.h" #include "dawn_native/dawn_platform.h" @@ -30,10 +31,8 @@ namespace dawn_native { class ProgrammablePassEncoder : public ObjectBase { public: ProgrammablePassEncoder(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator); - - void EndPass(); + EncodingContext* encodingContext, + PassType passType); void InsertDebugMarker(const char* groupLabel); void PopDebugGroup(); @@ -42,21 +41,17 @@ namespace dawn_native { void SetBindGroup(uint32_t groupIndex, BindGroupBase* group, uint32_t dynamicOffsetCount, - const uint64_t* dynamicOffsets); + const uint32_t* dynamicOffsets); protected: // Construct an "error" programmable pass encoder. ProgrammablePassEncoder(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - ErrorTag errorTag); - - MaybeError ValidateCanRecordCommands() const; + EncodingContext* encodingContext, + ErrorTag errorTag, + PassType passType); - // The allocator is borrowed from the top level encoder. Keep a reference to the encoder - // to make sure the allocator isn't freed. - Ref mTopLevelEncoder = nullptr; - // mAllocator is cleared at the end of the pass so it acts as a tag that EndPass was called - CommandAllocator* mAllocator = nullptr; + EncodingContext* mEncodingContext = nullptr; + PassResourceUsageTracker mUsageTracker; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/QuerySet.cpp b/third_party/dawn/src/dawn_native/QuerySet.cpp new file mode 100644 index 00000000000..bd65ed05baa --- /dev/null +++ b/third_party/dawn/src/dawn_native/QuerySet.cpp @@ -0,0 +1,161 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/QuerySet.h" + +#include "dawn_native/Device.h" +#include "dawn_native/Extensions.h" +#include "dawn_native/ValidationUtils_autogen.h" + +#include + +namespace dawn_native { + + namespace { + + class ErrorQuerySet final : public QuerySetBase { + public: + ErrorQuerySet(DeviceBase* device) : QuerySetBase(device, ObjectBase::kError) { + } + + private: + void DestroyImpl() override { + UNREACHABLE(); + } + }; + + } // anonymous namespace + + MaybeError ValidateQuerySetDescriptor(DeviceBase* device, + const QuerySetDescriptor* descriptor) { + if (descriptor->nextInChain != nullptr) { + return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); + } + + DAWN_TRY(ValidateQueryType(descriptor->type)); + + switch (descriptor->type) { + case wgpu::QueryType::Occlusion: + if (descriptor->pipelineStatisticsCount != 0) { + return DAWN_VALIDATION_ERROR( + "The pipeline statistics should not be set if query type is Occlusion"); + } + break; + + case wgpu::QueryType::PipelineStatistics: { + if (!device->IsExtensionEnabled(Extension::PipelineStatisticsQuery)) { + return DAWN_VALIDATION_ERROR( + "The pipeline statistics query feature is not supported"); + } + + if (descriptor->pipelineStatisticsCount == 0) { + return DAWN_VALIDATION_ERROR( + "At least one pipeline statistics is set if query type is " + "PipelineStatistics"); + } + + std::set pipelineStatisticsSet; + for (uint32_t i = 0; i < descriptor->pipelineStatisticsCount; i++) { + DAWN_TRY(ValidatePipelineStatisticName(descriptor->pipelineStatistics[i])); + + std::pair::iterator, bool> res = + pipelineStatisticsSet.insert((descriptor->pipelineStatistics[i])); + if (!res.second) { + return DAWN_VALIDATION_ERROR("Duplicate pipeline statistics found"); + } + } + } break; + + case wgpu::QueryType::Timestamp: + if (!device->IsExtensionEnabled(Extension::TimestampQuery)) { + return DAWN_VALIDATION_ERROR("The timestamp query feature is not supported"); + } + + if (descriptor->pipelineStatisticsCount != 0) { + return DAWN_VALIDATION_ERROR( + "The pipeline statistics should not be set if query type is Timestamp"); + } + break; + + default: + break; + } + + return {}; + } + + QuerySetBase::QuerySetBase(DeviceBase* device, const QuerySetDescriptor* descriptor) + : ObjectBase(device), + mQueryType(descriptor->type), + mQueryCount(descriptor->count), + mState(QuerySetState::Available) { + for (uint32_t i = 0; i < descriptor->pipelineStatisticsCount; i++) { + mPipelineStatistics.push_back(descriptor->pipelineStatistics[i]); + } + } + + QuerySetBase::QuerySetBase(DeviceBase* device, ObjectBase::ErrorTag tag) + : ObjectBase(device, tag) { + } + + QuerySetBase::~QuerySetBase() { + // Uninitialized or already destroyed + ASSERT(mState == QuerySetState::Unavailable || mState == QuerySetState::Destroyed); + } + + // static + QuerySetBase* QuerySetBase::MakeError(DeviceBase* device) { + return new ErrorQuerySet(device); + } + + wgpu::QueryType QuerySetBase::GetQueryType() const { + return mQueryType; + } + + uint32_t QuerySetBase::GetQueryCount() const { + return mQueryCount; + } + + const std::vector& QuerySetBase::GetPipelineStatistics() const { + return mPipelineStatistics; + } + + MaybeError QuerySetBase::ValidateCanUseInSubmitNow() const { + ASSERT(!IsError()); + if (mState == QuerySetState::Destroyed) { + return DAWN_VALIDATION_ERROR("Destroyed query set used in a submit"); + } + return {}; + } + + void QuerySetBase::Destroy() { + if (GetDevice()->ConsumedError(ValidateDestroy())) { + return; + } + DestroyInternal(); + } + + MaybeError QuerySetBase::ValidateDestroy() const { + DAWN_TRY(GetDevice()->ValidateObject(this)); + return {}; + } + + void QuerySetBase::DestroyInternal() { + if (mState != QuerySetState::Destroyed) { + DestroyImpl(); + } + mState = QuerySetState::Destroyed; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/QuerySet.h b/third_party/dawn/src/dawn_native/QuerySet.h new file mode 100644 index 00000000000..a8f4deace6a --- /dev/null +++ b/third_party/dawn/src/dawn_native/QuerySet.h @@ -0,0 +1,63 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_QUERYSET_H_ +#define DAWNNATIVE_QUERYSET_H_ + +#include "dawn_native/Error.h" +#include "dawn_native/Forward.h" +#include "dawn_native/ObjectBase.h" + +#include "dawn_native/dawn_platform.h" + +namespace dawn_native { + + MaybeError ValidateQuerySetDescriptor(DeviceBase* device, const QuerySetDescriptor* descriptor); + + class QuerySetBase : public ObjectBase { + public: + QuerySetBase(DeviceBase* device, const QuerySetDescriptor* descriptor); + + static QuerySetBase* MakeError(DeviceBase* device); + + wgpu::QueryType GetQueryType() const; + uint32_t GetQueryCount() const; + const std::vector& GetPipelineStatistics() const; + + MaybeError ValidateCanUseInSubmitNow() const; + + void Destroy(); + + protected: + QuerySetBase(DeviceBase* device, ObjectBase::ErrorTag tag); + ~QuerySetBase() override; + + void DestroyInternal(); + + private: + virtual void DestroyImpl() = 0; + + MaybeError ValidateDestroy() const; + + wgpu::QueryType mQueryType; + uint32_t mQueryCount; + std::vector mPipelineStatistics; + + enum class QuerySetState { Unavailable, Available, Destroyed }; + QuerySetState mState = QuerySetState::Unavailable; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_QUERYSET_H_ diff --git a/third_party/dawn/src/dawn_native/Queue.cpp b/third_party/dawn/src/dawn_native/Queue.cpp index 23cc91277f9..efc716701b4 100644 --- a/third_party/dawn/src/dawn_native/Queue.cpp +++ b/third_party/dawn/src/dawn_native/Queue.cpp @@ -16,10 +16,19 @@ #include "dawn_native/Buffer.h" #include "dawn_native/CommandBuffer.h" +#include "dawn_native/CommandValidation.h" #include "dawn_native/Device.h" +#include "dawn_native/DynamicUploader.h" +#include "dawn_native/ErrorScope.h" +#include "dawn_native/ErrorScopeTracker.h" #include "dawn_native/Fence.h" #include "dawn_native/FenceSignalTracker.h" +#include "dawn_native/QuerySet.h" #include "dawn_native/Texture.h" +#include "dawn_platform/DawnPlatform.h" +#include "dawn_platform/tracing/TraceEvent.h" + +#include namespace dawn_native { @@ -28,35 +37,136 @@ namespace dawn_native { QueueBase::QueueBase(DeviceBase* device) : ObjectBase(device) { } + QueueBase::QueueBase(DeviceBase* device, ObjectBase::ErrorTag tag) : ObjectBase(device, tag) { + } + + // static + QueueBase* QueueBase::MakeError(DeviceBase* device) { + return new QueueBase(device, ObjectBase::kError); + } + + MaybeError QueueBase::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { + UNREACHABLE(); + return {}; + } + void QueueBase::Submit(uint32_t commandCount, CommandBufferBase* const* commands) { - if (GetDevice()->ConsumedError(ValidateSubmit(commandCount, commands))) { + DeviceBase* device = GetDevice(); + if (device->ConsumedError(device->ValidateIsAlive())) { + // If device is lost, don't let any commands be submitted + return; + } + + TRACE_EVENT0(device->GetPlatform(), General, "Queue::Submit"); + if (device->IsValidationEnabled() && + device->ConsumedError(ValidateSubmit(commandCount, commands))) { return; } ASSERT(!IsError()); - SubmitImpl(commandCount, commands); + if (device->ConsumedError(SubmitImpl(commandCount, commands))) { + return; + } + device->GetErrorScopeTracker()->TrackUntilLastSubmitComplete( + device->GetCurrentErrorScope()); } - void QueueBase::Signal(FenceBase* fence, uint64_t signalValue) { - if (GetDevice()->ConsumedError(ValidateSignal(fence, signalValue))) { + void QueueBase::Signal(Fence* fence, uint64_t signalValue) { + DeviceBase* device = GetDevice(); + if (device->ConsumedError(ValidateSignal(fence, signalValue))) { return; } ASSERT(!IsError()); fence->SetSignaledValue(signalValue); - GetDevice()->GetFenceSignalTracker()->UpdateFenceOnComplete(fence, signalValue); + device->GetFenceSignalTracker()->UpdateFenceOnComplete(fence, signalValue); + device->GetErrorScopeTracker()->TrackUntilLastSubmitComplete( + device->GetCurrentErrorScope()); } - FenceBase* QueueBase::CreateFence(const FenceDescriptor* descriptor) { + Fence* QueueBase::CreateFence(const FenceDescriptor* descriptor) { if (GetDevice()->ConsumedError(ValidateCreateFence(descriptor))) { - return FenceBase::MakeError(GetDevice()); + return Fence::MakeError(GetDevice()); + } + + if (descriptor == nullptr) { + FenceDescriptor defaultDescriptor = {}; + return new Fence(this, &defaultDescriptor); } + return new Fence(this, descriptor); + } - return new FenceBase(this, descriptor); + void QueueBase::WriteBuffer(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size) { + GetDevice()->ConsumedError(WriteBufferInternal(buffer, bufferOffset, data, size)); + } + + MaybeError QueueBase::WriteBufferInternal(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size) { + DAWN_TRY(ValidateWriteBuffer(buffer, bufferOffset, size)); + return WriteBufferImpl(buffer, bufferOffset, data, size); + } + + MaybeError QueueBase::WriteBufferImpl(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size) { + if (size == 0) { + return {}; + } + + DeviceBase* device = GetDevice(); + + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( + size, device->GetPendingCommandSerial())); + ASSERT(uploadHandle.mappedBuffer != nullptr); + + memcpy(uploadHandle.mappedBuffer, data, size); + + return device->CopyFromStagingToBuffer(uploadHandle.stagingBuffer, uploadHandle.startOffset, + buffer, bufferOffset, size); + } + + void QueueBase::WriteTexture(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) { + GetDevice()->ConsumedError( + WriteTextureInternal(destination, data, dataSize, dataLayout, writeSize)); + } + + MaybeError QueueBase::WriteTextureInternal(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) { + DAWN_TRY(ValidateWriteTexture(destination, dataSize, dataLayout, writeSize)); + + if (writeSize->width == 0 || writeSize->height == 0 || writeSize->depth == 0) { + return {}; + } + + return WriteTextureImpl(destination, data, dataSize, dataLayout, writeSize); + } + + MaybeError QueueBase::WriteTextureImpl(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) { + // TODO(tommek@google.com): This should be implemented. + return {}; } MaybeError QueueBase::ValidateSubmit(uint32_t commandCount, - CommandBufferBase* const* commands) { + CommandBufferBase* const* commands) const { + TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "Queue::ValidateSubmit"); DAWN_TRY(GetDevice()->ValidateObject(this)); for (uint32_t i = 0; i < commandCount; ++i) { @@ -66,7 +176,7 @@ namespace dawn_native { for (const PassResourceUsage& passUsages : usages.perPass) { for (const BufferBase* buffer : passUsages.buffers) { - DAWN_TRY(buffer->ValidateCanUseInSubmitNow()); + DAWN_TRY(buffer->ValidateCanUseOnQueueNow()); } for (const TextureBase* texture : passUsages.textures) { DAWN_TRY(texture->ValidateCanUseInSubmitNow()); @@ -74,17 +184,21 @@ namespace dawn_native { } for (const BufferBase* buffer : usages.topLevelBuffers) { - DAWN_TRY(buffer->ValidateCanUseInSubmitNow()); + DAWN_TRY(buffer->ValidateCanUseOnQueueNow()); } for (const TextureBase* texture : usages.topLevelTextures) { DAWN_TRY(texture->ValidateCanUseInSubmitNow()); } + for (const QuerySetBase* querySet : usages.usedQuerySets) { + DAWN_TRY(querySet->ValidateCanUseInSubmitNow()); + } } return {}; } - MaybeError QueueBase::ValidateSignal(const FenceBase* fence, uint64_t signalValue) { + MaybeError QueueBase::ValidateSignal(const Fence* fence, uint64_t signalValue) const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); DAWN_TRY(GetDevice()->ValidateObject(this)); DAWN_TRY(GetDevice()->ValidateObject(fence)); @@ -98,9 +212,71 @@ namespace dawn_native { return {}; } - MaybeError QueueBase::ValidateCreateFence(const FenceDescriptor* descriptor) { + MaybeError QueueBase::ValidateCreateFence(const FenceDescriptor* descriptor) const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); + DAWN_TRY(GetDevice()->ValidateObject(this)); + if (descriptor != nullptr) { + DAWN_TRY(ValidateFenceDescriptor(descriptor)); + } + + return {}; + } + + MaybeError QueueBase::ValidateWriteBuffer(const BufferBase* buffer, + uint64_t bufferOffset, + size_t size) const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); + DAWN_TRY(GetDevice()->ValidateObject(this)); + DAWN_TRY(GetDevice()->ValidateObject(buffer)); + + if (bufferOffset % 4 != 0) { + return DAWN_VALIDATION_ERROR("Queue::WriteBuffer bufferOffset must be a multiple of 4"); + } + if (size % 4 != 0) { + return DAWN_VALIDATION_ERROR("Queue::WriteBuffer size must be a multiple of 4"); + } + + uint64_t bufferSize = buffer->GetSize(); + if (bufferOffset > bufferSize || size > (bufferSize - bufferOffset)) { + return DAWN_VALIDATION_ERROR("Queue::WriteBuffer out of range"); + } + + if (!(buffer->GetUsage() & wgpu::BufferUsage::CopyDst)) { + return DAWN_VALIDATION_ERROR("Buffer needs the CopyDst usage bit"); + } + + return buffer->ValidateCanUseOnQueueNow(); + } + + MaybeError QueueBase::ValidateWriteTexture(const TextureCopyView* destination, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); DAWN_TRY(GetDevice()->ValidateObject(this)); - DAWN_TRY(ValidateFenceDescriptor(descriptor)); + DAWN_TRY(GetDevice()->ValidateObject(destination->texture)); + + DAWN_TRY(ValidateTextureCopyView(GetDevice(), *destination)); + + if (dataLayout->offset > dataSize) { + return DAWN_VALIDATION_ERROR("Queue::WriteTexture out of range"); + } + + if (!(destination->texture->GetUsage() & wgpu::TextureUsage::CopyDst)) { + return DAWN_VALIDATION_ERROR("Texture needs the CopyDst usage bit"); + } + + if (destination->texture->GetSampleCount() > 1) { + return DAWN_VALIDATION_ERROR("The sample count of textures must be 1"); + } + + // We validate texture copy range before validating linear texture data, + // because in the latter we divide copyExtent.width by blockWidth and + // copyExtent.height by blockHeight while the divisibility conditions are + // checked in validating texture copy range. + DAWN_TRY(ValidateTextureCopyRange(*destination, *writeSize)); + DAWN_TRY(ValidateLinearTextureData(*dataLayout, dataSize, destination->texture->GetFormat(), + *writeSize)); return {}; } diff --git a/third_party/dawn/src/dawn_native/Queue.h b/third_party/dawn/src/dawn_native/Queue.h index 7b1031eab5a..694d53b8454 100644 --- a/third_party/dawn/src/dawn_native/Queue.h +++ b/third_party/dawn/src/dawn_native/Queue.h @@ -27,17 +27,53 @@ namespace dawn_native { public: QueueBase(DeviceBase* device); + static QueueBase* MakeError(DeviceBase* device); + // Dawn API void Submit(uint32_t commandCount, CommandBufferBase* const* commands); - void Signal(FenceBase* fence, uint64_t signalValue); - FenceBase* CreateFence(const FenceDescriptor* descriptor); + void Signal(Fence* fence, uint64_t signalValue); + Fence* CreateFence(const FenceDescriptor* descriptor); + void WriteBuffer(BufferBase* buffer, uint64_t bufferOffset, const void* data, size_t size); + void WriteTexture(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize); private: - virtual void SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) = 0; + QueueBase(DeviceBase* device, ObjectBase::ErrorTag tag); + + MaybeError WriteBufferInternal(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size); + MaybeError WriteTextureInternal(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize); + + virtual MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands); + virtual MaybeError WriteBufferImpl(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size); + virtual MaybeError WriteTextureImpl(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize); - MaybeError ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands); - MaybeError ValidateSignal(const FenceBase* fence, uint64_t signalValue); - MaybeError ValidateCreateFence(const FenceDescriptor* descriptor); + MaybeError ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands) const; + MaybeError ValidateSignal(const Fence* fence, uint64_t signalValue) const; + MaybeError ValidateCreateFence(const FenceDescriptor* descriptor) const; + MaybeError ValidateWriteBuffer(const BufferBase* buffer, + uint64_t bufferOffset, + size_t size) const; + MaybeError ValidateWriteTexture(const TextureCopyView* destination, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) const; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/RenderBundle.cpp b/third_party/dawn/src/dawn_native/RenderBundle.cpp new file mode 100644 index 00000000000..b3478867976 --- /dev/null +++ b/third_party/dawn/src/dawn_native/RenderBundle.cpp @@ -0,0 +1,61 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/RenderBundle.h" + +#include "common/BitSetIterator.h" +#include "dawn_native/Commands.h" +#include "dawn_native/Device.h" +#include "dawn_native/RenderBundleEncoder.h" + +namespace dawn_native { + + RenderBundleBase::RenderBundleBase(RenderBundleEncoder* encoder, + const RenderBundleDescriptor* descriptor, + AttachmentState* attachmentState, + PassResourceUsage resourceUsage) + : ObjectBase(encoder->GetDevice()), + mCommands(encoder->AcquireCommands()), + mAttachmentState(attachmentState), + mResourceUsage(std::move(resourceUsage)) { + } + + RenderBundleBase::~RenderBundleBase() { + FreeCommands(&mCommands); + } + + // static + RenderBundleBase* RenderBundleBase::MakeError(DeviceBase* device) { + return new RenderBundleBase(device, ObjectBase::kError); + } + + RenderBundleBase::RenderBundleBase(DeviceBase* device, ErrorTag errorTag) + : ObjectBase(device, errorTag) { + } + + CommandIterator* RenderBundleBase::GetCommands() { + return &mCommands; + } + + const AttachmentState* RenderBundleBase::GetAttachmentState() const { + ASSERT(!IsError()); + return mAttachmentState.Get(); + } + + const PassResourceUsage& RenderBundleBase::GetResourceUsage() const { + ASSERT(!IsError()); + return mResourceUsage; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/RenderBundle.h b/third_party/dawn/src/dawn_native/RenderBundle.h new file mode 100644 index 00000000000..ed80c69df15 --- /dev/null +++ b/third_party/dawn/src/dawn_native/RenderBundle.h @@ -0,0 +1,62 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_RENDERBUNDLE_H_ +#define DAWNNATIVE_RENDERBUNDLE_H_ + +#include "common/Constants.h" +#include "dawn_native/AttachmentState.h" +#include "dawn_native/CommandAllocator.h" +#include "dawn_native/Error.h" +#include "dawn_native/ObjectBase.h" +#include "dawn_native/PassResourceUsage.h" + +#include "dawn_native/dawn_platform.h" + +#include + +namespace dawn_native { + + struct BeginRenderPassCmd; + struct RenderBundleDescriptor; + class RenderBundleEncoder; + + class RenderBundleBase : public ObjectBase { + public: + RenderBundleBase(RenderBundleEncoder* encoder, + const RenderBundleDescriptor* descriptor, + AttachmentState* attachmentState, + PassResourceUsage resourceUsage); + + static RenderBundleBase* MakeError(DeviceBase* device); + + CommandIterator* GetCommands(); + + const AttachmentState* GetAttachmentState() const; + const PassResourceUsage& GetResourceUsage() const; + + protected: + ~RenderBundleBase() override; + + private: + RenderBundleBase(DeviceBase* device, ErrorTag errorTag); + + CommandIterator mCommands; + Ref mAttachmentState; + PassResourceUsage mResourceUsage; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_RENDERBUNDLE_H_ diff --git a/third_party/dawn/src/dawn_native/RenderBundleEncoder.cpp b/third_party/dawn/src/dawn_native/RenderBundleEncoder.cpp new file mode 100644 index 00000000000..49700429f33 --- /dev/null +++ b/third_party/dawn/src/dawn_native/RenderBundleEncoder.cpp @@ -0,0 +1,131 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/RenderBundleEncoder.h" + +#include "dawn_native/CommandValidation.h" +#include "dawn_native/Commands.h" +#include "dawn_native/Device.h" +#include "dawn_native/Format.h" +#include "dawn_native/RenderPipeline.h" +#include "dawn_native/ValidationUtils_autogen.h" +#include "dawn_platform/DawnPlatform.h" +#include "dawn_platform/tracing/TraceEvent.h" + +namespace dawn_native { + + MaybeError ValidateColorAttachmentFormat(const DeviceBase* device, + wgpu::TextureFormat textureFormat) { + DAWN_TRY(ValidateTextureFormat(textureFormat)); + const Format* format = nullptr; + DAWN_TRY_ASSIGN(format, device->GetInternalFormat(textureFormat)); + if (!format->IsColor() || !format->isRenderable) { + return DAWN_VALIDATION_ERROR( + "The color attachment texture format is not color renderable"); + } + return {}; + } + + MaybeError ValidateDepthStencilAttachmentFormat(const DeviceBase* device, + wgpu::TextureFormat textureFormat) { + DAWN_TRY(ValidateTextureFormat(textureFormat)); + const Format* format = nullptr; + DAWN_TRY_ASSIGN(format, device->GetInternalFormat(textureFormat)); + if (!format->HasDepthOrStencil() || !format->isRenderable) { + return DAWN_VALIDATION_ERROR( + "The depth stencil attachment texture format is not a renderable depth/stencil " + "format"); + } + return {}; + } + + MaybeError ValidateRenderBundleEncoderDescriptor( + const DeviceBase* device, + const RenderBundleEncoderDescriptor* descriptor) { + if (!IsValidSampleCount(descriptor->sampleCount)) { + return DAWN_VALIDATION_ERROR("Sample count is not supported"); + } + + if (descriptor->colorFormatsCount > kMaxColorAttachments) { + return DAWN_VALIDATION_ERROR("Color formats count exceeds maximum"); + } + + if (descriptor->colorFormatsCount == 0 && + descriptor->depthStencilFormat == wgpu::TextureFormat::Undefined) { + return DAWN_VALIDATION_ERROR("Should have at least one attachment format"); + } + + for (uint32_t i = 0; i < descriptor->colorFormatsCount; ++i) { + DAWN_TRY(ValidateColorAttachmentFormat(device, descriptor->colorFormats[i])); + } + + if (descriptor->depthStencilFormat != wgpu::TextureFormat::Undefined) { + DAWN_TRY(ValidateDepthStencilAttachmentFormat(device, descriptor->depthStencilFormat)); + } + + return {}; + } + + RenderBundleEncoder::RenderBundleEncoder(DeviceBase* device, + const RenderBundleEncoderDescriptor* descriptor) + : RenderEncoderBase(device, &mBundleEncodingContext), + mBundleEncodingContext(device, this), + mAttachmentState(device->GetOrCreateAttachmentState(descriptor)) { + } + + RenderBundleEncoder::RenderBundleEncoder(DeviceBase* device, ErrorTag errorTag) + : RenderEncoderBase(device, &mBundleEncodingContext, errorTag), + mBundleEncodingContext(device, this) { + } + + // static + RenderBundleEncoder* RenderBundleEncoder::MakeError(DeviceBase* device) { + return new RenderBundleEncoder(device, ObjectBase::kError); + } + + const AttachmentState* RenderBundleEncoder::GetAttachmentState() const { + return mAttachmentState.Get(); + } + + CommandIterator RenderBundleEncoder::AcquireCommands() { + return mBundleEncodingContext.AcquireCommands(); + } + + RenderBundleBase* RenderBundleEncoder::Finish(const RenderBundleDescriptor* descriptor) { + PassResourceUsage usages = mUsageTracker.AcquireResourceUsage(); + + DeviceBase* device = GetDevice(); + // Even if mBundleEncodingContext.Finish() validation fails, calling it will mutate the + // internal state of the encoding context. Subsequent calls to encode commands will generate + // errors. + if (device->ConsumedError(mBundleEncodingContext.Finish()) || + (device->IsValidationEnabled() && + device->ConsumedError(ValidateFinish(mBundleEncodingContext.GetIterator(), usages)))) { + return RenderBundleBase::MakeError(device); + } + + ASSERT(!IsError()); + return new RenderBundleBase(this, descriptor, mAttachmentState.Get(), std::move(usages)); + } + + MaybeError RenderBundleEncoder::ValidateFinish(CommandIterator* commands, + const PassResourceUsage& usages) const { + TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "RenderBundleEncoder::ValidateFinish"); + DAWN_TRY(GetDevice()->ValidateObject(this)); + DAWN_TRY(ValidatePassResourceUsage(usages)); + DAWN_TRY(ValidateRenderBundle(commands, mAttachmentState.Get())); + return {}; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/RenderBundleEncoder.h b/third_party/dawn/src/dawn_native/RenderBundleEncoder.h new file mode 100644 index 00000000000..e6354ab0428 --- /dev/null +++ b/third_party/dawn/src/dawn_native/RenderBundleEncoder.h @@ -0,0 +1,52 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_RENDERBUNDLEENCODER_H_ +#define DAWNNATIVE_RENDERBUNDLEENCODER_H_ + +#include "dawn_native/AttachmentState.h" +#include "dawn_native/EncodingContext.h" +#include "dawn_native/Error.h" +#include "dawn_native/RenderBundle.h" +#include "dawn_native/RenderEncoderBase.h" + +namespace dawn_native { + + MaybeError ValidateRenderBundleEncoderDescriptor( + const DeviceBase* device, + const RenderBundleEncoderDescriptor* descriptor); + + class RenderBundleEncoder final : public RenderEncoderBase { + public: + RenderBundleEncoder(DeviceBase* device, const RenderBundleEncoderDescriptor* descriptor); + + static RenderBundleEncoder* MakeError(DeviceBase* device); + + const AttachmentState* GetAttachmentState() const; + + RenderBundleBase* Finish(const RenderBundleDescriptor* descriptor); + + CommandIterator AcquireCommands(); + + private: + RenderBundleEncoder(DeviceBase* device, ErrorTag errorTag); + + MaybeError ValidateFinish(CommandIterator* commands, const PassResourceUsage& usages) const; + + EncodingContext mBundleEncodingContext; + Ref mAttachmentState; + }; +} // namespace dawn_native + +#endif // DAWNNATIVE_RENDERBUNDLEENCODER_H_ diff --git a/third_party/dawn/src/dawn_native/RenderEncoderBase.cpp b/third_party/dawn/src/dawn_native/RenderEncoderBase.cpp new file mode 100644 index 00000000000..7285fbabed7 --- /dev/null +++ b/third_party/dawn/src/dawn_native/RenderEncoderBase.cpp @@ -0,0 +1,206 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/RenderEncoderBase.h" + +#include "common/Constants.h" +#include "dawn_native/Buffer.h" +#include "dawn_native/CommandEncoder.h" +#include "dawn_native/Commands.h" +#include "dawn_native/Device.h" +#include "dawn_native/RenderPipeline.h" + +#include +#include + +namespace dawn_native { + + RenderEncoderBase::RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext) + : ProgrammablePassEncoder(device, encodingContext, PassType::Render), + mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)), + mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) { + } + + RenderEncoderBase::RenderEncoderBase(DeviceBase* device, + EncodingContext* encodingContext, + ErrorTag errorTag) + : ProgrammablePassEncoder(device, encodingContext, errorTag, PassType::Render), + mDisableBaseVertex(device->IsToggleEnabled(Toggle::DisableBaseVertex)), + mDisableBaseInstance(device->IsToggleEnabled(Toggle::DisableBaseInstance)) { + } + + void RenderEncoderBase::Draw(uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (mDisableBaseInstance && firstInstance != 0) { + return DAWN_VALIDATION_ERROR("Non-zero first instance not supported"); + } + + DrawCmd* draw = allocator->Allocate(Command::Draw); + draw->vertexCount = vertexCount; + draw->instanceCount = instanceCount; + draw->firstVertex = firstVertex; + draw->firstInstance = firstInstance; + + return {}; + }); + } + + void RenderEncoderBase::DrawIndexed(uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t baseVertex, + uint32_t firstInstance) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (mDisableBaseInstance && firstInstance != 0) { + return DAWN_VALIDATION_ERROR("Non-zero first instance not supported"); + } + if (mDisableBaseInstance && baseVertex != 0) { + return DAWN_VALIDATION_ERROR("Non-zero base vertex not supported"); + } + + DrawIndexedCmd* draw = allocator->Allocate(Command::DrawIndexed); + draw->indexCount = indexCount; + draw->instanceCount = instanceCount; + draw->firstIndex = firstIndex; + draw->baseVertex = baseVertex; + draw->firstInstance = firstInstance; + + return {}; + }); + } + + void RenderEncoderBase::DrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer)); + + if (indirectOffset >= indirectBuffer->GetSize() || + indirectOffset + kDrawIndirectSize > indirectBuffer->GetSize()) { + return DAWN_VALIDATION_ERROR("Indirect offset out of bounds"); + } + + DrawIndirectCmd* cmd = allocator->Allocate(Command::DrawIndirect); + cmd->indirectBuffer = indirectBuffer; + cmd->indirectOffset = indirectOffset; + + mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect); + + return {}; + }); + } + + void RenderEncoderBase::DrawIndexedIndirect(BufferBase* indirectBuffer, + uint64_t indirectOffset) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(indirectBuffer)); + + if ((indirectOffset >= indirectBuffer->GetSize() || + indirectOffset + kDrawIndexedIndirectSize > indirectBuffer->GetSize())) { + return DAWN_VALIDATION_ERROR("Indirect offset out of bounds"); + } + + DrawIndexedIndirectCmd* cmd = + allocator->Allocate(Command::DrawIndexedIndirect); + cmd->indirectBuffer = indirectBuffer; + cmd->indirectOffset = indirectOffset; + + mUsageTracker.BufferUsedAs(indirectBuffer, wgpu::BufferUsage::Indirect); + + return {}; + }); + } + + void RenderEncoderBase::SetPipeline(RenderPipelineBase* pipeline) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(pipeline)); + + SetRenderPipelineCmd* cmd = + allocator->Allocate(Command::SetRenderPipeline); + cmd->pipeline = pipeline; + + return {}; + }); + } + + void RenderEncoderBase::SetIndexBuffer(BufferBase* buffer, uint64_t offset, uint64_t size) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(buffer)); + + uint64_t bufferSize = buffer->GetSize(); + if (offset > bufferSize) { + return DAWN_VALIDATION_ERROR("Offset larger than the buffer size"); + } + uint64_t remainingSize = bufferSize - offset; + + if (size == 0) { + size = remainingSize; + } else { + if (size > remainingSize) { + return DAWN_VALIDATION_ERROR("Size + offset larger than the buffer size"); + } + } + + SetIndexBufferCmd* cmd = + allocator->Allocate(Command::SetIndexBuffer); + cmd->buffer = buffer; + cmd->offset = offset; + cmd->size = size; + + mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Index); + + return {}; + }); + } + + void RenderEncoderBase::SetVertexBuffer(uint32_t slot, + BufferBase* buffer, + uint64_t offset, + uint64_t size) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + DAWN_TRY(GetDevice()->ValidateObject(buffer)); + + if (slot >= kMaxVertexBuffers) { + return DAWN_VALIDATION_ERROR("Vertex buffer slot out of bounds"); + } + + uint64_t bufferSize = buffer->GetSize(); + if (offset > bufferSize) { + return DAWN_VALIDATION_ERROR("Offset larger than the buffer size"); + } + uint64_t remainingSize = bufferSize - offset; + + if (size == 0) { + size = remainingSize; + } else { + if (size > remainingSize) { + return DAWN_VALIDATION_ERROR("Size + offset larger than the buffer size"); + } + } + + SetVertexBufferCmd* cmd = + allocator->Allocate(Command::SetVertexBuffer); + cmd->slot = slot; + cmd->buffer = buffer; + cmd->offset = offset; + cmd->size = size; + + mUsageTracker.BufferUsedAs(buffer, wgpu::BufferUsage::Vertex); + + return {}; + }); + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/RenderEncoderBase.h b/third_party/dawn/src/dawn_native/RenderEncoderBase.h new file mode 100644 index 00000000000..a4f3b9ffc7c --- /dev/null +++ b/third_party/dawn/src/dawn_native/RenderEncoderBase.h @@ -0,0 +1,56 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_RENDERENCODERBASE_H_ +#define DAWNNATIVE_RENDERENCODERBASE_H_ + +#include "dawn_native/Error.h" +#include "dawn_native/ProgrammablePassEncoder.h" + +namespace dawn_native { + + class RenderEncoderBase : public ProgrammablePassEncoder { + public: + RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext); + + void Draw(uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance); + void DrawIndexed(uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t baseVertex, + uint32_t firstInstance); + + void DrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset); + void DrawIndexedIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset); + + void SetPipeline(RenderPipelineBase* pipeline); + + void SetVertexBuffer(uint32_t slot, BufferBase* buffer, uint64_t offset, uint64_t size); + void SetIndexBuffer(BufferBase* buffer, uint64_t offset, uint64_t size); + + protected: + // Construct an "error" render encoder base. + RenderEncoderBase(DeviceBase* device, EncodingContext* encodingContext, ErrorTag errorTag); + + private: + const bool mDisableBaseVertex; + const bool mDisableBaseInstance; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_RENDERENCODERBASE_H_ diff --git a/third_party/dawn/src/dawn_native/RenderPassEncoder.cpp b/third_party/dawn/src/dawn_native/RenderPassEncoder.cpp index c3820d0e012..e51b52f7662 100644 --- a/third_party/dawn/src/dawn_native/RenderPassEncoder.cpp +++ b/third_party/dawn/src/dawn_native/RenderPassEncoder.cpp @@ -17,185 +17,169 @@ #include "common/Constants.h" #include "dawn_native/Buffer.h" #include "dawn_native/CommandEncoder.h" +#include "dawn_native/CommandValidation.h" #include "dawn_native/Commands.h" #include "dawn_native/Device.h" +#include "dawn_native/QuerySet.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/RenderPipeline.h" -#include +#include +#include namespace dawn_native { - RenderPassEncoderBase::RenderPassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator) - : ProgrammablePassEncoder(device, topLevelEncoder, allocator) { + // The usage tracker is passed in here, because it is prepopulated with usages from the + // BeginRenderPassCmd. If we had RenderPassEncoder responsible for recording the + // command, then this wouldn't be necessary. + RenderPassEncoder::RenderPassEncoder(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext, + PassResourceUsageTracker usageTracker) + : RenderEncoderBase(device, encodingContext), mCommandEncoder(commandEncoder) { + mUsageTracker = std::move(usageTracker); } - RenderPassEncoderBase::RenderPassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - ErrorTag errorTag) - : ProgrammablePassEncoder(device, topLevelEncoder, errorTag) { + RenderPassEncoder::RenderPassEncoder(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext, + ErrorTag errorTag) + : RenderEncoderBase(device, encodingContext, errorTag), mCommandEncoder(commandEncoder) { } - RenderPassEncoderBase* RenderPassEncoderBase::MakeError(DeviceBase* device, - CommandEncoderBase* topLevelEncoder) { - return new RenderPassEncoderBase(device, topLevelEncoder, ObjectBase::kError); + RenderPassEncoder* RenderPassEncoder::MakeError(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext) { + return new RenderPassEncoder(device, commandEncoder, encodingContext, ObjectBase::kError); } - void RenderPassEncoderBase::Draw(uint32_t vertexCount, - uint32_t instanceCount, - uint32_t firstVertex, - uint32_t firstInstance) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + void RenderPassEncoder::EndPass() { + if (mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + allocator->Allocate(Command::EndRenderPass); - DrawCmd* draw = mAllocator->Allocate(Command::Draw); - draw->vertexCount = vertexCount; - draw->instanceCount = instanceCount; - draw->firstVertex = firstVertex; - draw->firstInstance = firstInstance; + return {}; + })) { + mEncodingContext->ExitPass(this, mUsageTracker.AcquireResourceUsage()); + } } - void RenderPassEncoderBase::DrawIndexed(uint32_t indexCount, - uint32_t instanceCount, - uint32_t firstIndex, - int32_t baseVertex, - uint32_t firstInstance) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + void RenderPassEncoder::SetStencilReference(uint32_t reference) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + SetStencilReferenceCmd* cmd = + allocator->Allocate(Command::SetStencilReference); + cmd->reference = reference; - DrawIndexedCmd* draw = mAllocator->Allocate(Command::DrawIndexed); - draw->indexCount = indexCount; - draw->instanceCount = instanceCount; - draw->firstIndex = firstIndex; - draw->baseVertex = baseVertex; - draw->firstInstance = firstInstance; + return {}; + }); } - void RenderPassEncoderBase::DrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) { - return; - } - - if (indirectOffset >= indirectBuffer->GetSize() || - indirectOffset + kDrawIndirectSize > indirectBuffer->GetSize()) { - mTopLevelEncoder->HandleError("Indirect offset out of bounds"); - return; - } + void RenderPassEncoder::SetBlendColor(const Color* color) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + SetBlendColorCmd* cmd = allocator->Allocate(Command::SetBlendColor); + cmd->color = *color; - DrawIndirectCmd* cmd = mAllocator->Allocate(Command::DrawIndirect); - cmd->indirectBuffer = indirectBuffer; - cmd->indirectOffset = indirectOffset; + return {}; + }); } - void RenderPassEncoderBase::DrawIndexedIndirect(BufferBase* indirectBuffer, - uint64_t indirectOffset) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) { - return; - } + void RenderPassEncoder::SetViewport(float x, + float y, + float width, + float height, + float minDepth, + float maxDepth) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if ((isnan(x) || isnan(y) || isnan(width) || isnan(height) || isnan(minDepth) || + isnan(maxDepth))) { + return DAWN_VALIDATION_ERROR("NaN is not allowed."); + } - if (indirectOffset >= indirectBuffer->GetSize() || - indirectOffset + kDrawIndexedIndirectSize > indirectBuffer->GetSize()) { - mTopLevelEncoder->HandleError("Indirect offset out of bounds"); - return; - } + // TODO(yunchao.he@intel.com): there are more restrictions for x, y, width and height in + // Vulkan, and height can be a negative value in Vulkan 1.1. Revisit this part later + // (say, for WebGPU v1). + if (width <= 0 || height <= 0) { + return DAWN_VALIDATION_ERROR("Width and height must be greater than 0."); + } - DrawIndexedIndirectCmd* cmd = - mAllocator->Allocate(Command::DrawIndexedIndirect); - cmd->indirectBuffer = indirectBuffer; - cmd->indirectOffset = indirectOffset; - } + if (minDepth < 0 || minDepth > 1 || maxDepth < 0 || maxDepth > 1) { + return DAWN_VALIDATION_ERROR("minDepth and maxDepth must be in [0, 1]."); + } - void RenderPassEncoderBase::SetPipeline(RenderPipelineBase* pipeline) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(pipeline))) { - return; - } + SetViewportCmd* cmd = allocator->Allocate(Command::SetViewport); + cmd->x = x; + cmd->y = y; + cmd->width = width; + cmd->height = height; + cmd->minDepth = minDepth; + cmd->maxDepth = maxDepth; - SetRenderPipelineCmd* cmd = - mAllocator->Allocate(Command::SetRenderPipeline); - cmd->pipeline = pipeline; + return {}; + }); } - void RenderPassEncoderBase::SetStencilReference(uint32_t reference) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + void RenderPassEncoder::SetScissorRect(uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (width == 0 || height == 0) { + return DAWN_VALIDATION_ERROR("Width and height must be greater than 0."); + } - SetStencilReferenceCmd* cmd = - mAllocator->Allocate(Command::SetStencilReference); - cmd->reference = reference; + SetScissorRectCmd* cmd = + allocator->Allocate(Command::SetScissorRect); + cmd->x = x; + cmd->y = y; + cmd->width = width; + cmd->height = height; + + return {}; + }); } - void RenderPassEncoderBase::SetBlendColor(const Color* color) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } + void RenderPassEncoder::ExecuteBundles(uint32_t count, RenderBundleBase* const* renderBundles) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + for (uint32_t i = 0; i < count; ++i) { + DAWN_TRY(GetDevice()->ValidateObject(renderBundles[i])); + } - SetBlendColorCmd* cmd = mAllocator->Allocate(Command::SetBlendColor); - cmd->color = *color; - } + ExecuteBundlesCmd* cmd = + allocator->Allocate(Command::ExecuteBundles); + cmd->count = count; - void RenderPassEncoderBase::SetScissorRect(uint32_t x, - uint32_t y, - uint32_t width, - uint32_t height) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } - if (width == 0 || height == 0) { - mTopLevelEncoder->HandleError("Width and height must be greater than 0."); - return; - } + Ref* bundles = allocator->AllocateData>(count); + for (uint32_t i = 0; i < count; ++i) { + bundles[i] = renderBundles[i]; - SetScissorRectCmd* cmd = mAllocator->Allocate(Command::SetScissorRect); - cmd->x = x; - cmd->y = y; - cmd->width = width; - cmd->height = height; - } + const PassResourceUsage& usages = bundles[i]->GetResourceUsage(); + for (uint32_t i = 0; i < usages.buffers.size(); ++i) { + mUsageTracker.BufferUsedAs(usages.buffers[i], usages.bufferUsages[i]); + } - void RenderPassEncoderBase::SetIndexBuffer(BufferBase* buffer, uint64_t offset) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) || - mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(buffer))) { - return; - } + for (uint32_t i = 0; i < usages.textures.size(); ++i) { + mUsageTracker.AddTextureUsage(usages.textures[i], usages.textureUsages[i]); + } + } - SetIndexBufferCmd* cmd = mAllocator->Allocate(Command::SetIndexBuffer); - cmd->buffer = buffer; - cmd->offset = offset; + return {}; + }); } - void RenderPassEncoderBase::SetVertexBuffers(uint32_t startSlot, - uint32_t count, - BufferBase* const* buffers, - uint64_t const* offsets) { - if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands())) { - return; - } - - for (size_t i = 0; i < count; ++i) { - if (mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(buffers[i]))) { - return; + void RenderPassEncoder::WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex) { + mEncodingContext->TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError { + if (GetDevice()->IsValidationEnabled()) { + DAWN_TRY(GetDevice()->ValidateObject(querySet)); + DAWN_TRY(ValidateTimestampQuery(querySet, queryIndex)); + mCommandEncoder->TrackUsedQuerySet(querySet); } - } - - SetVertexBuffersCmd* cmd = - mAllocator->Allocate(Command::SetVertexBuffers); - cmd->startSlot = startSlot; - cmd->count = count; - Ref* cmdBuffers = mAllocator->AllocateData>(count); - for (size_t i = 0; i < count; ++i) { - cmdBuffers[i] = buffers[i]; - } + WriteTimestampCmd* cmd = + allocator->Allocate(Command::WriteTimestamp); + cmd->querySet = querySet; + cmd->queryIndex = queryIndex; - uint64_t* cmdOffsets = mAllocator->AllocateData(count); - memcpy(cmdOffsets, offsets, count * sizeof(uint64_t)); + return {}; + }); } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/RenderPassEncoder.h b/third_party/dawn/src/dawn_native/RenderPassEncoder.h index c6029c5d7d1..d5a2f7aebbc 100644 --- a/third_party/dawn/src/dawn_native/RenderPassEncoder.h +++ b/third_party/dawn/src/dawn_native/RenderPassEncoder.h @@ -16,60 +16,48 @@ #define DAWNNATIVE_RENDERPASSENCODER_H_ #include "dawn_native/Error.h" -#include "dawn_native/ProgrammablePassEncoder.h" +#include "dawn_native/RenderEncoderBase.h" namespace dawn_native { - // This is called RenderPassEncoderBase to match the code generator expectations. Note that it - // is a pure frontend type to record in its parent CommandEncoder and never has a backend - // implementation. - // TODO(cwallez@chromium.org): Remove that generator limitation and rename to ComputePassEncoder - class RenderPassEncoderBase : public ProgrammablePassEncoder { - public: - RenderPassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - CommandAllocator* allocator); - - static RenderPassEncoderBase* MakeError(DeviceBase* device, - CommandEncoderBase* topLevelEncoder); + class RenderBundleBase; - void Draw(uint32_t vertexCount, - uint32_t instanceCount, - uint32_t firstVertex, - uint32_t firstInstance); - void DrawIndexed(uint32_t vertexCount, - uint32_t instanceCount, - uint32_t firstIndex, - int32_t baseVertex, - uint32_t firstInstance); + class RenderPassEncoder final : public RenderEncoderBase { + public: + RenderPassEncoder(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext, + PassResourceUsageTracker usageTracker); - void DrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset); - void DrawIndexedIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset); + static RenderPassEncoder* MakeError(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext); - void SetPipeline(RenderPipelineBase* pipeline); + void EndPass(); void SetStencilReference(uint32_t reference); void SetBlendColor(const Color* color); + void SetViewport(float x, + float y, + float width, + float height, + float minDepth, + float maxDepth); void SetScissorRect(uint32_t x, uint32_t y, uint32_t width, uint32_t height); + void ExecuteBundles(uint32_t count, RenderBundleBase* const* renderBundles); - template - void SetVertexBuffers(uint32_t startSlot, - uint32_t count, - T* const* buffers, - uint64_t const* offsets) { - static_assert(std::is_base_of::value, ""); - SetVertexBuffers(startSlot, count, buffers, offsets); - } - void SetVertexBuffers(uint32_t startSlot, - uint32_t count, - BufferBase* const* buffers, - uint64_t const* offsets); - void SetIndexBuffer(BufferBase* buffer, uint64_t offset); + void WriteTimestamp(QuerySetBase* querySet, uint32_t queryIndex); protected: - RenderPassEncoderBase(DeviceBase* device, - CommandEncoderBase* topLevelEncoder, - ErrorTag errorTag); + RenderPassEncoder(DeviceBase* device, + CommandEncoder* commandEncoder, + EncodingContext* encodingContext, + ErrorTag errorTag); + + private: + // For render and compute passes, the encoding context is borrowed from the command encoder. + // Keep a reference to the encoder to make sure the context isn't freed. + Ref mCommandEncoder; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/RenderPipeline.cpp b/third_party/dawn/src/dawn_native/RenderPipeline.cpp index c4b05126b80..7c3a05ad7a0 100644 --- a/third_party/dawn/src/dawn_native/RenderPipeline.cpp +++ b/third_party/dawn/src/dawn_native/RenderPipeline.cpp @@ -18,9 +18,10 @@ #include "common/HashUtils.h" #include "dawn_native/Commands.h" #include "dawn_native/Device.h" -#include "dawn_native/Texture.h" #include "dawn_native/ValidationUtils_autogen.h" +#include + namespace dawn_native { // Helper functions namespace { @@ -50,6 +51,10 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("Setting attribute offset out of bounds"); } + if (attribute->offset % 4 != 0) { + return DAWN_VALIDATION_ERROR("Attribute offset needs to be a multiple of 4 bytes"); + } + if ((*attributesSetMask)[attribute->shaderLocation]) { return DAWN_VALIDATION_ERROR("Setting already set attribute"); } @@ -58,39 +63,44 @@ namespace dawn_native { return {}; } - MaybeError ValidateVertexBufferDescriptor( - const VertexBufferDescriptor* buffer, + MaybeError ValidateVertexBufferLayoutDescriptor( + const VertexBufferLayoutDescriptor* buffer, std::bitset* attributesSetMask) { DAWN_TRY(ValidateInputStepMode(buffer->stepMode)); - if (buffer->stride > kMaxVertexBufferStride) { - return DAWN_VALIDATION_ERROR("Setting input stride out of bounds"); + if (buffer->arrayStride > kMaxVertexBufferStride) { + return DAWN_VALIDATION_ERROR("Setting arrayStride out of bounds"); + } + + if (buffer->arrayStride % 4 != 0) { + return DAWN_VALIDATION_ERROR( + "arrayStride of Vertex buffer needs to be a multiple of 4 bytes"); } for (uint32_t i = 0; i < buffer->attributeCount; ++i) { - DAWN_TRY(ValidateVertexAttributeDescriptor(&buffer->attributes[i], buffer->stride, - attributesSetMask)); + DAWN_TRY(ValidateVertexAttributeDescriptor(&buffer->attributes[i], + buffer->arrayStride, attributesSetMask)); } return {}; } - MaybeError ValidateVertexInputDescriptor( - const VertexInputDescriptor* descriptor, + MaybeError ValidateVertexStateDescriptor( + const VertexStateDescriptor* descriptor, std::bitset* attributesSetMask) { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } DAWN_TRY(ValidateIndexFormat(descriptor->indexFormat)); - if (descriptor->bufferCount > kMaxVertexBuffers) { - return DAWN_VALIDATION_ERROR("Vertex Inputs number exceeds maximum"); + if (descriptor->vertexBufferCount > kMaxVertexBuffers) { + return DAWN_VALIDATION_ERROR("Vertex buffer count exceeds maximum"); } uint32_t totalAttributesNum = 0; - for (uint32_t i = 0; i < descriptor->bufferCount; ++i) { - DAWN_TRY( - ValidateVertexBufferDescriptor(&descriptor->buffers[i], attributesSetMask)); - totalAttributesNum += descriptor->buffers[i].attributeCount; + for (uint32_t i = 0; i < descriptor->vertexBufferCount; ++i) { + DAWN_TRY(ValidateVertexBufferLayoutDescriptor(&descriptor->vertexBuffers[i], + attributesSetMask)); + totalAttributesNum += descriptor->vertexBuffers[i].attributeCount; } // Every vertex attribute has a member called shaderLocation, and there are some @@ -107,33 +117,48 @@ namespace dawn_native { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } + DAWN_TRY(ValidateFrontFace(descriptor->frontFace)); DAWN_TRY(ValidateCullMode(descriptor->cullMode)); + + if (std::isnan(descriptor->depthBiasSlopeScale) || + std::isnan(descriptor->depthBiasClamp)) { + return DAWN_VALIDATION_ERROR("Depth bias parameters must not be NaN."); + } + return {}; } - MaybeError ValidateColorStateDescriptor(const ColorStateDescriptor* descriptor) { - if (descriptor->nextInChain != nullptr) { + MaybeError ValidateColorStateDescriptor(const DeviceBase* device, + const ColorStateDescriptor& descriptor, + Format::Type fragmentOutputBaseType) { + if (descriptor.nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - DAWN_TRY(ValidateBlendOperation(descriptor->alphaBlend.operation)); - DAWN_TRY(ValidateBlendFactor(descriptor->alphaBlend.srcFactor)); - DAWN_TRY(ValidateBlendFactor(descriptor->alphaBlend.dstFactor)); - DAWN_TRY(ValidateBlendOperation(descriptor->colorBlend.operation)); - DAWN_TRY(ValidateBlendFactor(descriptor->colorBlend.srcFactor)); - DAWN_TRY(ValidateBlendFactor(descriptor->colorBlend.dstFactor)); - DAWN_TRY(ValidateColorWriteMask(descriptor->writeMask)); - - dawn::TextureFormat format = descriptor->format; - DAWN_TRY(ValidateTextureFormat(format)); - if (!IsColorRenderableTextureFormat(format)) { + DAWN_TRY(ValidateBlendOperation(descriptor.alphaBlend.operation)); + DAWN_TRY(ValidateBlendFactor(descriptor.alphaBlend.srcFactor)); + DAWN_TRY(ValidateBlendFactor(descriptor.alphaBlend.dstFactor)); + DAWN_TRY(ValidateBlendOperation(descriptor.colorBlend.operation)); + DAWN_TRY(ValidateBlendFactor(descriptor.colorBlend.srcFactor)); + DAWN_TRY(ValidateBlendFactor(descriptor.colorBlend.dstFactor)); + DAWN_TRY(ValidateColorWriteMask(descriptor.writeMask)); + + const Format* format; + DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor.format)); + if (!format->IsColor() || !format->isRenderable) { return DAWN_VALIDATION_ERROR("Color format must be color renderable"); } + if (fragmentOutputBaseType != Format::Type::Other && + fragmentOutputBaseType != format->type) { + return DAWN_VALIDATION_ERROR( + "Color format must match the fragment stage output type"); + } return {}; } MaybeError ValidateDepthStencilStateDescriptor( + const DeviceBase* device, const DepthStencilStateDescriptor* descriptor) { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); @@ -148,9 +173,9 @@ namespace dawn_native { DAWN_TRY(ValidateStencilOperation(descriptor->stencilBack.depthFailOp)); DAWN_TRY(ValidateStencilOperation(descriptor->stencilBack.passOp)); - dawn::TextureFormat format = descriptor->format; - DAWN_TRY(ValidateTextureFormat(format)); - if (!IsDepthStencilRenderableTextureFormat(format)) { + const Format* format; + DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format)); + if (!format->HasDepthOrStencil() || !format->isRenderable) { return DAWN_VALIDATION_ERROR( "Depth stencil format must be depth-stencil renderable"); } @@ -158,131 +183,163 @@ namespace dawn_native { return {}; } + RequiredBufferSizes ComputeMinBufferSizes(const RenderPipelineDescriptor* descriptor) { + RequiredBufferSizes bufferSizes = + descriptor->vertexStage.module->ComputeRequiredBufferSizesForLayout( + descriptor->layout); + + // Merge the two buffer size requirements by taking the larger element from each + if (descriptor->fragmentStage != nullptr) { + RequiredBufferSizes fragmentSizes = + descriptor->fragmentStage->module->ComputeRequiredBufferSizesForLayout( + descriptor->layout); + + for (BindGroupIndex group(0); group < bufferSizes.size(); ++group) { + ASSERT(bufferSizes[group].size() == fragmentSizes[group].size()); + for (size_t i = 0; i < bufferSizes[group].size(); ++i) { + bufferSizes[group][i] = + std::max(bufferSizes[group][i], fragmentSizes[group][i]); + } + } + } + + return bufferSizes; + } + } // anonymous namespace // Helper functions - size_t IndexFormatSize(dawn::IndexFormat format) { + size_t IndexFormatSize(wgpu::IndexFormat format) { switch (format) { - case dawn::IndexFormat::Uint16: + case wgpu::IndexFormat::Uint16: return sizeof(uint16_t); - case dawn::IndexFormat::Uint32: + case wgpu::IndexFormat::Uint32: return sizeof(uint32_t); default: UNREACHABLE(); } } - uint32_t VertexFormatNumComponents(dawn::VertexFormat format) { + uint32_t VertexFormatNumComponents(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar4: - case dawn::VertexFormat::Char4: - case dawn::VertexFormat::UChar4Norm: - case dawn::VertexFormat::Char4Norm: - case dawn::VertexFormat::UShort4: - case dawn::VertexFormat::Short4: - case dawn::VertexFormat::UShort4Norm: - case dawn::VertexFormat::Short4Norm: - case dawn::VertexFormat::Half4: - case dawn::VertexFormat::Float4: - case dawn::VertexFormat::UInt4: - case dawn::VertexFormat::Int4: + case wgpu::VertexFormat::UChar4: + case wgpu::VertexFormat::Char4: + case wgpu::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::Char4Norm: + case wgpu::VertexFormat::UShort4: + case wgpu::VertexFormat::Short4: + case wgpu::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::Short4Norm: + case wgpu::VertexFormat::Half4: + case wgpu::VertexFormat::Float4: + case wgpu::VertexFormat::UInt4: + case wgpu::VertexFormat::Int4: return 4; - case dawn::VertexFormat::Float3: - case dawn::VertexFormat::UInt3: - case dawn::VertexFormat::Int3: + case wgpu::VertexFormat::Float3: + case wgpu::VertexFormat::UInt3: + case wgpu::VertexFormat::Int3: return 3; - case dawn::VertexFormat::UChar2: - case dawn::VertexFormat::Char2: - case dawn::VertexFormat::UChar2Norm: - case dawn::VertexFormat::Char2Norm: - case dawn::VertexFormat::UShort2: - case dawn::VertexFormat::Short2: - case dawn::VertexFormat::UShort2Norm: - case dawn::VertexFormat::Short2Norm: - case dawn::VertexFormat::Half2: - case dawn::VertexFormat::Float2: - case dawn::VertexFormat::UInt2: - case dawn::VertexFormat::Int2: + case wgpu::VertexFormat::UChar2: + case wgpu::VertexFormat::Char2: + case wgpu::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::Char2Norm: + case wgpu::VertexFormat::UShort2: + case wgpu::VertexFormat::Short2: + case wgpu::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Half2: + case wgpu::VertexFormat::Float2: + case wgpu::VertexFormat::UInt2: + case wgpu::VertexFormat::Int2: return 2; - case dawn::VertexFormat::Float: - case dawn::VertexFormat::UInt: - case dawn::VertexFormat::Int: + case wgpu::VertexFormat::Float: + case wgpu::VertexFormat::UInt: + case wgpu::VertexFormat::Int: return 1; default: UNREACHABLE(); } } - size_t VertexFormatComponentSize(dawn::VertexFormat format) { + size_t VertexFormatComponentSize(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar2: - case dawn::VertexFormat::UChar4: - case dawn::VertexFormat::Char2: - case dawn::VertexFormat::Char4: - case dawn::VertexFormat::UChar2Norm: - case dawn::VertexFormat::UChar4Norm: - case dawn::VertexFormat::Char2Norm: - case dawn::VertexFormat::Char4Norm: + case wgpu::VertexFormat::UChar2: + case wgpu::VertexFormat::UChar4: + case wgpu::VertexFormat::Char2: + case wgpu::VertexFormat::Char4: + case wgpu::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::Char2Norm: + case wgpu::VertexFormat::Char4Norm: return sizeof(char); - case dawn::VertexFormat::UShort2: - case dawn::VertexFormat::UShort4: - case dawn::VertexFormat::UShort2Norm: - case dawn::VertexFormat::UShort4Norm: - case dawn::VertexFormat::Short2: - case dawn::VertexFormat::Short4: - case dawn::VertexFormat::Short2Norm: - case dawn::VertexFormat::Short4Norm: - case dawn::VertexFormat::Half2: - case dawn::VertexFormat::Half4: + case wgpu::VertexFormat::UShort2: + case wgpu::VertexFormat::UShort4: + case wgpu::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::Short2: + case wgpu::VertexFormat::Short4: + case wgpu::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Short4Norm: + case wgpu::VertexFormat::Half2: + case wgpu::VertexFormat::Half4: return sizeof(uint16_t); - case dawn::VertexFormat::Float: - case dawn::VertexFormat::Float2: - case dawn::VertexFormat::Float3: - case dawn::VertexFormat::Float4: + case wgpu::VertexFormat::Float: + case wgpu::VertexFormat::Float2: + case wgpu::VertexFormat::Float3: + case wgpu::VertexFormat::Float4: return sizeof(float); - case dawn::VertexFormat::UInt: - case dawn::VertexFormat::UInt2: - case dawn::VertexFormat::UInt3: - case dawn::VertexFormat::UInt4: - case dawn::VertexFormat::Int: - case dawn::VertexFormat::Int2: - case dawn::VertexFormat::Int3: - case dawn::VertexFormat::Int4: + case wgpu::VertexFormat::UInt: + case wgpu::VertexFormat::UInt2: + case wgpu::VertexFormat::UInt3: + case wgpu::VertexFormat::UInt4: + case wgpu::VertexFormat::Int: + case wgpu::VertexFormat::Int2: + case wgpu::VertexFormat::Int3: + case wgpu::VertexFormat::Int4: return sizeof(int32_t); default: UNREACHABLE(); } } - size_t VertexFormatSize(dawn::VertexFormat format) { + size_t VertexFormatSize(wgpu::VertexFormat format) { return VertexFormatNumComponents(format) * VertexFormatComponentSize(format); } - MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device, + MaybeError ValidateRenderPipelineDescriptor(const DeviceBase* device, const RenderPipelineDescriptor* descriptor) { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - DAWN_TRY(device->ValidateObject(descriptor->layout)); + if (descriptor->layout != nullptr) { + DAWN_TRY(device->ValidateObject(descriptor->layout)); + } - if (descriptor->vertexInput == nullptr) { - return DAWN_VALIDATION_ERROR("Input state must not be null"); + // TODO(crbug.com/dawn/136): Support vertex-only pipelines. + if (descriptor->fragmentStage == nullptr) { + return DAWN_VALIDATION_ERROR("Null fragment stage is not supported (yet)"); } std::bitset attributesSetMask; - DAWN_TRY(ValidateVertexInputDescriptor(descriptor->vertexInput, &attributesSetMask)); + if (descriptor->vertexState) { + DAWN_TRY(ValidateVertexStateDescriptor(descriptor->vertexState, &attributesSetMask)); + } + DAWN_TRY(ValidatePrimitiveTopology(descriptor->primitiveTopology)); - DAWN_TRY(ValidatePipelineStageDescriptor(device, descriptor->vertexStage, - descriptor->layout, dawn::ShaderStage::Vertex)); - DAWN_TRY(ValidatePipelineStageDescriptor(device, descriptor->fragmentStage, - descriptor->layout, dawn::ShaderStage::Fragment)); - DAWN_TRY(ValidateRasterizationStateDescriptor(descriptor->rasterizationState)); + DAWN_TRY(ValidateProgrammableStageDescriptor( + device, &descriptor->vertexStage, descriptor->layout, SingleShaderStage::Vertex)); + DAWN_TRY(ValidateProgrammableStageDescriptor( + device, descriptor->fragmentStage, descriptor->layout, SingleShaderStage::Fragment)); - if ((descriptor->vertexStage->module->GetUsedVertexAttributes() & ~attributesSetMask) + if (descriptor->rasterizationState) { + DAWN_TRY(ValidateRasterizationStateDescriptor(descriptor->rasterizationState)); + } + + if ((descriptor->vertexStage.module->GetUsedVertexAttributes() & ~attributesSetMask) .any()) { return DAWN_VALIDATION_ERROR( - "Pipeline vertex stage uses inputs not in the input state"); + "Pipeline vertex stage uses vertex buffers not in the vertex state"); } if (!IsValidSampleCount(descriptor->sampleCount)) { @@ -297,99 +354,118 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("Should have at least one attachment"); } + ASSERT(descriptor->fragmentStage != nullptr); + const ShaderModuleBase::FragmentOutputBaseTypes& fragmentOutputBaseTypes = + descriptor->fragmentStage->module->GetFragmentOutputBaseTypes(); for (uint32_t i = 0; i < descriptor->colorStateCount; ++i) { - DAWN_TRY(ValidateColorStateDescriptor(descriptor->colorStates[i])); + DAWN_TRY(ValidateColorStateDescriptor(device, descriptor->colorStates[i], + fragmentOutputBaseTypes[i])); } if (descriptor->depthStencilState) { - DAWN_TRY(ValidateDepthStencilStateDescriptor(descriptor->depthStencilState)); + DAWN_TRY(ValidateDepthStencilStateDescriptor(device, descriptor->depthStencilState)); + } + + if (descriptor->alphaToCoverageEnabled) { + return DAWN_VALIDATION_ERROR("alphaToCoverageEnabled isn't supported (yet)"); } return {}; } bool StencilTestEnabled(const DepthStencilStateDescriptor* mDepthStencilState) { - return mDepthStencilState->stencilBack.compare != dawn::CompareFunction::Always || - mDepthStencilState->stencilBack.failOp != dawn::StencilOperation::Keep || - mDepthStencilState->stencilBack.depthFailOp != dawn::StencilOperation::Keep || - mDepthStencilState->stencilBack.passOp != dawn::StencilOperation::Keep || - mDepthStencilState->stencilFront.compare != dawn::CompareFunction::Always || - mDepthStencilState->stencilFront.failOp != dawn::StencilOperation::Keep || - mDepthStencilState->stencilFront.depthFailOp != dawn::StencilOperation::Keep || - mDepthStencilState->stencilFront.passOp != dawn::StencilOperation::Keep; + return mDepthStencilState->stencilBack.compare != wgpu::CompareFunction::Always || + mDepthStencilState->stencilBack.failOp != wgpu::StencilOperation::Keep || + mDepthStencilState->stencilBack.depthFailOp != wgpu::StencilOperation::Keep || + mDepthStencilState->stencilBack.passOp != wgpu::StencilOperation::Keep || + mDepthStencilState->stencilFront.compare != wgpu::CompareFunction::Always || + mDepthStencilState->stencilFront.failOp != wgpu::StencilOperation::Keep || + mDepthStencilState->stencilFront.depthFailOp != wgpu::StencilOperation::Keep || + mDepthStencilState->stencilFront.passOp != wgpu::StencilOperation::Keep; } bool BlendEnabled(const ColorStateDescriptor* mColorState) { - return mColorState->alphaBlend.operation != dawn::BlendOperation::Add || - mColorState->alphaBlend.srcFactor != dawn::BlendFactor::One || - mColorState->alphaBlend.dstFactor != dawn::BlendFactor::Zero || - mColorState->colorBlend.operation != dawn::BlendOperation::Add || - mColorState->colorBlend.srcFactor != dawn::BlendFactor::One || - mColorState->colorBlend.dstFactor != dawn::BlendFactor::Zero; + return mColorState->alphaBlend.operation != wgpu::BlendOperation::Add || + mColorState->alphaBlend.srcFactor != wgpu::BlendFactor::One || + mColorState->alphaBlend.dstFactor != wgpu::BlendFactor::Zero || + mColorState->colorBlend.operation != wgpu::BlendOperation::Add || + mColorState->colorBlend.srcFactor != wgpu::BlendFactor::One || + mColorState->colorBlend.dstFactor != wgpu::BlendFactor::Zero; } // RenderPipelineBase RenderPipelineBase::RenderPipelineBase(DeviceBase* device, - const RenderPipelineDescriptor* descriptor, - bool blueprint) + const RenderPipelineDescriptor* descriptor) : PipelineBase(device, descriptor->layout, - dawn::ShaderStageBit::Vertex | dawn::ShaderStageBit::Fragment), - mVertexInput(*descriptor->vertexInput), - mHasDepthStencilAttachment(descriptor->depthStencilState != nullptr), + wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment, + ComputeMinBufferSizes(descriptor)), + mAttachmentState(device->GetOrCreateAttachmentState(descriptor)), mPrimitiveTopology(descriptor->primitiveTopology), - mRasterizationState(*descriptor->rasterizationState), - mSampleCount(descriptor->sampleCount), - mVertexModule(descriptor->vertexStage->module), - mVertexEntryPoint(descriptor->vertexStage->entryPoint), + mSampleMask(descriptor->sampleMask), + mAlphaToCoverageEnabled(descriptor->alphaToCoverageEnabled), + mVertexModule(descriptor->vertexStage.module), + mVertexEntryPoint(descriptor->vertexStage.entryPoint), mFragmentModule(descriptor->fragmentStage->module), - mFragmentEntryPoint(descriptor->fragmentStage->entryPoint), - mIsBlueprint(blueprint) { - for (uint32_t slot = 0; slot < mVertexInput.bufferCount; ++slot) { - if (mVertexInput.buffers[slot].attributeCount == 0) { + mFragmentEntryPoint(descriptor->fragmentStage->entryPoint) { + if (descriptor->vertexState != nullptr) { + mVertexState = *descriptor->vertexState; + } else { + mVertexState = VertexStateDescriptor(); + } + + for (uint32_t slot = 0; slot < mVertexState.vertexBufferCount; ++slot) { + if (mVertexState.vertexBuffers[slot].attributeCount == 0) { continue; } - mInputsSetMask.set(slot); - mInputInfos[slot].stride = mVertexInput.buffers[slot].stride; - mInputInfos[slot].stepMode = mVertexInput.buffers[slot].stepMode; + mVertexBufferSlotsUsed.set(slot); + mVertexBufferInfos[slot].arrayStride = mVertexState.vertexBuffers[slot].arrayStride; + mVertexBufferInfos[slot].stepMode = mVertexState.vertexBuffers[slot].stepMode; uint32_t location = 0; - for (uint32_t i = 0; i < mVertexInput.buffers[slot].attributeCount; ++i) { - location = mVertexInput.buffers[slot].attributes[i].shaderLocation; - mAttributesSetMask.set(location); + for (uint32_t i = 0; i < mVertexState.vertexBuffers[slot].attributeCount; ++i) { + location = mVertexState.vertexBuffers[slot].attributes[i].shaderLocation; + mAttributeLocationsUsed.set(location); mAttributeInfos[location].shaderLocation = location; - mAttributeInfos[location].inputSlot = slot; - mAttributeInfos[location].offset = mVertexInput.buffers[slot].attributes[i].offset; - mAttributeInfos[location].format = mVertexInput.buffers[slot].attributes[i].format; + mAttributeInfos[location].vertexBufferSlot = slot; + mAttributeInfos[location].offset = + mVertexState.vertexBuffers[slot].attributes[i].offset; + mAttributeInfos[location].format = + mVertexState.vertexBuffers[slot].attributes[i].format; } } - if (mHasDepthStencilAttachment) { + if (descriptor->rasterizationState != nullptr) { + mRasterizationState = *descriptor->rasterizationState; + } else { + mRasterizationState = RasterizationStateDescriptor(); + } + + if (mAttachmentState->HasDepthStencilAttachment()) { mDepthStencilState = *descriptor->depthStencilState; } else { // These default values below are useful for backends to fill information. // The values indicate that depth and stencil test are disabled when backends // set their own depth stencil states/descriptors according to the values in // mDepthStencilState. - mDepthStencilState.depthCompare = dawn::CompareFunction::Always; + mDepthStencilState.depthCompare = wgpu::CompareFunction::Always; mDepthStencilState.depthWriteEnabled = false; - mDepthStencilState.stencilBack.compare = dawn::CompareFunction::Always; - mDepthStencilState.stencilBack.failOp = dawn::StencilOperation::Keep; - mDepthStencilState.stencilBack.depthFailOp = dawn::StencilOperation::Keep; - mDepthStencilState.stencilBack.passOp = dawn::StencilOperation::Keep; - mDepthStencilState.stencilFront.compare = dawn::CompareFunction::Always; - mDepthStencilState.stencilFront.failOp = dawn::StencilOperation::Keep; - mDepthStencilState.stencilFront.depthFailOp = dawn::StencilOperation::Keep; - mDepthStencilState.stencilFront.passOp = dawn::StencilOperation::Keep; + mDepthStencilState.stencilBack.compare = wgpu::CompareFunction::Always; + mDepthStencilState.stencilBack.failOp = wgpu::StencilOperation::Keep; + mDepthStencilState.stencilBack.depthFailOp = wgpu::StencilOperation::Keep; + mDepthStencilState.stencilBack.passOp = wgpu::StencilOperation::Keep; + mDepthStencilState.stencilFront.compare = wgpu::CompareFunction::Always; + mDepthStencilState.stencilFront.failOp = wgpu::StencilOperation::Keep; + mDepthStencilState.stencilFront.depthFailOp = wgpu::StencilOperation::Keep; + mDepthStencilState.stencilFront.passOp = wgpu::StencilOperation::Keep; mDepthStencilState.stencilReadMask = 0xff; mDepthStencilState.stencilWriteMask = 0xff; } - for (uint32_t i = 0; i < descriptor->colorStateCount; ++i) { - mColorAttachmentsSet.set(i); - mColorStates[i] = *descriptor->colorStates[i]; + for (uint32_t i : IterateBitSet(mAttachmentState->GetColorAttachmentsMask())) { + mColorStates[i] = descriptor->colorStates[i]; } // TODO(cwallez@chromium.org): Check against the shader module that the correct color @@ -406,37 +482,36 @@ namespace dawn_native { } RenderPipelineBase::~RenderPipelineBase() { - // Do not uncache the actual cached object if we are a blueprint - if (!mIsBlueprint && !IsError()) { + if (IsCachedReference()) { GetDevice()->UncacheRenderPipeline(this); } } - const VertexInputDescriptor* RenderPipelineBase::GetVertexInputDescriptor() const { + const VertexStateDescriptor* RenderPipelineBase::GetVertexStateDescriptor() const { ASSERT(!IsError()); - return &mVertexInput; + return &mVertexState; } - const std::bitset& RenderPipelineBase::GetAttributesSetMask() const { + const std::bitset& RenderPipelineBase::GetAttributeLocationsUsed() const { ASSERT(!IsError()); - return mAttributesSetMask; + return mAttributeLocationsUsed; } const VertexAttributeInfo& RenderPipelineBase::GetAttribute(uint32_t location) const { ASSERT(!IsError()); - ASSERT(mAttributesSetMask[location]); + ASSERT(mAttributeLocationsUsed[location]); return mAttributeInfos[location]; } - const std::bitset& RenderPipelineBase::GetInputsSetMask() const { + const std::bitset& RenderPipelineBase::GetVertexBufferSlotsUsed() const { ASSERT(!IsError()); - return mInputsSetMask; + return mVertexBufferSlotsUsed; } - const VertexBufferInfo& RenderPipelineBase::GetInput(uint32_t slot) const { + const VertexBufferInfo& RenderPipelineBase::GetVertexBuffer(uint32_t slot) const { ASSERT(!IsError()); - ASSERT(mInputsSetMask[slot]); - return mInputInfos[slot]; + ASSERT(mVertexBufferSlotsUsed[slot]); + return mVertexBufferInfos[slot]; } const ColorStateDescriptor* RenderPipelineBase::GetColorStateDescriptor( @@ -451,83 +526,62 @@ namespace dawn_native { return &mDepthStencilState; } - dawn::PrimitiveTopology RenderPipelineBase::GetPrimitiveTopology() const { + wgpu::PrimitiveTopology RenderPipelineBase::GetPrimitiveTopology() const { ASSERT(!IsError()); return mPrimitiveTopology; } - dawn::CullMode RenderPipelineBase::GetCullMode() const { + wgpu::CullMode RenderPipelineBase::GetCullMode() const { ASSERT(!IsError()); return mRasterizationState.cullMode; } - dawn::FrontFace RenderPipelineBase::GetFrontFace() const { + wgpu::FrontFace RenderPipelineBase::GetFrontFace() const { ASSERT(!IsError()); return mRasterizationState.frontFace; } std::bitset RenderPipelineBase::GetColorAttachmentsMask() const { ASSERT(!IsError()); - return mColorAttachmentsSet; + return mAttachmentState->GetColorAttachmentsMask(); } bool RenderPipelineBase::HasDepthStencilAttachment() const { ASSERT(!IsError()); - return mHasDepthStencilAttachment; + return mAttachmentState->HasDepthStencilAttachment(); } - dawn::TextureFormat RenderPipelineBase::GetColorAttachmentFormat(uint32_t attachment) const { + wgpu::TextureFormat RenderPipelineBase::GetColorAttachmentFormat(uint32_t attachment) const { ASSERT(!IsError()); return mColorStates[attachment].format; } - dawn::TextureFormat RenderPipelineBase::GetDepthStencilFormat() const { + wgpu::TextureFormat RenderPipelineBase::GetDepthStencilFormat() const { ASSERT(!IsError()); - ASSERT(mHasDepthStencilAttachment); + ASSERT(mAttachmentState->HasDepthStencilAttachment()); return mDepthStencilState.format; } uint32_t RenderPipelineBase::GetSampleCount() const { ASSERT(!IsError()); - return mSampleCount; + return mAttachmentState->GetSampleCount(); } - bool RenderPipelineBase::IsCompatibleWith(const BeginRenderPassCmd* renderPass) const { + uint32_t RenderPipelineBase::GetSampleMask() const { ASSERT(!IsError()); - // TODO(cwallez@chromium.org): This is called on every SetPipeline command. Optimize it for - // example by caching some "attachment compatibility" object that would make the - // compatibility check a single pointer comparison. - - if (renderPass->colorAttachmentsSet != mColorAttachmentsSet) { - return false; - } - - for (uint32_t i : IterateBitSet(mColorAttachmentsSet)) { - if (renderPass->colorAttachments[i].view->GetFormat() != mColorStates[i].format) { - return false; - } - } - - if (renderPass->hasDepthStencilAttachment != mHasDepthStencilAttachment) { - return false; - } - - if (mHasDepthStencilAttachment && - (renderPass->depthStencilAttachment.view->GetFormat() != mDepthStencilState.format)) { - return false; - } + return mSampleMask; + } - if (renderPass->sampleCount != mSampleCount) { - return false; - } + const AttachmentState* RenderPipelineBase::GetAttachmentState() const { + ASSERT(!IsError()); - return true; + return mAttachmentState.Get(); } - std::bitset RenderPipelineBase::GetAttributesUsingInput( + std::bitset RenderPipelineBase::GetAttributesUsingVertexBuffer( uint32_t slot) const { ASSERT(!IsError()); - return attributesUsingInput[slot]; + return attributesUsingVertexBuffer[slot]; } size_t RenderPipelineBase::HashFunc::operator()(const RenderPipelineBase* pipeline) const { @@ -538,20 +592,23 @@ namespace dawn_native { HashCombine(&hash, pipeline->mVertexModule.Get(), pipeline->mFragmentEntryPoint); HashCombine(&hash, pipeline->mFragmentModule.Get(), pipeline->mFragmentEntryPoint); + // Hierarchically hash the attachment state. + // It contains the attachments set, texture formats, and sample count. + HashCombine(&hash, pipeline->mAttachmentState.Get()); + // Hash attachments - HashCombine(&hash, pipeline->mColorAttachmentsSet); - for (uint32_t i : IterateBitSet(pipeline->mColorAttachmentsSet)) { + for (uint32_t i : IterateBitSet(pipeline->mAttachmentState->GetColorAttachmentsMask())) { const ColorStateDescriptor& desc = *pipeline->GetColorStateDescriptor(i); - HashCombine(&hash, desc.format, desc.writeMask); + HashCombine(&hash, desc.writeMask); HashCombine(&hash, desc.colorBlend.operation, desc.colorBlend.srcFactor, desc.colorBlend.dstFactor); HashCombine(&hash, desc.alphaBlend.operation, desc.alphaBlend.srcFactor, desc.alphaBlend.dstFactor); } - if (pipeline->mHasDepthStencilAttachment) { + if (pipeline->mAttachmentState->HasDepthStencilAttachment()) { const DepthStencilStateDescriptor& desc = pipeline->mDepthStencilState; - HashCombine(&hash, desc.format, desc.depthWriteEnabled, desc.depthCompare); + HashCombine(&hash, desc.depthWriteEnabled, desc.depthCompare); HashCombine(&hash, desc.stencilReadMask, desc.stencilWriteMask); HashCombine(&hash, desc.stencilFront.compare, desc.stencilFront.failOp, desc.stencilFront.depthFailOp, desc.stencilFront.passOp); @@ -559,20 +616,21 @@ namespace dawn_native { desc.stencilBack.depthFailOp, desc.stencilBack.passOp); } - // Hash vertex input state - HashCombine(&hash, pipeline->mAttributesSetMask); - for (uint32_t i : IterateBitSet(pipeline->mAttributesSetMask)) { + // Hash vertex state + HashCombine(&hash, pipeline->mAttributeLocationsUsed); + for (uint32_t i : IterateBitSet(pipeline->mAttributeLocationsUsed)) { const VertexAttributeInfo& desc = pipeline->GetAttribute(i); - HashCombine(&hash, desc.shaderLocation, desc.inputSlot, desc.offset, desc.format); + HashCombine(&hash, desc.shaderLocation, desc.vertexBufferSlot, desc.offset, + desc.format); } - HashCombine(&hash, pipeline->mInputsSetMask); - for (uint32_t i : IterateBitSet(pipeline->mInputsSetMask)) { - const VertexBufferInfo& desc = pipeline->GetInput(i); - HashCombine(&hash, desc.stride, desc.stepMode); + HashCombine(&hash, pipeline->mVertexBufferSlotsUsed); + for (uint32_t i : IterateBitSet(pipeline->mVertexBufferSlotsUsed)) { + const VertexBufferInfo& desc = pipeline->GetVertexBuffer(i); + HashCombine(&hash, desc.arrayStride, desc.stepMode); } - HashCombine(&hash, pipeline->mVertexInput.indexFormat); + HashCombine(&hash, pipeline->mVertexState.indexFormat); // Hash rasterization state { @@ -582,7 +640,8 @@ namespace dawn_native { } // Hash other state - HashCombine(&hash, pipeline->mSampleCount, pipeline->mPrimitiveTopology); + HashCombine(&hash, pipeline->mPrimitiveTopology, pipeline->mSampleMask, + pipeline->mAlphaToCoverageEnabled); return hash; } @@ -597,16 +656,16 @@ namespace dawn_native { return false; } - // Check attachments - if (a->mColorAttachmentsSet != b->mColorAttachmentsSet || - a->mHasDepthStencilAttachment != b->mHasDepthStencilAttachment) { + // Check the attachment state. + // It contains the attachments set, texture formats, and sample count. + if (a->mAttachmentState.Get() != b->mAttachmentState.Get()) { return false; } - for (uint32_t i : IterateBitSet(a->mColorAttachmentsSet)) { + for (uint32_t i : IterateBitSet(a->mAttachmentState->GetColorAttachmentsMask())) { const ColorStateDescriptor& descA = *a->GetColorStateDescriptor(i); const ColorStateDescriptor& descB = *b->GetColorStateDescriptor(i); - if (descA.format != descB.format || descA.writeMask != descB.writeMask) { + if (descA.writeMask != descB.writeMask) { return false; } if (descA.colorBlend.operation != descB.colorBlend.operation || @@ -621,11 +680,10 @@ namespace dawn_native { } } - if (a->mHasDepthStencilAttachment) { + if (a->mAttachmentState->HasDepthStencilAttachment()) { const DepthStencilStateDescriptor& descA = a->mDepthStencilState; const DepthStencilStateDescriptor& descB = b->mDepthStencilState; - if (descA.format != descB.format || - descA.depthWriteEnabled != descB.depthWriteEnabled || + if (descA.depthWriteEnabled != descB.depthWriteEnabled || descA.depthCompare != descB.depthCompare) { return false; } @@ -647,34 +705,34 @@ namespace dawn_native { } } - // Check vertex input state - if (a->mAttributesSetMask != b->mAttributesSetMask) { + // Check vertex state + if (a->mAttributeLocationsUsed != b->mAttributeLocationsUsed) { return false; } - for (uint32_t i : IterateBitSet(a->mAttributesSetMask)) { + for (uint32_t i : IterateBitSet(a->mAttributeLocationsUsed)) { const VertexAttributeInfo& descA = a->GetAttribute(i); const VertexAttributeInfo& descB = b->GetAttribute(i); if (descA.shaderLocation != descB.shaderLocation || - descA.inputSlot != descB.inputSlot || descA.offset != descB.offset || + descA.vertexBufferSlot != descB.vertexBufferSlot || descA.offset != descB.offset || descA.format != descB.format) { return false; } } - if (a->mInputsSetMask != b->mInputsSetMask) { + if (a->mVertexBufferSlotsUsed != b->mVertexBufferSlotsUsed) { return false; } - for (uint32_t i : IterateBitSet(a->mInputsSetMask)) { - const VertexBufferInfo& descA = a->GetInput(i); - const VertexBufferInfo& descB = b->GetInput(i); - if (descA.stride != descB.stride || descA.stepMode != descB.stepMode) { + for (uint32_t i : IterateBitSet(a->mVertexBufferSlotsUsed)) { + const VertexBufferInfo& descA = a->GetVertexBuffer(i); + const VertexBufferInfo& descB = b->GetVertexBuffer(i); + if (descA.arrayStride != descB.arrayStride || descA.stepMode != descB.stepMode) { return false; } } - if (a->mVertexInput.indexFormat != b->mVertexInput.indexFormat) { + if (a->mVertexState.indexFormat != b->mVertexState.indexFormat) { return false; } @@ -685,6 +743,12 @@ namespace dawn_native { if (descA.frontFace != descB.frontFace || descA.cullMode != descB.cullMode) { return false; } + + ASSERT(!std::isnan(descA.depthBiasSlopeScale)); + ASSERT(!std::isnan(descB.depthBiasSlopeScale)); + ASSERT(!std::isnan(descA.depthBiasClamp)); + ASSERT(!std::isnan(descB.depthBiasClamp)); + if (descA.depthBias != descB.depthBias || descA.depthBiasSlopeScale != descB.depthBiasSlopeScale || descA.depthBiasClamp != descB.depthBiasClamp) { @@ -693,7 +757,8 @@ namespace dawn_native { } // Check other state - if (a->mSampleCount != b->mSampleCount || a->mPrimitiveTopology != b->mPrimitiveTopology) { + if (a->mPrimitiveTopology != b->mPrimitiveTopology || a->mSampleMask != b->mSampleMask || + a->mAlphaToCoverageEnabled != b->mAlphaToCoverageEnabled) { return false; } diff --git a/third_party/dawn/src/dawn_native/RenderPipeline.h b/third_party/dawn/src/dawn_native/RenderPipeline.h index 0f33fa67185..e1bf7a0e48d 100644 --- a/third_party/dawn/src/dawn_native/RenderPipeline.h +++ b/third_party/dawn/src/dawn_native/RenderPipeline.h @@ -15,6 +15,7 @@ #ifndef DAWNNATIVE_RENDERPIPELINE_H_ #define DAWNNATIVE_RENDERPIPELINE_H_ +#include "dawn_native/AttachmentState.h" #include "dawn_native/Pipeline.h" #include "dawn_native/dawn_platform.h" @@ -27,61 +28,61 @@ namespace dawn_native { struct BeginRenderPassCmd; class DeviceBase; + class RenderBundleEncoder; - MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device, + MaybeError ValidateRenderPipelineDescriptor(const DeviceBase* device, const RenderPipelineDescriptor* descriptor); - size_t IndexFormatSize(dawn::IndexFormat format); - uint32_t VertexFormatNumComponents(dawn::VertexFormat format); - size_t VertexFormatComponentSize(dawn::VertexFormat format); - size_t VertexFormatSize(dawn::VertexFormat format); + size_t IndexFormatSize(wgpu::IndexFormat format); + uint32_t VertexFormatNumComponents(wgpu::VertexFormat format); + size_t VertexFormatComponentSize(wgpu::VertexFormat format); + size_t VertexFormatSize(wgpu::VertexFormat format); bool StencilTestEnabled(const DepthStencilStateDescriptor* mDepthStencilState); bool BlendEnabled(const ColorStateDescriptor* mColorState); struct VertexAttributeInfo { - uint32_t shaderLocation; - uint32_t inputSlot; + wgpu::VertexFormat format; uint64_t offset; - dawn::VertexFormat format; + uint32_t shaderLocation; + uint32_t vertexBufferSlot; }; struct VertexBufferInfo { - uint64_t stride; - dawn::InputStepMode stepMode; + uint64_t arrayStride; + wgpu::InputStepMode stepMode; }; class RenderPipelineBase : public PipelineBase { public: - RenderPipelineBase(DeviceBase* device, - const RenderPipelineDescriptor* descriptor, - bool blueprint = false); + RenderPipelineBase(DeviceBase* device, const RenderPipelineDescriptor* descriptor); ~RenderPipelineBase() override; static RenderPipelineBase* MakeError(DeviceBase* device); - const VertexInputDescriptor* GetVertexInputDescriptor() const; - const std::bitset& GetAttributesSetMask() const; + const VertexStateDescriptor* GetVertexStateDescriptor() const; + const std::bitset& GetAttributeLocationsUsed() const; const VertexAttributeInfo& GetAttribute(uint32_t location) const; - const std::bitset& GetInputsSetMask() const; - const VertexBufferInfo& GetInput(uint32_t slot) const; + const std::bitset& GetVertexBufferSlotsUsed() const; + const VertexBufferInfo& GetVertexBuffer(uint32_t slot) const; const ColorStateDescriptor* GetColorStateDescriptor(uint32_t attachmentSlot) const; const DepthStencilStateDescriptor* GetDepthStencilStateDescriptor() const; - dawn::PrimitiveTopology GetPrimitiveTopology() const; - dawn::CullMode GetCullMode() const; - dawn::FrontFace GetFrontFace() const; + wgpu::PrimitiveTopology GetPrimitiveTopology() const; + wgpu::CullMode GetCullMode() const; + wgpu::FrontFace GetFrontFace() const; std::bitset GetColorAttachmentsMask() const; bool HasDepthStencilAttachment() const; - dawn::TextureFormat GetColorAttachmentFormat(uint32_t attachment) const; - dawn::TextureFormat GetDepthStencilFormat() const; + wgpu::TextureFormat GetColorAttachmentFormat(uint32_t attachment) const; + wgpu::TextureFormat GetDepthStencilFormat() const; uint32_t GetSampleCount() const; + uint32_t GetSampleMask() const; - // A pipeline can be used in a render pass if its attachment info matches the actual - // attachments in the render pass. This returns whether it is the case. - bool IsCompatibleWith(const BeginRenderPassCmd* renderPassCmd) const; - std::bitset GetAttributesUsingInput(uint32_t slot) const; - std::array, kMaxVertexBuffers> attributesUsingInput; + const AttachmentState* GetAttachmentState() const; + + std::bitset GetAttributesUsingVertexBuffer(uint32_t slot) const; + std::array, kMaxVertexBuffers> + attributesUsingVertexBuffer; // Functors necessary for the unordered_set-based cache. struct HashFunc { @@ -94,23 +95,23 @@ namespace dawn_native { private: RenderPipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag); - // Vertex input - VertexInputDescriptor mVertexInput; - std::bitset mAttributesSetMask; + // Vertex state + VertexStateDescriptor mVertexState; + std::bitset mAttributeLocationsUsed; std::array mAttributeInfos; - std::bitset mInputsSetMask; - std::array mInputInfos; + std::bitset mVertexBufferSlotsUsed; + std::array mVertexBufferInfos; // Attachments - bool mHasDepthStencilAttachment = false; + Ref mAttachmentState; DepthStencilStateDescriptor mDepthStencilState; - std::bitset mColorAttachmentsSet; std::array mColorStates; // Other state - dawn::PrimitiveTopology mPrimitiveTopology; + wgpu::PrimitiveTopology mPrimitiveTopology; RasterizationStateDescriptor mRasterizationState; - uint32_t mSampleCount; + uint32_t mSampleMask; + bool mAlphaToCoverageEnabled; // Stage information // TODO(cwallez@chromium.org): Store a crypto hash of the modules instead. @@ -118,8 +119,6 @@ namespace dawn_native { std::string mVertexEntryPoint; Ref mFragmentModule; std::string mFragmentEntryPoint; - - bool mIsBlueprint = false; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ResourceHeap.h b/third_party/dawn/src/dawn_native/ResourceHeap.h new file mode 100644 index 00000000000..e9a4a672263 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ResourceHeap.h @@ -0,0 +1,31 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_RESOURCEHEAP_H_ +#define DAWNNATIVE_RESOURCEHEAP_H_ + +#include "dawn_native/Error.h" + +namespace dawn_native { + + // Wrapper for a resource backed by a heap. + class ResourceHeapBase { + public: + ResourceHeapBase() = default; + virtual ~ResourceHeapBase() = default; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_RESOURCEHEAP_H_ diff --git a/third_party/dawn/src/dawn_native/ResourceHeapAllocator.h b/third_party/dawn/src/dawn_native/ResourceHeapAllocator.h new file mode 100644 index 00000000000..1b0fd621de6 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ResourceHeapAllocator.h @@ -0,0 +1,37 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_RESOURCEHEAPALLOCATOR_H_ +#define DAWNNATIVE_RESOURCEHEAPALLOCATOR_H_ + +#include "dawn_native/Error.h" +#include "dawn_native/ResourceHeap.h" + +#include + +namespace dawn_native { + + // Interface for backend allocators that create memory heaps resoruces can be suballocated in. + class ResourceHeapAllocator { + public: + virtual ~ResourceHeapAllocator() = default; + + virtual ResultOrError> AllocateResourceHeap( + uint64_t size) = 0; + virtual void DeallocateResourceHeap(std::unique_ptr allocation) = 0; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_RESOURCEHEAPALLOCATOR_H_ diff --git a/third_party/dawn/src/dawn_native/ResourceMemoryAllocation.cpp b/third_party/dawn/src/dawn_native/ResourceMemoryAllocation.cpp new file mode 100644 index 00000000000..b1c35d41727 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ResourceMemoryAllocation.cpp @@ -0,0 +1,53 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/ResourceMemoryAllocation.h" +#include "common/Assert.h" + +namespace dawn_native { + + ResourceMemoryAllocation::ResourceMemoryAllocation() + : mOffset(0), mResourceHeap(nullptr), mMappedPointer(nullptr) { + } + + ResourceMemoryAllocation::ResourceMemoryAllocation(const AllocationInfo& info, + uint64_t offset, + ResourceHeapBase* resourceHeap, + uint8_t* mappedPointer) + : mInfo(info), mOffset(offset), mResourceHeap(resourceHeap), mMappedPointer(mappedPointer) { + } + + ResourceHeapBase* ResourceMemoryAllocation::GetResourceHeap() const { + ASSERT(mInfo.mMethod != AllocationMethod::kInvalid); + return mResourceHeap; + } + + uint64_t ResourceMemoryAllocation::GetOffset() const { + ASSERT(mInfo.mMethod != AllocationMethod::kInvalid); + return mOffset; + } + + AllocationInfo ResourceMemoryAllocation::GetInfo() const { + return mInfo; + } + + uint8_t* ResourceMemoryAllocation::GetMappedPointer() const { + return mMappedPointer; + } + + void ResourceMemoryAllocation::Invalidate() { + mResourceHeap = nullptr; + mInfo = {}; + } +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ResourceMemoryAllocation.h b/third_party/dawn/src/dawn_native/ResourceMemoryAllocation.h new file mode 100644 index 00000000000..1eb68e75526 --- /dev/null +++ b/third_party/dawn/src/dawn_native/ResourceMemoryAllocation.h @@ -0,0 +1,77 @@ +// Copyright 2018 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_RESOURCEMEMORYALLOCATION_H_ +#define DAWNNATIVE_RESOURCEMEMORYALLOCATION_H_ + +#include + +namespace dawn_native { + + class ResourceHeapBase; + + // Allocation method determines how memory was sub-divided. + // Used by the device to get the allocator that was responsible for the allocation. + enum class AllocationMethod { + + // Memory not sub-divided. + kDirect, + + // Memory sub-divided using one or more blocks of various sizes. + kSubAllocated, + + // Memory was allocated outside of Dawn. + kExternal, + + // Memory not allocated or freed. + kInvalid + }; + + // Metadata that describes how the allocation was allocated. + struct AllocationInfo { + // AllocationInfo contains a separate offset to not confuse block vs memory offsets. + // The block offset is within the entire allocator memory range and only required by the + // buddy sub-allocator to get the corresponding memory. Unlike the block offset, the + // allocation offset is always local to the memory. + uint64_t mBlockOffset = 0; + + AllocationMethod mMethod = AllocationMethod::kInvalid; + }; + + // Handle into a resource heap pool. + class ResourceMemoryAllocation { + public: + ResourceMemoryAllocation(); + ResourceMemoryAllocation(const AllocationInfo& info, + uint64_t offset, + ResourceHeapBase* resourceHeap, + uint8_t* mappedPointer = nullptr); + virtual ~ResourceMemoryAllocation() = default; + + ResourceHeapBase* GetResourceHeap() const; + uint64_t GetOffset() const; + uint8_t* GetMappedPointer() const; + AllocationInfo GetInfo() const; + + virtual void Invalidate(); + + private: + AllocationInfo mInfo; + uint64_t mOffset; + ResourceHeapBase* mResourceHeap; + uint8_t* mMappedPointer; + }; +} // namespace dawn_native + +#endif // DAWNNATIVE_RESOURCEMEMORYALLOCATION_H_ diff --git a/third_party/dawn/src/dawn_native/RingBufferAllocator.cpp b/third_party/dawn/src/dawn_native/RingBufferAllocator.cpp new file mode 100644 index 00000000000..bf73a978f16 --- /dev/null +++ b/third_party/dawn/src/dawn_native/RingBufferAllocator.cpp @@ -0,0 +1,122 @@ +// Copyright 2018 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/RingBufferAllocator.h" + +// Note: Current RingBufferAllocator implementation uses two indices (start and end) to implement a +// circular queue. However, this approach defines a full queue when one element is still unused. +// +// For example, [E,E,E,E] would be equivelent to [U,U,U,U]. +// ^ ^ +// S=E=1 S=E=1 +// +// The latter case is eliminated by counting used bytes >= capacity. This definition prevents +// (the last) byte and requires an extra variable to count used bytes. Alternatively, we could use +// only two indices that keep increasing (unbounded) but can be still indexed using bit masks. +// However, this 1) requires the size to always be a power-of-two and 2) remove tests that check +// used bytes. +// TODO(bryan.bernhart@intel.com): Follow-up with ringbuffer optimization. +namespace dawn_native { + + RingBufferAllocator::RingBufferAllocator(uint64_t maxSize) : mMaxBlockSize(maxSize) { + } + + void RingBufferAllocator::Deallocate(Serial lastCompletedSerial) { + // Reclaim memory from previously recorded blocks. + for (Request& request : mInflightRequests.IterateUpTo(lastCompletedSerial)) { + mUsedStartOffset = request.endOffset; + mUsedSize -= request.size; + } + + // Dequeue previously recorded requests. + mInflightRequests.ClearUpTo(lastCompletedSerial); + } + + uint64_t RingBufferAllocator::GetSize() const { + return mMaxBlockSize; + } + + uint64_t RingBufferAllocator::GetUsedSize() const { + return mUsedSize; + } + + bool RingBufferAllocator::Empty() const { + return mInflightRequests.Empty(); + } + + // Sub-allocate the ring-buffer by requesting a chunk of the specified size. + // This is a serial-based resource scheme, the life-span of resources (and the allocations) get + // tracked by GPU progress via serials. Memory can be reused by determining if the GPU has + // completed up to a given serial. Each sub-allocation request is tracked in the serial offset + // queue, which identifies an existing (or new) frames-worth of resources. Internally, the + // ring-buffer maintains offsets of 3 "memory" states: Free, Reclaimed, and Used. This is done + // in FIFO order as older frames would free resources before newer ones. + uint64_t RingBufferAllocator::Allocate(uint64_t allocationSize, Serial serial) { + // Check if the buffer is full by comparing the used size. + // If the buffer is not split where waste occurs (e.g. cannot fit new sub-alloc in front), a + // subsequent sub-alloc could fail where the used size was previously adjusted to include + // the wasted. + if (mUsedSize >= mMaxBlockSize) { + return kInvalidOffset; + } + + // Ensure adding allocationSize does not overflow. + const uint64_t remainingSize = (mMaxBlockSize - mUsedSize); + if (allocationSize > remainingSize) { + return kInvalidOffset; + } + + uint64_t startOffset = kInvalidOffset; + + // Check if the buffer is NOT split (i.e sub-alloc on ends) + if (mUsedStartOffset <= mUsedEndOffset) { + // Order is important (try to sub-alloc at end first). + // This is due to FIFO order where sub-allocs are inserted from left-to-right (when not + // wrapped). + if (mUsedEndOffset + allocationSize <= mMaxBlockSize) { + startOffset = mUsedEndOffset; + mUsedEndOffset += allocationSize; + mUsedSize += allocationSize; + mCurrentRequestSize += allocationSize; + } else if (allocationSize <= mUsedStartOffset) { // Try to sub-alloc at front. + // Count the space at the end so that a subsequent + // sub-alloc cannot not succeed when the buffer is full. + const uint64_t requestSize = (mMaxBlockSize - mUsedEndOffset) + allocationSize; + + startOffset = 0; + mUsedEndOffset = allocationSize; + mUsedSize += requestSize; + mCurrentRequestSize += requestSize; + } + } else if (mUsedEndOffset + allocationSize <= + mUsedStartOffset) { // Otherwise, buffer is split where sub-alloc must be + // in-between. + startOffset = mUsedEndOffset; + mUsedEndOffset += allocationSize; + mUsedSize += allocationSize; + mCurrentRequestSize += allocationSize; + } + + if (startOffset != kInvalidOffset) { + Request request; + request.endOffset = mUsedEndOffset; + request.size = mCurrentRequestSize; + + mInflightRequests.Enqueue(std::move(request), serial); + mCurrentRequestSize = 0; // reset + } + + return startOffset; + } +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/RingBufferAllocator.h b/third_party/dawn/src/dawn_native/RingBufferAllocator.h new file mode 100644 index 00000000000..50b0f15d001 --- /dev/null +++ b/third_party/dawn/src/dawn_native/RingBufferAllocator.h @@ -0,0 +1,59 @@ +// Copyright 2018 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_RINGBUFFERALLOCATOR_H_ +#define DAWNNATIVE_RINGBUFFERALLOCATOR_H_ + +#include "common/SerialQueue.h" + +#include +#include + +// RingBufferAllocator is the front-end implementation used to manage a ring buffer in GPU memory. +namespace dawn_native { + + class RingBufferAllocator { + public: + RingBufferAllocator() = default; + RingBufferAllocator(uint64_t maxSize); + ~RingBufferAllocator() = default; + + uint64_t Allocate(uint64_t allocationSize, Serial serial); + void Deallocate(Serial lastCompletedSerial); + + uint64_t GetSize() const; + bool Empty() const; + uint64_t GetUsedSize() const; + + static constexpr uint64_t kInvalidOffset = std::numeric_limits::max(); + + private: + struct Request { + uint64_t endOffset; + uint64_t size; + }; + + SerialQueue mInflightRequests; // Queue of the recorded sub-alloc requests (e.g. + // frame of resources). + + uint64_t mUsedEndOffset = 0; // Tail of used sub-alloc requests (in bytes). + uint64_t mUsedStartOffset = 0; // Head of used sub-alloc requests (in bytes). + uint64_t mMaxBlockSize = 0; // Max size of the ring buffer (in bytes). + uint64_t mUsedSize = 0; // Size of the sub-alloc requests (in bytes) of the ring buffer. + uint64_t mCurrentRequestSize = + 0; // Size of the sub-alloc requests (in bytes) of the current serial. + }; +} // namespace dawn_native + +#endif // DAWNNATIVE_RINGBUFFERALLOCATOR_H_ diff --git a/third_party/dawn/src/dawn_native/Sampler.cpp b/third_party/dawn/src/dawn_native/Sampler.cpp index 78dfe8d6a4c..748956e60c4 100644 --- a/third_party/dawn/src/dawn_native/Sampler.cpp +++ b/third_party/dawn/src/dawn_native/Sampler.cpp @@ -27,12 +27,12 @@ namespace dawn_native { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - if (!std::isfinite(descriptor->lodMinClamp) || !std::isfinite(descriptor->lodMaxClamp)) { - return DAWN_VALIDATION_ERROR("LOD must be finite"); + if (std::isnan(descriptor->lodMinClamp) || std::isnan(descriptor->lodMaxClamp)) { + return DAWN_VALIDATION_ERROR("LOD clamp bounds must not be NaN"); } if (descriptor->lodMinClamp < 0 || descriptor->lodMaxClamp < 0) { - return DAWN_VALIDATION_ERROR("LOD must be positive"); + return DAWN_VALIDATION_ERROR("LOD clamp bounds must be positive"); } if (descriptor->lodMinClamp > descriptor->lodMaxClamp) { @@ -46,16 +46,14 @@ namespace dawn_native { DAWN_TRY(ValidateAddressMode(descriptor->addressModeU)); DAWN_TRY(ValidateAddressMode(descriptor->addressModeV)); DAWN_TRY(ValidateAddressMode(descriptor->addressModeW)); - DAWN_TRY(ValidateCompareFunction(descriptor->compareFunction)); + DAWN_TRY(ValidateCompareFunction(descriptor->compare)); return {}; } // SamplerBase - SamplerBase::SamplerBase(DeviceBase* device, - const SamplerDescriptor* descriptor, - bool blueprint) - : ObjectBase(device), + SamplerBase::SamplerBase(DeviceBase* device, const SamplerDescriptor* descriptor) + : CachedObject(device), mAddressModeU(descriptor->addressModeU), mAddressModeV(descriptor->addressModeV), mAddressModeW(descriptor->addressModeW), @@ -64,17 +62,15 @@ namespace dawn_native { mMipmapFilter(descriptor->mipmapFilter), mLodMinClamp(descriptor->lodMinClamp), mLodMaxClamp(descriptor->lodMaxClamp), - mCompareFunction(descriptor->compareFunction), - mIsBlueprint(blueprint) { + mCompareFunction(descriptor->compare) { } SamplerBase::SamplerBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag) { + : CachedObject(device, tag) { } SamplerBase::~SamplerBase() { - // Do not uncache the actual cached object if we are a blueprint - if (!mIsBlueprint && !IsError()) { + if (IsCachedReference()) { GetDevice()->UncacheSampler(this); } } @@ -84,6 +80,10 @@ namespace dawn_native { return new SamplerBase(device, ObjectBase::kError); } + bool SamplerBase::HasCompareFunction() const { + return mCompareFunction != wgpu::CompareFunction::Undefined; + } + size_t SamplerBase::HashFunc::operator()(const SamplerBase* module) const { size_t hash = 0; @@ -105,10 +105,10 @@ namespace dawn_native { return true; } - ASSERT(std::isfinite(a->mLodMinClamp)); - ASSERT(std::isfinite(b->mLodMinClamp)); - ASSERT(std::isfinite(a->mLodMaxClamp)); - ASSERT(std::isfinite(b->mLodMaxClamp)); + ASSERT(!std::isnan(a->mLodMinClamp)); + ASSERT(!std::isnan(b->mLodMinClamp)); + ASSERT(!std::isnan(a->mLodMaxClamp)); + ASSERT(!std::isnan(b->mLodMaxClamp)); return a->mAddressModeU == b->mAddressModeU && a->mAddressModeV == b->mAddressModeV && a->mAddressModeW == b->mAddressModeW && a->mMagFilter == b->mMagFilter && diff --git a/third_party/dawn/src/dawn_native/Sampler.h b/third_party/dawn/src/dawn_native/Sampler.h index 202f3cde1f4..3829b90d3b0 100644 --- a/third_party/dawn/src/dawn_native/Sampler.h +++ b/third_party/dawn/src/dawn_native/Sampler.h @@ -15,8 +15,8 @@ #ifndef DAWNNATIVE_SAMPLER_H_ #define DAWNNATIVE_SAMPLER_H_ +#include "dawn_native/CachedObject.h" #include "dawn_native/Error.h" -#include "dawn_native/ObjectBase.h" #include "dawn_native/dawn_platform.h" @@ -26,15 +26,15 @@ namespace dawn_native { MaybeError ValidateSamplerDescriptor(DeviceBase* device, const SamplerDescriptor* descriptor); - class SamplerBase : public ObjectBase { + class SamplerBase : public CachedObject { public: - SamplerBase(DeviceBase* device, - const SamplerDescriptor* descriptor, - bool blueprint = false); + SamplerBase(DeviceBase* device, const SamplerDescriptor* descriptor); ~SamplerBase() override; static SamplerBase* MakeError(DeviceBase* device); + bool HasCompareFunction() const; + // Functors necessary for the unordered_set-based cache. struct HashFunc { size_t operator()(const SamplerBase* module) const; @@ -47,16 +47,15 @@ namespace dawn_native { SamplerBase(DeviceBase* device, ObjectBase::ErrorTag tag); // TODO(cwallez@chromium.org): Store a crypto hash of the items instead? - dawn::AddressMode mAddressModeU; - dawn::AddressMode mAddressModeV; - dawn::AddressMode mAddressModeW; - dawn::FilterMode mMagFilter; - dawn::FilterMode mMinFilter; - dawn::FilterMode mMipmapFilter; + wgpu::AddressMode mAddressModeU; + wgpu::AddressMode mAddressModeV; + wgpu::AddressMode mAddressModeW; + wgpu::FilterMode mMagFilter; + wgpu::FilterMode mMinFilter; + wgpu::FilterMode mMipmapFilter; float mLodMinClamp; float mLodMaxClamp; - dawn::CompareFunction mCompareFunction; - bool mIsBlueprint = false; + wgpu::CompareFunction mCompareFunction; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ShaderModule.cpp b/third_party/dawn/src/dawn_native/ShaderModule.cpp index ac025e6594b..04b6dfc2079 100644 --- a/third_party/dawn/src/dawn_native/ShaderModule.cpp +++ b/third_party/dawn/src/dawn_native/ShaderModule.cpp @@ -20,19 +20,284 @@ #include "dawn_native/Pipeline.h" #include "dawn_native/PipelineLayout.h" -#include #include +#include + +#ifdef DAWN_ENABLE_WGSL +// Tint include must be after spirv_cross.hpp, because spirv-cross has its own +// version of spirv_headers. +// clang-format off +#include +// clang-format on +#endif // DAWN_ENABLE_WGSL #include namespace dawn_native { - MaybeError ValidateShaderModuleDescriptor(DeviceBase*, - const ShaderModuleDescriptor* descriptor) { - if (descriptor->nextInChain != nullptr) { - return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); + namespace { + Format::Type SpirvCrossBaseTypeToFormatType(spirv_cross::SPIRType::BaseType spirvBaseType) { + switch (spirvBaseType) { + case spirv_cross::SPIRType::Float: + return Format::Float; + case spirv_cross::SPIRType::Int: + return Format::Sint; + case spirv_cross::SPIRType::UInt: + return Format::Uint; + default: + UNREACHABLE(); + return Format::Other; + } + } + + wgpu::TextureViewDimension SpirvDimToTextureViewDimension(spv::Dim dim, bool arrayed) { + switch (dim) { + case spv::Dim::Dim1D: + return wgpu::TextureViewDimension::e1D; + case spv::Dim::Dim2D: + if (arrayed) { + return wgpu::TextureViewDimension::e2DArray; + } else { + return wgpu::TextureViewDimension::e2D; + } + case spv::Dim::Dim3D: + return wgpu::TextureViewDimension::e3D; + case spv::Dim::DimCube: + if (arrayed) { + return wgpu::TextureViewDimension::CubeArray; + } else { + return wgpu::TextureViewDimension::Cube; + } + default: + UNREACHABLE(); + return wgpu::TextureViewDimension::Undefined; + } + } + + wgpu::TextureViewDimension ToWGPUTextureViewDimension( + shaderc_spvc_texture_view_dimension dim) { + switch (dim) { + case shaderc_spvc_texture_view_dimension_undefined: + return wgpu::TextureViewDimension::Undefined; + case shaderc_spvc_texture_view_dimension_e1D: + return wgpu::TextureViewDimension::e1D; + case shaderc_spvc_texture_view_dimension_e2D: + return wgpu::TextureViewDimension::e2D; + case shaderc_spvc_texture_view_dimension_e2D_array: + return wgpu::TextureViewDimension::e2DArray; + case shaderc_spvc_texture_view_dimension_cube: + return wgpu::TextureViewDimension::Cube; + case shaderc_spvc_texture_view_dimension_cube_array: + return wgpu::TextureViewDimension::CubeArray; + case shaderc_spvc_texture_view_dimension_e3D: + return wgpu::TextureViewDimension::e3D; + } + UNREACHABLE(); + } + + Format::Type ToDawnFormatType(shaderc_spvc_texture_format_type type) { + switch (type) { + case shaderc_spvc_texture_format_type_float: + return Format::Type::Float; + case shaderc_spvc_texture_format_type_sint: + return Format::Type::Sint; + case shaderc_spvc_texture_format_type_uint: + return Format::Type::Uint; + case shaderc_spvc_texture_format_type_other: + return Format::Type::Other; + } + UNREACHABLE(); + } + + wgpu::BindingType ToWGPUBindingType(shaderc_spvc_binding_type type) { + switch (type) { + case shaderc_spvc_binding_type_uniform_buffer: + return wgpu::BindingType::UniformBuffer; + case shaderc_spvc_binding_type_storage_buffer: + return wgpu::BindingType::StorageBuffer; + case shaderc_spvc_binding_type_readonly_storage_buffer: + return wgpu::BindingType::ReadonlyStorageBuffer; + case shaderc_spvc_binding_type_sampler: + return wgpu::BindingType::Sampler; + case shaderc_spvc_binding_type_comparison_sampler: + return wgpu::BindingType::ComparisonSampler; + case shaderc_spvc_binding_type_sampled_texture: + return wgpu::BindingType::SampledTexture; + case shaderc_spvc_binding_type_readonly_storage_texture: + return wgpu::BindingType::ReadonlyStorageTexture; + case shaderc_spvc_binding_type_writeonly_storage_texture: + return wgpu::BindingType::WriteonlyStorageTexture; + case shaderc_spvc_binding_type_storage_texture: + return wgpu::BindingType::StorageTexture; + default: + UNREACHABLE(); + } + } + + SingleShaderStage ToSingleShaderStage(shaderc_spvc_execution_model execution_model) { + switch (execution_model) { + case shaderc_spvc_execution_model_vertex: + return SingleShaderStage::Vertex; + case shaderc_spvc_execution_model_fragment: + return SingleShaderStage::Fragment; + case shaderc_spvc_execution_model_glcompute: + return SingleShaderStage::Compute; + default: + UNREACHABLE(); + } + } + + wgpu::TextureFormat ToWGPUTextureFormat(spv::ImageFormat format) { + switch (format) { + case spv::ImageFormatR8: + return wgpu::TextureFormat::R8Unorm; + case spv::ImageFormatR8Snorm: + return wgpu::TextureFormat::R8Snorm; + case spv::ImageFormatR8ui: + return wgpu::TextureFormat::R8Uint; + case spv::ImageFormatR8i: + return wgpu::TextureFormat::R8Sint; + case spv::ImageFormatR16ui: + return wgpu::TextureFormat::R16Uint; + case spv::ImageFormatR16i: + return wgpu::TextureFormat::R16Sint; + case spv::ImageFormatR16f: + return wgpu::TextureFormat::R16Float; + case spv::ImageFormatRg8: + return wgpu::TextureFormat::RG8Unorm; + case spv::ImageFormatRg8Snorm: + return wgpu::TextureFormat::RG8Snorm; + case spv::ImageFormatRg8ui: + return wgpu::TextureFormat::RG8Uint; + case spv::ImageFormatRg8i: + return wgpu::TextureFormat::RG8Sint; + case spv::ImageFormatR32f: + return wgpu::TextureFormat::R32Float; + case spv::ImageFormatR32ui: + return wgpu::TextureFormat::R32Uint; + case spv::ImageFormatR32i: + return wgpu::TextureFormat::R32Sint; + case spv::ImageFormatRg16ui: + return wgpu::TextureFormat::RG16Uint; + case spv::ImageFormatRg16i: + return wgpu::TextureFormat::RG16Sint; + case spv::ImageFormatRg16f: + return wgpu::TextureFormat::RG16Float; + case spv::ImageFormatRgba8: + return wgpu::TextureFormat::RGBA8Unorm; + case spv::ImageFormatRgba8Snorm: + return wgpu::TextureFormat::RGBA8Snorm; + case spv::ImageFormatRgba8ui: + return wgpu::TextureFormat::RGBA8Uint; + case spv::ImageFormatRgba8i: + return wgpu::TextureFormat::RGBA8Sint; + case spv::ImageFormatRgb10A2: + return wgpu::TextureFormat::RGB10A2Unorm; + case spv::ImageFormatR11fG11fB10f: + return wgpu::TextureFormat::RG11B10Float; + case spv::ImageFormatRg32f: + return wgpu::TextureFormat::RG32Float; + case spv::ImageFormatRg32ui: + return wgpu::TextureFormat::RG32Uint; + case spv::ImageFormatRg32i: + return wgpu::TextureFormat::RG32Sint; + case spv::ImageFormatRgba16ui: + return wgpu::TextureFormat::RGBA16Uint; + case spv::ImageFormatRgba16i: + return wgpu::TextureFormat::RGBA16Sint; + case spv::ImageFormatRgba16f: + return wgpu::TextureFormat::RGBA16Float; + case spv::ImageFormatRgba32f: + return wgpu::TextureFormat::RGBA32Float; + case spv::ImageFormatRgba32ui: + return wgpu::TextureFormat::RGBA32Uint; + case spv::ImageFormatRgba32i: + return wgpu::TextureFormat::RGBA32Sint; + default: + return wgpu::TextureFormat::Undefined; + } } + wgpu::TextureFormat ToWGPUTextureFormat(shaderc_spvc_storage_texture_format format) { + switch (format) { + case shaderc_spvc_storage_texture_format_r8unorm: + return wgpu::TextureFormat::R8Unorm; + case shaderc_spvc_storage_texture_format_r8snorm: + return wgpu::TextureFormat::R8Snorm; + case shaderc_spvc_storage_texture_format_r8uint: + return wgpu::TextureFormat::R8Uint; + case shaderc_spvc_storage_texture_format_r8sint: + return wgpu::TextureFormat::R8Sint; + case shaderc_spvc_storage_texture_format_r16uint: + return wgpu::TextureFormat::R16Uint; + case shaderc_spvc_storage_texture_format_r16sint: + return wgpu::TextureFormat::R16Sint; + case shaderc_spvc_storage_texture_format_r16float: + return wgpu::TextureFormat::R16Float; + case shaderc_spvc_storage_texture_format_rg8unorm: + return wgpu::TextureFormat::RG8Unorm; + case shaderc_spvc_storage_texture_format_rg8snorm: + return wgpu::TextureFormat::RG8Snorm; + case shaderc_spvc_storage_texture_format_rg8uint: + return wgpu::TextureFormat::RG8Uint; + case shaderc_spvc_storage_texture_format_rg8sint: + return wgpu::TextureFormat::RG8Sint; + case shaderc_spvc_storage_texture_format_r32float: + return wgpu::TextureFormat::R32Float; + case shaderc_spvc_storage_texture_format_r32uint: + return wgpu::TextureFormat::R32Uint; + case shaderc_spvc_storage_texture_format_r32sint: + return wgpu::TextureFormat::R32Sint; + case shaderc_spvc_storage_texture_format_rg16uint: + return wgpu::TextureFormat::RG16Uint; + case shaderc_spvc_storage_texture_format_rg16sint: + return wgpu::TextureFormat::RG16Sint; + case shaderc_spvc_storage_texture_format_rg16float: + return wgpu::TextureFormat::RG16Float; + case shaderc_spvc_storage_texture_format_rgba8unorm: + return wgpu::TextureFormat::RGBA8Unorm; + case shaderc_spvc_storage_texture_format_rgba8snorm: + return wgpu::TextureFormat::RGBA8Snorm; + case shaderc_spvc_storage_texture_format_rgba8uint: + return wgpu::TextureFormat::RGBA8Uint; + case shaderc_spvc_storage_texture_format_rgba8sint: + return wgpu::TextureFormat::RGBA8Sint; + case shaderc_spvc_storage_texture_format_rgb10a2unorm: + return wgpu::TextureFormat::RGB10A2Unorm; + case shaderc_spvc_storage_texture_format_rg11b10float: + return wgpu::TextureFormat::RG11B10Float; + case shaderc_spvc_storage_texture_format_rg32float: + return wgpu::TextureFormat::RG32Float; + case shaderc_spvc_storage_texture_format_rg32uint: + return wgpu::TextureFormat::RG32Uint; + case shaderc_spvc_storage_texture_format_rg32sint: + return wgpu::TextureFormat::RG32Sint; + case shaderc_spvc_storage_texture_format_rgba16uint: + return wgpu::TextureFormat::RGBA16Uint; + case shaderc_spvc_storage_texture_format_rgba16sint: + return wgpu::TextureFormat::RGBA16Sint; + case shaderc_spvc_storage_texture_format_rgba16float: + return wgpu::TextureFormat::RGBA16Float; + case shaderc_spvc_storage_texture_format_rgba32float: + return wgpu::TextureFormat::RGBA32Float; + case shaderc_spvc_storage_texture_format_rgba32uint: + return wgpu::TextureFormat::RGBA32Uint; + case shaderc_spvc_storage_texture_format_rgba32sint: + return wgpu::TextureFormat::RGBA32Sint; + default: + return wgpu::TextureFormat::Undefined; + } + } + + std::string GetShaderDeclarationString(BindGroupIndex group, BindingNumber binding) { + std::ostringstream ostream; + ostream << "the shader module declaration at set " << static_cast(group) + << " binding " << static_cast(binding); + return ostream.str(); + } + } // anonymous namespace + + MaybeError ValidateSpirv(DeviceBase*, const uint32_t* code, uint32_t codeSize) { spvtools::SpirvTools spirvTools(SPV_ENV_VULKAN_1_1); std::ostringstream errorStream; @@ -59,41 +324,157 @@ namespace dawn_native { } }); - if (!spirvTools.Validate(descriptor->code, descriptor->codeSize)) { + if (!spirvTools.Validate(code, codeSize)) { return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); } return {}; } - dawn::BindingType NonDynamicBindingType(dawn::BindingType type) { - switch (type) { - case dawn::BindingType::DynamicUniformBuffer: - return dawn::BindingType::UniformBuffer; - case dawn::BindingType::DynamicStorageBuffer: - return dawn::BindingType::StorageBuffer; - default: - return type; +#ifdef DAWN_ENABLE_WGSL + MaybeError ValidateWGSL(const char* source) { + std::ostringstream errorStream; + errorStream << "Tint WGSL failure:" << std::endl; + + tint::Context context; + tint::reader::wgsl::Parser parser(&context, source); + + if (!parser.Parse()) { + errorStream << "Parser: " << parser.error() << std::endl; + return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); + } + + tint::ast::Module module = parser.module(); + if (!module.IsValid()) { + errorStream << "Invalid module generated..." << std::endl; + return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); + } + + tint::TypeDeterminer type_determiner(&context, &module); + if (!type_determiner.Determine()) { + errorStream << "Type Determination: " << type_determiner.error(); + return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); + } + + tint::Validator validator; + if (!validator.Validate(module)) { + errorStream << "Validation: " << validator.error() << std::endl; + return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); } + + return {}; } + ResultOrError> ConvertWGSLToSPIRV(const char* source) { + std::ostringstream errorStream; + errorStream << "Tint WGSL->SPIR-V failure:" << std::endl; + + tint::Context context; + tint::reader::wgsl::Parser parser(&context, source); + + // TODO: This is a duplicate parse with ValidateWGSL, need to store + // state between calls to avoid this. + if (!parser.Parse()) { + errorStream << "Parser: " << parser.error() << std::endl; + return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); + } + + tint::ast::Module module = parser.module(); + if (!module.IsValid()) { + errorStream << "Invalid module generated..." << std::endl; + return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); + } + + tint::TypeDeterminer type_determiner(&context, &module); + if (!type_determiner.Determine()) { + errorStream << "Type Determination: " << type_determiner.error(); + return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); + } + + tint::writer::spirv::Generator generator(std::move(module)); + if (!generator.Generate()) { + errorStream << "Generator: " << generator.error() << std::endl; + return DAWN_VALIDATION_ERROR(errorStream.str().c_str()); + } + + std::vector spirv = generator.result(); + return std::move(spirv); + } +#endif // DAWN_ENABLE_WGSL + + MaybeError ValidateShaderModuleDescriptor(DeviceBase* device, + const ShaderModuleDescriptor* descriptor) { + const ChainedStruct* chainedDescriptor = descriptor->nextInChain; + if (chainedDescriptor == nullptr) { + return DAWN_VALIDATION_ERROR("Shader module descriptor missing chained descriptor"); + } + // For now only a single SPIRV or WGSL subdescriptor is allowed. + if (chainedDescriptor->nextInChain != nullptr) { + return DAWN_VALIDATION_ERROR( + "Shader module descriptor chained nextInChain must be nullptr"); + } + + switch (chainedDescriptor->sType) { + case wgpu::SType::ShaderModuleSPIRVDescriptor: { + const auto* spirvDesc = + static_cast(chainedDescriptor); + DAWN_TRY(ValidateSpirv(device, spirvDesc->code, spirvDesc->codeSize)); + break; + } + + case wgpu::SType::ShaderModuleWGSLDescriptor: { +#ifdef DAWN_ENABLE_WGSL + const auto* wgslDesc = + static_cast(chainedDescriptor); + DAWN_TRY(ValidateWGSL(wgslDesc->source)); + break; +#else + return DAWN_VALIDATION_ERROR("WGSL not supported (yet)"); +#endif // DAWN_ENABLE_WGSL + } + default: + return DAWN_VALIDATION_ERROR("Unsupported sType"); + } + + return {}; + } // namespace + // ShaderModuleBase - ShaderModuleBase::ShaderModuleBase(DeviceBase* device, - const ShaderModuleDescriptor* descriptor, - bool blueprint) - : ObjectBase(device), - mCode(descriptor->code, descriptor->code + descriptor->codeSize), - mIsBlueprint(blueprint) { + ShaderModuleBase::ShaderModuleBase(DeviceBase* device, const ShaderModuleDescriptor* descriptor) + : CachedObject(device), mType(Type::Undefined) { + ASSERT(descriptor->nextInChain != nullptr); + switch (descriptor->nextInChain->sType) { + case wgpu::SType::ShaderModuleSPIRVDescriptor: { + mType = Type::Spirv; + const auto* spirvDesc = + static_cast(descriptor->nextInChain); + mSpirv.assign(spirvDesc->code, spirvDesc->code + spirvDesc->codeSize); + break; + } + case wgpu::SType::ShaderModuleWGSLDescriptor: { + mType = Type::Wgsl; + const auto* wgslDesc = + static_cast(descriptor->nextInChain); + mWgsl = std::string(wgslDesc->source); + break; + } + default: + UNREACHABLE(); + } + + mFragmentOutputFormatBaseTypes.fill(Format::Other); + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvcParser)) { + mSpvcContext.SetUseSpvcParser(true); + } } ShaderModuleBase::ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag) { + : CachedObject(device, tag), mType(Type::Undefined) { } ShaderModuleBase::~ShaderModuleBase() { - // Do not uncache the actual cached object if we are a blueprint - if (!mIsBlueprint && !IsError()) { + if (IsCachedReference()) { GetDevice()->UncacheShaderModule(this); } } @@ -103,100 +484,384 @@ namespace dawn_native { return new ShaderModuleBase(device, ObjectBase::kError); } - void ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) { + MaybeError ShaderModuleBase::ExtractSpirvInfo(const spirv_cross::Compiler& compiler) { ASSERT(!IsError()); + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + DAWN_TRY(ExtractSpirvInfoWithSpvc()); + } else { + DAWN_TRY(ExtractSpirvInfoWithSpirvCross(compiler)); + } + return {}; + } - DeviceBase* device = GetDevice(); + MaybeError ShaderModuleBase::ExtractSpirvInfoWithSpvc() { + shaderc_spvc_execution_model execution_model; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetExecutionModel(&execution_model), + "Unable to get execution model for shader.")); + mExecutionModel = ToSingleShaderStage(execution_model); + + size_t push_constant_buffers_count; + DAWN_TRY( + CheckSpvcSuccess(mSpvcContext.GetPushConstantBufferCount(&push_constant_buffers_count), + "Unable to get push constant buffer count for shader.")); + + // TODO(rharrison): This should be handled by spirv-val pass in spvc, + // but need to confirm. + if (push_constant_buffers_count > 0) { + return DAWN_VALIDATION_ERROR("Push constants aren't supported."); + } + + // Fill in bindingInfo with the SPIRV bindings + auto ExtractResourcesBinding = + [this](std::vector bindings) -> MaybeError { + for (const auto& binding : bindings) { + BindGroupIndex bindGroupIndex(binding.set); + + if (bindGroupIndex >= kMaxBindGroupsTyped) { + return DAWN_VALIDATION_ERROR("Bind group index over limits in the SPIRV"); + } + + const auto& it = mBindingInfo[bindGroupIndex].emplace( + BindingNumber(binding.binding), ShaderBindingInfo{}); + if (!it.second) { + return DAWN_VALIDATION_ERROR("Shader has duplicate bindings"); + } + + ShaderBindingInfo* info = &it.first->second; + info->id = binding.id; + info->base_type_id = binding.base_type_id; + info->type = ToWGPUBindingType(binding.binding_type); + + switch (info->type) { + case wgpu::BindingType::SampledTexture: { + info->multisampled = binding.multisampled; + info->viewDimension = ToWGPUTextureViewDimension(binding.texture_dimension); + info->textureComponentType = + ToDawnFormatType(binding.texture_component_type); + break; + } + case wgpu::BindingType::StorageTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + wgpu::TextureFormat storageTextureFormat = + ToWGPUTextureFormat(binding.storage_texture_format); + if (storageTextureFormat == wgpu::TextureFormat::Undefined) { + return DAWN_VALIDATION_ERROR( + "Invalid image format declaration on storage image"); + } + const Format& format = + GetDevice()->GetValidInternalFormat(storageTextureFormat); + if (!format.supportsStorageUsage) { + return DAWN_VALIDATION_ERROR( + "The storage texture format is not supported"); + } + info->multisampled = binding.multisampled; + info->storageTextureFormat = storageTextureFormat; + info->viewDimension = ToWGPUTextureViewDimension(binding.texture_dimension); + break; + } + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + info->minBufferBindingSize = binding.minimum_buffer_size; + break; + default: + break; + } + } + return {}; + }; + + std::vector resource_bindings; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetBindingInfo( + shaderc_spvc_shader_resource_uniform_buffers, + shaderc_spvc_binding_type_uniform_buffer, &resource_bindings), + "Unable to get binding info for uniform buffers from shader")); + DAWN_TRY(ExtractResourcesBinding(resource_bindings)); + + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_images, + shaderc_spvc_binding_type_sampled_texture, + &resource_bindings), + "Unable to get binding info for sampled textures from shader")); + DAWN_TRY(ExtractResourcesBinding(resource_bindings)); + + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_separate_samplers, + shaderc_spvc_binding_type_sampler, &resource_bindings), + "Unable to get binding info for samples from shader")); + DAWN_TRY(ExtractResourcesBinding(resource_bindings)); + + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetBindingInfo( + shaderc_spvc_shader_resource_storage_buffers, + shaderc_spvc_binding_type_storage_buffer, &resource_bindings), + "Unable to get binding info for storage buffers from shader")); + DAWN_TRY(ExtractResourcesBinding(resource_bindings)); + + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.GetBindingInfo(shaderc_spvc_shader_resource_storage_images, + shaderc_spvc_binding_type_storage_texture, + &resource_bindings), + "Unable to get binding info for storage textures from shader")); + DAWN_TRY(ExtractResourcesBinding(resource_bindings)); + + std::vector input_stage_locations; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetInputStageLocationInfo(&input_stage_locations), + "Unable to get input stage location information from shader")); + + for (const auto& input : input_stage_locations) { + if (mExecutionModel == SingleShaderStage::Vertex) { + if (input.location >= kMaxVertexAttributes) { + return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV"); + } + mUsedVertexAttributes.set(input.location); + } else if (mExecutionModel == SingleShaderStage::Fragment) { + // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives + // them all the location 0, causing a compile error. + if (!input.has_location) { + return DAWN_VALIDATION_ERROR("Need location qualifier on fragment input"); + } + } + } + + std::vector output_stage_locations; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetOutputStageLocationInfo(&output_stage_locations), + "Unable to get output stage location information from shader")); + + for (const auto& output : output_stage_locations) { + if (mExecutionModel == SingleShaderStage::Vertex) { + // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL + // gives them all the location 0, causing a compile error. + if (!output.has_location) { + return DAWN_VALIDATION_ERROR("Need location qualifier on vertex output"); + } + } else if (mExecutionModel == SingleShaderStage::Fragment) { + if (output.location >= kMaxColorAttachments) { + return DAWN_VALIDATION_ERROR( + "Fragment output location over limits in the SPIRV"); + } + } + } + + if (mExecutionModel == SingleShaderStage::Fragment) { + std::vector output_types; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetOutputStageTypeInfo(&output_types), + "Unable to get output stage type information from shader")); + + for (const auto& output : output_types) { + if (output.type == shaderc_spvc_texture_format_type_other) { + return DAWN_VALIDATION_ERROR("Unexpected Fragment output type"); + } + mFragmentOutputFormatBaseTypes[output.location] = ToDawnFormatType(output.type); + } + } + return {}; + } + + MaybeError ShaderModuleBase::ExtractSpirvInfoWithSpirvCross( + const spirv_cross::Compiler& compiler) { // TODO(cwallez@chromium.org): make errors here creation errors // currently errors here do not prevent the shadermodule from being used const auto& resources = compiler.get_shader_resources(); switch (compiler.get_execution_model()) { case spv::ExecutionModelVertex: - mExecutionModel = dawn::ShaderStage::Vertex; + mExecutionModel = SingleShaderStage::Vertex; break; case spv::ExecutionModelFragment: - mExecutionModel = dawn::ShaderStage::Fragment; + mExecutionModel = SingleShaderStage::Fragment; break; case spv::ExecutionModelGLCompute: - mExecutionModel = dawn::ShaderStage::Compute; + mExecutionModel = SingleShaderStage::Compute; break; default: UNREACHABLE(); + return DAWN_VALIDATION_ERROR("Unexpected shader execution model"); } if (resources.push_constant_buffers.size() > 0) { - GetDevice()->HandleError("Push constants aren't supported."); + return DAWN_VALIDATION_ERROR("Push constants aren't supported."); + } + + if (resources.sampled_images.size() > 0) { + return DAWN_VALIDATION_ERROR("Combined images and samplers aren't supported."); } // Fill in bindingInfo with the SPIRV bindings - auto ExtractResourcesBinding = [this](const spirv_cross::SmallVector& - resources, - const spirv_cross::Compiler& compiler, - dawn::BindingType bindingType) { + auto ExtractResourcesBinding = + [this](const spirv_cross::SmallVector& resources, + const spirv_cross::Compiler& compiler, + wgpu::BindingType bindingType) -> MaybeError { for (const auto& resource : resources) { - ASSERT(compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding)); - ASSERT( - compiler.get_decoration_bitset(resource.id).get(spv::DecorationDescriptorSet)); + if (!compiler.get_decoration_bitset(resource.id).get(spv::DecorationBinding)) { + return DAWN_VALIDATION_ERROR("No Binding decoration set for resource"); + } - uint32_t binding = compiler.get_decoration(resource.id, spv::DecorationBinding); - uint32_t set = compiler.get_decoration(resource.id, spv::DecorationDescriptorSet); + if (!compiler.get_decoration_bitset(resource.id) + .get(spv::DecorationDescriptorSet)) { + return DAWN_VALIDATION_ERROR("No Descriptor Decoration set for resource"); + } + + BindingNumber bindingNumber( + compiler.get_decoration(resource.id, spv::DecorationBinding)); + BindGroupIndex bindGroupIndex( + compiler.get_decoration(resource.id, spv::DecorationDescriptorSet)); - if (binding >= kMaxBindingsPerGroup || set >= kMaxBindGroups) { - GetDevice()->HandleError("Binding over limits in the SPIRV"); - continue; + if (bindGroupIndex >= kMaxBindGroupsTyped) { + return DAWN_VALIDATION_ERROR("Bind group index over limits in the SPIRV"); } - auto& info = mBindingInfo[set][binding]; - info.used = true; - info.id = resource.id; - info.base_type_id = resource.base_type_id; - info.type = bindingType; + const auto& it = + mBindingInfo[bindGroupIndex].emplace(bindingNumber, ShaderBindingInfo{}); + if (!it.second) { + return DAWN_VALIDATION_ERROR("Shader has duplicate bindings"); + } + + ShaderBindingInfo* info = &it.first->second; + info->id = resource.id; + info->base_type_id = resource.base_type_id; + + if (bindingType == wgpu::BindingType::UniformBuffer || + bindingType == wgpu::BindingType::StorageBuffer || + bindingType == wgpu::BindingType::ReadonlyStorageBuffer) { + // Determine buffer size, with a minimum of 1 element in the runtime array + spirv_cross::SPIRType type = compiler.get_type(info->base_type_id); + info->minBufferBindingSize = + compiler.get_declared_struct_size_runtime_array(type, 1); + } + + switch (bindingType) { + case wgpu::BindingType::SampledTexture: { + spirv_cross::SPIRType::ImageType imageType = + compiler.get_type(info->base_type_id).image; + spirv_cross::SPIRType::BaseType textureComponentType = + compiler.get_type(imageType.type).basetype; + + info->multisampled = imageType.ms; + info->viewDimension = + SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed); + info->textureComponentType = + SpirvCrossBaseTypeToFormatType(textureComponentType); + info->type = bindingType; + break; + } + case wgpu::BindingType::StorageBuffer: { + // Differentiate between readonly storage bindings and writable ones + // based on the NonWritable decoration + spirv_cross::Bitset flags = compiler.get_buffer_block_flags(resource.id); + if (flags.get(spv::DecorationNonWritable)) { + info->type = wgpu::BindingType::ReadonlyStorageBuffer; + } else { + info->type = wgpu::BindingType::StorageBuffer; + } + break; + } + case wgpu::BindingType::StorageTexture: { + spirv_cross::Bitset flags = compiler.get_decoration_bitset(resource.id); + if (flags.get(spv::DecorationNonReadable)) { + info->type = wgpu::BindingType::WriteonlyStorageTexture; + } else if (flags.get(spv::DecorationNonWritable)) { + info->type = wgpu::BindingType::ReadonlyStorageTexture; + } else { + info->type = wgpu::BindingType::StorageTexture; + } + + spirv_cross::SPIRType::ImageType imageType = + compiler.get_type(info->base_type_id).image; + wgpu::TextureFormat storageTextureFormat = + ToWGPUTextureFormat(imageType.format); + if (storageTextureFormat == wgpu::TextureFormat::Undefined) { + return DAWN_VALIDATION_ERROR( + "Invalid image format declaration on storage image"); + } + const Format& format = + GetDevice()->GetValidInternalFormat(storageTextureFormat); + if (!format.supportsStorageUsage) { + return DAWN_VALIDATION_ERROR( + "The storage texture format is not supported"); + } + info->multisampled = imageType.ms; + info->storageTextureFormat = storageTextureFormat; + info->viewDimension = + SpirvDimToTextureViewDimension(imageType.dim, imageType.arrayed); + break; + } + default: + info->type = bindingType; + } } + return {}; }; - ExtractResourcesBinding(resources.uniform_buffers, compiler, - dawn::BindingType::UniformBuffer); - ExtractResourcesBinding(resources.separate_images, compiler, - dawn::BindingType::SampledTexture); - ExtractResourcesBinding(resources.separate_samplers, compiler, dawn::BindingType::Sampler); - ExtractResourcesBinding(resources.storage_buffers, compiler, - dawn::BindingType::StorageBuffer); + DAWN_TRY(ExtractResourcesBinding(resources.uniform_buffers, compiler, + wgpu::BindingType::UniformBuffer)); + DAWN_TRY(ExtractResourcesBinding(resources.separate_images, compiler, + wgpu::BindingType::SampledTexture)); + DAWN_TRY(ExtractResourcesBinding(resources.separate_samplers, compiler, + wgpu::BindingType::Sampler)); + DAWN_TRY(ExtractResourcesBinding(resources.storage_buffers, compiler, + wgpu::BindingType::StorageBuffer)); + DAWN_TRY(ExtractResourcesBinding(resources.storage_images, compiler, + wgpu::BindingType::StorageTexture)); // Extract the vertex attributes - if (mExecutionModel == dawn::ShaderStage::Vertex) { + if (mExecutionModel == SingleShaderStage::Vertex) { for (const auto& attrib : resources.stage_inputs) { - ASSERT(compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)); + if (!(compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation))) { + return DAWN_VALIDATION_ERROR( + "Unable to find Location decoration for Vertex input"); + } uint32_t location = compiler.get_decoration(attrib.id, spv::DecorationLocation); if (location >= kMaxVertexAttributes) { - device->HandleError("Attribute location over limits in the SPIRV"); - return; + return DAWN_VALIDATION_ERROR("Attribute location over limits in the SPIRV"); } mUsedVertexAttributes.set(location); } - // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives them - // all the location 0, causing a compile error. + // Without a location qualifier on vertex outputs, spirv_cross::CompilerMSL gives + // them all the location 0, causing a compile error. for (const auto& attrib : resources.stage_outputs) { if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) { - device->HandleError("Need location qualifier on vertex output"); - return; + return DAWN_VALIDATION_ERROR("Need location qualifier on vertex output"); } } } - if (mExecutionModel == dawn::ShaderStage::Fragment) { - // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives them - // all the location 0, causing a compile error. + if (mExecutionModel == SingleShaderStage::Fragment) { + // Without a location qualifier on vertex inputs, spirv_cross::CompilerMSL gives + // them all the location 0, causing a compile error. for (const auto& attrib : resources.stage_inputs) { if (!compiler.get_decoration_bitset(attrib.id).get(spv::DecorationLocation)) { - device->HandleError("Need location qualifier on fragment input"); - return; + return DAWN_VALIDATION_ERROR("Need location qualifier on fragment input"); + } + } + + for (const auto& fragmentOutput : resources.stage_outputs) { + if (!compiler.get_decoration_bitset(fragmentOutput.id) + .get(spv::DecorationLocation)) { + return DAWN_VALIDATION_ERROR( + "Unable to find Location decoration for Fragment output"); + } + uint32_t location = + compiler.get_decoration(fragmentOutput.id, spv::DecorationLocation); + if (location >= kMaxColorAttachments) { + return DAWN_VALIDATION_ERROR( + "Fragment output location over limits in the SPIRV"); } + + spirv_cross::SPIRType::BaseType shaderFragmentOutputBaseType = + compiler.get_type(fragmentOutput.base_type_id).basetype; + Format::Type formatType = + SpirvCrossBaseTypeToFormatType(shaderFragmentOutputBaseType); + if (formatType == Format::Type::Other) { + return DAWN_VALIDATION_ERROR("Unexpected Fragment output type"); + } + mFragmentOutputFormatBaseTypes[location] = formatType; } } + return {}; } const ShaderModuleBase::ModuleBindingInfo& ShaderModuleBase::GetBindingInfo() const { @@ -209,62 +874,199 @@ namespace dawn_native { return mUsedVertexAttributes; } - dawn::ShaderStage ShaderModuleBase::GetExecutionModel() const { + const ShaderModuleBase::FragmentOutputBaseTypes& ShaderModuleBase::GetFragmentOutputBaseTypes() + const { ASSERT(!IsError()); - return mExecutionModel; + return mFragmentOutputFormatBaseTypes; } - bool ShaderModuleBase::IsCompatibleWithPipelineLayout(const PipelineLayoutBase* layout) { + SingleShaderStage ShaderModuleBase::GetExecutionModel() const { ASSERT(!IsError()); + return mExecutionModel; + } - for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { - if (!IsCompatibleWithBindGroupLayout(group, layout->GetBindGroupLayout(group))) { - return false; + RequiredBufferSizes ShaderModuleBase::ComputeRequiredBufferSizesForLayout( + const PipelineLayoutBase* layout) const { + RequiredBufferSizes bufferSizes; + for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { + bufferSizes[group] = + GetBindGroupMinBufferSizes(mBindingInfo[group], layout->GetBindGroupLayout(group)); + } + + return bufferSizes; + } + + std::vector ShaderModuleBase::GetBindGroupMinBufferSizes( + const BindingInfoMap& shaderMap, + const BindGroupLayoutBase* layout) const { + std::vector requiredBufferSizes(layout->GetUnverifiedBufferCount()); + uint32_t packedIdx = 0; + + for (BindingIndex bindingIndex{0}; bindingIndex < layout->GetBufferCount(); + ++bindingIndex) { + const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex); + if (bindingInfo.minBufferBindingSize != 0) { + // Skip bindings that have minimum buffer size set in the layout + continue; } + + ASSERT(packedIdx < requiredBufferSizes.size()); + const auto& shaderInfo = shaderMap.find(bindingInfo.binding); + if (shaderInfo != shaderMap.end()) { + requiredBufferSizes[packedIdx] = shaderInfo->second.minBufferBindingSize; + } else { + // We have to include buffers if they are included in the bind group's + // packed vector. We don't actually need to check these at draw time, so + // if this is a problem in the future we can optimize it further. + requiredBufferSizes[packedIdx] = 0; + } + ++packedIdx; } - for (uint32_t group : IterateBitSet(~layout->GetBindGroupLayoutsMask())) { - for (size_t i = 0; i < kMaxBindingsPerGroup; ++i) { - if (mBindingInfo[group][i].used) { - return false; - } + return requiredBufferSizes; + } + + MaybeError ShaderModuleBase::ValidateCompatibilityWithPipelineLayout( + const PipelineLayoutBase* layout) const { + ASSERT(!IsError()); + + for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { + DAWN_TRY( + ValidateCompatibilityWithBindGroupLayout(group, layout->GetBindGroupLayout(group))); + } + + for (BindGroupIndex group : IterateBitSet(~layout->GetBindGroupLayoutsMask())) { + if (mBindingInfo[group].size() > 0) { + std::ostringstream ostream; + ostream << "No bind group layout entry matches the declaration set " + << static_cast(group) << " in the shader module"; + return DAWN_VALIDATION_ERROR(ostream.str()); } } - return true; + return {}; } - bool ShaderModuleBase::IsCompatibleWithBindGroupLayout(size_t group, - const BindGroupLayoutBase* layout) { + MaybeError ShaderModuleBase::ValidateCompatibilityWithBindGroupLayout( + BindGroupIndex group, + const BindGroupLayoutBase* layout) const { ASSERT(!IsError()); - const auto& layoutInfo = layout->GetBindingInfo(); - for (size_t i = 0; i < kMaxBindingsPerGroup; ++i) { - const auto& moduleInfo = mBindingInfo[group][i]; - const auto& layoutBindingType = layoutInfo.types[i]; + const BindGroupLayoutBase::BindingMap& bindingMap = layout->GetBindingMap(); - if (!moduleInfo.used) { - continue; + // Iterate over all bindings used by this group in the shader, and find the + // corresponding binding in the BindGroupLayout, if it exists. + for (const auto& it : mBindingInfo[group]) { + BindingNumber bindingNumber = it.first; + const ShaderBindingInfo& moduleInfo = it.second; + + const auto& bindingIt = bindingMap.find(bindingNumber); + if (bindingIt == bindingMap.end()) { + return DAWN_VALIDATION_ERROR("Missing bind group layout entry for " + + GetShaderDeclarationString(group, bindingNumber)); } + BindingIndex bindingIndex(bindingIt->second); + + const BindingInfo& bindingInfo = layout->GetBindingInfo(bindingIndex); + + if (bindingInfo.type != moduleInfo.type) { + // Binding mismatch between shader and bind group is invalid. For example, a + // writable binding in the shader with a readonly storage buffer in the bind group + // layout is invalid. However, a readonly binding in the shader with a writable + // storage buffer in the bind group layout is valid. + bool validBindingConversion = + bindingInfo.type == wgpu::BindingType::StorageBuffer && + moduleInfo.type == wgpu::BindingType::ReadonlyStorageBuffer; - // DynamicUniformBuffer and DynamicStorageBuffer are uniform buffer and - // storage buffer in shader. Need to translate them. - if (NonDynamicBindingType(layoutBindingType) != moduleInfo.type) { - return false; + // TODO(crbug.com/dawn/367): Temporarily allow using either a sampler or a + // comparison sampler until we can perform the proper shader analysis of what type + // is used in the shader module. + validBindingConversion |= (bindingInfo.type == wgpu::BindingType::Sampler && + moduleInfo.type == wgpu::BindingType::ComparisonSampler); + validBindingConversion |= + (bindingInfo.type == wgpu::BindingType::ComparisonSampler && + moduleInfo.type == wgpu::BindingType::Sampler); + + if (!validBindingConversion) { + return DAWN_VALIDATION_ERROR( + "The binding type of the bind group layout entry conflicts " + + GetShaderDeclarationString(group, bindingNumber)); + } } - if ((layoutInfo.visibilities[i] & StageBit(mExecutionModel)) == 0) { - return false; + if ((bindingInfo.visibility & StageBit(mExecutionModel)) == 0) { + return DAWN_VALIDATION_ERROR("The bind group layout entry for " + + GetShaderDeclarationString(group, bindingNumber) + + " is not visible for the shader stage"); + } + + switch (bindingInfo.type) { + case wgpu::BindingType::SampledTexture: { + if (bindingInfo.textureComponentType != moduleInfo.textureComponentType) { + return DAWN_VALIDATION_ERROR( + "The textureComponentType of the bind group layout entry is different " + "from " + + GetShaderDeclarationString(group, bindingNumber)); + } + + if (bindingInfo.viewDimension != moduleInfo.viewDimension) { + return DAWN_VALIDATION_ERROR( + "The viewDimension of the bind group layout entry is different " + "from " + + GetShaderDeclarationString(group, bindingNumber)); + } + break; + } + + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + ASSERT(bindingInfo.storageTextureFormat != wgpu::TextureFormat::Undefined); + ASSERT(moduleInfo.storageTextureFormat != wgpu::TextureFormat::Undefined); + if (bindingInfo.storageTextureFormat != moduleInfo.storageTextureFormat) { + return DAWN_VALIDATION_ERROR( + "The storageTextureFormat of the bind group layout entry is different " + "from " + + GetShaderDeclarationString(group, bindingNumber)); + } + if (bindingInfo.viewDimension != moduleInfo.viewDimension) { + return DAWN_VALIDATION_ERROR( + "The viewDimension of the bind group layout entry is different " + "from " + + GetShaderDeclarationString(group, bindingNumber)); + } + break; + } + + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::StorageBuffer: { + if (bindingInfo.minBufferBindingSize != 0 && + moduleInfo.minBufferBindingSize > bindingInfo.minBufferBindingSize) { + return DAWN_VALIDATION_ERROR( + "The minimum buffer size of the bind group layout entry is smaller " + "than " + + GetShaderDeclarationString(group, bindingNumber)); + } + break; + } + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + break; + + case wgpu::BindingType::StorageTexture: + default: + UNREACHABLE(); + return DAWN_VALIDATION_ERROR("Unsupported binding type"); } } - return true; + return {}; } size_t ShaderModuleBase::HashFunc::operator()(const ShaderModuleBase* module) const { size_t hash = 0; - for (uint32_t word : module->mCode) { + for (uint32_t word : module->mSpirv) { HashCombine(&hash, word); } @@ -273,7 +1075,40 @@ namespace dawn_native { bool ShaderModuleBase::EqualityFunc::operator()(const ShaderModuleBase* a, const ShaderModuleBase* b) const { - return a->mCode == b->mCode; + return a->mSpirv == b->mSpirv; } + MaybeError ShaderModuleBase::CheckSpvcSuccess(shaderc_spvc_status status, + const char* error_msg) { + if (status != shaderc_spvc_status_success) { + return DAWN_VALIDATION_ERROR(error_msg); + } + return {}; + } + + shaderc_spvc::Context* ShaderModuleBase::GetContext() { + return &mSpvcContext; + } + + const std::vector& ShaderModuleBase::GetSpirv() const { + return mSpirv; + } + + shaderc_spvc::CompileOptions ShaderModuleBase::GetCompileOptions() const { + shaderc_spvc::CompileOptions options; + options.SetValidate(GetDevice()->IsValidationEnabled()); + return options; + } + + MaybeError ShaderModuleBase::InitializeBase() { + if (mType == Type::Wgsl) { +#ifdef DAWN_ENABLE_WGSL + DAWN_TRY_ASSIGN(mSpirv, ConvertWGSLToSPIRV(mWgsl.c_str())); +#else + return DAWN_VALIDATION_ERROR("WGSL not supported (yet)"); +#endif // DAWN_ENABLE_WGSL + } + + return {}; + } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ShaderModule.h b/third_party/dawn/src/dawn_native/ShaderModule.h index 035c2d71a4f..4e771aa8335 100644 --- a/third_party/dawn/src/dawn_native/ShaderModule.h +++ b/third_party/dawn/src/dawn_native/ShaderModule.h @@ -16,14 +16,20 @@ #define DAWNNATIVE_SHADERMODULE_H_ #include "common/Constants.h" +#include "common/ityp_array.h" +#include "dawn_native/BindingInfo.h" +#include "dawn_native/CachedObject.h" #include "dawn_native/Error.h" +#include "dawn_native/Format.h" #include "dawn_native/Forward.h" -#include "dawn_native/ObjectBase.h" +#include "dawn_native/PerStage.h" #include "dawn_native/dawn_platform.h" -#include +#include "spvc/spvc.hpp" + #include +#include #include namespace spirv_cross { @@ -35,32 +41,44 @@ namespace dawn_native { MaybeError ValidateShaderModuleDescriptor(DeviceBase* device, const ShaderModuleDescriptor* descriptor); - class ShaderModuleBase : public ObjectBase { + class ShaderModuleBase : public CachedObject { public: - ShaderModuleBase(DeviceBase* device, - const ShaderModuleDescriptor* descriptor, - bool blueprint = false); + enum class Type { Undefined, Spirv, Wgsl }; + + ShaderModuleBase(DeviceBase* device, const ShaderModuleDescriptor* descriptor); ~ShaderModuleBase() override; static ShaderModuleBase* MakeError(DeviceBase* device); - void ExtractSpirvInfo(const spirv_cross::Compiler& compiler); + MaybeError ExtractSpirvInfo(const spirv_cross::Compiler& compiler); - struct BindingInfo { + struct ShaderBindingInfo : BindingInfo { // The SPIRV ID of the resource. uint32_t id; uint32_t base_type_id; - dawn::BindingType type; - bool used = false; + + private: + // Disallow access to unused members. + using BindingInfo::hasDynamicOffset; + using BindingInfo::visibility; }; - using ModuleBindingInfo = - std::array, kMaxBindGroups>; + + using BindingInfoMap = std::map; + using ModuleBindingInfo = ityp::array; const ModuleBindingInfo& GetBindingInfo() const; const std::bitset& GetUsedVertexAttributes() const; - dawn::ShaderStage GetExecutionModel() const; + SingleShaderStage GetExecutionModel() const; + + // An array to record the basic types (float, int and uint) of the fragment shader outputs + // or Format::Type::Other means the fragment shader output is unused. + using FragmentOutputBaseTypes = std::array; + const FragmentOutputBaseTypes& GetFragmentOutputBaseTypes() const; + + MaybeError ValidateCompatibilityWithPipelineLayout(const PipelineLayoutBase* layout) const; - bool IsCompatibleWithPipelineLayout(const PipelineLayoutBase* layout); + RequiredBufferSizes ComputeRequiredBufferSizesForLayout( + const PipelineLayoutBase* layout) const; // Functors necessary for the unordered_set-based cache. struct HashFunc { @@ -70,19 +88,40 @@ namespace dawn_native { bool operator()(const ShaderModuleBase* a, const ShaderModuleBase* b) const; }; + shaderc_spvc::Context* GetContext(); + const std::vector& GetSpirv() const; + + protected: + static MaybeError CheckSpvcSuccess(shaderc_spvc_status status, const char* error_msg); + shaderc_spvc::CompileOptions GetCompileOptions() const; + MaybeError InitializeBase(); + + shaderc_spvc::Context mSpvcContext; + private: ShaderModuleBase(DeviceBase* device, ObjectBase::ErrorTag tag); - bool IsCompatibleWithBindGroupLayout(size_t group, const BindGroupLayoutBase* layout); + MaybeError ValidateCompatibilityWithBindGroupLayout( + BindGroupIndex group, + const BindGroupLayoutBase* layout) const; - // TODO(cwallez@chromium.org): The code is only stored for deduplication. We could maybe - // store a cryptographic hash of the code instead? - std::vector mCode; - bool mIsBlueprint = false; + std::vector GetBindGroupMinBufferSizes(const BindingInfoMap& shaderMap, + const BindGroupLayoutBase* layout) const; + + // Different implementations reflection into the shader depending on + // whether using spvc, or directly accessing spirv-cross. + MaybeError ExtractSpirvInfoWithSpvc(); + MaybeError ExtractSpirvInfoWithSpirvCross(const spirv_cross::Compiler& compiler); + + Type mType; + std::vector mSpirv; + std::string mWgsl; ModuleBindingInfo mBindingInfo; std::bitset mUsedVertexAttributes; - dawn::ShaderStage mExecutionModel; + SingleShaderStage mExecutionModel; + + FragmentOutputBaseTypes mFragmentOutputFormatBaseTypes; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/StagingBuffer.cpp b/third_party/dawn/src/dawn_native/StagingBuffer.cpp index 51f5fa8c5e5..63dd65e9bf7 100644 --- a/third_party/dawn/src/dawn_native/StagingBuffer.cpp +++ b/third_party/dawn/src/dawn_native/StagingBuffer.cpp @@ -26,4 +26,4 @@ namespace dawn_native { void* StagingBufferBase::GetMappedPointer() const { return mMappedPointer; } -} // namespace dawn_native \ No newline at end of file +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/StagingBuffer.h b/third_party/dawn/src/dawn_native/StagingBuffer.h index 1da8900a128..4d195488bc9 100644 --- a/third_party/dawn/src/dawn_native/StagingBuffer.h +++ b/third_party/dawn/src/dawn_native/StagingBuffer.h @@ -38,4 +38,4 @@ namespace dawn_native { } // namespace dawn_native -#endif // DAWNNATIVE_STAGINGBUFFER_H_ \ No newline at end of file +#endif // DAWNNATIVE_STAGINGBUFFER_H_ diff --git a/third_party/dawn/src/dawn_native/Surface.cpp b/third_party/dawn/src/dawn_native/Surface.cpp new file mode 100644 index 00000000000..ccd240c9dfc --- /dev/null +++ b/third_party/dawn/src/dawn_native/Surface.cpp @@ -0,0 +1,193 @@ +// Copyright 2020 the Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/Surface.h" + +#include "common/Platform.h" +#include "dawn_native/Instance.h" +#include "dawn_native/SwapChain.h" + +#if defined(DAWN_PLATFORM_WINDOWS) +# include "common/windows_with_undefs.h" +#endif // DAWN_PLATFORM_WINDOWS + +#if defined(DAWN_USE_X11) +# include "common/xlib_with_undefs.h" +#endif // defined(DAWN_USE_X11) + +namespace dawn_native { + +#if defined(DAWN_ENABLE_BACKEND_METAL) + bool InheritsFromCAMetalLayer(void* obj); +#endif // defined(DAWN_ENABLE_BACKEND_METAL) + + MaybeError ValidateSurfaceDescriptor(const InstanceBase* instance, + const SurfaceDescriptor* descriptor) { + // TODO(cwallez@chromium.org): Have some type of helper to iterate over all the chained + // structures. + if (descriptor->nextInChain == nullptr) { + return DAWN_VALIDATION_ERROR("Surface cannot be created with just the base descriptor"); + } + + const ChainedStruct* chainedDescriptor = descriptor->nextInChain; + if (chainedDescriptor->nextInChain != nullptr) { + return DAWN_VALIDATION_ERROR("Cannot specify two windows for a single surface"); + } + + switch (chainedDescriptor->sType) { +#if defined(DAWN_ENABLE_BACKEND_METAL) + case wgpu::SType::SurfaceDescriptorFromMetalLayer: { + const SurfaceDescriptorFromMetalLayer* metalDesc = + static_cast(chainedDescriptor); + + // Check that the layer is a CAMetalLayer (or a derived class). + if (!InheritsFromCAMetalLayer(metalDesc->layer)) { + return DAWN_VALIDATION_ERROR("layer must be a CAMetalLayer"); + } + break; + } +#endif // defined(DAWN_ENABLE_BACKEND_METAL) + +#if defined(DAWN_PLATFORM_WINDOWS) + case wgpu::SType::SurfaceDescriptorFromWindowsHWND: { + const SurfaceDescriptorFromWindowsHWND* hwndDesc = + static_cast(chainedDescriptor); + + // It is not possible to validate an HINSTANCE. + + // Validate the hwnd using the windows.h IsWindow function. + if (IsWindow(static_cast(hwndDesc->hwnd)) == 0) { + return DAWN_VALIDATION_ERROR("Invalid HWND"); + } + break; + } +#endif // defined(DAWN_PLATFORM_WINDOWS) + +#if defined(DAWN_USE_X11) + case wgpu::SType::SurfaceDescriptorFromXlib: { + const SurfaceDescriptorFromXlib* xDesc = + static_cast(chainedDescriptor); + + // It is not possible to validate an X Display. + + // Check the validity of the window by calling a getter function on the window that + // returns a status code. If the window is bad the call return a status of zero. We + // need to set a temporary X11 error handler while doing this because the default + // X11 error handler exits the program on any error. + XErrorHandler oldErrorHandler = + XSetErrorHandler([](Display*, XErrorEvent*) { return 0; }); + XWindowAttributes attributes; + int status = XGetWindowAttributes(reinterpret_cast(xDesc->display), + xDesc->window, &attributes); + XSetErrorHandler(oldErrorHandler); + + if (status == 0) { + return DAWN_VALIDATION_ERROR("Invalid X Window"); + } + break; + } +#endif // defined(DAWN_USE_X11) + + case wgpu::SType::SurfaceDescriptorFromCanvasHTMLSelector: + default: + return DAWN_VALIDATION_ERROR("Unsupported sType"); + } + + return {}; + } + + Surface::Surface(InstanceBase* instance, const SurfaceDescriptor* descriptor) + : mInstance(instance) { + ASSERT(descriptor->nextInChain != nullptr); + const ChainedStruct* chainedDescriptor = descriptor->nextInChain; + + switch (chainedDescriptor->sType) { + case wgpu::SType::SurfaceDescriptorFromMetalLayer: { + const SurfaceDescriptorFromMetalLayer* metalDesc = + static_cast(chainedDescriptor); + mType = Type::MetalLayer; + mMetalLayer = metalDesc->layer; + break; + } + + case wgpu::SType::SurfaceDescriptorFromWindowsHWND: { + const SurfaceDescriptorFromWindowsHWND* hwndDesc = + static_cast(chainedDescriptor); + mType = Type::WindowsHWND; + mHInstance = hwndDesc->hinstance; + mHWND = hwndDesc->hwnd; + break; + } + + case wgpu::SType::SurfaceDescriptorFromXlib: { + const SurfaceDescriptorFromXlib* xDesc = + static_cast(chainedDescriptor); + mType = Type::Xlib; + mXDisplay = xDesc->display; + mXWindow = xDesc->window; + break; + } + + default: + UNREACHABLE(); + } + } + + Surface::~Surface() { + if (mSwapChain != nullptr) { + mSwapChain->DetachFromSurface(); + mSwapChain = nullptr; + } + } + + NewSwapChainBase* Surface::GetAttachedSwapChain() const { + return mSwapChain; + } + + void Surface::SetAttachedSwapChain(NewSwapChainBase* swapChain) { + mSwapChain = swapChain; + } + + InstanceBase* Surface::GetInstance() { + return mInstance.Get(); + } + + Surface::Type Surface::GetType() const { + return mType; + } + + void* Surface::GetMetalLayer() const { + ASSERT(mType == Type::MetalLayer); + return mMetalLayer; + } + + void* Surface::GetHInstance() const { + ASSERT(mType == Type::WindowsHWND); + return mHInstance; + } + void* Surface::GetHWND() const { + ASSERT(mType == Type::WindowsHWND); + return mHWND; + } + + void* Surface::GetXDisplay() const { + ASSERT(mType == Type::Xlib); + return mXDisplay; + } + uint32_t Surface::GetXWindow() const { + ASSERT(mType == Type::Xlib); + return mXWindow; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Surface.h b/third_party/dawn/src/dawn_native/Surface.h new file mode 100644 index 00000000000..048298b00bc --- /dev/null +++ b/third_party/dawn/src/dawn_native/Surface.h @@ -0,0 +1,80 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_SURFACE_H_ +#define DAWNNATIVE_SURFACE_H_ + +#include "common/RefCounted.h" +#include "dawn_native/Error.h" +#include "dawn_native/Forward.h" + +#include "dawn_native/dawn_platform.h" + +namespace dawn_native { + + MaybeError ValidateSurfaceDescriptor(const InstanceBase* instance, + const SurfaceDescriptor* descriptor); + + // A surface is a sum types of all the kind of windows Dawn supports. The OS-specific types + // aren't used because they would cause compilation errors on other OSes (or require + // ObjectiveC). + // The surface is also used to store the current swapchain so that we can detach it when it is + // replaced. + class Surface final : public RefCounted { + public: + Surface(InstanceBase* instance, const SurfaceDescriptor* descriptor); + + void SetAttachedSwapChain(NewSwapChainBase* swapChain); + NewSwapChainBase* GetAttachedSwapChain() const; + + // These are valid to call on all Surfaces. + enum class Type { MetalLayer, WindowsHWND, Xlib }; + Type GetType() const; + InstanceBase* GetInstance(); + + // Valid to call if the type is MetalLayer + void* GetMetalLayer() const; + + // Valid to call if the type is WindowsHWND + void* GetHInstance() const; + void* GetHWND() const; + + // Valid to call if the type is WindowsXlib + void* GetXDisplay() const; + uint32_t GetXWindow() const; + + private: + ~Surface() override; + + Ref mInstance; + Type mType; + + // The swapchain will set this to null when it is destroyed. + NewSwapChainBase* mSwapChain = nullptr; + + // MetalLayer + void* mMetalLayer = nullptr; + + // WindowsHwnd + void* mHInstance = nullptr; + void* mHWND = nullptr; + + // Xlib + void* mXDisplay = nullptr; + uint32_t mXWindow = 0; + }; + +} // namespace dawn_native + +#endif // DAWNNATIVE_SURFACE_H_ diff --git a/third_party/dawn/src/dawn_native/Surface_metal.mm b/third_party/dawn/src/dawn_native/Surface_metal.mm new file mode 100644 index 00000000000..9989674fe6a --- /dev/null +++ b/third_party/dawn/src/dawn_native/Surface_metal.mm @@ -0,0 +1,30 @@ +// Copyright 2020 the Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Contains a helper function for Surface.cpp that needs to be written in ObjectiveC. + +#if !defined(DAWN_ENABLE_BACKEND_METAL) +# error "Surface_metal.mm requires the Metal backend to be enabled." +#endif // !defined(DAWN_ENABLE_BACKEND_METAL) + +#import + +namespace dawn_native { + + bool InheritsFromCAMetalLayer(void* obj) { + id object = static_cast(obj); + return [object isKindOfClass:[CAMetalLayer class]]; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/SwapChain.cpp b/third_party/dawn/src/dawn_native/SwapChain.cpp index 3151c98de34..6358567f4f2 100644 --- a/third_party/dawn/src/dawn_native/SwapChain.cpp +++ b/third_party/dawn/src/dawn_native/SwapChain.cpp @@ -14,7 +14,10 @@ #include "dawn_native/SwapChain.h" +#include "common/Constants.h" +#include "dawn_native/Adapter.h" #include "dawn_native/Device.h" +#include "dawn_native/Surface.h" #include "dawn_native/Texture.h" #include "dawn_native/ValidationUtils_autogen.h" @@ -22,46 +25,93 @@ namespace dawn_native { namespace { - class ErrorSwapChain : public SwapChainBase { + class ErrorSwapChain final : public SwapChainBase { public: ErrorSwapChain(DeviceBase* device) : SwapChainBase(device, ObjectBase::kError) { } private: - TextureBase* GetNextTextureImpl(const TextureDescriptor*) override { - UNREACHABLE(); + void Configure(wgpu::TextureFormat format, + wgpu::TextureUsage allowedUsage, + uint32_t width, + uint32_t height) override { + GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain")); } - void OnBeforePresent(TextureBase* texture) override { - UNREACHABLE(); + TextureViewBase* GetCurrentTextureView() override { + GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain")); + return TextureViewBase::MakeError(GetDevice()); + } + + void Present() override { + GetDevice()->ConsumedError(DAWN_VALIDATION_ERROR("error swapchain")); } }; } // anonymous namespace MaybeError ValidateSwapChainDescriptor(const DeviceBase* device, + const Surface* surface, const SwapChainDescriptor* descriptor) { - if (descriptor->implementation == 0) { - return DAWN_VALIDATION_ERROR("Null implementation for the swapchain"); - } + if (descriptor->implementation != 0) { + if (surface != nullptr) { + return DAWN_VALIDATION_ERROR( + "Exactly one of surface or implementation must be set"); + } + + DawnSwapChainImplementation* impl = + reinterpret_cast(descriptor->implementation); + + if (!impl->Init || !impl->Destroy || !impl->Configure || !impl->GetNextTexture || + !impl->Present) { + return DAWN_VALIDATION_ERROR("Implementation is incomplete"); + } + + } else { + if (surface == nullptr) { + return DAWN_VALIDATION_ERROR( + "At least one of surface or implementation must be set"); + } + + DAWN_TRY(ValidatePresentMode(descriptor->presentMode)); + + // TODO(cwallez@chromium.org): Lift this restriction once + // wgpu::Instance::GetPreferredSurfaceFormat is implemented. + if (descriptor->format != wgpu::TextureFormat::BGRA8Unorm) { + return DAWN_VALIDATION_ERROR("Format must (currently) be BGRA8Unorm"); + } + + if (descriptor->usage != wgpu::TextureUsage::OutputAttachment) { + return DAWN_VALIDATION_ERROR("Usage must (currently) be OutputAttachment"); + } - DawnSwapChainImplementation* impl = - reinterpret_cast(descriptor->implementation); + if (descriptor->width == 0 || descriptor->height == 0) { + return DAWN_VALIDATION_ERROR("Swapchain size can't be empty"); + } - if (!impl->Init || !impl->Destroy || !impl->Configure || !impl->GetNextTexture || - !impl->Present) { - return DAWN_VALIDATION_ERROR("Implementation is incomplete"); + if (descriptor->width > kMaxTextureSize || descriptor->height > kMaxTextureSize) { + return DAWN_VALIDATION_ERROR("Swapchain size too big"); + } } return {}; } - // SwapChain + TextureDescriptor GetSwapChainBaseTextureDescriptor(NewSwapChainBase* swapChain) { + TextureDescriptor desc; + desc.usage = swapChain->GetUsage(); + desc.dimension = wgpu::TextureDimension::e2D; + desc.size = {swapChain->GetWidth(), swapChain->GetHeight(), 1}; + desc.format = swapChain->GetFormat(); + desc.mipLevelCount = 1; + desc.sampleCount = 1; - SwapChainBase::SwapChainBase(DeviceBase* device, const SwapChainDescriptor* descriptor) - : ObjectBase(device), - mImplementation( - *reinterpret_cast(descriptor->implementation)) { + return desc; + } + + // SwapChainBase + + SwapChainBase::SwapChainBase(DeviceBase* device) : ObjectBase(device) { } SwapChainBase::SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag) @@ -69,10 +119,6 @@ namespace dawn_native { } SwapChainBase::~SwapChainBase() { - if (!IsError()) { - const auto& im = GetImplementation(); - im.Destroy(im.userData); - } } // static @@ -80,70 +126,102 @@ namespace dawn_native { return new ErrorSwapChain(device); } - void SwapChainBase::Configure(dawn::TextureFormat format, - dawn::TextureUsageBit allowedUsage, - uint32_t width, - uint32_t height) { + // OldSwapChainBase + + OldSwapChainBase::OldSwapChainBase(DeviceBase* device, const SwapChainDescriptor* descriptor) + : SwapChainBase(device), + mImplementation( + *reinterpret_cast(descriptor->implementation)) { + } + + OldSwapChainBase::~OldSwapChainBase() { + if (!IsError()) { + const auto& im = GetImplementation(); + im.Destroy(im.userData); + } + } + + void OldSwapChainBase::Configure(wgpu::TextureFormat format, + wgpu::TextureUsage allowedUsage, + uint32_t width, + uint32_t height) { if (GetDevice()->ConsumedError(ValidateConfigure(format, allowedUsage, width, height))) { return; } ASSERT(!IsError()); - allowedUsage |= dawn::TextureUsageBit::Present; + allowedUsage |= wgpu::TextureUsage::Present; mFormat = format; mAllowedUsage = allowedUsage; mWidth = width; mHeight = height; - mImplementation.Configure(mImplementation.userData, static_cast(format), - static_cast(allowedUsage), width, height); + mImplementation.Configure(mImplementation.userData, static_cast(format), + static_cast(allowedUsage), width, height); } - TextureBase* SwapChainBase::GetNextTexture() { - if (GetDevice()->ConsumedError(ValidateGetNextTexture())) { - return TextureBase::MakeError(GetDevice()); + TextureViewBase* OldSwapChainBase::GetCurrentTextureView() { + if (GetDevice()->ConsumedError(ValidateGetCurrentTextureView())) { + return TextureViewBase::MakeError(GetDevice()); } ASSERT(!IsError()); + // Return the same current texture view until Present is called. + if (mCurrentTextureView.Get() != nullptr) { + // Calling GetCurrentTextureView always returns a new reference so add it even when + // reuse the existing texture view. + mCurrentTextureView->Reference(); + return mCurrentTextureView.Get(); + } + + // Create the backing texture and the view. TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = mWidth; descriptor.size.height = mHeight; descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; descriptor.sampleCount = 1; descriptor.format = mFormat; descriptor.mipLevelCount = 1; descriptor.usage = mAllowedUsage; - auto* texture = GetNextTextureImpl(&descriptor); - mLastNextTexture = texture; - return texture; + // Get the texture but remove the external refcount because it is never passed outside + // of dawn_native + mCurrentTexture = AcquireRef(GetNextTextureImpl(&descriptor)); + + mCurrentTextureView = mCurrentTexture->CreateView(nullptr); + return mCurrentTextureView.Get(); } - void SwapChainBase::Present(TextureBase* texture) { - if (GetDevice()->ConsumedError(ValidatePresent(texture))) { + void OldSwapChainBase::Present() { + if (GetDevice()->ConsumedError(ValidatePresent())) { return; } ASSERT(!IsError()); - OnBeforePresent(texture); + if (GetDevice()->ConsumedError(OnBeforePresent(mCurrentTextureView.Get()))) { + return; + } mImplementation.Present(mImplementation.userData); + + mCurrentTexture = nullptr; + mCurrentTextureView = nullptr; } - const DawnSwapChainImplementation& SwapChainBase::GetImplementation() { + const DawnSwapChainImplementation& OldSwapChainBase::GetImplementation() { ASSERT(!IsError()); return mImplementation; } - MaybeError SwapChainBase::ValidateConfigure(dawn::TextureFormat format, - dawn::TextureUsageBit allowedUsage, - uint32_t width, - uint32_t height) const { + MaybeError OldSwapChainBase::ValidateConfigure(wgpu::TextureFormat format, + wgpu::TextureUsage allowedUsage, + uint32_t width, + uint32_t height) const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); DAWN_TRY(GetDevice()->ValidateObject(this)); - DAWN_TRY(ValidateTextureUsageBit(allowedUsage)); + DAWN_TRY(ValidateTextureUsage(allowedUsage)); DAWN_TRY(ValidateTextureFormat(format)); if (width == 0 || height == 0) { @@ -153,7 +231,8 @@ namespace dawn_native { return {}; } - MaybeError SwapChainBase::ValidateGetNextTexture() const { + MaybeError OldSwapChainBase::ValidateGetCurrentTextureView() const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); DAWN_TRY(GetDevice()->ValidateObject(this)); if (mWidth == 0) { @@ -164,14 +243,158 @@ namespace dawn_native { return {}; } - MaybeError SwapChainBase::ValidatePresent(TextureBase* texture) const { + MaybeError OldSwapChainBase::ValidatePresent() const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); DAWN_TRY(GetDevice()->ValidateObject(this)); - DAWN_TRY(GetDevice()->ValidateObject(texture)); - // This also checks that the texture is valid since mLastNextTexture is always valid. - if (texture != mLastNextTexture) { + if (mCurrentTextureView.Get() == nullptr) { return DAWN_VALIDATION_ERROR( - "Tried to present something other than the last NextTexture"); + "Cannot call present without a GetCurrentTextureView call for this frame"); + } + + return {}; + } + + // Implementation of NewSwapChainBase + + NewSwapChainBase::NewSwapChainBase(DeviceBase* device, + Surface* surface, + const SwapChainDescriptor* descriptor) + : SwapChainBase(device), + mAttached(true), + mWidth(descriptor->width), + mHeight(descriptor->height), + mFormat(descriptor->format), + mUsage(descriptor->usage), + mPresentMode(descriptor->presentMode), + mSurface(surface) { + } + + NewSwapChainBase::~NewSwapChainBase() { + if (mCurrentTextureView.Get() != nullptr) { + ASSERT(mCurrentTextureView->GetTexture()->GetTextureState() == + TextureBase::TextureState::Destroyed); + } + + ASSERT(!mAttached); + ASSERT(mSurface == nullptr); + } + + void NewSwapChainBase::DetachFromSurface() { + if (mAttached) { + DetachFromSurfaceImpl(); + GetSurface()->SetAttachedSwapChain(nullptr); + mSurface = nullptr; + mAttached = false; + } + } + + void NewSwapChainBase::Configure(wgpu::TextureFormat format, + wgpu::TextureUsage allowedUsage, + uint32_t width, + uint32_t height) { + GetDevice()->ConsumedError( + DAWN_VALIDATION_ERROR("Configure is invalid for surface-based swapchains")); + } + + TextureViewBase* NewSwapChainBase::GetCurrentTextureView() { + if (GetDevice()->ConsumedError(ValidateGetCurrentTextureView())) { + return TextureViewBase::MakeError(GetDevice()); + } + + if (mCurrentTextureView.Get() != nullptr) { + // Calling GetCurrentTextureView always returns a new reference so add it even when + // reusing the existing texture view. + mCurrentTextureView->Reference(); + return mCurrentTextureView.Get(); + } + + TextureViewBase* view = nullptr; + if (GetDevice()->ConsumedError(GetCurrentTextureViewImpl(), &view)) { + return TextureViewBase::MakeError(GetDevice()); + } + + // Check that the return texture view matches exactly what was given for this descriptor. + ASSERT(view->GetTexture()->GetFormat().format == mFormat); + ASSERT((view->GetTexture()->GetUsage() & mUsage) == mUsage); + ASSERT(view->GetLevelCount() == 1); + ASSERT(view->GetLayerCount() == 1); + ASSERT(view->GetDimension() == wgpu::TextureViewDimension::e2D); + ASSERT(view->GetTexture()->GetMipLevelVirtualSize(view->GetBaseMipLevel()).width == mWidth); + ASSERT(view->GetTexture()->GetMipLevelVirtualSize(view->GetBaseMipLevel()).height == + mHeight); + + mCurrentTextureView = view; + return view; + } + + void NewSwapChainBase::Present() { + if (GetDevice()->ConsumedError(ValidatePresent())) { + return; + } + + if (GetDevice()->ConsumedError(PresentImpl())) { + return; + } + + ASSERT(mCurrentTextureView->GetTexture()->GetTextureState() == + TextureBase::TextureState::Destroyed); + mCurrentTextureView = nullptr; + } + + uint32_t NewSwapChainBase::GetWidth() const { + return mWidth; + } + + uint32_t NewSwapChainBase::GetHeight() const { + return mHeight; + } + + wgpu::TextureFormat NewSwapChainBase::GetFormat() const { + return mFormat; + } + + wgpu::TextureUsage NewSwapChainBase::GetUsage() const { + return mUsage; + } + + wgpu::PresentMode NewSwapChainBase::GetPresentMode() const { + return mPresentMode; + } + + Surface* NewSwapChainBase::GetSurface() const { + return mSurface; + } + + bool NewSwapChainBase::IsAttached() const { + return mAttached; + } + + wgpu::BackendType NewSwapChainBase::GetBackendType() const { + return GetDevice()->GetAdapter()->GetBackendType(); + } + + MaybeError NewSwapChainBase::ValidatePresent() const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); + DAWN_TRY(GetDevice()->ValidateObject(this)); + + if (!mAttached) { + return DAWN_VALIDATION_ERROR("Presenting on detached swapchain"); + } + + if (mCurrentTextureView.Get() == nullptr) { + return DAWN_VALIDATION_ERROR("Presenting without prior GetCurrentTextureView"); + } + + return {}; + } + + MaybeError NewSwapChainBase::ValidateGetCurrentTextureView() const { + DAWN_TRY(GetDevice()->ValidateIsAlive()); + DAWN_TRY(GetDevice()->ValidateObject(this)); + + if (!mAttached) { + return DAWN_VALIDATION_ERROR("Getting view on detached swapchain"); } return {}; diff --git a/third_party/dawn/src/dawn_native/SwapChain.h b/third_party/dawn/src/dawn_native/SwapChain.h index c8479c6a157..6e32e534048 100644 --- a/third_party/dawn/src/dawn_native/SwapChain.h +++ b/third_party/dawn/src/dawn_native/SwapChain.h @@ -25,44 +25,138 @@ namespace dawn_native { MaybeError ValidateSwapChainDescriptor(const DeviceBase* device, + const Surface* surface, const SwapChainDescriptor* descriptor); + TextureDescriptor GetSwapChainBaseTextureDescriptor(NewSwapChainBase* swapChain); + class SwapChainBase : public ObjectBase { public: - SwapChainBase(DeviceBase* device, const SwapChainDescriptor* descriptor); - ~SwapChainBase(); + SwapChainBase(DeviceBase* device); static SwapChainBase* MakeError(DeviceBase* device); // Dawn API - void Configure(dawn::TextureFormat format, - dawn::TextureUsageBit allowedUsage, - uint32_t width, - uint32_t height); - TextureBase* GetNextTexture(); - void Present(TextureBase* texture); + virtual void Configure(wgpu::TextureFormat format, + wgpu::TextureUsage allowedUsage, + uint32_t width, + uint32_t height) = 0; + virtual TextureViewBase* GetCurrentTextureView() = 0; + virtual void Present() = 0; protected: SwapChainBase(DeviceBase* device, ObjectBase::ErrorTag tag); + ~SwapChainBase() override; + }; + + // The base class for implementation-based SwapChains that are deprecated. + class OldSwapChainBase : public SwapChainBase { + public: + OldSwapChainBase(DeviceBase* device, const SwapChainDescriptor* descriptor); + + static SwapChainBase* MakeError(DeviceBase* device); + // Dawn API + void Configure(wgpu::TextureFormat format, + wgpu::TextureUsage allowedUsage, + uint32_t width, + uint32_t height) override; + TextureViewBase* GetCurrentTextureView() override; + void Present() override; + + protected: + ~OldSwapChainBase() override; const DawnSwapChainImplementation& GetImplementation(); virtual TextureBase* GetNextTextureImpl(const TextureDescriptor*) = 0; - virtual void OnBeforePresent(TextureBase* texture) = 0; + virtual MaybeError OnBeforePresent(TextureViewBase* view) = 0; private: - MaybeError ValidateConfigure(dawn::TextureFormat format, - dawn::TextureUsageBit allowedUsage, + MaybeError ValidateConfigure(wgpu::TextureFormat format, + wgpu::TextureUsage allowedUsage, uint32_t width, uint32_t height) const; - MaybeError ValidateGetNextTexture() const; - MaybeError ValidatePresent(TextureBase* texture) const; + MaybeError ValidateGetCurrentTextureView() const; + MaybeError ValidatePresent() const; DawnSwapChainImplementation mImplementation = {}; - dawn::TextureFormat mFormat = {}; - dawn::TextureUsageBit mAllowedUsage; + wgpu::TextureFormat mFormat = {}; + wgpu::TextureUsage mAllowedUsage; uint32_t mWidth = 0; uint32_t mHeight = 0; - TextureBase* mLastNextTexture = nullptr; + Ref mCurrentTexture; + Ref mCurrentTextureView; + }; + + // The base class for surface-based SwapChains that aren't ready yet. + class NewSwapChainBase : public SwapChainBase { + public: + NewSwapChainBase(DeviceBase* device, + Surface* surface, + const SwapChainDescriptor* descriptor); + + // This is called when the swapchain is detached for any reason: + // + // - The swapchain is being destroyed. + // - The surface it is attached to is being destroyed. + // - The swapchain is being replaced by another one on the surface. + // + // The call for the old swapchain being replaced should be called inside the backend + // implementation of SwapChains. This is to allow them to acquire any resources before + // calling detach to make a seamless transition from the previous swapchain. + // + // Likewise the call for the swapchain being destroyed must be done in the backend's + // swapchain's destructor since C++ says it is UB to call virtual methods in the base class + // destructor. + void DetachFromSurface(); + + // Dawn API + void Configure(wgpu::TextureFormat format, + wgpu::TextureUsage allowedUsage, + uint32_t width, + uint32_t height) override; + TextureViewBase* GetCurrentTextureView() override; + void Present() override; + + uint32_t GetWidth() const; + uint32_t GetHeight() const; + wgpu::TextureFormat GetFormat() const; + wgpu::TextureUsage GetUsage() const; + wgpu::PresentMode GetPresentMode() const; + Surface* GetSurface() const; + bool IsAttached() const; + wgpu::BackendType GetBackendType() const; + + protected: + ~NewSwapChainBase() override; + + private: + bool mAttached; + uint32_t mWidth; + uint32_t mHeight; + wgpu::TextureFormat mFormat; + wgpu::TextureUsage mUsage; + wgpu::PresentMode mPresentMode; + + // This is a weak reference to the surface. If the surface is destroyed it will call + // DetachFromSurface and mSurface will be updated to nullptr. + Surface* mSurface = nullptr; + Ref mCurrentTextureView; + + MaybeError ValidatePresent() const; + MaybeError ValidateGetCurrentTextureView() const; + + // GetCurrentTextureViewImpl and PresentImpl are guaranteed to be called in an interleaved + // manner, starting with GetCurrentTextureViewImpl. + + // The returned texture view must match the swapchain descriptor exactly. + virtual ResultOrError GetCurrentTextureViewImpl() = 0; + // The call to present must destroy the current view's texture so further access to it are + // invalid. + virtual MaybeError PresentImpl() = 0; + + // Guaranteed to be called exactly once during the lifetime of the SwapChain. After it is + // called no other virtual method can be called. + virtual void DetachFromSurfaceImpl() = 0; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Texture.cpp b/third_party/dawn/src/dawn_native/Texture.cpp index d94a707e1ec..9cf2d689f44 100644 --- a/third_party/dawn/src/dawn_native/Texture.cpp +++ b/third_party/dawn/src/dawn_native/Texture.cpp @@ -20,6 +20,7 @@ #include "common/Constants.h" #include "common/Math.h" #include "dawn_native/Device.h" +#include "dawn_native/PassResourceUsage.h" #include "dawn_native/ValidationUtils_autogen.h" namespace dawn_native { @@ -27,7 +28,7 @@ namespace dawn_native { // TODO(jiawei.shao@intel.com): implement texture view format compatibility rule MaybeError ValidateTextureViewFormatCompatibility(const TextureBase* texture, const TextureViewDescriptor* descriptor) { - if (texture->GetFormat() != descriptor->format) { + if (texture->GetFormat().format != descriptor->format) { return DAWN_VALIDATION_ERROR( "The format of texture view is not compatible to the original texture"); } @@ -37,14 +38,14 @@ namespace dawn_native { // TODO(jiawei.shao@intel.com): support validation on all texture view dimensions bool IsTextureViewDimensionCompatibleWithTextureDimension( - dawn::TextureViewDimension textureViewDimension, - dawn::TextureDimension textureDimension) { + wgpu::TextureViewDimension textureViewDimension, + wgpu::TextureDimension textureDimension) { switch (textureViewDimension) { - case dawn::TextureViewDimension::e2D: - case dawn::TextureViewDimension::e2DArray: - case dawn::TextureViewDimension::Cube: - case dawn::TextureViewDimension::CubeArray: - return textureDimension == dawn::TextureDimension::e2D; + case wgpu::TextureViewDimension::e2D: + case wgpu::TextureViewDimension::e2DArray: + case wgpu::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::CubeArray: + return textureDimension == wgpu::TextureDimension::e2D; default: UNREACHABLE(); return false; @@ -53,16 +54,16 @@ namespace dawn_native { // TODO(jiawei.shao@intel.com): support validation on all texture view dimensions bool IsArrayLayerValidForTextureViewDimension( - dawn::TextureViewDimension textureViewDimension, + wgpu::TextureViewDimension textureViewDimension, uint32_t textureViewArrayLayer) { switch (textureViewDimension) { - case dawn::TextureViewDimension::e2D: + case wgpu::TextureViewDimension::e2D: return textureViewArrayLayer == 1u; - case dawn::TextureViewDimension::e2DArray: + case wgpu::TextureViewDimension::e2DArray: return true; - case dawn::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::Cube: return textureViewArrayLayer == 6u; - case dawn::TextureViewDimension::CubeArray: + case wgpu::TextureViewDimension::CubeArray: return textureViewArrayLayer % 6 == 0; default: UNREACHABLE(); @@ -71,14 +72,14 @@ namespace dawn_native { } bool IsTextureSizeValidForTextureViewDimension( - dawn::TextureViewDimension textureViewDimension, + wgpu::TextureViewDimension textureViewDimension, const Extent3D& textureSize) { switch (textureViewDimension) { - case dawn::TextureViewDimension::Cube: - case dawn::TextureViewDimension::CubeArray: + case wgpu::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::CubeArray: return textureSize.width == textureSize.height; - case dawn::TextureViewDimension::e2D: - case dawn::TextureViewDimension::e2DArray: + case wgpu::TextureViewDimension::e2D: + case wgpu::TextureViewDimension::e2DArray: return true; default: UNREACHABLE(); @@ -86,42 +87,8 @@ namespace dawn_native { } } - bool IsBCFormat(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::BC1RGBAUnorm: - case dawn::TextureFormat::BC1RGBAUnormSrgb: - case dawn::TextureFormat::BC2RGBAUnorm: - case dawn::TextureFormat::BC2RGBAUnormSrgb: - case dawn::TextureFormat::BC3RGBAUnorm: - case dawn::TextureFormat::BC3RGBAUnormSrgb: - case dawn::TextureFormat::BC4RUnorm: - case dawn::TextureFormat::BC4RSnorm: - case dawn::TextureFormat::BC5RGUnorm: - case dawn::TextureFormat::BC5RGSnorm: - case dawn::TextureFormat::BC6HRGBUfloat: - case dawn::TextureFormat::BC6HRGBSfloat: - case dawn::TextureFormat::BC7RGBAUnorm: - case dawn::TextureFormat::BC7RGBAUnormSrgb: - return true; - default: - return false; - } - } - - bool IsCompressedFormat(dawn::TextureFormat format) { - return IsBCFormat(format); - } - - bool Is4x4CompressedFormat(dawn::TextureFormat format) { - return IsBCFormat(format); - } - - bool IsWritableFormat(dawn::TextureFormat format) { - return !IsBCFormat(format); - } - // TODO(jiawei.shao@intel.com): support more sample count. - MaybeError ValidateSampleCount(const TextureDescriptor* descriptor) { + MaybeError ValidateSampleCount(const TextureDescriptor* descriptor, const Format* format) { if (!IsValidSampleCount(descriptor->sampleCount)) { return DAWN_VALIDATION_ERROR("The sample count of the texture is not supported."); } @@ -134,14 +101,20 @@ namespace dawn_native { // Multisampled 2D array texture is not supported because on Metal it requires the // version of macOS be greater than 10.14. - if (descriptor->arrayLayerCount > 1) { - return DAWN_VALIDATION_ERROR("Multisampled 2D array texture is not supported."); + if (descriptor->size.depth > 1) { + return DAWN_VALIDATION_ERROR( + "Multisampled textures with depth > 1 are not supported."); } - if (IsCompressedFormat(descriptor->format)) { + if (format->isCompressed) { return DAWN_VALIDATION_ERROR( "The sample counts of the textures in BC formats must be 1."); } + + if (descriptor->usage & wgpu::TextureUsage::Storage) { + return DAWN_VALIDATION_ERROR( + "The sample counts of the storage textures must be 1."); + } } return {}; @@ -173,101 +146,119 @@ namespace dawn_native { return {}; } - TextureViewDescriptor MakeDefaultTextureViewDescriptor(const TextureBase* texture) { - TextureViewDescriptor descriptor; - descriptor.format = texture->GetFormat(); - descriptor.baseArrayLayer = 0; - descriptor.arrayLayerCount = texture->GetArrayLayers(); - descriptor.baseMipLevel = 0; - descriptor.mipLevelCount = texture->GetNumMipLevels(); - - // TODO(jiawei.shao@intel.com): support all texture dimensions. - switch (texture->GetDimension()) { - case dawn::TextureDimension::e2D: - if (texture->GetArrayLayers() == 1u) { - descriptor.dimension = dawn::TextureViewDimension::e2D; - } else { - descriptor.dimension = dawn::TextureViewDimension::e2DArray; - } - break; - default: - UNREACHABLE(); - } - - return descriptor; - } - - MaybeError ValidateTextureSize(const TextureDescriptor* descriptor) { + MaybeError ValidateTextureSize(const TextureDescriptor* descriptor, const Format* format) { ASSERT(descriptor->size.width != 0 && descriptor->size.height != 0); + if (descriptor->size.width > kMaxTextureSize || + descriptor->size.height > kMaxTextureSize) { + return DAWN_VALIDATION_ERROR("Texture max size exceeded"); + } if (Log2(std::max(descriptor->size.width, descriptor->size.height)) + 1 < descriptor->mipLevelCount) { return DAWN_VALIDATION_ERROR("Texture has too many mip levels"); } - if (Is4x4CompressedFormat(descriptor->format)) { - if (descriptor->size.width % 4 != 0 || descriptor->size.height % 4 != 0) { - return DAWN_VALIDATION_ERROR( - "The size of the texture is incompatible with the texture format"); - } + if (format->isCompressed && (descriptor->size.width % format->blockWidth != 0 || + descriptor->size.height % format->blockHeight != 0)) { + return DAWN_VALIDATION_ERROR( + "The size of the texture is incompatible with the texture format"); + } + + if (descriptor->dimension == wgpu::TextureDimension::e2D && + descriptor->size.depth > kMaxTexture2DArrayLayers) { + return DAWN_VALIDATION_ERROR("Texture 2D array layer count exceeded"); + } + if (descriptor->mipLevelCount > kMaxTexture2DMipLevels) { + return DAWN_VALIDATION_ERROR("Max texture 2D mip level exceeded"); } return {}; } - } // anonymous namespace - MaybeError ValidateTextureUsageBit(const TextureDescriptor* descriptor) { - DAWN_TRY(ValidateTextureUsageBit(descriptor->usage)); - if (!IsWritableFormat(descriptor->format)) { - constexpr dawn::TextureUsageBit kValidUsage = dawn::TextureUsageBit::Sampled | - dawn::TextureUsageBit::TransferSrc | - dawn::TextureUsageBit::TransferDst; - if (descriptor->usage & (~kValidUsage)) { + MaybeError ValidateTextureUsage(const TextureDescriptor* descriptor, const Format* format) { + DAWN_TRY(dawn_native::ValidateTextureUsage(descriptor->usage)); + + constexpr wgpu::TextureUsage kValidCompressedUsages = wgpu::TextureUsage::Sampled | + wgpu::TextureUsage::CopySrc | + wgpu::TextureUsage::CopyDst; + if (format->isCompressed && (descriptor->usage & (~kValidCompressedUsages))) { + return DAWN_VALIDATION_ERROR( + "Compressed texture format is incompatible with the texture usage"); + } + + if (!format->isRenderable && + (descriptor->usage & wgpu::TextureUsage::OutputAttachment)) { return DAWN_VALIDATION_ERROR( - "Texture format is incompatible with the texture usage"); + "Non-renderable format used with OutputAttachment usage"); } + + if (!format->supportsStorageUsage && + (descriptor->usage & wgpu::TextureUsage::Storage)) { + return DAWN_VALIDATION_ERROR("Format cannot be used in storage textures"); + } + + return {}; } - return {}; - } + } // anonymous namespace - MaybeError ValidateTextureDescriptor(DeviceBase*, const TextureDescriptor* descriptor) { + MaybeError ValidateTextureDescriptor(const DeviceBase* device, + const TextureDescriptor* descriptor) { + if (descriptor == nullptr) { + return DAWN_VALIDATION_ERROR("Texture descriptor is nullptr"); + } if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - DAWN_TRY(ValidateTextureUsageBit(descriptor)); + const Format* format; + DAWN_TRY_ASSIGN(format, device->GetInternalFormat(descriptor->format)); + + DAWN_TRY(ValidateTextureUsage(descriptor, format)); DAWN_TRY(ValidateTextureDimension(descriptor->dimension)); - DAWN_TRY(ValidateTextureFormat(descriptor->format)); - DAWN_TRY(ValidateSampleCount(descriptor)); + DAWN_TRY(ValidateSampleCount(descriptor, format)); // TODO(jiawei.shao@intel.com): check stuff based on the dimension if (descriptor->size.width == 0 || descriptor->size.height == 0 || - descriptor->size.depth == 0 || descriptor->arrayLayerCount == 0 || - descriptor->mipLevelCount == 0) { + descriptor->size.depth == 0 || descriptor->mipLevelCount == 0) { return DAWN_VALIDATION_ERROR("Cannot create an empty texture"); } - DAWN_TRY(ValidateTextureSize(descriptor)); + if (descriptor->dimension != wgpu::TextureDimension::e2D) { + return DAWN_VALIDATION_ERROR("Texture dimension must be 2D (for now)"); + } + + DAWN_TRY(ValidateTextureSize(descriptor, format)); return {}; } - MaybeError ValidateTextureViewDescriptor(const DeviceBase* device, - const TextureBase* texture, + MaybeError ValidateTextureViewDescriptor(const TextureBase* texture, const TextureViewDescriptor* descriptor) { if (descriptor->nextInChain != nullptr) { return DAWN_VALIDATION_ERROR("nextInChain must be nullptr"); } - DAWN_TRY(device->ValidateObject(texture)); + // Parent texture should have been already validated. + ASSERT(texture); + ASSERT(!texture->IsError()); if (texture->GetTextureState() == TextureBase::TextureState::Destroyed) { return DAWN_VALIDATION_ERROR("Destroyed texture used to create texture view"); } DAWN_TRY(ValidateTextureViewDimension(descriptor->dimension)); + if (descriptor->dimension == wgpu::TextureViewDimension::e1D || + descriptor->dimension == wgpu::TextureViewDimension::e3D) { + return DAWN_VALIDATION_ERROR("Texture view dimension must be 2D compatible."); + } + DAWN_TRY(ValidateTextureFormat(descriptor->format)); + DAWN_TRY(ValidateTextureAspect(descriptor->aspect)); + if (descriptor->aspect != wgpu::TextureAspect::All) { + return DAWN_VALIDATION_ERROR("Texture aspect must be 'all'"); + } + // TODO(jiawei.shao@intel.com): check stuff based on resource limits if (descriptor->arrayLayerCount == 0 || descriptor->mipLevelCount == 0) { return DAWN_VALIDATION_ERROR("Cannot create an empty texture view"); @@ -289,90 +280,70 @@ namespace dawn_native { return {}; } - uint32_t TextureFormatPixelSize(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::R8Unorm: - case dawn::TextureFormat::R8Uint: - return 1; - case dawn::TextureFormat::R8G8Unorm: - case dawn::TextureFormat::R8G8Uint: - return 2; - case dawn::TextureFormat::R8G8B8A8Unorm: - case dawn::TextureFormat::R8G8B8A8Uint: - case dawn::TextureFormat::B8G8R8A8Unorm: - return 4; - case dawn::TextureFormat::D32FloatS8Uint: - return 8; - default: - UNREACHABLE(); - } - } + TextureViewDescriptor GetTextureViewDescriptorWithDefaults( + const TextureBase* texture, + const TextureViewDescriptor* descriptor) { + ASSERT(texture); - bool TextureFormatHasDepth(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::D32FloatS8Uint: - return true; - default: - return false; + TextureViewDescriptor desc = {}; + if (descriptor) { + desc = *descriptor; } - } - bool TextureFormatHasStencil(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::D32FloatS8Uint: - return true; - default: - return false; - } - } + // The default value for the view dimension depends on the texture's dimension with a + // special case for 2DArray being chosen automatically if arrayLayerCount is unspecified. + if (desc.dimension == wgpu::TextureViewDimension::Undefined) { + switch (texture->GetDimension()) { + case wgpu::TextureDimension::e1D: + desc.dimension = wgpu::TextureViewDimension::e1D; + break; - bool TextureFormatHasDepthOrStencil(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::D32FloatS8Uint: - return true; - default: - return false; - } - } + case wgpu::TextureDimension::e2D: + if (texture->GetArrayLayers() > 1u && desc.arrayLayerCount == 0) { + desc.dimension = wgpu::TextureViewDimension::e2DArray; + } else { + desc.dimension = wgpu::TextureViewDimension::e2D; + } + break; - bool IsColorRenderableTextureFormat(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::B8G8R8A8Unorm: - case dawn::TextureFormat::R8G8B8A8Uint: - case dawn::TextureFormat::R8G8B8A8Unorm: - case dawn::TextureFormat::R8G8Uint: - case dawn::TextureFormat::R8G8Unorm: - case dawn::TextureFormat::R8Uint: - case dawn::TextureFormat::R8Unorm: - return true; + case wgpu::TextureDimension::e3D: + desc.dimension = wgpu::TextureViewDimension::e3D; + break; - case dawn::TextureFormat::D32FloatS8Uint: - return false; + default: + UNREACHABLE(); + } + } - default: - UNREACHABLE(); - return false; + if (desc.format == wgpu::TextureFormat::Undefined) { + desc.format = texture->GetFormat().format; + } + if (desc.arrayLayerCount == 0) { + desc.arrayLayerCount = texture->GetArrayLayers() - desc.baseArrayLayer; } + if (desc.mipLevelCount == 0) { + desc.mipLevelCount = texture->GetNumMipLevels() - desc.baseMipLevel; + } + return desc; } - bool IsDepthStencilRenderableTextureFormat(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::D32FloatS8Uint: - return true; + ResultOrError FixTextureDescriptor(DeviceBase* device, + const TextureDescriptor* desc) { + TextureDescriptor fixedDesc = *desc; - case dawn::TextureFormat::B8G8R8A8Unorm: - case dawn::TextureFormat::R8G8B8A8Uint: - case dawn::TextureFormat::R8G8B8A8Unorm: - case dawn::TextureFormat::R8G8Uint: - case dawn::TextureFormat::R8G8Unorm: - case dawn::TextureFormat::R8Uint: - case dawn::TextureFormat::R8Unorm: - return false; - - default: - UNREACHABLE(); - return false; + if (desc->arrayLayerCount != 1) { + if (desc->size.depth != 1) { + return DAWN_VALIDATION_ERROR("arrayLayerCount and size.depth cannot both be != 1"); + } else { + fixedDesc.size.depth = fixedDesc.arrayLayerCount; + fixedDesc.arrayLayerCount = 1; + device->EmitDeprecationWarning( + "wgpu::TextureDescriptor::arrayLayerCount is deprecated in favor of " + "::size::depth"); + } } + + return {std::move(fixedDesc)}; } bool IsValidSampleCount(uint32_t sampleCount) { @@ -386,6 +357,12 @@ namespace dawn_native { } } + // static + SubresourceRange SubresourceRange::SingleSubresource(uint32_t baseMipLevel, + uint32_t baseArrayLayer) { + return {baseMipLevel, 1, baseArrayLayer, 1}; + } + // TextureBase TextureBase::TextureBase(DeviceBase* device, @@ -393,20 +370,26 @@ namespace dawn_native { TextureState state) : ObjectBase(device), mDimension(descriptor->dimension), - mFormat(descriptor->format), + mFormat(device->GetValidInternalFormat(descriptor->format)), mSize(descriptor->size), - mArrayLayerCount(descriptor->arrayLayerCount), mMipLevelCount(descriptor->mipLevelCount), mSampleCount(descriptor->sampleCount), mUsage(descriptor->usage), mState(state) { - uint32_t subresourceCount = - GetSubresourceIndex(descriptor->mipLevelCount, descriptor->arrayLayerCount); + uint32_t subresourceCount = GetSubresourceCount(); mIsSubresourceContentInitializedAtIndex = std::vector(subresourceCount, false); + + // Add readonly storage usage if the texture has a storage usage. The validation rules in + // ValidatePassResourceUsage will make sure we don't use both at the same time. + if (mUsage & wgpu::TextureUsage::Storage) { + mUsage |= kReadonlyStorageTexture; + } } + static Format kUnusedFormat; + TextureBase::TextureBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag) { + : ObjectBase(device, tag), mFormat(kUnusedFormat) { } // static @@ -414,11 +397,13 @@ namespace dawn_native { return new TextureBase(device, ObjectBase::kError); } - dawn::TextureDimension TextureBase::GetDimension() const { + wgpu::TextureDimension TextureBase::GetDimension() const { ASSERT(!IsError()); return mDimension; } - dawn::TextureFormat TextureBase::GetFormat() const { + + // TODO(jiawei.shao@intel.com): return more information about texture format + const Format& TextureBase::GetFormat() const { ASSERT(!IsError()); return mFormat; } @@ -426,19 +411,44 @@ namespace dawn_native { ASSERT(!IsError()); return mSize; } + uint32_t TextureBase::GetWidth() const { + ASSERT(!IsError()); + return mSize.width; + } + uint32_t TextureBase::GetHeight() const { + ASSERT(!IsError()); + ASSERT(mDimension == wgpu::TextureDimension::e2D || + mDimension == wgpu::TextureDimension::e3D); + return mSize.height; + } + uint32_t TextureBase::GetDepth() const { + ASSERT(!IsError()); + ASSERT(mDimension == wgpu::TextureDimension::e3D); + return mSize.depth; + } uint32_t TextureBase::GetArrayLayers() const { ASSERT(!IsError()); - return mArrayLayerCount; + // TODO(cwallez@chromium.org): Update for 1D / 3D textures when they are supported. + ASSERT(mDimension == wgpu::TextureDimension::e2D); + return mSize.depth; } uint32_t TextureBase::GetNumMipLevels() const { ASSERT(!IsError()); return mMipLevelCount; } + SubresourceRange TextureBase::GetAllSubresources() const { + ASSERT(!IsError()); + return {0, mMipLevelCount, 0, GetArrayLayers()}; + } uint32_t TextureBase::GetSampleCount() const { ASSERT(!IsError()); return mSampleCount; } - dawn::TextureUsageBit TextureBase::GetUsage() const { + uint32_t TextureBase::GetSubresourceCount() const { + ASSERT(!IsError()); + return mMipLevelCount * mSize.depth; + } + wgpu::TextureUsage TextureBase::GetUsage() const { ASSERT(!IsError()); return mUsage; } @@ -457,14 +467,12 @@ namespace dawn_native { return GetNumMipLevels() * arraySlice + mipLevel; } - bool TextureBase::IsSubresourceContentInitialized(uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount) const { + bool TextureBase::IsSubresourceContentInitialized(const SubresourceRange& range) const { ASSERT(!IsError()); - for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) { - for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount; - ++arrayLayer) { + for (uint32_t arrayLayer = range.baseArrayLayer; + arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) { + for (uint32_t mipLevel = range.baseMipLevel; + mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) { uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer); ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); if (!mIsSubresourceContentInitializedAtIndex[subresourceIndex]) { @@ -475,17 +483,16 @@ namespace dawn_native { return true; } - void TextureBase::SetIsSubresourceContentInitialized(uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount) { + void TextureBase::SetIsSubresourceContentInitialized(bool isInitialized, + const SubresourceRange& range) { ASSERT(!IsError()); - for (uint32_t mipLevel = baseMipLevel; mipLevel < baseMipLevel + levelCount; ++mipLevel) { - for (uint32_t arrayLayer = baseArrayLayer; arrayLayer < baseArrayLayer + layerCount; - ++arrayLayer) { + for (uint32_t arrayLayer = range.baseArrayLayer; + arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) { + for (uint32_t mipLevel = range.baseMipLevel; + mipLevel < range.baseMipLevel + range.levelCount; ++mipLevel) { uint32_t subresourceIndex = GetSubresourceIndex(mipLevel, arrayLayer); ASSERT(subresourceIndex < mIsSubresourceContentInitializedAtIndex.size()); - mIsSubresourceContentInitializedAtIndex[subresourceIndex] = true; + mIsSubresourceContentInitializedAtIndex[subresourceIndex] = isInitialized; } } } @@ -503,14 +510,48 @@ namespace dawn_native { return mSampleCount > 1; } - TextureViewBase* TextureBase::CreateDefaultView() { - TextureViewDescriptor descriptor = {}; + Extent3D TextureBase::GetMipLevelVirtualSize(uint32_t level) const { + Extent3D extent = {std::max(mSize.width >> level, 1u), 1u, 1u}; + if (mDimension == wgpu::TextureDimension::e1D) { + return extent; + } + + extent.height = std::max(mSize.height >> level, 1u); + if (mDimension == wgpu::TextureDimension::e2D) { + return extent; + } + + extent.depth = std::max(mSize.depth >> level, 1u); + return extent; + } + + Extent3D TextureBase::GetMipLevelPhysicalSize(uint32_t level) const { + Extent3D extent = GetMipLevelVirtualSize(level); - if (!IsError()) { - descriptor = MakeDefaultTextureViewDescriptor(this); + // Compressed Textures will have paddings if their width or height is not a multiple of + // 4 at non-zero mipmap levels. + if (mFormat.isCompressed) { + // TODO(jiawei.shao@intel.com): check if there are any overflows. + uint32_t blockWidth = mFormat.blockWidth; + uint32_t blockHeight = mFormat.blockHeight; + extent.width = (extent.width + blockWidth - 1) / blockWidth * blockWidth; + extent.height = (extent.height + blockHeight - 1) / blockHeight * blockHeight; } - return GetDevice()->CreateTextureView(this, &descriptor); + return extent; + } + + Extent3D TextureBase::ClampToMipLevelVirtualSize(uint32_t level, + const Origin3D& origin, + const Extent3D& extent) const { + const Extent3D virtualSizeAtLevel = GetMipLevelVirtualSize(level); + uint32_t clampedCopyExtentWidth = (origin.x + extent.width > virtualSizeAtLevel.width) + ? (virtualSizeAtLevel.width - origin.x) + : extent.width; + uint32_t clampedCopyExtentHeight = (origin.y + extent.height > virtualSizeAtLevel.height) + ? (virtualSizeAtLevel.height - origin.y) + : extent.height; + return {clampedCopyExtentWidth, clampedCopyExtentHeight, extent.depth}; } TextureViewBase* TextureBase::CreateView(const TextureViewDescriptor* descriptor) { @@ -529,9 +570,7 @@ namespace dawn_native { } void TextureBase::DestroyInternal() { - if (mState == TextureState::OwnedInternal) { - DestroyImpl(); - } + DestroyImpl(); mState = TextureState::Destroyed; } @@ -545,15 +584,15 @@ namespace dawn_native { TextureViewBase::TextureViewBase(TextureBase* texture, const TextureViewDescriptor* descriptor) : ObjectBase(texture->GetDevice()), mTexture(texture), - mFormat(descriptor->format), - mBaseMipLevel(descriptor->baseMipLevel), - mMipLevelCount(descriptor->mipLevelCount), - mBaseArrayLayer(descriptor->baseArrayLayer), - mArrayLayerCount(descriptor->arrayLayerCount) { + mAspect(descriptor->aspect), + mFormat(GetDevice()->GetValidInternalFormat(descriptor->format)), + mDimension(descriptor->dimension), + mRange({descriptor->baseMipLevel, descriptor->mipLevelCount, descriptor->baseArrayLayer, + descriptor->arrayLayerCount}) { } TextureViewBase::TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag) - : ObjectBase(device, tag) { + : ObjectBase(device, tag), mFormat(kUnusedFormat) { } // static @@ -571,28 +610,43 @@ namespace dawn_native { return mTexture.Get(); } - dawn::TextureFormat TextureViewBase::GetFormat() const { + wgpu::TextureAspect TextureViewBase::GetAspect() const { + ASSERT(!IsError()); + return mAspect; + } + + const Format& TextureViewBase::GetFormat() const { ASSERT(!IsError()); return mFormat; } + wgpu::TextureViewDimension TextureViewBase::GetDimension() const { + ASSERT(!IsError()); + return mDimension; + } + uint32_t TextureViewBase::GetBaseMipLevel() const { ASSERT(!IsError()); - return mBaseMipLevel; + return mRange.baseMipLevel; } uint32_t TextureViewBase::GetLevelCount() const { ASSERT(!IsError()); - return mMipLevelCount; + return mRange.levelCount; } uint32_t TextureViewBase::GetBaseArrayLayer() const { ASSERT(!IsError()); - return mBaseArrayLayer; + return mRange.baseArrayLayer; } uint32_t TextureViewBase::GetLayerCount() const { ASSERT(!IsError()); - return mArrayLayerCount; + return mRange.layerCount; + } + + const SubresourceRange& TextureViewBase::GetSubresourceRange() const { + ASSERT(!IsError()); + return mRange; } } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Texture.h b/third_party/dawn/src/dawn_native/Texture.h index dc3771eb0ae..7c476ba2520 100644 --- a/third_party/dawn/src/dawn_native/Texture.h +++ b/third_party/dawn/src/dawn_native/Texture.h @@ -24,59 +24,78 @@ #include namespace dawn_native { - MaybeError ValidateTextureDescriptor(DeviceBase* device, const TextureDescriptor* descriptor); - MaybeError ValidateTextureViewDescriptor(const DeviceBase* device, - const TextureBase* texture, + MaybeError ValidateTextureDescriptor(const DeviceBase* device, + const TextureDescriptor* descriptor); + MaybeError ValidateTextureViewDescriptor(const TextureBase* texture, const TextureViewDescriptor* descriptor); + TextureViewDescriptor GetTextureViewDescriptorWithDefaults( + const TextureBase* texture, + const TextureViewDescriptor* descriptor); + + // TODO(dawn:22): Remove once migration from GPUTextureDescriptor.arrayLayerCount to + // GPUTextureDescriptor.size.depth is done. + ResultOrError FixTextureDescriptor(DeviceBase* device, + const TextureDescriptor* desc); - uint32_t TextureFormatPixelSize(dawn::TextureFormat format); - bool TextureFormatHasDepth(dawn::TextureFormat format); - bool TextureFormatHasStencil(dawn::TextureFormat format); - bool TextureFormatHasDepthOrStencil(dawn::TextureFormat format); - bool IsColorRenderableTextureFormat(dawn::TextureFormat format); - bool IsDepthStencilRenderableTextureFormat(dawn::TextureFormat format); bool IsValidSampleCount(uint32_t sampleCount); - static constexpr dawn::TextureUsageBit kReadOnlyTextureUsages = - dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::Sampled | - dawn::TextureUsageBit::Present; + static constexpr wgpu::TextureUsage kReadOnlyTextureUsages = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::Sampled | kReadonlyStorageTexture; + + static constexpr wgpu::TextureUsage kWritableTextureUsages = + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Storage | + wgpu::TextureUsage::OutputAttachment; - static constexpr dawn::TextureUsageBit kWritableTextureUsages = - dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Storage | - dawn::TextureUsageBit::OutputAttachment; + struct SubresourceRange { + uint32_t baseMipLevel; + uint32_t levelCount; + uint32_t baseArrayLayer; + uint32_t layerCount; + + static SubresourceRange SingleSubresource(uint32_t baseMipLevel, uint32_t baseArrayLayer); + }; class TextureBase : public ObjectBase { public: enum class TextureState { OwnedInternal, OwnedExternal, Destroyed }; - + enum class ClearValue { Zero, NonZero }; TextureBase(DeviceBase* device, const TextureDescriptor* descriptor, TextureState state); static TextureBase* MakeError(DeviceBase* device); - dawn::TextureDimension GetDimension() const; - dawn::TextureFormat GetFormat() const; + wgpu::TextureDimension GetDimension() const; + const Format& GetFormat() const; const Extent3D& GetSize() const; + uint32_t GetWidth() const; + uint32_t GetHeight() const; + uint32_t GetDepth() const; uint32_t GetArrayLayers() const; uint32_t GetNumMipLevels() const; + SubresourceRange GetAllSubresources() const; uint32_t GetSampleCount() const; - dawn::TextureUsageBit GetUsage() const; + uint32_t GetSubresourceCount() const; + wgpu::TextureUsage GetUsage() const; TextureState GetTextureState() const; uint32_t GetSubresourceIndex(uint32_t mipLevel, uint32_t arraySlice) const; - bool IsSubresourceContentInitialized(uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount) const; - void SetIsSubresourceContentInitialized(uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount); + bool IsSubresourceContentInitialized(const SubresourceRange& range) const; + void SetIsSubresourceContentInitialized(bool isInitialized, const SubresourceRange& range); MaybeError ValidateCanUseInSubmitNow() const; bool IsMultisampledTexture() const; + // For a texture with non-block-compressed texture format, its physical size is always equal + // to its virtual size. For a texture with block compressed texture format, the physical + // size is the one with paddings if necessary, which is always a multiple of the block size + // and used in texture copying. The virtual size is the one without paddings, which is not + // required to be a multiple of the block size and used in texture sampling. + Extent3D GetMipLevelPhysicalSize(uint32_t level) const; + Extent3D GetMipLevelVirtualSize(uint32_t level) const; + Extent3D ClampToMipLevelVirtualSize(uint32_t level, + const Origin3D& origin, + const Extent3D& extent) const; + // Dawn API - TextureViewBase* CreateDefaultView(); TextureViewBase* CreateView(const TextureViewDescriptor* descriptor); void Destroy(); @@ -88,13 +107,13 @@ namespace dawn_native { virtual void DestroyImpl(); MaybeError ValidateDestroy() const; - dawn::TextureDimension mDimension; - dawn::TextureFormat mFormat; + wgpu::TextureDimension mDimension; + // TODO(cwallez@chromium.org): This should be deduplicated in the Device + const Format& mFormat; Extent3D mSize; - uint32_t mArrayLayerCount; uint32_t mMipLevelCount; uint32_t mSampleCount; - dawn::TextureUsageBit mUsage = dawn::TextureUsageBit::None; + wgpu::TextureUsage mUsage = wgpu::TextureUsage::None; TextureState mState; // TODO(natlee@microsoft.com): Use a more optimized data structure to save space @@ -110,22 +129,25 @@ namespace dawn_native { const TextureBase* GetTexture() const; TextureBase* GetTexture(); - dawn::TextureFormat GetFormat() const; + wgpu::TextureAspect GetAspect() const; + const Format& GetFormat() const; + wgpu::TextureViewDimension GetDimension() const; uint32_t GetBaseMipLevel() const; uint32_t GetLevelCount() const; uint32_t GetBaseArrayLayer() const; uint32_t GetLayerCount() const; + const SubresourceRange& GetSubresourceRange() const; private: TextureViewBase(DeviceBase* device, ObjectBase::ErrorTag tag); Ref mTexture; - dawn::TextureFormat mFormat; - uint32_t mBaseMipLevel; - uint32_t mMipLevelCount; - uint32_t mBaseArrayLayer; - uint32_t mArrayLayerCount; + wgpu::TextureAspect mAspect; + // TODO(cwallez@chromium.org): This should be deduplicated in the Device + const Format& mFormat; + wgpu::TextureViewDimension mDimension; + SubresourceRange mRange; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/ToBackend.h b/third_party/dawn/src/dawn_native/ToBackend.h index 4f11fd45ae1..3cc071580c2 100644 --- a/third_party/dawn/src/dawn_native/ToBackend.h +++ b/third_party/dawn/src/dawn_native/ToBackend.h @@ -63,6 +63,11 @@ namespace dawn_native { using BackendType = typename BackendTraits::PipelineLayoutType; }; + template + struct ToBackendTraits { + using BackendType = typename BackendTraits::QuerySetType; + }; + template struct ToBackendTraits { using BackendType = typename BackendTraits::QueueType; @@ -73,6 +78,11 @@ namespace dawn_native { using BackendType = typename BackendTraits::RenderPipelineType; }; + template + struct ToBackendTraits { + using BackendType = typename BackendTraits::ResourceHeapType; + }; + template struct ToBackendTraits { using BackendType = typename BackendTraits::SamplerType; diff --git a/third_party/dawn/src/dawn_native/Toggles.cpp b/third_party/dawn/src/dawn_native/Toggles.cpp new file mode 100644 index 00000000000..7f40ebd0c49 --- /dev/null +++ b/third_party/dawn/src/dawn_native/Toggles.cpp @@ -0,0 +1,217 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/Assert.h" +#include "common/BitSetIterator.h" +#include "dawn_native/Toggles.h" + +namespace dawn_native { + namespace { + + struct ToggleEnumAndInfo { + Toggle toggle; + ToggleInfo info; + }; + + using ToggleEnumAndInfoList = + std::array(Toggle::EnumCount)>; + + static constexpr ToggleEnumAndInfoList kToggleNameAndInfoList = { + {{Toggle::EmulateStoreAndMSAAResolve, + {"emulate_store_and_msaa_resolve", + "Emulate storing into multisampled color attachments and doing MSAA resolve " + "simultaneously. This workaround is enabled by default on the Metal drivers that do " + "not support MTLStoreActionStoreAndMultisampleResolve. To support StoreOp::Store on " + "those platforms, we should do MSAA resolve in another render pass after ending the " + "previous one.", + "https://crbug.com/dawn/56"}}, + {Toggle::NonzeroClearResourcesOnCreationForTesting, + {"nonzero_clear_resources_on_creation_for_testing", + "Clears texture to full 1 bits as soon as they are created, but doesn't update " + "the tracking state of the texture. This way we can test the logic of clearing " + "textures that use recycled memory.", + "https://crbug.com/dawn/145"}}, + {Toggle::AlwaysResolveIntoZeroLevelAndLayer, + {"always_resolve_into_zero_level_and_layer", + "When the resolve target is a texture view that is created on the non-zero level or " + "layer of a texture, we first resolve into a temporarily 2D texture with only one " + "mipmap level and one array layer, and copy the result of MSAA resolve into the " + "true resolve target. This workaround is enabled by default on the Metal drivers " + "that have bugs when setting non-zero resolveLevel or resolveSlice.", + "https://crbug.com/dawn/56"}}, + {Toggle::LazyClearResourceOnFirstUse, + {"lazy_clear_resource_on_first_use", + "Clears resource to zero on first usage. This initializes the resource " + "so that no dirty bits from recycled memory is present in the new resource.", + "https://crbug.com/dawn/145"}}, + {Toggle::TurnOffVsync, + {"turn_off_vsync", + "Turn off vsync when rendering. In order to do performance test or run perf tests, " + "turn off vsync so that the fps can exeed 60.", + "https://crbug.com/dawn/237"}}, + {Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy, + {"use_temporary_buffer_in_texture_to_texture_copy", + "Split texture-to-texture copy into two copies: copy from source texture into a " + "temporary buffer, and copy from the temporary buffer into the destination texture " + "when copying between compressed textures that don't have block-aligned sizes. This " + "workaround is enabled by default on all Vulkan drivers to solve an issue in the " + "Vulkan SPEC about the texture-to-texture copies with compressed formats. See #1005 " + "(https://github.com/KhronosGroup/Vulkan-Docs/issues/1005) for more details.", + "https://crbug.com/dawn/42"}}, + {Toggle::UseD3D12ResourceHeapTier2, + {"use_d3d12_resource_heap_tier2", + "Enable support for resource heap tier 2. Resource heap tier 2 allows mixing of " + "texture and buffers in the same heap. This allows better heap re-use and reduces " + "fragmentation.", + "https://crbug.com/dawn/27"}}, + {Toggle::UseD3D12RenderPass, + {"use_d3d12_render_pass", + "Use the D3D12 render pass API introduced in Windows build 1809 by default. On " + "versions of Windows prior to build 1809, or when this toggle is turned off, Dawn " + "will emulate a render pass.", + "https://crbug.com/dawn/36"}}, + {Toggle::UseD3D12ResidencyManagement, + {"use_d3d12_residency_management", + "Enable residency management. This allows page-in and page-out of resource heaps in " + "GPU memory. This component improves overcommitted performance by keeping the most " + "recently used resources local to the GPU. Turning this component off can cause " + "allocation failures when application memory exceeds physical device memory.", + "https://crbug.com/dawn/193"}}, + {Toggle::SkipValidation, + {"skip_validation", "Skip expensive validation of Dawn commands.", + "https://crbug.com/dawn/271"}}, + {Toggle::UseSpvc, + {"use_spvc", + "Enable use of spvc for shader compilation, instead of accessing spirv_cross " + "directly.", + "https://crbug.com/dawn/288"}}, + {Toggle::UseSpvcParser, + {"use_spvc_parser", + "Enable usage of spvc's internal parsing and IR generation code, instead of " + "spirv_cross's.", + "https://crbug.com/dawn/288"}}, + {Toggle::VulkanUseD32S8, + {"vulkan_use_d32s8", + "Vulkan mandates support of either D32_FLOAT_S8 or D24_UNORM_S8. When available the " + "backend will use D32S8 (toggle to on) but setting the toggle to off will make it" + "use the D24S8 format when possible.", + "https://crbug.com/dawn/286"}}, + {Toggle::MetalDisableSamplerCompare, + {"metal_disable_sampler_compare", + "Disables the use of sampler compare on Metal. This is unsupported before A9 " + "processors.", + "https://crbug.com/dawn/342"}}, + {Toggle::DisableBaseVertex, + {"disable_base_vertex", + "Disables the use of non-zero base vertex which is unsupported on some platforms.", + "https://crbug.com/dawn/343"}}, + {Toggle::DisableBaseInstance, + {"disable_base_instance", + "Disables the use of non-zero base instance which is unsupported on some " + "platforms.", + "https://crbug.com/dawn/343"}}, + {Toggle::UseD3D12SmallShaderVisibleHeapForTesting, + {"use_d3d12_small_shader_visible_heap", + "Enable use of a small D3D12 shader visible heap, instead of using a large one by " + "default. This setting is used to test bindgroup encoding.", + "https://crbug.com/dawn/155"}}, + {Toggle::UseDXC, + {"use_dxc", "Use DXC instead of FXC for compiling HLSL", + "https://crbug.com/dawn/402"}}, + {Toggle::DisableRobustness, + {"disable_robustness", "Disable robust buffer access", "https://crbug.com/dawn/480"}}, + {Toggle::LazyClearBufferOnFirstUse, + {"lazy_clear_buffer_on_first_use", + "Clear buffers on their first use. This is a temporary toggle only for the " + "development of buffer lazy initialization and will be removed after buffer lazy " + "initialization is completely implemented.", + "https://crbug.com/dawn/414"}}}}; + + } // anonymous namespace + + void TogglesSet::Set(Toggle toggle, bool enabled) { + ASSERT(toggle != Toggle::InvalidEnum); + const size_t toggleIndex = static_cast(toggle); + toggleBitset.set(toggleIndex, enabled); + } + + bool TogglesSet::Has(Toggle toggle) const { + ASSERT(toggle != Toggle::InvalidEnum); + const size_t toggleIndex = static_cast(toggle); + return toggleBitset.test(toggleIndex); + } + + std::vector TogglesSet::GetContainedToggleNames() const { + std::vector togglesNameInUse(toggleBitset.count()); + + uint32_t index = 0; + for (uint32_t i : IterateBitSet(toggleBitset)) { + const char* toggleName = ToggleEnumToName(static_cast(i)); + togglesNameInUse[index] = toggleName; + ++index; + } + + return togglesNameInUse; + } + + const char* ToggleEnumToName(Toggle toggle) { + ASSERT(toggle != Toggle::InvalidEnum); + + const ToggleEnumAndInfo& toggleNameAndInfo = + kToggleNameAndInfoList[static_cast(toggle)]; + ASSERT(toggleNameAndInfo.toggle == toggle); + return toggleNameAndInfo.info.name; + } + + const ToggleInfo* TogglesInfo::GetToggleInfo(const char* toggleName) { + ASSERT(toggleName); + + EnsureToggleNameToEnumMapInitialized(); + + const auto& iter = mToggleNameToEnumMap.find(toggleName); + if (iter != mToggleNameToEnumMap.cend()) { + return &kToggleNameAndInfoList[static_cast(iter->second)].info; + } + return nullptr; + } + + Toggle TogglesInfo::ToggleNameToEnum(const char* toggleName) { + ASSERT(toggleName); + + EnsureToggleNameToEnumMapInitialized(); + + const auto& iter = mToggleNameToEnumMap.find(toggleName); + if (iter != mToggleNameToEnumMap.cend()) { + return kToggleNameAndInfoList[static_cast(iter->second)].toggle; + } + return Toggle::InvalidEnum; + } + + void TogglesInfo::EnsureToggleNameToEnumMapInitialized() { + if (mToggleNameToEnumMapInitialized) { + return; + } + + for (size_t index = 0; index < kToggleNameAndInfoList.size(); ++index) { + const ToggleEnumAndInfo& toggleNameAndInfo = kToggleNameAndInfoList[index]; + ASSERT(index == static_cast(toggleNameAndInfo.toggle)); + mToggleNameToEnumMap[toggleNameAndInfo.info.name] = toggleNameAndInfo.toggle; + } + + mToggleNameToEnumMapInitialized = true; + } + +} // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/Toggles.h b/third_party/dawn/src/dawn_native/Toggles.h index ac8b3dc1abf..f9d66a2d0f9 100644 --- a/third_party/dawn/src/dawn_native/Toggles.h +++ b/third_party/dawn/src/dawn_native/Toggles.h @@ -16,6 +16,8 @@ #define DAWNNATIVE_TOGGLES_H_ #include +#include +#include #include "dawn_native/DawnNative.h" @@ -26,27 +28,51 @@ namespace dawn_native { NonzeroClearResourcesOnCreationForTesting, AlwaysResolveIntoZeroLevelAndLayer, LazyClearResourceOnFirstUse, + TurnOffVsync, + UseTemporaryBufferInCompressedTextureToTextureCopy, + UseD3D12ResourceHeapTier2, + UseD3D12RenderPass, + UseD3D12ResidencyManagement, + SkipValidation, + UseSpvc, + UseSpvcParser, + VulkanUseD32S8, + MetalDisableSamplerCompare, + DisableBaseVertex, + DisableBaseInstance, + UseD3D12SmallShaderVisibleHeapForTesting, + UseDXC, + DisableRobustness, + LazyClearBufferOnFirstUse, EnumCount, InvalidEnum = EnumCount, }; - // A wrapper of the bitset to store if a toggle is enabled or not. This wrapper provides the + // A wrapper of the bitset to store if a toggle is present or not. This wrapper provides the // convenience to convert the enums of enum class Toggle to the indices of a bitset. struct TogglesSet { std::bitset(Toggle::EnumCount)> toggleBitset; - void SetToggle(Toggle toggle, bool enabled) { - ASSERT(toggle != Toggle::InvalidEnum); - const size_t toggleIndex = static_cast(toggle); - toggleBitset.set(toggleIndex, enabled); - } - - bool IsEnabled(Toggle toggle) const { - ASSERT(toggle != Toggle::InvalidEnum); - const size_t toggleIndex = static_cast(toggle); - return toggleBitset.test(toggleIndex); - } + void Set(Toggle toggle, bool enabled); + bool Has(Toggle toggle) const; + std::vector GetContainedToggleNames() const; + }; + + const char* ToggleEnumToName(Toggle toggle); + + class TogglesInfo { + public: + // Used to query the details of a toggle. Return nullptr if toggleName is not a valid name + // of a toggle supported in Dawn. + const ToggleInfo* GetToggleInfo(const char* toggleName); + Toggle ToggleNameToEnum(const char* toggleName); + + private: + void EnsureToggleNameToEnumMapInitialized(); + + bool mToggleNameToEnumMapInitialized = false; + std::unordered_map mToggleNameToEnumMap; }; } // namespace dawn_native diff --git a/third_party/dawn/src/dawn_native/d3d12/AdapterD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/AdapterD3D12.cpp index e2c92ccf147..5b98915ddcc 100644 --- a/third_party/dawn/src/dawn_native/d3d12/AdapterD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/AdapterD3D12.cpp @@ -15,7 +15,9 @@ #include "dawn_native/d3d12/AdapterD3D12.h" #include "common/Constants.h" +#include "dawn_native/Instance.h" #include "dawn_native/d3d12/BackendD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" @@ -34,17 +36,21 @@ namespace dawn_native { namespace d3d12 { } }; - Adapter::Adapter(Backend* backend, ComPtr hardwareAdapter) - : AdapterBase(backend->GetInstance(), BackendType::D3D12), + Adapter::Adapter(Backend* backend, ComPtr hardwareAdapter) + : AdapterBase(backend->GetInstance(), wgpu::BackendType::D3D12), mHardwareAdapter(hardwareAdapter), mBackend(backend) { } + Adapter::~Adapter() { + CleanUpDebugLayerFilters(); + } + const D3D12DeviceInfo& Adapter::GetDeviceInfo() const { return mDeviceInfo; } - IDXGIAdapter1* Adapter::GetHardwareAdapter() const { + IDXGIAdapter3* Adapter::GetHardwareAdapter() const { return mHardwareAdapter.Get(); } @@ -63,10 +69,10 @@ namespace dawn_native { namespace d3d12 { const PlatformFunctions* functions = GetBackend()->GetFunctions(); if (FAILED(functions->d3d12CreateDevice(GetHardwareAdapter(), D3D_FEATURE_LEVEL_11_0, _uuidof(ID3D12Device), &mD3d12Device))) { - return DAWN_CONTEXT_LOST_ERROR("D3D12CreateDevice failed"); + return DAWN_INTERNAL_ERROR("D3D12CreateDevice failed"); } - DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this)); + DAWN_TRY(InitializeDebugLayerFilters()); DXGI_ADAPTER_DESC1 adapterDesc; mHardwareAdapter->GetDesc1(&adapterDesc); @@ -74,23 +80,107 @@ namespace dawn_native { namespace d3d12 { mPCIInfo.deviceId = adapterDesc.DeviceId; mPCIInfo.vendorId = adapterDesc.VendorId; + DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this)); + if (adapterDesc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) { - mDeviceType = DeviceType::CPU; + mAdapterType = wgpu::AdapterType::CPU; } else { - mDeviceType = (mDeviceInfo.isUMA) ? DeviceType::IntegratedGPU : DeviceType::DiscreteGPU; + mAdapterType = (mDeviceInfo.isUMA) ? wgpu::AdapterType::IntegratedGPU + : wgpu::AdapterType::DiscreteGPU; } std::wstring_convert>> converter( "Error converting"); mPCIInfo.name = converter.to_bytes(adapterDesc.Description); + InitializeSupportedExtensions(); + + return {}; + } + + void Adapter::InitializeSupportedExtensions() { + mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC); + mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery); + mSupportedExtensions.EnableExtension(Extension::TimestampQuery); + if (mDeviceInfo.supportsShaderFloat16 && GetBackend()->GetFunctions()->IsDXCAvailable()) { + mSupportedExtensions.EnableExtension(Extension::ShaderFloat16); + } + } + + MaybeError Adapter::InitializeDebugLayerFilters() { + if (!GetInstance()->IsBackendValidationEnabled()) { + return {}; + } + + D3D12_MESSAGE_ID denyIds[] = { + + // + // Permanent IDs: list of warnings that are not applicable + // + + // Resource sub-allocation partially maps pre-allocated heaps. This means the + // entire physical addresses space may have no resources or have many resources + // assigned the same heap. + D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_HAS_NO_RESOURCE, + D3D12_MESSAGE_ID_HEAP_ADDRESS_RANGE_INTERSECTS_MULTIPLE_BUFFERS, + + // The debug layer validates pipeline objects when they are created. Dawn validates + // them when them when they are set. Therefore, since the issue is caught at a later + // time, we can silence this warnings. + D3D12_MESSAGE_ID_CREATEGRAPHICSPIPELINESTATE_RENDERTARGETVIEW_NOT_SET, + + // Adding a clear color during resource creation would require heuristics or delayed + // creation. + // https://crbug.com/dawn/418 + D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE, + D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE, + + // Dawn enforces proper Unmaps at a later time. + // https://crbug.com/dawn/422 + D3D12_MESSAGE_ID_EXECUTECOMMANDLISTS_GPU_WRITTEN_READBACK_RESOURCE_MAPPED, + + // + // Temporary IDs: list of warnings that should be fixed or promoted + // + + // Remove after warning have been addressed + // https://crbug.com/dawn/421 + D3D12_MESSAGE_ID_GPU_BASED_VALIDATION_INCOMPATIBLE_RESOURCE_STATE, + }; + + // Create a retrieval filter with a deny list to suppress messages. + // Any messages remaining will be converted to Dawn errors. + D3D12_INFO_QUEUE_FILTER filter{}; + // Filter out info/message and only create errors from warnings or worse. + D3D12_MESSAGE_SEVERITY severities[] = { + D3D12_MESSAGE_SEVERITY_INFO, + D3D12_MESSAGE_SEVERITY_MESSAGE, + }; + filter.DenyList.NumSeverities = ARRAYSIZE(severities); + filter.DenyList.pSeverityList = severities; + filter.DenyList.NumIDs = ARRAYSIZE(denyIds); + filter.DenyList.pIDList = denyIds; + + ComPtr infoQueue; + ASSERT_SUCCESS(mD3d12Device.As(&infoQueue)); + + DAWN_TRY(CheckHRESULT(infoQueue->PushRetrievalFilter(&filter), + "ID3D12InfoQueue::PushRetrievalFilter")); + return {}; } + void Adapter::CleanUpDebugLayerFilters() { + if (!GetInstance()->IsBackendValidationEnabled()) { + return; + } + ComPtr infoQueue; + ASSERT_SUCCESS(mD3d12Device.As(&infoQueue)); + infoQueue->PopRetrievalFilter(); + } + ResultOrError Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) { - std::unique_ptr device = std::make_unique(this, descriptor); - DAWN_TRY(device->Initialize()); - return device.release(); + return Device::Create(this, descriptor); } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/AdapterD3D12.h b/third_party/dawn/src/dawn_native/d3d12/AdapterD3D12.h index b5a726b88ab..e0910bffa55 100644 --- a/third_party/dawn/src/dawn_native/d3d12/AdapterD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/AdapterD3D12.h @@ -26,11 +26,11 @@ namespace dawn_native { namespace d3d12 { class Adapter : public AdapterBase { public: - Adapter(Backend* backend, ComPtr hardwareAdapter); - virtual ~Adapter() = default; + Adapter(Backend* backend, ComPtr hardwareAdapter); + ~Adapter() override; const D3D12DeviceInfo& GetDeviceInfo() const; - IDXGIAdapter1* GetHardwareAdapter() const; + IDXGIAdapter3* GetHardwareAdapter() const; Backend* GetBackend() const; ComPtr GetDevice() const; @@ -38,8 +38,11 @@ namespace dawn_native { namespace d3d12 { private: ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override; + void InitializeSupportedExtensions(); + MaybeError InitializeDebugLayerFilters(); + void CleanUpDebugLayerFilters(); - ComPtr mHardwareAdapter; + ComPtr mHardwareAdapter; ComPtr mD3d12Device; Backend* mBackend; @@ -48,4 +51,4 @@ namespace dawn_native { namespace d3d12 { }} // namespace dawn_native::d3d12 -#endif // DAWNNATIVE_D3D12_ADAPTERD3D12_H_ \ No newline at end of file +#endif // DAWNNATIVE_D3D12_ADAPTERD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/BackendD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/BackendD3D12.cpp index b702fe49d0c..70750939534 100644 --- a/third_party/dawn/src/dawn_native/d3d12/BackendD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/BackendD3D12.cpp @@ -17,6 +17,7 @@ #include "dawn_native/D3D12Backend.h" #include "dawn_native/Instance.h" #include "dawn_native/d3d12/AdapterD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/PlatformFunctions.h" namespace dawn_native { namespace d3d12 { @@ -24,7 +25,8 @@ namespace dawn_native { namespace d3d12 { namespace { ResultOrError> CreateFactory(const PlatformFunctions* functions, - bool enableBackendValidation) { + bool enableBackendValidation, + bool beginCaptureOnStartup) { ComPtr factory; uint32_t dxgiFactoryFlags = 0; @@ -32,11 +34,12 @@ namespace dawn_native { namespace d3d12 { // Enable the debug layer (requires the Graphics Tools "optional feature"). { if (enableBackendValidation) { - ComPtr debugController; + ComPtr debugController; if (SUCCEEDED( functions->d3d12GetDebugInterface(IID_PPV_ARGS(&debugController)))) { ASSERT(debugController != nullptr); debugController->EnableDebugLayer(); + debugController->SetEnableGPUBasedValidation(true); // Enable additional debug layers. dxgiFactoryFlags |= DXGI_CREATE_FACTORY_DEBUG; @@ -49,27 +52,51 @@ namespace dawn_native { namespace d3d12 { DXGI_DEBUG_RLO_FLAGS(DXGI_DEBUG_RLO_ALL)); } } + + if (beginCaptureOnStartup) { + ComPtr graphicsAnalysis; + if (SUCCEEDED(functions->dxgiGetDebugInterface1( + 0, IID_PPV_ARGS(&graphicsAnalysis)))) { + graphicsAnalysis->BeginCapture(); + } + } } if (FAILED(functions->createDxgiFactory2(dxgiFactoryFlags, IID_PPV_ARGS(&factory)))) { - return DAWN_CONTEXT_LOST_ERROR("Failed to create a DXGI factory"); + return DAWN_INTERNAL_ERROR("Failed to create a DXGI factory"); } ASSERT(factory != nullptr); - return factory; + return std::move(factory); + } + + ResultOrError> CreateAdapterFromIDXGIAdapter( + Backend* backend, + ComPtr dxgiAdapter) { + ComPtr dxgiAdapter3; + DAWN_TRY(CheckHRESULT(dxgiAdapter.As(&dxgiAdapter3), "DXGIAdapter retrieval")); + std::unique_ptr adapter = + std::make_unique(backend, std::move(dxgiAdapter3)); + DAWN_TRY(adapter->Initialize()); + + return {std::move(adapter)}; } } // anonymous namespace - Backend::Backend(InstanceBase* instance) : BackendConnection(instance, BackendType::D3D12) { + Backend::Backend(InstanceBase* instance) + : BackendConnection(instance, wgpu::BackendType::D3D12) { } MaybeError Backend::Initialize() { mFunctions = std::make_unique(); DAWN_TRY(mFunctions->LoadFunctions()); - DAWN_TRY_ASSIGN( - mFactory, CreateFactory(mFunctions.get(), GetInstance()->IsBackendValidationEnabled())); + const auto instance = GetInstance(); + + DAWN_TRY_ASSIGN(mFactory, + CreateFactory(mFunctions.get(), instance->IsBackendValidationEnabled(), + instance->IsBeginCaptureOnStartupEnabled())); return {}; } @@ -78,6 +105,26 @@ namespace dawn_native { namespace d3d12 { return mFactory; } + ResultOrError Backend::GetOrCreateDxcLibrary() { + if (mDxcLibrary == nullptr) { + DAWN_TRY(CheckHRESULT( + mFunctions->dxcCreateInstance(CLSID_DxcLibrary, IID_PPV_ARGS(&mDxcLibrary)), + "DXC create library")); + ASSERT(mDxcLibrary != nullptr); + } + return mDxcLibrary.Get(); + } + + ResultOrError Backend::GetOrCreateDxcCompiler() { + if (mDxcCompiler == nullptr) { + DAWN_TRY(CheckHRESULT( + mFunctions->dxcCreateInstance(CLSID_DxcCompiler, IID_PPV_ARGS(&mDxcCompiler)), + "DXC create compiler")); + ASSERT(mDxcCompiler != nullptr); + } + return mDxcCompiler.Get(); + } + const PlatformFunctions* Backend::GetFunctions() const { return mFunctions.get(); } @@ -92,18 +139,34 @@ namespace dawn_native { namespace d3d12 { } ASSERT(dxgiAdapter != nullptr); - - std::unique_ptr adapter = std::make_unique(this, dxgiAdapter); - if (GetInstance()->ConsumedError(adapter->Initialize())) { + ResultOrError> adapter = + CreateAdapterFromIDXGIAdapter(this, dxgiAdapter); + if (adapter.IsError()) { + adapter.AcquireError(); continue; } - adapters.push_back(std::move(adapter)); + adapters.push_back(std::move(adapter.AcquireSuccess())); } return adapters; } + ResultOrError>> Backend::DiscoverAdapters( + const AdapterDiscoveryOptionsBase* optionsBase) { + ASSERT(optionsBase->backendType == WGPUBackendType_D3D12); + const AdapterDiscoveryOptions* options = + static_cast(optionsBase); + + ASSERT(options->dxgiAdapter != nullptr); + + std::unique_ptr adapter; + DAWN_TRY_ASSIGN(adapter, CreateAdapterFromIDXGIAdapter(this, options->dxgiAdapter)); + std::vector> adapters; + adapters.push_back(std::move(adapter)); + return std::move(adapters); + } + BackendConnection* Connect(InstanceBase* instance) { Backend* backend = new Backend(instance); diff --git a/third_party/dawn/src/dawn_native/d3d12/BackendD3D12.h b/third_party/dawn/src/dawn_native/d3d12/BackendD3D12.h index 3161048a088..87c2d13f04b 100644 --- a/third_party/dawn/src/dawn_native/d3d12/BackendD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/BackendD3D12.h @@ -30,17 +30,23 @@ namespace dawn_native { namespace d3d12 { MaybeError Initialize(); ComPtr GetFactory() const; + ResultOrError GetOrCreateDxcLibrary(); + ResultOrError GetOrCreateDxcCompiler(); const PlatformFunctions* GetFunctions() const; std::vector> DiscoverDefaultAdapters() override; + ResultOrError>> DiscoverAdapters( + const AdapterDiscoveryOptionsBase* optionsBase) override; private: // Keep mFunctions as the first member so that in the destructor it is freed last. Otherwise // the D3D12 DLLs are unloaded before we are done using them. std::unique_ptr mFunctions; ComPtr mFactory; + ComPtr mDxcLibrary; + ComPtr mDxcCompiler; }; }} // namespace dawn_native::d3d12 -#endif // DAWNNATIVE_D3D12_BACKENDD3D12_H_ \ No newline at end of file +#endif // DAWNNATIVE_D3D12_BACKENDD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/BindGroupD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/BindGroupD3D12.cpp index 1c5862cf0eb..21d2743e94f 100644 --- a/third_party/dawn/src/dawn_native/d3d12/BindGroupD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/BindGroupD3D12.cpp @@ -13,110 +13,193 @@ // limitations under the License. #include "dawn_native/d3d12/BindGroupD3D12.h" + #include "common/BitSetIterator.h" #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" #include "dawn_native/d3d12/BufferD3D12.h" -#include "dawn_native/d3d12/SamplerD3D12.h" -#include "dawn_native/d3d12/TextureD3D12.h" - #include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/SamplerHeapCacheD3D12.h" +#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" +#include "dawn_native/d3d12/TextureD3D12.h" namespace dawn_native { namespace d3d12 { - BindGroup::BindGroup(Device* device, const BindGroupDescriptor* descriptor) - : BindGroupBase(device, descriptor) { + // static + ResultOrError BindGroup::Create(Device* device, + const BindGroupDescriptor* descriptor) { + return ToBackend(descriptor->layout)->AllocateBindGroup(device, descriptor); } - void BindGroup::AllocateDescriptors(const DescriptorHeapHandle& cbvUavSrvHeapStart, - uint32_t* cbvUavSrvHeapOffset, - const DescriptorHeapHandle& samplerHeapStart, - uint32_t* samplerHeapOffset) { - const auto* bgl = ToBackend(GetLayout()); - const auto& layout = bgl->GetBindingInfo(); + BindGroup::BindGroup(Device* device, + const BindGroupDescriptor* descriptor, + uint32_t viewSizeIncrement, + const CPUDescriptorHeapAllocation& viewAllocation) + : BindGroupBase(this, device, descriptor) { + BindGroupLayout* bgl = ToBackend(GetLayout()); - // Save the offset to the start of the descriptor table in the heap - mCbvUavSrvHeapOffset = *cbvUavSrvHeapOffset; - mSamplerHeapOffset = *samplerHeapOffset; + mCPUViewAllocation = viewAllocation; const auto& bindingOffsets = bgl->GetBindingOffsets(); - auto d3d12Device = ToBackend(GetDevice())->GetD3D12Device(); - for (uint32_t bindingIndex : IterateBitSet(layout.mask)) { - switch (layout.types[bindingIndex]) { - case dawn::BindingType::UniformBuffer: { + ID3D12Device* d3d12Device = device->GetD3D12Device(); + + // It's not necessary to create descriptors in the descriptor heap for dynamic resources. + // This is because they are created as root descriptors which are never heap allocated. + // Since dynamic buffers are packed in the front, we can skip over these bindings by + // starting from the dynamic buffer count. + for (BindingIndex bindingIndex = bgl->GetDynamicBufferCount(); + bindingIndex < bgl->GetBindingCount(); ++bindingIndex) { + const BindingInfo& bindingInfo = bgl->GetBindingInfo(bindingIndex); + + // Increment size does not need to be stored and is only used to get a handle + // local to the allocation with OffsetFrom(). + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: { BufferBinding binding = GetBindingAsBufferBinding(bindingIndex); D3D12_CONSTANT_BUFFER_VIEW_DESC desc; - // TODO(enga@google.com): investigate if this needs to be a constraint at the - // API level + // TODO(enga@google.com): investigate if this needs to be a constraint at + // the API level desc.SizeInBytes = Align(binding.size, 256); desc.BufferLocation = ToBackend(binding.buffer)->GetVA() + binding.offset; d3d12Device->CreateConstantBufferView( - &desc, cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset + - bindingOffsets[bindingIndex])); - } break; - case dawn::BindingType::StorageBuffer: { + &desc, + viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex])); + break; + } + case wgpu::BindingType::StorageBuffer: { BufferBinding binding = GetBindingAsBufferBinding(bindingIndex); + // Since SPIRV-Cross outputs HLSL shaders with RWByteAddressBuffer, + // we must use D3D12_BUFFER_UAV_FLAG_RAW when making the + // UNORDERED_ACCESS_VIEW_DESC. Using D3D12_BUFFER_UAV_FLAG_RAW requires + // that we use DXGI_FORMAT_R32_TYPELESS as the format of the view. + // DXGI_FORMAT_R32_TYPELESS requires that the element size be 4 + // byte aligned. Since binding.size and binding.offset are in bytes, + // we need to divide by 4 to obtain the element size. D3D12_UNORDERED_ACCESS_VIEW_DESC desc; - // TODO(enga@google.com): investigate if this needs to be a constraint at the - // API level - desc.Buffer.NumElements = Align(binding.size, 256); - desc.Format = DXGI_FORMAT_UNKNOWN; + desc.Buffer.NumElements = binding.size / 4; + desc.Format = DXGI_FORMAT_R32_TYPELESS; desc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; - desc.Buffer.FirstElement = binding.offset; - desc.Buffer.StructureByteStride = 1; + desc.Buffer.FirstElement = binding.offset / 4; + desc.Buffer.StructureByteStride = 0; desc.Buffer.CounterOffsetInBytes = 0; - desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE; + desc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_RAW; d3d12Device->CreateUnorderedAccessView( - ToBackend(binding.buffer)->GetD3D12Resource().Get(), nullptr, &desc, - cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset + - bindingOffsets[bindingIndex])); - } break; - case dawn::BindingType::SampledTexture: { + ToBackend(binding.buffer)->GetD3D12Resource(), nullptr, &desc, + viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex])); + break; + } + case wgpu::BindingType::ReadonlyStorageBuffer: { + BufferBinding binding = GetBindingAsBufferBinding(bindingIndex); + + // Like StorageBuffer, SPIRV-Cross outputs HLSL shaders for readonly storage + // buffer with ByteAddressBuffer. So we must use D3D12_BUFFER_SRV_FLAG_RAW + // when making the SRV descriptor. And it has similar requirement for + // format, element size, etc. + D3D12_SHADER_RESOURCE_VIEW_DESC desc; + desc.Format = DXGI_FORMAT_R32_TYPELESS; + desc.ViewDimension = D3D12_SRV_DIMENSION_BUFFER; + desc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + desc.Buffer.FirstElement = binding.offset / 4; + desc.Buffer.NumElements = binding.size / 4; + desc.Buffer.StructureByteStride = 0; + desc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_RAW; + d3d12Device->CreateShaderResourceView( + ToBackend(binding.buffer)->GetD3D12Resource(), &desc, + viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex])); + break; + } + + // Readonly storage is implemented as SRV so it can be used at the same time as a + // sampled texture. + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::ReadonlyStorageTexture: { auto* view = ToBackend(GetBindingAsTextureView(bindingIndex)); auto& srv = view->GetSRVDescriptor(); d3d12Device->CreateShaderResourceView( ToBackend(view->GetTexture())->GetD3D12Resource(), &srv, - cbvUavSrvHeapStart.GetCPUHandle(*cbvUavSrvHeapOffset + - bindingOffsets[bindingIndex])); - } break; - case dawn::BindingType::Sampler: { - auto* sampler = ToBackend(GetBindingAsSampler(bindingIndex)); - auto& samplerDesc = sampler->GetSamplerDescriptor(); - d3d12Device->CreateSampler( - &samplerDesc, samplerHeapStart.GetCPUHandle(*samplerHeapOffset + - bindingOffsets[bindingIndex])); - } break; - - // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: + viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex])); + break; + } + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: { + // No-op as samplers will be later initialized by CreateSamplers(). + break; + } + + case wgpu::BindingType::WriteonlyStorageTexture: { + TextureView* view = ToBackend(GetBindingAsTextureView(bindingIndex)); + D3D12_UNORDERED_ACCESS_VIEW_DESC uav = view->GetUAVDescriptor(); + d3d12Device->CreateUnorderedAccessView( + ToBackend(view->GetTexture())->GetD3D12Resource(), nullptr, &uav, + viewAllocation.OffsetFrom(viewSizeIncrement, bindingOffsets[bindingIndex])); + break; + } + + case wgpu::BindingType::StorageTexture: UNREACHABLE(); break; + + // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. } } + } + + BindGroup::~BindGroup() { + ToBackend(GetLayout())->DeallocateBindGroup(this, &mCPUViewAllocation); + ASSERT(!mCPUViewAllocation.IsValid()); + } + + bool BindGroup::PopulateViews(ShaderVisibleDescriptorAllocator* viewAllocator) { + const BindGroupLayout* bgl = ToBackend(GetLayout()); + + const uint32_t descriptorCount = bgl->GetCbvUavSrvDescriptorCount(); + if (descriptorCount == 0 || viewAllocator->IsAllocationStillValid(mGPUViewAllocation)) { + return true; + } + + // Attempt to allocate descriptors for the currently bound shader-visible heaps. + // If either failed, return early to re-allocate and switch the heaps. + Device* device = ToBackend(GetDevice()); + + D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor; + if (!viewAllocator->AllocateGPUDescriptors(descriptorCount, + device->GetPendingCommandSerial(), + &baseCPUDescriptor, &mGPUViewAllocation)) { + return false; + } + + // CPU bindgroups are sparsely allocated across CPU heaps. Instead of doing + // simple copies per bindgroup, a single non-simple copy could be issued. + // TODO(dawn:155): Consider doing this optimization. + device->GetD3D12Device()->CopyDescriptorsSimple(descriptorCount, baseCPUDescriptor, + mCPUViewAllocation.GetBaseDescriptor(), + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - // Offset by the number of descriptors created - *cbvUavSrvHeapOffset += bgl->GetCbvUavSrvDescriptorCount(); - *samplerHeapOffset += bgl->GetSamplerDescriptorCount(); + return true; } - uint32_t BindGroup::GetCbvUavSrvHeapOffset() const { - return mCbvUavSrvHeapOffset; + D3D12_GPU_DESCRIPTOR_HANDLE BindGroup::GetBaseViewDescriptor() const { + return mGPUViewAllocation.GetBaseDescriptor(); } - uint32_t BindGroup::GetSamplerHeapOffset() const { - return mSamplerHeapOffset; + D3D12_GPU_DESCRIPTOR_HANDLE BindGroup::GetBaseSamplerDescriptor() const { + ASSERT(mSamplerAllocationEntry.Get() != nullptr); + return mSamplerAllocationEntry->GetBaseDescriptor(); } - bool BindGroup::TestAndSetCounted(uint64_t heapSerial, uint32_t indexInSubmit) { - bool isCounted = (mHeapSerial == heapSerial && mIndexInSubmit == indexInSubmit); - mHeapSerial = heapSerial; - mIndexInSubmit = indexInSubmit; - return isCounted; + bool BindGroup::PopulateSamplers(Device* device, + ShaderVisibleDescriptorAllocator* samplerAllocator) { + if (mSamplerAllocationEntry.Get() == nullptr) { + return true; + } + return mSamplerAllocationEntry->Populate(device, samplerAllocator); } + void BindGroup::SetSamplerAllocationEntry(Ref entry) { + mSamplerAllocationEntry = std::move(entry); + } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/BindGroupD3D12.h b/third_party/dawn/src/dawn_native/d3d12/BindGroupD3D12.h index 458e9926f7d..54acb3de38e 100644 --- a/third_party/dawn/src/dawn_native/d3d12/BindGroupD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/BindGroupD3D12.h @@ -15,37 +15,46 @@ #ifndef DAWNNATIVE_D3D12_BINDGROUPD3D12_H_ #define DAWNNATIVE_D3D12_BINDGROUPD3D12_H_ +#include "common/PlacementAllocated.h" +#include "common/Serial.h" #include "dawn_native/BindGroup.h" - -#include "dawn_native/d3d12/d3d12_platform.h" - -#include "dawn_native/d3d12/DescriptorHeapAllocator.h" +#include "dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.h" +#include "dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h" namespace dawn_native { namespace d3d12 { class Device; + class SamplerHeapCacheEntry; + class ShaderVisibleDescriptorAllocator; + class StagingDescriptorAllocator; - class BindGroup : public BindGroupBase { + class BindGroup final : public BindGroupBase, public PlacementAllocated { public: - BindGroup(Device* device, const BindGroupDescriptor* descriptor); + static ResultOrError Create(Device* device, + const BindGroupDescriptor* descriptor); + + BindGroup(Device* device, + const BindGroupDescriptor* descriptor, + uint32_t viewSizeIncrement, + const CPUDescriptorHeapAllocation& viewAllocation); - void AllocateDescriptors(const DescriptorHeapHandle& cbvSrvUavHeapStart, - uint32_t* cbvUavSrvHeapOffset, - const DescriptorHeapHandle& samplerHeapStart, - uint32_t* samplerHeapOffset); - uint32_t GetCbvUavSrvHeapOffset() const; - uint32_t GetSamplerHeapOffset() const; + // Returns true if the BindGroup was successfully populated. + bool PopulateViews(ShaderVisibleDescriptorAllocator* viewAllocator); + bool PopulateSamplers(Device* device, ShaderVisibleDescriptorAllocator* samplerAllocator); - bool TestAndSetCounted(uint64_t heapSerial, uint32_t indexInSubmit); + D3D12_GPU_DESCRIPTOR_HANDLE GetBaseViewDescriptor() const; + D3D12_GPU_DESCRIPTOR_HANDLE GetBaseSamplerDescriptor() const; + + void SetSamplerAllocationEntry(Ref entry); private: - uint32_t mCbvUavSrvHeapOffset; - uint32_t mSamplerHeapOffset; + ~BindGroup() override; - uint64_t mHeapSerial = 0; - uint32_t mIndexInSubmit = 0; - }; + Ref mSamplerAllocationEntry; + GPUDescriptorHeapAllocation mGPUViewAllocation; + CPUDescriptorHeapAllocation mCPUViewAllocation; + }; }} // namespace dawn_native::d3d12 #endif // DAWNNATIVE_D3D12_BINDGROUPD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp index f97eebb3e78..62722f6bd6d 100644 --- a/third_party/dawn/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/BindGroupLayoutD3D12.cpp @@ -15,35 +15,53 @@ #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" #include "common/BitSetIterator.h" +#include "dawn_native/d3d12/BindGroupD3D12.h" #include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/SamplerHeapCacheD3D12.h" +#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" namespace dawn_native { namespace d3d12 { - - BindGroupLayout::BindGroupLayout(Device* device, const BindGroupLayoutDescriptor* descriptor) - : BindGroupLayoutBase(device, descriptor), mDescriptorCounts{} { - const auto& groupInfo = GetBindingInfo(); - - for (uint32_t binding : IterateBitSet(groupInfo.mask)) { - switch (groupInfo.types[binding]) { - case dawn::BindingType::UniformBuffer: - mBindingOffsets[binding] = mDescriptorCounts[CBV]++; - break; - case dawn::BindingType::StorageBuffer: - mBindingOffsets[binding] = mDescriptorCounts[UAV]++; - break; - case dawn::BindingType::SampledTexture: - mBindingOffsets[binding] = mDescriptorCounts[SRV]++; - break; - case dawn::BindingType::Sampler: - mBindingOffsets[binding] = mDescriptorCounts[Sampler]++; - break; - // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: + namespace { + BindGroupLayout::DescriptorType WGPUBindingTypeToDescriptorType( + wgpu::BindingType bindingType) { + switch (bindingType) { + case wgpu::BindingType::UniformBuffer: + return BindGroupLayout::DescriptorType::CBV; + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::WriteonlyStorageTexture: + return BindGroupLayout::DescriptorType::UAV; + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::ReadonlyStorageTexture: + return BindGroupLayout::DescriptorType::SRV; + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + return BindGroupLayout::DescriptorType::Sampler; + case wgpu::BindingType::StorageTexture: UNREACHABLE(); - break; + return BindGroupLayout::DescriptorType::UAV; } } + } // anonymous namespace + + BindGroupLayout::BindGroupLayout(Device* device, const BindGroupLayoutDescriptor* descriptor) + : BindGroupLayoutBase(device, descriptor), + mBindingOffsets(GetBindingCount()), + mDescriptorCounts{}, + mBindGroupAllocator(MakeFrontendBindGroupAllocator(4096)) { + for (BindingIndex bindingIndex = GetDynamicBufferCount(); bindingIndex < GetBindingCount(); + ++bindingIndex) { + const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex); + + // For dynamic resources, Dawn uses root descriptor in D3D12 backend. + // So there is no need to allocate the descriptor from descriptor heap. + // This loop starts after the dynamic buffer indices to skip counting + // dynamic resources in calculating the size of the descriptor heap. + ASSERT(!bindingInfo.hasDynamicOffset); + + DescriptorType descriptorType = WGPUBindingTypeToDescriptorType(bindingInfo.type); + mBindingOffsets[bindingIndex] = mDescriptorCounts[descriptorType]++; + } auto SetDescriptorRange = [&](uint32_t index, uint32_t count, uint32_t* baseRegister, D3D12_DESCRIPTOR_RANGE_TYPE type) -> bool { @@ -86,31 +104,75 @@ namespace dawn_native { namespace d3d12 { D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER); descriptorOffsets[Sampler] = 0; - for (uint32_t binding : IterateBitSet(groupInfo.mask)) { - switch (groupInfo.types[binding]) { - case dawn::BindingType::UniformBuffer: - mBindingOffsets[binding] += descriptorOffsets[CBV]; - break; - case dawn::BindingType::StorageBuffer: - mBindingOffsets[binding] += descriptorOffsets[UAV]; - break; - case dawn::BindingType::SampledTexture: - mBindingOffsets[binding] += descriptorOffsets[SRV]; - break; - case dawn::BindingType::Sampler: - mBindingOffsets[binding] += descriptorOffsets[Sampler]; - break; - // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: - UNREACHABLE(); - break; + for (BindingIndex bindingIndex{0}; bindingIndex < GetBindingCount(); ++bindingIndex) { + const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex); + + if (bindingInfo.hasDynamicOffset) { + // Dawn is using values in mBindingOffsets to decide register number in HLSL. + // Root descriptor needs to set this value to set correct register number in + // generated HLSL shader. + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + mBindingOffsets[bindingIndex] = baseRegister++; + break; + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::StorageTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + UNREACHABLE(); + break; + } + continue; } + + // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. + DescriptorType descriptorType = WGPUBindingTypeToDescriptorType(bindingInfo.type); + mBindingOffsets[bindingIndex] += descriptorOffsets[descriptorType]; } + + mViewAllocator = device->GetViewStagingDescriptorAllocator(GetCbvUavSrvDescriptorCount()); + mSamplerAllocator = + device->GetSamplerStagingDescriptorAllocator(GetSamplerDescriptorCount()); + } + + ResultOrError BindGroupLayout::AllocateBindGroup( + Device* device, + const BindGroupDescriptor* descriptor) { + uint32_t viewSizeIncrement = 0; + CPUDescriptorHeapAllocation viewAllocation; + if (GetCbvUavSrvDescriptorCount() > 0) { + DAWN_TRY_ASSIGN(viewAllocation, mViewAllocator->AllocateCPUDescriptors()); + viewSizeIncrement = mViewAllocator->GetSizeIncrement(); + } + + Ref bindGroup = AcquireRef( + mBindGroupAllocator.Allocate(device, descriptor, viewSizeIncrement, viewAllocation)); + + if (GetSamplerDescriptorCount() > 0) { + Ref samplerHeapCacheEntry; + DAWN_TRY_ASSIGN(samplerHeapCacheEntry, device->GetSamplerHeapCache()->GetOrCreate( + bindGroup.Get(), mSamplerAllocator)); + bindGroup->SetSamplerAllocationEntry(std::move(samplerHeapCacheEntry)); + } + + return bindGroup.Detach(); + } + + void BindGroupLayout::DeallocateBindGroup(BindGroup* bindGroup, + CPUDescriptorHeapAllocation* viewAllocation) { + if (viewAllocation->IsValid()) { + mViewAllocator->Deallocate(viewAllocation); + } + + mBindGroupAllocator.Deallocate(bindGroup); } - const std::array& BindGroupLayout::GetBindingOffsets() const { - return mBindingOffsets; + ityp::span BindGroupLayout::GetBindingOffsets() const { + return {mBindingOffsets.data(), mBindingOffsets.size()}; } uint32_t BindGroupLayout::GetCbvUavSrvDescriptorTableSize() const { diff --git a/third_party/dawn/src/dawn_native/d3d12/BindGroupLayoutD3D12.h b/third_party/dawn/src/dawn_native/d3d12/BindGroupLayoutD3D12.h index e5766e635d2..c5f32f54f3b 100644 --- a/third_party/dawn/src/dawn_native/d3d12/BindGroupLayoutD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/BindGroupLayoutD3D12.h @@ -17,16 +17,26 @@ #include "dawn_native/BindGroupLayout.h" +#include "common/SlabAllocator.h" +#include "common/ityp_stack_vec.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { + class BindGroup; + class CPUDescriptorHeapAllocation; class Device; + class SamplerHeapCacheEntry; + class StagingDescriptorAllocator; - class BindGroupLayout : public BindGroupLayoutBase { + class BindGroupLayout final : public BindGroupLayoutBase { public: BindGroupLayout(Device* device, const BindGroupLayoutDescriptor* descriptor); + ResultOrError AllocateBindGroup(Device* device, + const BindGroupDescriptor* descriptor); + void DeallocateBindGroup(BindGroup* bindGroup, CPUDescriptorHeapAllocation* viewAllocation); + enum DescriptorType { CBV, UAV, @@ -35,7 +45,7 @@ namespace dawn_native { namespace d3d12 { Count, }; - const std::array& GetBindingOffsets() const; + ityp::span GetBindingOffsets() const; uint32_t GetCbvUavSrvDescriptorTableSize() const; uint32_t GetSamplerDescriptorTableSize() const; uint32_t GetCbvUavSrvDescriptorCount() const; @@ -44,9 +54,15 @@ namespace dawn_native { namespace d3d12 { const D3D12_DESCRIPTOR_RANGE* GetSamplerDescriptorRanges() const; private: - std::array mBindingOffsets; + ~BindGroupLayout() override = default; + ityp::stack_vec mBindingOffsets; std::array mDescriptorCounts; D3D12_DESCRIPTOR_RANGE mRanges[DescriptorType::Count]; + + SlabAllocator mBindGroupAllocator; + + StagingDescriptorAllocator* mSamplerAllocator = nullptr; + StagingDescriptorAllocator* mViewAllocator = nullptr; }; }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/BufferD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/BufferD3D12.cpp index 6ee3636abca..877069b29f8 100644 --- a/third_party/dawn/src/dawn_native/d3d12/BufferD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/BufferD3D12.cpp @@ -17,51 +17,60 @@ #include "common/Assert.h" #include "common/Constants.h" #include "common/Math.h" +#include "dawn_native/CommandBuffer.h" +#include "dawn_native/DynamicUploader.h" +#include "dawn_native/d3d12/CommandRecordingContext.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" -#include "dawn_native/d3d12/ResourceAllocator.h" +#include "dawn_native/d3d12/HeapD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" namespace dawn_native { namespace d3d12 { namespace { - D3D12_RESOURCE_FLAGS D3D12ResourceFlags(dawn::BufferUsageBit usage) { + D3D12_RESOURCE_FLAGS D3D12ResourceFlags(wgpu::BufferUsage usage) { D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE; - if (usage & dawn::BufferUsageBit::Storage) { + if (usage & wgpu::BufferUsage::Storage) { flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } return flags; } - D3D12_RESOURCE_STATES D3D12BufferUsage(dawn::BufferUsageBit usage) { + D3D12_RESOURCE_STATES D3D12BufferUsage(wgpu::BufferUsage usage) { D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON; - if (usage & dawn::BufferUsageBit::TransferSrc) { + if (usage & wgpu::BufferUsage::CopySrc) { resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE; } - if (usage & dawn::BufferUsageBit::TransferDst) { + if (usage & wgpu::BufferUsage::CopyDst) { resourceState |= D3D12_RESOURCE_STATE_COPY_DEST; } - if (usage & (dawn::BufferUsageBit::Vertex | dawn::BufferUsageBit::Uniform)) { + if (usage & (wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Uniform)) { resourceState |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER; } - if (usage & dawn::BufferUsageBit::Index) { + if (usage & wgpu::BufferUsage::Index) { resourceState |= D3D12_RESOURCE_STATE_INDEX_BUFFER; } - if (usage & dawn::BufferUsageBit::Storage) { + if (usage & wgpu::BufferUsage::Storage) { resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS; } - if (usage & dawn::BufferUsageBit::Indirect) { + if (usage & kReadOnlyStorageBuffer) { + resourceState |= (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); + } + if (usage & wgpu::BufferUsage::Indirect) { resourceState |= D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT; } return resourceState; } - D3D12_HEAP_TYPE D3D12HeapType(dawn::BufferUsageBit allowedUsage) { - if (allowedUsage & dawn::BufferUsageBit::MapRead) { + D3D12_HEAP_TYPE D3D12HeapType(wgpu::BufferUsage allowedUsage) { + if (allowedUsage & wgpu::BufferUsage::MapRead) { return D3D12_HEAP_TYPE_READBACK; - } else if (allowedUsage & dawn::BufferUsageBit::MapWrite) { + } else if (allowedUsage & wgpu::BufferUsage::MapWrite) { return D3D12_HEAP_TYPE_UPLOAD; } else { return D3D12_HEAP_TYPE_DEFAULT; @@ -71,10 +80,15 @@ namespace dawn_native { namespace d3d12 { Buffer::Buffer(Device* device, const BufferDescriptor* descriptor) : BufferBase(device, descriptor) { + } + + MaybeError Buffer::Initialize() { D3D12_RESOURCE_DESC resourceDescriptor; resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; resourceDescriptor.Alignment = 0; - resourceDescriptor.Width = GetD3D12Size(); + // TODO(cwallez@chromium.org): Have a global "zero" buffer that can do everything instead + // of creating a new 4-byte buffer? + resourceDescriptor.Width = std::max(GetSize(), uint64_t(4u)); resourceDescriptor.Height = 1; resourceDescriptor.DepthOrArraySize = 1; resourceDescriptor.MipLevels = 1; @@ -82,10 +96,9 @@ namespace dawn_native { namespace d3d12 { resourceDescriptor.SampleDesc.Count = 1; resourceDescriptor.SampleDesc.Quality = 0; resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; - // Add TransferDst for non-mappable buffer initialization in CreateBufferMapped + // Add CopyDst for non-mappable buffer initialization in CreateBufferMapped // and robust resource initialization. - resourceDescriptor.Flags = - D3D12ResourceFlags(GetUsage() | dawn::BufferUsageBit::TransferDst); + resourceDescriptor.Flags = D3D12ResourceFlags(GetUsage() | wgpu::BufferUsage::CopyDst); auto heapType = D3D12HeapType(GetUsage()); auto bufferUsage = D3D12_RESOURCE_STATE_COMMON; @@ -95,7 +108,7 @@ namespace dawn_native { namespace d3d12 { if (heapType == D3D12_HEAP_TYPE_READBACK) { bufferUsage |= D3D12_RESOURCE_STATE_COPY_DEST; mFixedResourceState = true; - mLastUsage = dawn::BufferUsageBit::TransferDst; + mLastUsage = wgpu::BufferUsage::CopyDst; } // D3D12 requires buffers on the UPLOAD heap to have the D3D12_RESOURCE_STATE_GENERIC_READ @@ -103,36 +116,123 @@ namespace dawn_native { namespace d3d12 { if (heapType == D3D12_HEAP_TYPE_UPLOAD) { bufferUsage |= D3D12_RESOURCE_STATE_GENERIC_READ; mFixedResourceState = true; - mLastUsage = dawn::BufferUsageBit::TransferSrc; + mLastUsage = wgpu::BufferUsage::CopySrc; } - mResource = - device->GetResourceAllocator()->Allocate(heapType, resourceDescriptor, bufferUsage); + DAWN_TRY_ASSIGN( + mResourceAllocation, + ToBackend(GetDevice())->AllocateMemory(heapType, resourceDescriptor, bufferUsage)); + + if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { + CommandRecordingContext* commandRecordingContext; + DAWN_TRY_ASSIGN(commandRecordingContext, + ToBackend(GetDevice())->GetPendingCommandContext()); + + DAWN_TRY(ClearBuffer(commandRecordingContext, uint8_t(1u))); + } + + return {}; } Buffer::~Buffer() { DestroyInternal(); } - bool Buffer::CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier, - dawn::BufferUsageBit newUsage) const { + ID3D12Resource* Buffer::GetD3D12Resource() const { + return mResourceAllocation.GetD3D12Resource(); + } + + // When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a + // ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can + // cause subsequent errors. + bool Buffer::TrackUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, + D3D12_RESOURCE_BARRIER* barrier, + wgpu::BufferUsage newUsage) { + // Track the underlying heap to ensure residency. + Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); + commandContext->TrackHeapUsage(heap, GetDevice()->GetPendingCommandSerial()); + + // Return the resource barrier. + return TransitionUsageAndGetResourceBarrier(commandContext, barrier, newUsage); + } + + void Buffer::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, + wgpu::BufferUsage newUsage) { + D3D12_RESOURCE_BARRIER barrier; + + if (TrackUsageAndGetResourceBarrier(commandContext, &barrier, newUsage)) { + commandContext->GetCommandList()->ResourceBarrier(1, &barrier); + } + } + + // When true is returned, a D3D12_RESOURCE_BARRIER has been created and must be used in a + // ResourceBarrier call. Failing to do so will cause the tracked state to become invalid and can + // cause subsequent errors. + bool Buffer::TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, + D3D12_RESOURCE_BARRIER* barrier, + wgpu::BufferUsage newUsage) { // Resources in upload and readback heaps must be kept in the COPY_SOURCE/DEST state if (mFixedResourceState) { ASSERT(mLastUsage == newUsage); return false; } + D3D12_RESOURCE_STATES lastState = D3D12BufferUsage(mLastUsage); + D3D12_RESOURCE_STATES newState = D3D12BufferUsage(newUsage); + + // If the transition is from-UAV-to-UAV, then a UAV barrier is needed. + // If one of the usages isn't UAV, then other barriers are used. + bool needsUAVBarrier = lastState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS && + newState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + + if (needsUAVBarrier) { + barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier->UAV.pResource = GetD3D12Resource(); + + mLastUsage = newUsage; + return true; + } + // We can skip transitions to already current usages. - // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point. if ((mLastUsage & newUsage) == newUsage) { return false; } - D3D12_RESOURCE_STATES lastState = D3D12BufferUsage(mLastUsage); - D3D12_RESOURCE_STATES newState = D3D12BufferUsage(newUsage); + mLastUsage = newUsage; + + // The COMMON state represents a state where no write operations can be pending, which makes + // it possible to transition to and from some states without synchronizaton (i.e. without an + // explicit ResourceBarrier call). A buffer can be implicitly promoted to 1) a single write + // state, or 2) multiple read states. A buffer that is accessed within a command list will + // always implicitly decay to the COMMON state after the call to ExecuteCommandLists + // completes - this is because all buffer writes are guaranteed to be completed before the + // next ExecuteCommandLists call executes. + // https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions + + // To track implicit decays, we must record the pending serial on which a transition will + // occur. When that buffer is used again, the previously recorded serial must be compared to + // the last completed serial to determine if the buffer has implicity decayed to the common + // state. + const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial(); + if (pendingCommandSerial > mLastUsedSerial) { + lastState = D3D12_RESOURCE_STATE_COMMON; + mLastUsedSerial = pendingCommandSerial; + } + + // All possible buffer states used by Dawn are eligible for implicit promotion from COMMON. + // These are: COPY_SOURCE, VERTEX_AND_COPY_BUFFER, INDEX_BUFFER, COPY_DEST, + // UNORDERED_ACCESS, and INDIRECT_ARGUMENT. Note that for implicit promotion, the + // destination state cannot be 1) more than one write state, or 2) both a read and write + // state. This goes unchecked here because it should not be allowed through render/compute + // pass validation. + if (lastState == D3D12_RESOURCE_STATE_COMMON) { + return false; + } + barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier->Transition.pResource = mResource.Get(); + barrier->Transition.pResource = GetD3D12Resource(); barrier->Transition.StateBefore = lastState; barrier->Transition.StateAfter = newState; barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; @@ -140,111 +240,192 @@ namespace dawn_native { namespace d3d12 { return true; } - uint32_t Buffer::GetD3D12Size() const { - // TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level - return Align(GetSize(), 256); - } - - ComPtr Buffer::GetD3D12Resource() { - return mResource; + D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const { + return mResourceAllocation.GetGPUPointer(); } - void Buffer::SetUsage(dawn::BufferUsageBit newUsage) { - mLastUsage = newUsage; + bool Buffer::IsMappableAtCreation() const { + // TODO(enga): Handle CPU-visible memory on UMA + return (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0; } - void Buffer::TransitionUsageNow(ComPtr commandList, - dawn::BufferUsageBit usage) { - D3D12_RESOURCE_BARRIER barrier; + MaybeError Buffer::MapInternal(bool isWrite, + size_t offset, + size_t size, + const char* contextInfo) { + // The mapped buffer can be accessed at any time, so it must be locked to ensure it is never + // evicted. This buffer should already have been made resident when it was created. + Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); + DAWN_TRY(ToBackend(GetDevice())->GetResidencyManager()->LockAllocation(heap)); + + D3D12_RANGE range = {offset, offset + size}; + // mMappedData is the pointer to the start of the resource, irrespective of offset. + // MSDN says (note the weird use of "never"): + // + // When ppData is not NULL, the pointer returned is never offset by any values in + // pReadRange. + // + // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12resource-map + DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &range, &mMappedData), contextInfo)); - if (CreateD3D12ResourceBarrierIfNeeded(&barrier, usage)) { - commandList->ResourceBarrier(1, &barrier); + if (isWrite) { + mWrittenMappedRange = range; } - mLastUsage = usage; + return {}; } - D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const { - return mResource->GetGPUVirtualAddress(); - } + MaybeError Buffer::MapAtCreationImpl() { + CommandRecordingContext* commandContext; + DAWN_TRY_ASSIGN(commandContext, ToBackend(GetDevice())->GetPendingCommandContext()); + DAWN_TRY(EnsureDataInitialized(commandContext)); - void Buffer::OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite) { - if (isWrite) { - CallMapWriteCallback(mapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data, GetSize()); - } else { - CallMapReadCallback(mapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data, GetSize()); - } + // Setting isMapWrite to false on MapRead buffers to silence D3D12 debug layer warning. + bool isMapWrite = (GetUsage() & wgpu::BufferUsage::MapWrite) != 0; + DAWN_TRY(MapInternal(isMapWrite, 0, size_t(GetSize()), "D3D12 map at creation")); + return {}; } - bool Buffer::IsMapWritable() const { - // TODO(enga): Handle CPU-visible memory on UMA - return (GetUsage() & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) != 0; + MaybeError Buffer::MapReadAsyncImpl() { + return MapInternal(false, 0, size_t(GetSize()), "D3D12 map read async"); } - MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { - mWrittenMappedRange = {0, GetSize()}; - ASSERT_SUCCESS( - mResource->Map(0, &mWrittenMappedRange, reinterpret_cast(mappedPointer))); - return {}; + MaybeError Buffer::MapWriteAsyncImpl() { + return MapInternal(true, 0, size_t(GetSize()), "D3D12 map write async"); } - void Buffer::MapReadAsyncImpl(uint32_t serial) { - mWrittenMappedRange = {}; - D3D12_RANGE readRange = {0, GetSize()}; - char* data = nullptr; - ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast(&data))); + MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) { + CommandRecordingContext* commandContext; + DAWN_TRY_ASSIGN(commandContext, ToBackend(GetDevice())->GetPendingCommandContext()); + DAWN_TRY(EnsureDataInitialized(commandContext)); - // There is no need to transition the resource to a new state: D3D12 seems to make the GPU - // writes available when the fence is passed. - MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker(); - tracker->Track(this, serial, data, false); + return MapInternal(mode & wgpu::MapMode::Write, offset, size, "D3D12 map async"); } - void Buffer::MapWriteAsyncImpl(uint32_t serial) { - mWrittenMappedRange = {0, GetSize()}; - char* data = nullptr; - ASSERT_SUCCESS(mResource->Map(0, &mWrittenMappedRange, reinterpret_cast(&data))); - - // There is no need to transition the resource to a new state: D3D12 seems to make the CPU - // writes available on queue submission. - MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker(); - tracker->Track(this, serial, data, true); + void Buffer::UnmapImpl() { + GetD3D12Resource()->Unmap(0, &mWrittenMappedRange); + mMappedData = nullptr; + mWrittenMappedRange = {0, 0}; + + // When buffers are mapped, they are locked to keep them in resident memory. We must unlock + // them when they are unmapped. + Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); + ToBackend(GetDevice())->GetResidencyManager()->UnlockAllocation(heap); } - void Buffer::UnmapImpl() { - mResource->Unmap(0, &mWrittenMappedRange); - ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource); - mWrittenMappedRange = {}; + void* Buffer::GetMappedPointerImpl() { + // The frontend asks that the pointer returned is from the start of the resource + // irrespective of the offset passed in MapAsyncImpl, which is what mMappedData is. + return mMappedData; } void Buffer::DestroyImpl() { - ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource); - mResource = nullptr; + if (mMappedData != nullptr) { + // If the buffer is currently mapped, unmap without flushing the writes to the GPU + // since the buffer cannot be used anymore. UnmapImpl checks mWrittenRange to know + // which parts to flush, so we set it to an empty range to prevent flushes. + mWrittenMappedRange = {0, 0}; + UnmapImpl(); + } + + ToBackend(GetDevice())->DeallocateMemory(mResourceAllocation); } - MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) { + bool Buffer::CheckIsResidentForTesting() const { + Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); + return heap->IsInList() || heap->IsResidencyLocked(); } - MapRequestTracker::~MapRequestTracker() { - ASSERT(mInflightRequests.Empty()); + bool Buffer::CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const { + return mResourceAllocation.GetInfo().mMethod == allocationMethod; } - void MapRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite) { - Request request; - request.buffer = buffer; - request.mapSerial = mapSerial; - request.data = data; - request.isWrite = isWrite; + MaybeError Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return {}; + } + + DAWN_TRY(InitializeToZero(commandContext)); - mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial()); + return {}; } - void MapRequestTracker::Tick(Serial finishedSerial) { - for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) { - request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.data, - request.isWrite); + MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext, + uint64_t offset, + uint64_t size) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return {}; + } + + if (IsFullBufferRange(offset, size)) { + SetIsDataInitialized(); + } else { + DAWN_TRY(InitializeToZero(commandContext)); } - mInflightRequests.ClearUpTo(finishedSerial); + + return {}; } + MaybeError Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext, + const CopyTextureToBufferCmd* copy) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return {}; + } + + if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) { + SetIsDataInitialized(); + } else { + DAWN_TRY(InitializeToZero(commandContext)); + } + + return {}; + } + + MaybeError Buffer::InitializeToZero(CommandRecordingContext* commandContext) { + ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)); + ASSERT(!IsDataInitialized()); + + // TODO(jiawei.shao@intel.com): skip initializing the buffer when it is created on a heap + // that has already been zero initialized. + DAWN_TRY(ClearBuffer(commandContext, uint8_t(0u))); + SetIsDataInitialized(); + GetDevice()->IncrementLazyClearCountForTesting(); + + return {}; + } + + MaybeError Buffer::ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue) { + Device* device = ToBackend(GetDevice()); + + // The state of the buffers on UPLOAD heap must always be GENERIC_READ and cannot be + // changed away, so we can only clear such buffer with buffer mapping. + if (D3D12HeapType(GetUsage()) == D3D12_HEAP_TYPE_UPLOAD) { + DAWN_TRY(MapInternal(true, 0, size_t(GetSize()), "D3D12 map at clear buffer")); + memset(mMappedData, clearValue, GetSize()); + UnmapImpl(); + } else { + // TODO(jiawei.shao@intel.com): use ClearUnorderedAccessView*() when the buffer usage + // includes STORAGE. + DynamicUploader* uploader = device->GetDynamicUploader(); + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, + uploader->Allocate(GetSize(), device->GetPendingCommandSerial())); + + memset(uploadHandle.mappedBuffer, clearValue, GetSize()); + + device->CopyFromStagingToBufferImpl(commandContext, uploadHandle.stagingBuffer, + uploadHandle.startOffset, this, 0, GetSize()); + } + + return {}; + } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/BufferD3D12.h b/third_party/dawn/src/dawn_native/d3d12/BufferD3D12.h index 86954a748a0..a2af099a79e 100644 --- a/third_party/dawn/src/dawn_native/d3d12/BufferD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/BufferD3D12.h @@ -18,61 +18,67 @@ #include "common/SerialQueue.h" #include "dawn_native/Buffer.h" +#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { + class CommandRecordingContext; class Device; - class Buffer : public BufferBase { + class Buffer final : public BufferBase { public: Buffer(Device* device, const BufferDescriptor* descriptor); - ~Buffer(); - bool CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier, - dawn::BufferUsageBit newUsage) const; - uint32_t GetD3D12Size() const; - ComPtr GetD3D12Resource(); + MaybeError Initialize(); + + ID3D12Resource* GetD3D12Resource() const; D3D12_GPU_VIRTUAL_ADDRESS GetVA() const; - void OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite); - void SetUsage(dawn::BufferUsageBit newUsage); - void TransitionUsageNow(ComPtr commandList, - dawn::BufferUsageBit usage); + + bool TrackUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, + D3D12_RESOURCE_BARRIER* barrier, + wgpu::BufferUsage newUsage); + void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, + wgpu::BufferUsage newUsage); + + bool CheckAllocationMethodForTesting(AllocationMethod allocationMethod) const; + bool CheckIsResidentForTesting() const; + + MaybeError EnsureDataInitialized(CommandRecordingContext* commandContext); + MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext, + uint64_t offset, + uint64_t size); + MaybeError EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext, + const CopyTextureToBufferCmd* copy); private: + ~Buffer() override; // Dawn API - void MapReadAsyncImpl(uint32_t serial) override; - void MapWriteAsyncImpl(uint32_t serial) override; + MaybeError MapReadAsyncImpl() override; + MaybeError MapWriteAsyncImpl() override; + MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override; void UnmapImpl() override; void DestroyImpl() override; - bool IsMapWritable() const override; - virtual MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; + bool IsMappableAtCreation() const override; + virtual MaybeError MapAtCreationImpl() override; + void* GetMappedPointerImpl() override; + MaybeError MapInternal(bool isWrite, size_t start, size_t end, const char* contextInfo); - ComPtr mResource; - bool mFixedResourceState = false; - dawn::BufferUsageBit mLastUsage = dawn::BufferUsageBit::None; - D3D12_RANGE mWrittenMappedRange; - }; + bool TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, + D3D12_RESOURCE_BARRIER* barrier, + wgpu::BufferUsage newUsage); - class MapRequestTracker { - public: - MapRequestTracker(Device* device); - ~MapRequestTracker(); + MaybeError InitializeToZero(CommandRecordingContext* commandContext); + MaybeError ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue); - void Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite); - void Tick(Serial finishedSerial); + ResourceHeapAllocation mResourceAllocation; + bool mFixedResourceState = false; + wgpu::BufferUsage mLastUsage = wgpu::BufferUsage::None; + Serial mLastUsedSerial = UINT64_MAX; - private: - Device* mDevice; - - struct Request { - Ref buffer; - uint32_t mapSerial; - void* data; - bool isWrite; - }; - SerialQueue mInflightRequests; + D3D12_RANGE mWrittenMappedRange = {0, 0}; + void* mMappedData = nullptr; }; }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.cpp new file mode 100644 index 00000000000..d92398584d9 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.cpp @@ -0,0 +1,53 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.h" +#include "dawn_native/Error.h" + +namespace dawn_native { namespace d3d12 { + + CPUDescriptorHeapAllocation::CPUDescriptorHeapAllocation( + D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor, + uint32_t heapIndex) + : mBaseDescriptor(baseDescriptor), mHeapIndex(heapIndex) { + } + + D3D12_CPU_DESCRIPTOR_HANDLE CPUDescriptorHeapAllocation::GetBaseDescriptor() const { + ASSERT(IsValid()); + return mBaseDescriptor; + } + + D3D12_CPU_DESCRIPTOR_HANDLE CPUDescriptorHeapAllocation::OffsetFrom( + uint32_t sizeIncrementInBytes, + uint32_t offsetInDescriptorCount) const { + ASSERT(IsValid()); + D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = mBaseDescriptor; + cpuHandle.ptr += sizeIncrementInBytes * offsetInDescriptorCount; + return cpuHandle; + } + + uint32_t CPUDescriptorHeapAllocation::GetHeapIndex() const { + ASSERT(mHeapIndex >= 0); + return mHeapIndex; + } + + bool CPUDescriptorHeapAllocation::IsValid() const { + return mBaseDescriptor.ptr != 0; + } + + void CPUDescriptorHeapAllocation::Invalidate() { + mBaseDescriptor = {0}; + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.h b/third_party/dawn/src/dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.h new file mode 100644 index 00000000000..51ae2fdb61f --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.h @@ -0,0 +1,47 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_CPUDESCRIPTORHEAPALLOCATION_H_ +#define DAWNNATIVE_D3D12_CPUDESCRIPTORHEAPALLOCATION_H_ + +#include + +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + + // Wrapper for a handle into a CPU-only descriptor heap. + class CPUDescriptorHeapAllocation { + public: + CPUDescriptorHeapAllocation() = default; + CPUDescriptorHeapAllocation(D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor, uint32_t heapIndex); + + D3D12_CPU_DESCRIPTOR_HANDLE GetBaseDescriptor() const; + + D3D12_CPU_DESCRIPTOR_HANDLE OffsetFrom(uint32_t sizeIncrementInBytes, + uint32_t offsetInDescriptorCount) const; + uint32_t GetHeapIndex() const; + + bool IsValid() const; + + void Invalidate(); + + private: + D3D12_CPU_DESCRIPTOR_HANDLE mBaseDescriptor = {0}; + uint32_t mHeapIndex = -1; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_CPUDESCRIPTORHEAPALLOCATION_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/CommandAllocatorManager.cpp b/third_party/dawn/src/dawn_native/d3d12/CommandAllocatorManager.cpp index 90f7a5a8951..8c6029e5c31 100644 --- a/third_party/dawn/src/dawn_native/d3d12/CommandAllocatorManager.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/CommandAllocatorManager.cpp @@ -14,6 +14,7 @@ #include "dawn_native/d3d12/CommandAllocatorManager.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "common/Assert.h" @@ -26,12 +27,12 @@ namespace dawn_native { namespace d3d12 { mFreeAllocators.set(); } - ComPtr CommandAllocatorManager::ReserveCommandAllocator() { + ResultOrError CommandAllocatorManager::ReserveCommandAllocator() { // If there are no free allocators, get the oldest serial in flight and wait on it if (mFreeAllocators.none()) { const uint64_t firstSerial = mInFlightCommandAllocators.FirstSerial(); - device->WaitForSerial(firstSerial); - Tick(firstSerial); + DAWN_TRY(device->WaitForSerial(firstSerial)); + DAWN_TRY(Tick(firstSerial)); } ASSERT(mFreeAllocators.any()); @@ -42,8 +43,10 @@ namespace dawn_native { namespace d3d12 { if (firstFreeIndex >= mAllocatorCount) { ASSERT(firstFreeIndex == mAllocatorCount); mAllocatorCount++; - ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommandAllocator( - D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocators[firstFreeIndex]))); + DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateCommandAllocator( + D3D12_COMMAND_LIST_TYPE_DIRECT, + IID_PPV_ARGS(&mCommandAllocators[firstFreeIndex])), + "D3D12 create command allocator")); } // Mark the command allocator as used @@ -53,17 +56,17 @@ namespace dawn_native { namespace d3d12 { // ExecuteCommandLists mInFlightCommandAllocators.Enqueue({mCommandAllocators[firstFreeIndex], firstFreeIndex}, device->GetPendingCommandSerial()); - - return mCommandAllocators[firstFreeIndex]; + return mCommandAllocators[firstFreeIndex].Get(); } - void CommandAllocatorManager::Tick(uint64_t lastCompletedSerial) { + MaybeError CommandAllocatorManager::Tick(uint64_t lastCompletedSerial) { // Reset all command allocators that are no longer in flight for (auto it : mInFlightCommandAllocators.IterateUpTo(lastCompletedSerial)) { - ASSERT_SUCCESS(it.commandAllocator->Reset()); + DAWN_TRY(CheckHRESULT(it.commandAllocator->Reset(), "D3D12 reset command allocator")); mFreeAllocators.set(it.index); } mInFlightCommandAllocators.ClearUpTo(lastCompletedSerial); + return {}; } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/CommandAllocatorManager.h b/third_party/dawn/src/dawn_native/d3d12/CommandAllocatorManager.h index fd7c8ce27dd..654d3be467c 100644 --- a/third_party/dawn/src/dawn_native/d3d12/CommandAllocatorManager.h +++ b/third_party/dawn/src/dawn_native/d3d12/CommandAllocatorManager.h @@ -18,6 +18,7 @@ #include "dawn_native/d3d12/d3d12_platform.h" #include "common/SerialQueue.h" +#include "dawn_native/Error.h" #include @@ -31,8 +32,8 @@ namespace dawn_native { namespace d3d12 { // A CommandAllocator that is reserved must be used on the next ExecuteCommandLists // otherwise its commands may be reset before execution has completed on the GPU - ComPtr ReserveCommandAllocator(); - void Tick(uint64_t lastCompletedSerial); + ResultOrError ReserveCommandAllocator(); + MaybeError Tick(uint64_t lastCompletedSerial); private: Device* device; diff --git a/third_party/dawn/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/CommandBufferD3D12.cpp index 98afe265b19..8ee31ade37b 100644 --- a/third_party/dawn/src/dawn_native/d3d12/CommandBufferD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/CommandBufferD3D12.cpp @@ -15,377 +15,515 @@ #include "dawn_native/d3d12/CommandBufferD3D12.h" #include "common/Assert.h" +#include "dawn_native/BindGroupAndStorageBarrierTracker.h" #include "dawn_native/CommandEncoder.h" +#include "dawn_native/CommandValidation.h" #include "dawn_native/Commands.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/d3d12/BindGroupD3D12.h" #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" #include "dawn_native/d3d12/BufferD3D12.h" +#include "dawn_native/d3d12/CommandRecordingContext.h" #include "dawn_native/d3d12/ComputePipelineD3D12.h" -#include "dawn_native/d3d12/DescriptorHeapAllocator.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/PipelineLayoutD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" +#include "dawn_native/d3d12/RenderPassBuilderD3D12.h" #include "dawn_native/d3d12/RenderPipelineD3D12.h" -#include "dawn_native/d3d12/ResourceAllocator.h" #include "dawn_native/d3d12/SamplerD3D12.h" +#include "dawn_native/d3d12/SamplerHeapCacheD3D12.h" +#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" +#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" #include "dawn_native/d3d12/TextureCopySplitter.h" #include "dawn_native/d3d12/TextureD3D12.h" +#include "dawn_native/d3d12/UtilsD3D12.h" #include namespace dawn_native { namespace d3d12 { namespace { - DXGI_FORMAT DXGIIndexFormat(dawn::IndexFormat format) { + + DXGI_FORMAT DXGIIndexFormat(wgpu::IndexFormat format) { switch (format) { - case dawn::IndexFormat::Uint16: + case wgpu::IndexFormat::Uint16: return DXGI_FORMAT_R16_UINT; - case dawn::IndexFormat::Uint32: + case wgpu::IndexFormat::Uint32: return DXGI_FORMAT_R32_UINT; default: UNREACHABLE(); } } - D3D12_TEXTURE_COPY_LOCATION CreateTextureCopyLocationForTexture(const Texture& texture, - uint32_t level, - uint32_t slice) { - D3D12_TEXTURE_COPY_LOCATION copyLocation; - copyLocation.pResource = texture.GetD3D12Resource(); - copyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - copyLocation.SubresourceIndex = texture.GetSubresourceIndex(level, slice); - - return copyLocation; + bool CanUseCopyResource(const Texture* src, const Texture* dst, const Extent3D& copySize) { + // Checked by validation + ASSERT(src->GetSampleCount() == dst->GetSampleCount()); + ASSERT(src->GetFormat().format == dst->GetFormat().format); + + const Extent3D& srcSize = src->GetSize(); + const Extent3D& dstSize = dst->GetSize(); + + // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-copyresource + // In order to use D3D12's copy resource, the textures must be the same dimensions, and + // the copy must be of the entire resource. + // TODO(dawn:129): Support 1D textures. + return src->GetDimension() == dst->GetDimension() && // + dst->GetNumMipLevels() == 1 && // + src->GetNumMipLevels() == 1 && // A copy command is of a single mip, so if a + // resource has more than one, we definitely + // cannot use CopyResource. + copySize.width == dstSize.width && // + copySize.width == srcSize.width && // + copySize.height == dstSize.height && // + copySize.height == srcSize.height && // + copySize.depth == dstSize.depth && // + copySize.depth == srcSize.depth; } - bool CanUseCopyResource(const uint32_t sourceNumMipLevels, - const Extent3D& srcSize, - const Extent3D& dstSize, - const Extent3D& copySize) { - if (sourceNumMipLevels == 1 && srcSize.width == dstSize.width && - srcSize.height == dstSize.height && srcSize.depth == dstSize.depth && - srcSize.width == copySize.width && srcSize.height == copySize.height && - srcSize.depth == copySize.depth) { - return true; + void RecordCopyBufferToTextureFromTextureCopySplit(ID3D12GraphicsCommandList* commandList, + const Texture2DCopySplit& baseCopySplit, + Buffer* buffer, + uint64_t baseOffset, + uint64_t bufferBytesPerRow, + Texture* texture, + uint32_t textureMiplevel, + uint32_t textureSlice) { + const D3D12_TEXTURE_COPY_LOCATION textureLocation = + ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureSlice); + + const uint64_t offset = baseCopySplit.offset + baseOffset; + + for (uint32_t i = 0; i < baseCopySplit.count; ++i) { + const Texture2DCopySplit::CopyInfo& info = baseCopySplit.copies[i]; + + // TODO(jiawei.shao@intel.com): pre-compute bufferLocation and sourceRegion as + // members in Texture2DCopySplit::CopyInfo. + const D3D12_TEXTURE_COPY_LOCATION bufferLocation = + ComputeBufferLocationForCopyTextureRegion(texture, buffer->GetD3D12Resource(), + info.bufferSize, offset, + bufferBytesPerRow); + const D3D12_BOX sourceRegion = + ComputeD3D12BoxFromOffsetAndSize(info.bufferOffset, info.copySize); + + commandList->CopyTextureRegion(&textureLocation, info.textureOffset.x, + info.textureOffset.y, info.textureOffset.z, + &bufferLocation, &sourceRegion); } - - return false; } + void RecordCopyTextureToBufferFromTextureCopySplit(ID3D12GraphicsCommandList* commandList, + const Texture2DCopySplit& baseCopySplit, + Buffer* buffer, + uint64_t baseOffset, + uint64_t bufferBytesPerRow, + Texture* texture, + uint32_t textureMiplevel, + uint32_t textureSlice) { + const D3D12_TEXTURE_COPY_LOCATION textureLocation = + ComputeTextureCopyLocationForTexture(texture, textureMiplevel, textureSlice); + + const uint64_t offset = baseCopySplit.offset + baseOffset; + + for (uint32_t i = 0; i < baseCopySplit.count; ++i) { + const Texture2DCopySplit::CopyInfo& info = baseCopySplit.copies[i]; + + // TODO(jiawei.shao@intel.com): pre-compute bufferLocation and sourceRegion as + // members in Texture2DCopySplit::CopyInfo. + const D3D12_TEXTURE_COPY_LOCATION bufferLocation = + ComputeBufferLocationForCopyTextureRegion(texture, buffer->GetD3D12Resource(), + info.bufferSize, offset, + bufferBytesPerRow); + const D3D12_BOX sourceRegion = + ComputeD3D12BoxFromOffsetAndSize(info.textureOffset, info.copySize); + + commandList->CopyTextureRegion(&bufferLocation, info.bufferOffset.x, + info.bufferOffset.y, info.bufferOffset.z, + &textureLocation, &sourceRegion); + } + } } // anonymous namespace - class BindGroupStateTracker { + class BindGroupStateTracker : public BindGroupAndStorageBarrierTrackerBase { + using Base = BindGroupAndStorageBarrierTrackerBase; + public: - BindGroupStateTracker(Device* device) : mDevice(device) { + BindGroupStateTracker(Device* device) + : BindGroupAndStorageBarrierTrackerBase(), + mDevice(device), + mViewAllocator(device->GetViewShaderVisibleDescriptorAllocator()), + mSamplerAllocator(device->GetSamplerShaderVisibleDescriptorAllocator()) { } void SetInComputePass(bool inCompute_) { mInCompute = inCompute_; } - void AllocateDescriptorHeaps(Device* device) { - // This function should only be called once. - ASSERT(mCbvSrvUavGPUDescriptorHeap.Get() == nullptr && - mSamplerGPUDescriptorHeap.Get() == nullptr); + void OnSetPipeline(PipelineBase* pipeline) { + // Invalidate the root sampler tables previously set in the root signature. + // This is because changing the pipeline layout also changes the root signature. + const PipelineLayout* pipelineLayout = ToBackend(pipeline->GetLayout()); + if (mLastAppliedPipelineLayout != pipelineLayout) { + mBoundRootSamplerTables = {}; + } - DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator(); + Base::OnSetPipeline(pipeline); + } - if (mCbvSrvUavDescriptorHeapSize > 0) { - mCbvSrvUavGPUDescriptorHeap = descriptorHeapAllocator->AllocateGPUHeap( - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, mCbvSrvUavDescriptorHeapSize); + MaybeError Apply(CommandRecordingContext* commandContext) { + // Bindgroups are allocated in shader-visible descriptor heaps which are managed by a + // ringbuffer. There can be a single shader-visible descriptor heap of each type bound + // at any given time. This means that when we switch heaps, all other currently bound + // bindgroups must be re-populated. Bindgroups can fail allocation gracefully which is + // the signal to change the bounded heaps. + // Re-populating all bindgroups after the last one fails causes duplicated allocations + // to occur on overflow. + // TODO(bryan.bernhart@intel.com): Consider further optimization. + bool didCreateBindGroupViews = true; + bool didCreateBindGroupSamplers = true; + for (BindGroupIndex index : IterateBitSet(mDirtyBindGroups)) { + BindGroup* group = ToBackend(mBindGroups[index]); + didCreateBindGroupViews = group->PopulateViews(mViewAllocator); + didCreateBindGroupSamplers = group->PopulateSamplers(mDevice, mSamplerAllocator); + if (!didCreateBindGroupViews && !didCreateBindGroupSamplers) { + break; + } } - if (mSamplerDescriptorHeapSize > 0) { - mSamplerGPUDescriptorHeap = descriptorHeapAllocator->AllocateGPUHeap( - D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, mSamplerDescriptorHeapSize); - } + ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); - uint32_t cbvSrvUavDescriptorIndex = 0; - uint32_t samplerDescriptorIndex = 0; - for (BindGroup* group : mBindGroupsList) { - ASSERT(group); - ASSERT(cbvSrvUavDescriptorIndex + - ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount() <= - mCbvSrvUavDescriptorHeapSize); - ASSERT(samplerDescriptorIndex + - ToBackend(group->GetLayout())->GetSamplerDescriptorCount() <= - mSamplerDescriptorHeapSize); - group->AllocateDescriptors(mCbvSrvUavGPUDescriptorHeap, &cbvSrvUavDescriptorIndex, - mSamplerGPUDescriptorHeap, &samplerDescriptorIndex); - } + if (!didCreateBindGroupViews || !didCreateBindGroupSamplers) { + if (!didCreateBindGroupViews) { + DAWN_TRY(mViewAllocator->AllocateAndSwitchShaderVisibleHeap()); + } - ASSERT(cbvSrvUavDescriptorIndex == mCbvSrvUavDescriptorHeapSize); - ASSERT(samplerDescriptorIndex == mSamplerDescriptorHeapSize); - } + if (!didCreateBindGroupSamplers) { + DAWN_TRY(mSamplerAllocator->AllocateAndSwitchShaderVisibleHeap()); + } - // This function must only be called before calling AllocateDescriptorHeaps(). - void TrackSetBindGroup(BindGroup* group, uint32_t index, uint32_t indexInSubmit) { - if (mBindGroups[index] != group) { - mBindGroups[index] = group; + mDirtyBindGroupsObjectChangedOrIsDynamic |= mBindGroupLayoutsMask; + mDirtyBindGroups |= mBindGroupLayoutsMask; - if (!group->TestAndSetCounted(mDevice->GetPendingCommandSerial(), indexInSubmit)) { - const BindGroupLayout* layout = ToBackend(group->GetLayout()); + // Must be called before applying the bindgroups. + SetID3D12DescriptorHeaps(commandList); - mCbvSrvUavDescriptorHeapSize += layout->GetCbvUavSrvDescriptorCount(); - mSamplerDescriptorHeapSize += layout->GetSamplerDescriptorCount(); - mBindGroupsList.push_back(group); + for (BindGroupIndex index : IterateBitSet(mBindGroupLayoutsMask)) { + BindGroup* group = ToBackend(mBindGroups[index]); + didCreateBindGroupViews = group->PopulateViews(mViewAllocator); + didCreateBindGroupSamplers = + group->PopulateSamplers(mDevice, mSamplerAllocator); + ASSERT(didCreateBindGroupViews); + ASSERT(didCreateBindGroupSamplers); } } - } - // This function must only be called before calling AllocateDescriptorHeaps(). - void TrackInheritedGroups(PipelineLayout* oldLayout, - PipelineLayout* newLayout, - uint32_t indexInSubmit) { - if (oldLayout == nullptr) { - return; + for (BindGroupIndex index : IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) { + BindGroup* group = ToBackend(mBindGroups[index]); + ApplyBindGroup(commandList, ToBackend(mPipelineLayout), index, group, + mDynamicOffsetCounts[index], mDynamicOffsets[index].data()); } - uint32_t inheritUntil = oldLayout->GroupsInheritUpTo(newLayout); - for (uint32_t i = 0; i < inheritUntil; ++i) { - TrackSetBindGroup(mBindGroups[i], i, indexInSubmit); + if (mInCompute) { + for (BindGroupIndex index : IterateBitSet(mBindGroupLayoutsMask)) { + for (BindingIndex binding : IterateBitSet(mBindingsNeedingBarrier[index])) { + wgpu::BindingType bindingType = mBindingTypes[index][binding]; + switch (bindingType) { + case wgpu::BindingType::StorageBuffer: + static_cast(mBindings[index][binding]) + ->TrackUsageAndTransitionNow(commandContext, + wgpu::BufferUsage::Storage); + break; + + case wgpu::BindingType::ReadonlyStorageTexture: { + TextureViewBase* view = + static_cast(mBindings[index][binding]); + ToBackend(view->GetTexture()) + ->TrackUsageAndTransitionNow(commandContext, + kReadonlyStorageTexture, + view->GetSubresourceRange()); + break; + } + case wgpu::BindingType::WriteonlyStorageTexture: { + TextureViewBase* view = + static_cast(mBindings[index][binding]); + ToBackend(view->GetTexture()) + ->TrackUsageAndTransitionNow(commandContext, + wgpu::TextureUsage::Storage, + view->GetSubresourceRange()); + break; + } + case wgpu::BindingType::StorageTexture: + // Not implemented. + + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::SampledTexture: + // Don't require barriers. + + default: + UNREACHABLE(); + break; + } + } + } } - } - - void SetBindGroup(ComPtr commandList, - PipelineLayout* pipelineLayout, - BindGroup* group, - uint32_t index, - bool force = false) { - if (mBindGroups[index] != group || force) { - mBindGroups[index] = group; + DidApply(); - uint32_t cbvUavSrvCount = - ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount(); - uint32_t samplerCount = ToBackend(group->GetLayout())->GetSamplerDescriptorCount(); + return {}; + } - if (cbvUavSrvCount > 0) { - uint32_t parameterIndex = pipelineLayout->GetCbvUavSrvRootParameterIndex(index); + void SetID3D12DescriptorHeaps(ID3D12GraphicsCommandList* commandList) { + ASSERT(commandList != nullptr); + std::array descriptorHeaps = { + mViewAllocator->GetShaderVisibleHeap(), mSamplerAllocator->GetShaderVisibleHeap()}; + ASSERT(descriptorHeaps[0] != nullptr); + ASSERT(descriptorHeaps[1] != nullptr); + commandList->SetDescriptorHeaps(descriptorHeaps.size(), descriptorHeaps.data()); + } - if (mInCompute) { - commandList->SetComputeRootDescriptorTable( - parameterIndex, mCbvSrvUavGPUDescriptorHeap.GetGPUHandle( - group->GetCbvUavSrvHeapOffset())); - } else { - commandList->SetGraphicsRootDescriptorTable( - parameterIndex, mCbvSrvUavGPUDescriptorHeap.GetGPUHandle( - group->GetCbvUavSrvHeapOffset())); + private: + void ApplyBindGroup(ID3D12GraphicsCommandList* commandList, + const PipelineLayout* pipelineLayout, + BindGroupIndex index, + BindGroup* group, + uint32_t dynamicOffsetCountIn, + const uint64_t* dynamicOffsetsIn) { + ityp::span dynamicOffsets( + dynamicOffsetsIn, BindingIndex(dynamicOffsetCountIn)); + ASSERT(dynamicOffsets.size() == group->GetLayout()->GetDynamicBufferCount()); + + // Usually, the application won't set the same offsets many times, + // so always try to apply dynamic offsets even if the offsets stay the same + if (dynamicOffsets.size() != BindingIndex(0)) { + // Update dynamic offsets. + // Dynamic buffer bindings are packed at the beginning of the layout. + for (BindingIndex bindingIndex{0}; bindingIndex < dynamicOffsets.size(); + ++bindingIndex) { + const BindingInfo& bindingInfo = + group->GetLayout()->GetBindingInfo(bindingIndex); + if (bindingInfo.visibility == wgpu::ShaderStage::None) { + // Skip dynamic buffers that are not visible. D3D12 does not have None + // visibility. + continue; } - } - - if (samplerCount > 0) { - uint32_t parameterIndex = pipelineLayout->GetSamplerRootParameterIndex(index); - if (mInCompute) { - commandList->SetComputeRootDescriptorTable( - parameterIndex, - mSamplerGPUDescriptorHeap.GetGPUHandle(group->GetSamplerHeapOffset())); - } else { - commandList->SetGraphicsRootDescriptorTable( - parameterIndex, - mSamplerGPUDescriptorHeap.GetGPUHandle(group->GetSamplerHeapOffset())); + uint32_t parameterIndex = + pipelineLayout->GetDynamicRootParameterIndex(index, bindingIndex); + BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); + + // Calculate buffer locations that root descriptors links to. The location + // is (base buffer location + initial offset + dynamic offset) + uint64_t dynamicOffset = dynamicOffsets[bindingIndex]; + uint64_t offset = binding.offset + dynamicOffset; + D3D12_GPU_VIRTUAL_ADDRESS bufferLocation = + ToBackend(binding.buffer)->GetVA() + offset; + + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: + if (mInCompute) { + commandList->SetComputeRootConstantBufferView(parameterIndex, + bufferLocation); + } else { + commandList->SetGraphicsRootConstantBufferView(parameterIndex, + bufferLocation); + } + break; + case wgpu::BindingType::StorageBuffer: + if (mInCompute) { + commandList->SetComputeRootUnorderedAccessView(parameterIndex, + bufferLocation); + } else { + commandList->SetGraphicsRootUnorderedAccessView(parameterIndex, + bufferLocation); + } + break; + case wgpu::BindingType::ReadonlyStorageBuffer: + if (mInCompute) { + commandList->SetComputeRootShaderResourceView(parameterIndex, + bufferLocation); + } else { + commandList->SetGraphicsRootShaderResourceView(parameterIndex, + bufferLocation); + } + break; + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::StorageTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + UNREACHABLE(); + break; } } } - } - void SetInheritedBindGroups(ComPtr commandList, - PipelineLayout* oldLayout, - PipelineLayout* newLayout) { - if (oldLayout == nullptr) { + // It's not necessary to update descriptor tables if only the dynamic offset changed. + if (!mDirtyBindGroups[index]) { return; } - uint32_t inheritUntil = oldLayout->GroupsInheritUpTo(newLayout); - for (uint32_t i = 0; i < inheritUntil; ++i) { - SetBindGroup(commandList, newLayout, mBindGroups[i], i, true); + const uint32_t cbvUavSrvCount = + ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount(); + const uint32_t samplerCount = + ToBackend(group->GetLayout())->GetSamplerDescriptorCount(); + + if (cbvUavSrvCount > 0) { + uint32_t parameterIndex = pipelineLayout->GetCbvUavSrvRootParameterIndex(index); + const D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor = group->GetBaseViewDescriptor(); + if (mInCompute) { + commandList->SetComputeRootDescriptorTable(parameterIndex, baseDescriptor); + } else { + commandList->SetGraphicsRootDescriptorTable(parameterIndex, baseDescriptor); + } } - } - void Reset() { - for (uint32_t i = 0; i < kMaxBindGroups; ++i) { - mBindGroups[i] = nullptr; - } - } + if (samplerCount > 0) { + uint32_t parameterIndex = pipelineLayout->GetSamplerRootParameterIndex(index); + const D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor = + group->GetBaseSamplerDescriptor(); + // Check if the group requires its sampler table to be set in the pipeline. + // This because sampler heap allocations could be cached and use the same table. + if (mBoundRootSamplerTables[index].ptr != baseDescriptor.ptr) { + if (mInCompute) { + commandList->SetComputeRootDescriptorTable(parameterIndex, baseDescriptor); + } else { + commandList->SetGraphicsRootDescriptorTable(parameterIndex, baseDescriptor); + } - void SetID3D12DescriptorHeaps(ComPtr commandList) { - ASSERT(commandList != nullptr); - ID3D12DescriptorHeap* descriptorHeaps[2] = {mCbvSrvUavGPUDescriptorHeap.Get(), - mSamplerGPUDescriptorHeap.Get()}; - if (descriptorHeaps[0] && descriptorHeaps[1]) { - commandList->SetDescriptorHeaps(2, descriptorHeaps); - } else if (descriptorHeaps[0]) { - commandList->SetDescriptorHeaps(1, descriptorHeaps); - } else if (descriptorHeaps[1]) { - commandList->SetDescriptorHeaps(1, &descriptorHeaps[1]); + mBoundRootSamplerTables[index] = baseDescriptor; + } } } - private: - uint32_t mCbvSrvUavDescriptorHeapSize = 0; - uint32_t mSamplerDescriptorHeapSize = 0; - std::array mBindGroups = {}; - std::deque mBindGroupsList = {}; + Device* mDevice; + bool mInCompute = false; - DescriptorHeapHandle mCbvSrvUavGPUDescriptorHeap = {}; - DescriptorHeapHandle mSamplerGPUDescriptorHeap = {}; + ityp::array + mBoundRootSamplerTables = {}; - Device* mDevice; + ShaderVisibleDescriptorAllocator* mViewAllocator; + ShaderVisibleDescriptorAllocator* mSamplerAllocator; }; - struct OMSetRenderTargetArgs { - unsigned int numRTVs = 0; - std::array RTVs = {}; - D3D12_CPU_DESCRIPTOR_HANDLE dsv = {}; - }; + namespace { + class VertexBufferTracker { + public: + void OnSetVertexBuffer(uint32_t slot, Buffer* buffer, uint64_t offset, uint64_t size) { + mStartSlot = std::min(mStartSlot, slot); + mEndSlot = std::max(mEndSlot, slot + 1); + + auto* d3d12BufferView = &mD3D12BufferViews[slot]; + d3d12BufferView->BufferLocation = buffer->GetVA() + offset; + d3d12BufferView->SizeInBytes = size; + // The bufferView stride is set based on the vertex state before a draw. + } - class RenderPassDescriptorHeapTracker { - public: - RenderPassDescriptorHeapTracker(Device* device) : mDevice(device) { - } + void Apply(ID3D12GraphicsCommandList* commandList, + const RenderPipeline* renderPipeline) { + ASSERT(renderPipeline != nullptr); - // This function must only be called before calling AllocateRTVAndDSVHeaps(). - void TrackRenderPass(const BeginRenderPassCmd* renderPass) { - DAWN_ASSERT(mRTVHeap.Get() == nullptr && mDSVHeap.Get() == nullptr); + std::bitset vertexBufferSlotsUsed = + renderPipeline->GetVertexBufferSlotsUsed(); - mNumRTVs += static_cast(renderPass->colorAttachmentsSet.count()); - if (renderPass->hasDepthStencilAttachment) { - ++mNumDSVs; - } - } + uint32_t startSlot = mStartSlot; + uint32_t endSlot = mEndSlot; - void AllocateRTVAndDSVHeaps() { - // This function should only be called once. - DAWN_ASSERT(mRTVHeap.Get() == nullptr && mDSVHeap.Get() == nullptr); - DescriptorHeapAllocator* allocator = mDevice->GetDescriptorHeapAllocator(); - if (mNumRTVs > 0) { - mRTVHeap = allocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, mNumRTVs); - } - if (mNumDSVs > 0) { - mDSVHeap = allocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, mNumDSVs); - } - } + // If the vertex state has changed, we need to update the StrideInBytes + // for the D3D12 buffer views. We also need to extend the dirty range to + // touch all these slots because the stride may have changed. + if (mLastAppliedRenderPipeline != renderPipeline) { + mLastAppliedRenderPipeline = renderPipeline; - // TODO(jiawei.shao@intel.com): use hash map as cache to - // avoid redundant RTV and DSV memory allocations. - OMSetRenderTargetArgs GetSubpassOMSetRenderTargetArgs(BeginRenderPassCmd* renderPass) { - OMSetRenderTargetArgs args = {}; - - unsigned int rtvIndex = 0; - uint32_t rtvCount = static_cast(renderPass->colorAttachmentsSet.count()); - DAWN_ASSERT(mAllocatedRTVs + rtvCount <= mNumRTVs); - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { - TextureView* view = ToBackend(renderPass->colorAttachments[i].view).Get(); - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = mRTVHeap.GetCPUHandle(mAllocatedRTVs); - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = view->GetRTVDescriptor(); - mDevice->GetD3D12Device()->CreateRenderTargetView( - ToBackend(view->GetTexture())->GetD3D12Resource(), &rtvDesc, rtvHandle); - args.RTVs[i] = rtvHandle; - - ++rtvIndex; - ++mAllocatedRTVs; - } - args.numRTVs = rtvIndex; - - if (renderPass->hasDepthStencilAttachment) { - DAWN_ASSERT(mAllocatedDSVs < mNumDSVs); - TextureView* view = ToBackend(renderPass->depthStencilAttachment.view).Get(); - D3D12_CPU_DESCRIPTOR_HANDLE dsvHandle = mDSVHeap.GetCPUHandle(mAllocatedDSVs); - D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = view->GetDSVDescriptor(); - mDevice->GetD3D12Device()->CreateDepthStencilView( - ToBackend(view->GetTexture())->GetD3D12Resource(), &dsvDesc, dsvHandle); - args.dsv = dsvHandle; - - ++mAllocatedDSVs; - } + for (uint32_t slot : IterateBitSet(vertexBufferSlotsUsed)) { + startSlot = std::min(startSlot, slot); + endSlot = std::max(endSlot, slot + 1); + mD3D12BufferViews[slot].StrideInBytes = + renderPipeline->GetVertexBuffer(slot).arrayStride; + } + } - return args; - } + if (endSlot <= startSlot) { + return; + } - bool IsHeapAllocationCompleted() const { - return mNumRTVs == mAllocatedRTVs && mNumDSVs == mAllocatedDSVs; - } + // mD3D12BufferViews is kept up to date with the most recent data passed + // to SetVertexBuffer. This makes it correct to only track the start + // and end of the dirty range. When Apply is called, + // we will at worst set non-dirty vertex buffers in duplicate. + uint32_t count = endSlot - startSlot; + commandList->IASetVertexBuffers(startSlot, count, &mD3D12BufferViews[startSlot]); - private: - Device* mDevice; - DescriptorHeapHandle mRTVHeap = {}; - DescriptorHeapHandle mDSVHeap = {}; - uint32_t mNumRTVs = 0; - uint32_t mNumDSVs = 0; + mStartSlot = kMaxVertexBuffers; + mEndSlot = 0; + } - uint32_t mAllocatedRTVs = 0; - uint32_t mAllocatedDSVs = 0; - }; + private: + // startSlot and endSlot indicate the range of dirty vertex buffers. + // If there are multiple calls to SetVertexBuffer, the start and end + // represent the union of the dirty ranges (the union may have non-dirty + // data in the middle of the range). + const RenderPipeline* mLastAppliedRenderPipeline = nullptr; + uint32_t mStartSlot = kMaxVertexBuffers; + uint32_t mEndSlot = 0; + std::array mD3D12BufferViews = {}; + }; - namespace { + class IndexBufferTracker { + public: + void OnSetIndexBuffer(Buffer* buffer, uint64_t offset, uint64_t size) { + mD3D12BufferView.BufferLocation = buffer->GetVA() + offset; + mD3D12BufferView.SizeInBytes = size; - void AllocateAndSetDescriptorHeaps(Device* device, - BindGroupStateTracker* bindingTracker, - RenderPassDescriptorHeapTracker* renderPassTracker, - CommandIterator* commands, - uint32_t indexInSubmit) { - { - Command type; - PipelineLayout* lastLayout = nullptr; - - while (commands->NextCommandId(&type)) { - switch (type) { - case Command::SetComputePipeline: { - SetComputePipelineCmd* cmd = - commands->NextCommand(); - PipelineLayout* layout = ToBackend(cmd->pipeline->GetLayout()); - bindingTracker->TrackInheritedGroups(lastLayout, layout, indexInSubmit); - lastLayout = layout; - } break; - - case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = - commands->NextCommand(); - PipelineLayout* layout = ToBackend(cmd->pipeline->GetLayout()); - bindingTracker->TrackInheritedGroups(lastLayout, layout, indexInSubmit); - lastLayout = layout; - } break; - - case Command::SetBindGroup: { - SetBindGroupCmd* cmd = commands->NextCommand(); - BindGroup* group = ToBackend(cmd->group.Get()); - bindingTracker->TrackSetBindGroup(group, cmd->index, indexInSubmit); - } break; - case Command::BeginRenderPass: { - BeginRenderPassCmd* cmd = commands->NextCommand(); - renderPassTracker->TrackRenderPass(cmd); - } break; - default: - SkipCommand(commands, type); - } + // We don't need to dirty the state unless BufferLocation or SizeInBytes + // change, but most of the time this will always be the case. + mLastAppliedIndexFormat = DXGI_FORMAT_UNKNOWN; + } + + void OnSetPipeline(const RenderPipelineBase* pipeline) { + mD3D12BufferView.Format = + DXGIIndexFormat(pipeline->GetVertexStateDescriptor()->indexFormat); + } + + void Apply(ID3D12GraphicsCommandList* commandList) { + if (mD3D12BufferView.Format == mLastAppliedIndexFormat) { + return; } - commands->Reset(); + commandList->IASetIndexBuffer(&mD3D12BufferView); + mLastAppliedIndexFormat = mD3D12BufferView.Format; } - renderPassTracker->AllocateRTVAndDSVHeaps(); - bindingTracker->AllocateDescriptorHeaps(device); - } + private: + DXGI_FORMAT mLastAppliedIndexFormat = DXGI_FORMAT_UNKNOWN; + D3D12_INDEX_BUFFER_VIEW mD3D12BufferView = {}; + }; - void ResolveMultisampledRenderPass(ComPtr commandList, + void ResolveMultisampledRenderPass(CommandRecordingContext* commandContext, BeginRenderPassCmd* renderPass) { ASSERT(renderPass != nullptr); - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { + for (uint32_t i : + IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { TextureViewBase* resolveTarget = renderPass->colorAttachments[i].resolveTarget.Get(); if (resolveTarget == nullptr) { continue; } - Texture* colorTexture = - ToBackend(renderPass->colorAttachments[i].view->GetTexture()); + TextureViewBase* colorView = renderPass->colorAttachments[i].view.Get(); + Texture* colorTexture = ToBackend(colorView->GetTexture()); Texture* resolveTexture = ToBackend(resolveTarget->GetTexture()); // Transition the usages of the color attachment and resolve target. - colorTexture->TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RESOLVE_SOURCE); - resolveTexture->TransitionUsageNow(commandList, D3D12_RESOURCE_STATE_RESOLVE_DEST); + colorTexture->TrackUsageAndTransitionNow(commandContext, + D3D12_RESOURCE_STATE_RESOLVE_SOURCE, + colorView->GetSubresourceRange()); + resolveTexture->TrackUsageAndTransitionNow(commandContext, + D3D12_RESOURCE_STATE_RESOLVE_DEST, + resolveTarget->GetSubresourceRange()); // Do MSAA resolve with ResolveSubResource(). ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource(); @@ -393,7 +531,7 @@ namespace dawn_native { namespace d3d12 { const uint32_t resolveTextureSubresourceIndex = resolveTexture->GetSubresourceIndex( resolveTarget->GetBaseMipLevel(), resolveTarget->GetBaseArrayLayer()); constexpr uint32_t kColorTextureSubresourceIndex = 0; - commandList->ResolveSubresource( + commandContext->GetCommandList()->ResolveSubresource( resolveTextureHandle, resolveTextureSubresourceIndex, colorTextureHandle, kColorTextureSubresourceIndex, colorTexture->GetD3D12Format()); } @@ -401,57 +539,69 @@ namespace dawn_native { namespace d3d12 { } // anonymous namespace - CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder) - : CommandBufferBase(device, encoder), mCommands(encoder->AcquireCommands()) { + CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) + : CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) { } CommandBuffer::~CommandBuffer() { FreeCommands(&mCommands); } - void CommandBuffer::RecordCommands(ComPtr commandList, - uint32_t indexInSubmit) { + MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* commandContext) { Device* device = ToBackend(GetDevice()); BindGroupStateTracker bindingTracker(device); - RenderPassDescriptorHeapTracker renderPassTracker(device); - // Precompute the allocation of bindgroups in descriptor heaps - // TODO(cwallez@chromium.org): Iterating over all the commands here is inefficient. We - // should have a system where commands and descriptors are recorded in parallel then the - // heaps set using a small CommandList inserted just before the main CommandList. - { - AllocateAndSetDescriptorHeaps(device, &bindingTracker, &renderPassTracker, &mCommands, - indexInSubmit); - bindingTracker.Reset(); - bindingTracker.SetID3D12DescriptorHeaps(commandList); - } + ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); + + // Make sure we use the correct descriptors for this command list. Could be done once per + // actual command list but here is ok because there should be few command buffers. + bindingTracker.SetID3D12DescriptorHeaps(commandList); // Records the necessary barriers for the resource usage pre-computed by the frontend - auto TransitionForPass = [](ComPtr commandList, - const PassResourceUsage& usages) { + auto PrepareResourcesForSubmission = [](CommandRecordingContext* commandContext, + const PassResourceUsage& usages) -> bool { std::vector barriers; + ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); + + wgpu::BufferUsage bufferUsages = wgpu::BufferUsage::None; + for (size_t i = 0; i < usages.buffers.size(); ++i) { D3D12_RESOURCE_BARRIER barrier; if (ToBackend(usages.buffers[i]) - ->CreateD3D12ResourceBarrierIfNeeded(&barrier, usages.bufferUsages[i])) { + ->TrackUsageAndGetResourceBarrier(commandContext, &barrier, + usages.bufferUsages[i])) { barriers.push_back(barrier); - ToBackend(usages.buffers[i])->SetUsage(usages.bufferUsages[i]); } + bufferUsages |= usages.bufferUsages[i]; } for (size_t i = 0; i < usages.textures.size(); ++i) { - D3D12_RESOURCE_BARRIER barrier; - if (ToBackend(usages.textures[i]) - ->CreateD3D12ResourceBarrierIfNeeded(&barrier, usages.textureUsages[i])) { - barriers.push_back(barrier); - ToBackend(usages.textures[i])->SetUsage(usages.textureUsages[i]); + Texture* texture = ToBackend(usages.textures[i]); + // Clear textures that are not output attachments. Output attachments will be + // cleared during record render pass if the texture subresource has not been + // initialized before the render pass. + if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) { + texture->EnsureSubresourceContentInitialized(commandContext, + texture->GetAllSubresources()); } } + wgpu::TextureUsage textureUsages = wgpu::TextureUsage::None; + + for (size_t i = 0; i < usages.textures.size(); ++i) { + ToBackend(usages.textures[i]) + ->TrackUsageAndGetResourceBarrierForPass(commandContext, &barriers, + usages.textureUsages[i]); + textureUsages |= usages.textureUsages[i].usage; + } + if (barriers.size()) { commandList->ResourceBarrier(barriers.size(), barriers.data()); } + + return (bufferUsages & wgpu::BufferUsage::Storage || + textureUsages & wgpu::TextureUsage::Storage); }; const std::vector& passResourceUsages = GetResourceUsages().perPass; @@ -463,127 +613,167 @@ namespace dawn_native { namespace d3d12 { case Command::BeginComputePass: { mCommands.NextCommand(); - TransitionForPass(commandList, passResourceUsages[nextPassNumber]); + PrepareResourcesForSubmission(commandContext, + passResourceUsages[nextPassNumber]); bindingTracker.SetInComputePass(true); - RecordComputePass(commandList, &bindingTracker); + DAWN_TRY(RecordComputePass(commandContext, &bindingTracker)); nextPassNumber++; - } break; + break; + } case Command::BeginRenderPass: { BeginRenderPassCmd* beginRenderPassCmd = mCommands.NextCommand(); - TransitionForPass(commandList, passResourceUsages[nextPassNumber]); + const bool passHasUAV = PrepareResourcesForSubmission( + commandContext, passResourceUsages[nextPassNumber]); bindingTracker.SetInComputePass(false); - RecordRenderPass(commandList, &bindingTracker, &renderPassTracker, - beginRenderPassCmd); + + LazyClearRenderPassAttachments(beginRenderPassCmd); + DAWN_TRY(RecordRenderPass(commandContext, &bindingTracker, beginRenderPassCmd, + passHasUAV)); nextPassNumber++; - } break; + break; + } case Command::CopyBufferToBuffer: { CopyBufferToBufferCmd* copy = mCommands.NextCommand(); Buffer* srcBuffer = ToBackend(copy->source.Get()); Buffer* dstBuffer = ToBackend(copy->destination.Get()); - srcBuffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferSrc); - dstBuffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferDst); + DAWN_TRY(srcBuffer->EnsureDataInitialized(commandContext)); + DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination( + commandContext, copy->destinationOffset, copy->size)); + + srcBuffer->TrackUsageAndTransitionNow(commandContext, + wgpu::BufferUsage::CopySrc); + dstBuffer->TrackUsageAndTransitionNow(commandContext, + wgpu::BufferUsage::CopyDst); commandList->CopyBufferRegion( - dstBuffer->GetD3D12Resource().Get(), copy->destinationOffset, - srcBuffer->GetD3D12Resource().Get(), copy->sourceOffset, copy->size); - } break; + dstBuffer->GetD3D12Resource(), copy->destinationOffset, + srcBuffer->GetD3D12Resource(), copy->sourceOffset, copy->size); + break; + } case Command::CopyBufferToTexture: { CopyBufferToTextureCmd* copy = mCommands.NextCommand(); Buffer* buffer = ToBackend(copy->source.buffer.Get()); Texture* texture = ToBackend(copy->destination.texture.Get()); - buffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferSrc); - texture->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferDst); - - auto copySplit = ComputeTextureCopySplit( - copy->destination.origin, copy->copySize, - static_cast(TextureFormatPixelSize(texture->GetFormat())), - copy->source.offset, copy->source.rowPitch, copy->source.imageHeight); - - D3D12_TEXTURE_COPY_LOCATION textureLocation = - CreateTextureCopyLocationForTexture(*texture, copy->destination.level, - copy->destination.slice); - - for (uint32_t i = 0; i < copySplit.count; ++i) { - auto& info = copySplit.copies[i]; - - D3D12_TEXTURE_COPY_LOCATION bufferLocation; - bufferLocation.pResource = buffer->GetD3D12Resource().Get(); - bufferLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - bufferLocation.PlacedFootprint.Offset = copySplit.offset; - bufferLocation.PlacedFootprint.Footprint.Format = texture->GetD3D12Format(); - bufferLocation.PlacedFootprint.Footprint.Width = info.bufferSize.width; - bufferLocation.PlacedFootprint.Footprint.Height = info.bufferSize.height; - bufferLocation.PlacedFootprint.Footprint.Depth = info.bufferSize.depth; - bufferLocation.PlacedFootprint.Footprint.RowPitch = copy->source.rowPitch; - - D3D12_BOX sourceRegion; - sourceRegion.left = info.bufferOffset.x; - sourceRegion.top = info.bufferOffset.y; - sourceRegion.front = info.bufferOffset.z; - sourceRegion.right = info.bufferOffset.x + info.copySize.width; - sourceRegion.bottom = info.bufferOffset.y + info.copySize.height; - sourceRegion.back = info.bufferOffset.z + info.copySize.depth; - - commandList->CopyTextureRegion(&textureLocation, info.textureOffset.x, - info.textureOffset.y, info.textureOffset.z, - &bufferLocation, &sourceRegion); + DAWN_TRY(buffer->EnsureDataInitialized(commandContext)); + + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + SubresourceRange subresources = {copy->destination.mipLevel, 1, + copy->destination.origin.z, + copy->copySize.depth}; + if (IsCompleteSubresourceCopiedTo(texture, copy->copySize, + copy->destination.mipLevel)) { + texture->SetIsSubresourceContentInitialized(true, subresources); + } else { + texture->EnsureSubresourceContentInitialized(commandContext, subresources); + } + + buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc); + texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst, + subresources); + + // See comments in ComputeTextureCopySplits() for more details. + const TextureCopySplits copySplits = ComputeTextureCopySplits( + copy->destination.origin, copy->copySize, texture->GetFormat(), + copy->source.offset, copy->source.bytesPerRow, copy->source.rowsPerImage); + + const uint64_t bytesPerSlice = + copy->source.bytesPerRow * + (copy->source.rowsPerImage / texture->GetFormat().blockHeight); + + // copySplits.copies2D[1] is always calculated for the second copy slice with + // extra "bytesPerSlice" copy offset compared with the first copy slice. So + // here we use an array bufferOffsetsForNextSlice to record the extra offsets + // for each copy slice: bufferOffsetsForNextSlice[0] is the extra offset for + // the next copy slice that uses copySplits.copies2D[0], and + // bufferOffsetsForNextSlice[1] is the extra offset for the next copy slice + // that uses copySplits.copies2D[1]. + std::array + bufferOffsetsForNextSlice = {{0u, 0u}}; + for (uint32_t copySlice = 0; copySlice < copy->copySize.depth; ++copySlice) { + const uint32_t splitIndex = copySlice % copySplits.copies2D.size(); + + const Texture2DCopySplit& copySplitPerLayerBase = + copySplits.copies2D[splitIndex]; + const uint64_t bufferOffsetForNextSlice = + bufferOffsetsForNextSlice[splitIndex]; + const uint32_t copyTextureLayer = copySlice + copy->destination.origin.z; + + RecordCopyBufferToTextureFromTextureCopySplit( + commandList, copySplitPerLayerBase, buffer, bufferOffsetForNextSlice, + copy->source.bytesPerRow, texture, copy->destination.mipLevel, + copyTextureLayer); + + bufferOffsetsForNextSlice[splitIndex] += + bytesPerSlice * copySplits.copies2D.size(); } - } break; + + break; + } case Command::CopyTextureToBuffer: { CopyTextureToBufferCmd* copy = mCommands.NextCommand(); Texture* texture = ToBackend(copy->source.texture.Get()); Buffer* buffer = ToBackend(copy->destination.buffer.Get()); - texture->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferSrc); - buffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferDst); - - auto copySplit = ComputeTextureCopySplit( - copy->source.origin, copy->copySize, - static_cast(TextureFormatPixelSize(texture->GetFormat())), - copy->destination.offset, copy->destination.rowPitch, - copy->destination.imageHeight); - - D3D12_TEXTURE_COPY_LOCATION textureLocation = - CreateTextureCopyLocationForTexture(*texture, copy->source.level, - copy->source.slice); - - for (uint32_t i = 0; i < copySplit.count; ++i) { - auto& info = copySplit.copies[i]; - - D3D12_TEXTURE_COPY_LOCATION bufferLocation; - bufferLocation.pResource = buffer->GetD3D12Resource().Get(); - bufferLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; - bufferLocation.PlacedFootprint.Offset = copySplit.offset; - bufferLocation.PlacedFootprint.Footprint.Format = texture->GetD3D12Format(); - bufferLocation.PlacedFootprint.Footprint.Width = info.bufferSize.width; - bufferLocation.PlacedFootprint.Footprint.Height = info.bufferSize.height; - bufferLocation.PlacedFootprint.Footprint.Depth = info.bufferSize.depth; - bufferLocation.PlacedFootprint.Footprint.RowPitch = - copy->destination.rowPitch; - - D3D12_BOX sourceRegion; - sourceRegion.left = info.textureOffset.x; - sourceRegion.top = info.textureOffset.y; - sourceRegion.front = info.textureOffset.z; - sourceRegion.right = info.textureOffset.x + info.copySize.width; - sourceRegion.bottom = info.textureOffset.y + info.copySize.height; - sourceRegion.back = info.textureOffset.z + info.copySize.depth; - - commandList->CopyTextureRegion(&bufferLocation, info.bufferOffset.x, - info.bufferOffset.y, info.bufferOffset.z, - &textureLocation, &sourceRegion); + DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy)); + + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + SubresourceRange subresources = {copy->source.mipLevel, 1, + copy->source.origin.z, copy->copySize.depth}; + texture->EnsureSubresourceContentInitialized(commandContext, subresources); + + texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc, + subresources); + buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst); + + // See comments around ComputeTextureCopySplits() for more details. + const TextureCopySplits copySplits = ComputeTextureCopySplits( + copy->source.origin, copy->copySize, texture->GetFormat(), + copy->destination.offset, copy->destination.bytesPerRow, + copy->destination.rowsPerImage); + + const uint64_t bytesPerSlice = + copy->destination.bytesPerRow * + (copy->destination.rowsPerImage / texture->GetFormat().blockHeight); + + // copySplits.copies2D[1] is always calculated for the second copy slice with + // extra "bytesPerSlice" copy offset compared with the first copy slice. So + // here we use an array bufferOffsetsForNextSlice to record the extra offsets + // for each copy slice: bufferOffsetsForNextSlice[0] is the extra offset for + // the next copy slice that uses copySplits.copies2D[0], and + // bufferOffsetsForNextSlice[1] is the extra offset for the next copy slice + // that uses copySplits.copies2D[1]. + std::array + bufferOffsetsForNextSlice = {{0u, 0u}}; + for (uint32_t copySlice = 0; copySlice < copy->copySize.depth; ++copySlice) { + const uint32_t splitIndex = copySlice % copySplits.copies2D.size(); + + const Texture2DCopySplit& copySplitPerLayerBase = + copySplits.copies2D[splitIndex]; + const uint64_t bufferOffsetForNextSlice = + bufferOffsetsForNextSlice[splitIndex]; + const uint32_t copyTextureLayer = copySlice + copy->source.origin.z; + + RecordCopyTextureToBufferFromTextureCopySplit( + commandList, copySplitPerLayerBase, buffer, bufferOffsetForNextSlice, + copy->destination.bytesPerRow, texture, copy->source.mipLevel, + copyTextureLayer); + + bufferOffsetsForNextSlice[splitIndex] += + bytesPerSlice * copySplits.copies2D.size(); } - } break; + + break; + } case Command::CopyTextureToTexture: { CopyTextureToTextureCmd* copy = @@ -591,195 +781,374 @@ namespace dawn_native { namespace d3d12 { Texture* source = ToBackend(copy->source.texture.Get()); Texture* destination = ToBackend(copy->destination.texture.Get()); + SubresourceRange srcRange = {copy->source.mipLevel, 1, copy->source.origin.z, + copy->copySize.depth}; + SubresourceRange dstRange = {copy->destination.mipLevel, 1, + copy->destination.origin.z, copy->copySize.depth}; + + source->EnsureSubresourceContentInitialized(commandContext, srcRange); + if (IsCompleteSubresourceCopiedTo(destination, copy->copySize, + copy->destination.mipLevel)) { + destination->SetIsSubresourceContentInitialized(true, dstRange); + } else { + destination->EnsureSubresourceContentInitialized(commandContext, dstRange); + } - source->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferSrc); - destination->TransitionUsageNow(commandList, - dawn::TextureUsageBit::TransferDst); + if (copy->source.texture.Get() == copy->destination.texture.Get() && + copy->source.mipLevel == copy->destination.mipLevel) { + // When there are overlapped subresources, the layout of the overlapped + // subresources should all be COMMON instead of what we set now. Currently + // it is not allowed to copy with overlapped subresources, but we still + // add the ASSERT here as a reminder for this possible misuse. + ASSERT(!IsRangeOverlapped(copy->source.origin.z, copy->destination.origin.z, + copy->copySize.depth)); + } + source->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc, + srcRange); + destination->TrackUsageAndTransitionNow(commandContext, + wgpu::TextureUsage::CopyDst, dstRange); - if (CanUseCopyResource(source->GetNumMipLevels(), source->GetSize(), - destination->GetSize(), copy->copySize)) { + if (CanUseCopyResource(source, destination, copy->copySize)) { commandList->CopyResource(destination->GetD3D12Resource(), source->GetD3D12Resource()); - } else { - D3D12_TEXTURE_COPY_LOCATION srcLocation = - CreateTextureCopyLocationForTexture(*source, copy->source.level, - copy->source.slice); - - D3D12_TEXTURE_COPY_LOCATION dstLocation = - CreateTextureCopyLocationForTexture( - *destination, copy->destination.level, copy->destination.slice); - - D3D12_BOX sourceRegion; - sourceRegion.left = copy->source.origin.x; - sourceRegion.top = copy->source.origin.y; - sourceRegion.front = copy->source.origin.z; - sourceRegion.right = copy->source.origin.x + copy->copySize.width; - sourceRegion.bottom = copy->source.origin.y + copy->copySize.height; - sourceRegion.back = copy->source.origin.z + copy->copySize.depth; - - commandList->CopyTextureRegion( - &dstLocation, copy->destination.origin.x, copy->destination.origin.y, - copy->destination.origin.z, &srcLocation, &sourceRegion); + // TODO(jiawei.shao@intel.com): support copying with 1D and 3D textures. + ASSERT(source->GetDimension() == wgpu::TextureDimension::e2D && + destination->GetDimension() == wgpu::TextureDimension::e2D); + const dawn_native::Extent3D copyExtentOneSlice = { + copy->copySize.width, copy->copySize.height, 1u}; + for (uint32_t slice = 0; slice < copy->copySize.depth; ++slice) { + D3D12_TEXTURE_COPY_LOCATION srcLocation = + ComputeTextureCopyLocationForTexture(source, copy->source.mipLevel, + copy->source.origin.z + slice); + + D3D12_TEXTURE_COPY_LOCATION dstLocation = + ComputeTextureCopyLocationForTexture( + destination, copy->destination.mipLevel, + copy->destination.origin.z + slice); + + Origin3D sourceOriginInSubresource = copy->source.origin; + sourceOriginInSubresource.z = 0; + D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize( + sourceOriginInSubresource, copyExtentOneSlice); + + commandList->CopyTextureRegion(&dstLocation, copy->destination.origin.x, + copy->destination.origin.y, 0, + &srcLocation, &sourceRegion); + } } - } break; - - default: { UNREACHABLE(); } break; - } - } - - DAWN_ASSERT(renderPassTracker.IsHeapAllocationCompleted()); - } - - void CommandBuffer::FlushSetVertexBuffers(ComPtr commandList, - VertexBuffersInfo* vertexBuffersInfo, - const RenderPipeline* renderPipeline) { - DAWN_ASSERT(vertexBuffersInfo != nullptr); - DAWN_ASSERT(renderPipeline != nullptr); - - auto inputsMask = renderPipeline->GetInputsSetMask(); + break; + } - uint32_t startSlot = vertexBuffersInfo->startSlot; - uint32_t endSlot = vertexBuffersInfo->endSlot; + case Command::ResolveQuerySet: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } - // If the input state has changed, we need to update the StrideInBytes - // for the D3D12 buffer views. We also need to extend the dirty range to - // touch all these slots because the stride may have changed. - if (vertexBuffersInfo->lastRenderPipeline != renderPipeline) { - vertexBuffersInfo->lastRenderPipeline = renderPipeline; + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } - for (uint32_t slot : IterateBitSet(inputsMask)) { - startSlot = std::min(startSlot, slot); - endSlot = std::max(endSlot, slot + 1); - vertexBuffersInfo->d3d12BufferViews[slot].StrideInBytes = - renderPipeline->GetInput(slot).stride; + default: { + UNREACHABLE(); + break; + } } } - if (endSlot <= startSlot) { - return; - } - - // d3d12BufferViews is kept up to date with the most recent data passed - // to SetVertexBuffers. This makes it correct to only track the start - // and end of the dirty range. When FlushSetVertexBuffers is called, - // we will at worst set non-dirty vertex buffers in duplicate. - uint32_t count = endSlot - startSlot; - commandList->IASetVertexBuffers(startSlot, count, - &vertexBuffersInfo->d3d12BufferViews[startSlot]); - - vertexBuffersInfo->startSlot = kMaxVertexBuffers; - vertexBuffersInfo->endSlot = 0; + return {}; } - void CommandBuffer::RecordComputePass(ComPtr commandList, - BindGroupStateTracker* bindingTracker) { + MaybeError CommandBuffer::RecordComputePass(CommandRecordingContext* commandContext, + BindGroupStateTracker* bindingTracker) { PipelineLayout* lastLayout = nullptr; + ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::Dispatch: { DispatchCmd* dispatch = mCommands.NextCommand(); + + DAWN_TRY(bindingTracker->Apply(commandContext)); commandList->Dispatch(dispatch->x, dispatch->y, dispatch->z); - } break; + break; + } case Command::DispatchIndirect: { DispatchIndirectCmd* dispatch = mCommands.NextCommand(); + DAWN_TRY(bindingTracker->Apply(commandContext)); Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get()); ComPtr signature = ToBackend(GetDevice())->GetDispatchIndirectSignature(); - commandList->ExecuteIndirect(signature.Get(), 1, - buffer->GetD3D12Resource().Get(), + commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource(), dispatch->indirectOffset, nullptr, 0); - } break; + break; + } case Command::EndComputePass: { mCommands.NextCommand(); - return; - } break; + return {}; + } case Command::SetComputePipeline: { SetComputePipelineCmd* cmd = mCommands.NextCommand(); ComputePipeline* pipeline = ToBackend(cmd->pipeline).Get(); PipelineLayout* layout = ToBackend(pipeline->GetLayout()); - commandList->SetComputeRootSignature(layout->GetRootSignature().Get()); - commandList->SetPipelineState(pipeline->GetPipelineState().Get()); + commandList->SetComputeRootSignature(layout->GetRootSignature()); + commandList->SetPipelineState(pipeline->GetPipelineState()); + + bindingTracker->OnSetPipeline(pipeline); - bindingTracker->SetInheritedBindGroups(commandList, lastLayout, layout); lastLayout = layout; - } break; + break; + } case Command::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); - bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index); - } break; + uint32_t* dynamicOffsets = nullptr; + + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + } + + bindingTracker->OnSetBindGroup(cmd->index, group, cmd->dynamicOffsetCount, + dynamicOffsets); + break; + } + + case Command::InsertDebugMarker: { + InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); + const char* label = mCommands.NextData(cmd->length + 1); - default: { UNREACHABLE(); } break; + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { + // PIX color is 1 byte per channel in ARGB format + constexpr uint64_t kPIXBlackColor = 0xff000000; + ToBackend(GetDevice()) + ->GetFunctions() + ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label); + } + break; + } + + case Command::PopDebugGroup: { + mCommands.NextCommand(); + + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { + ToBackend(GetDevice()) + ->GetFunctions() + ->pixEndEventOnCommandList(commandList); + } + break; + } + + case Command::PushDebugGroup: { + PushDebugGroupCmd* cmd = mCommands.NextCommand(); + const char* label = mCommands.NextData(cmd->length + 1); + + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { + // PIX color is 1 byte per channel in ARGB format + constexpr uint64_t kPIXBlackColor = 0xff000000; + ToBackend(GetDevice()) + ->GetFunctions() + ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label); + } + break; + } + + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } + + default: { + UNREACHABLE(); + break; + } } } + + return {}; } - void CommandBuffer::RecordRenderPass(ComPtr commandList, - BindGroupStateTracker* bindingTracker, - RenderPassDescriptorHeapTracker* renderPassTracker, - BeginRenderPassCmd* renderPass) { - OMSetRenderTargetArgs args = renderPassTracker->GetSubpassOMSetRenderTargetArgs(renderPass); + MaybeError CommandBuffer::SetupRenderPass(CommandRecordingContext* commandContext, + BeginRenderPassCmd* renderPass, + RenderPassBuilder* renderPassBuilder) { + Device* device = ToBackend(GetDevice()); - // Clear framebuffer attachments as needed and transition to render target - { - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { - auto& attachmentInfo = renderPass->colorAttachments[i]; + for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { + RenderPassColorAttachmentInfo& attachmentInfo = renderPass->colorAttachments[i]; + TextureView* view = ToBackend(attachmentInfo.view.Get()); - // Load op - color - if (attachmentInfo.loadOp == dawn::LoadOp::Clear) { - D3D12_CPU_DESCRIPTOR_HANDLE handle = args.RTVs[i]; - commandList->ClearRenderTargetView(handle, &attachmentInfo.clearColor.r, 0, - nullptr); - } + // Set view attachment. + CPUDescriptorHeapAllocation rtvAllocation; + DAWN_TRY_ASSIGN( + rtvAllocation, + device->GetRenderTargetViewAllocator()->AllocateTransientCPUDescriptors()); + + const D3D12_RENDER_TARGET_VIEW_DESC viewDesc = view->GetRTVDescriptor(); + const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = rtvAllocation.GetBaseDescriptor(); + + device->GetD3D12Device()->CreateRenderTargetView( + ToBackend(view->GetTexture())->GetD3D12Resource(), &viewDesc, baseDescriptor); + + renderPassBuilder->SetRenderTargetView(i, baseDescriptor); + + // Set color load operation. + renderPassBuilder->SetRenderTargetBeginningAccess( + i, attachmentInfo.loadOp, attachmentInfo.clearColor, view->GetD3D12Format()); + + // Set color store operation. + if (attachmentInfo.resolveTarget.Get() != nullptr) { + TextureView* resolveDestinationView = ToBackend(attachmentInfo.resolveTarget.Get()); + Texture* resolveDestinationTexture = + ToBackend(resolveDestinationView->GetTexture()); + + resolveDestinationTexture->TrackUsageAndTransitionNow( + commandContext, D3D12_RESOURCE_STATE_RESOLVE_DEST, + resolveDestinationView->GetSubresourceRange()); + + renderPassBuilder->SetRenderTargetEndingAccessResolve(i, attachmentInfo.storeOp, + view, resolveDestinationView); + } else { + renderPassBuilder->SetRenderTargetEndingAccess(i, attachmentInfo.storeOp); + } + } + + if (renderPass->attachmentState->HasDepthStencilAttachment()) { + RenderPassDepthStencilAttachmentInfo& attachmentInfo = + renderPass->depthStencilAttachment; + TextureView* view = ToBackend(renderPass->depthStencilAttachment.view.Get()); + + // Set depth attachment. + CPUDescriptorHeapAllocation dsvAllocation; + DAWN_TRY_ASSIGN( + dsvAllocation, + device->GetDepthStencilViewAllocator()->AllocateTransientCPUDescriptors()); + + const D3D12_DEPTH_STENCIL_VIEW_DESC viewDesc = view->GetDSVDescriptor(); + const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = dsvAllocation.GetBaseDescriptor(); + + device->GetD3D12Device()->CreateDepthStencilView( + ToBackend(view->GetTexture())->GetD3D12Resource(), &viewDesc, baseDescriptor); + + renderPassBuilder->SetDepthStencilView(baseDescriptor); + + const bool hasDepth = view->GetTexture()->GetFormat().HasDepth(); + const bool hasStencil = view->GetTexture()->GetFormat().HasStencil(); + + // Set depth/stencil load operations. + if (hasDepth) { + renderPassBuilder->SetDepthAccess( + attachmentInfo.depthLoadOp, attachmentInfo.depthStoreOp, + attachmentInfo.clearDepth, view->GetD3D12Format()); + } else { + renderPassBuilder->SetDepthNoAccess(); } - if (renderPass->hasDepthStencilAttachment) { - auto& attachmentInfo = renderPass->depthStencilAttachment; - Texture* texture = ToBackend(renderPass->depthStencilAttachment.view->GetTexture()); + if (hasStencil) { + renderPassBuilder->SetStencilAccess( + attachmentInfo.stencilLoadOp, attachmentInfo.stencilStoreOp, + attachmentInfo.clearStencil, view->GetD3D12Format()); + } else { + renderPassBuilder->SetStencilNoAccess(); + } - // Load op - depth/stencil - bool doDepthClear = TextureFormatHasDepth(texture->GetFormat()) && - (attachmentInfo.depthLoadOp == dawn::LoadOp::Clear); - bool doStencilClear = TextureFormatHasStencil(texture->GetFormat()) && - (attachmentInfo.stencilLoadOp == dawn::LoadOp::Clear); + } else { + renderPassBuilder->SetDepthStencilNoAccess(); + } + return {}; + } + + void CommandBuffer::EmulateBeginRenderPass(CommandRecordingContext* commandContext, + const RenderPassBuilder* renderPassBuilder) const { + ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); + + // Clear framebuffer attachments as needed. + { + for (uint32_t i = 0; i < renderPassBuilder->GetColorAttachmentCount(); i++) { + // Load op - color + if (renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i] + .BeginningAccess.Type == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) { + commandList->ClearRenderTargetView( + renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i].cpuDescriptor, + renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i] + .BeginningAccess.Clear.ClearValue.Color, + 0, nullptr); + } + } + + if (renderPassBuilder->HasDepth()) { D3D12_CLEAR_FLAGS clearFlags = {}; - if (doDepthClear) { + float depthClear = 0.0f; + uint8_t stencilClear = 0u; + + if (renderPassBuilder->GetRenderPassDepthStencilDescriptor() + ->DepthBeginningAccess.Type == + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) { clearFlags |= D3D12_CLEAR_FLAG_DEPTH; + depthClear = renderPassBuilder->GetRenderPassDepthStencilDescriptor() + ->DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth; } - if (doStencilClear) { + if (renderPassBuilder->GetRenderPassDepthStencilDescriptor() + ->StencilBeginningAccess.Type == + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) { clearFlags |= D3D12_CLEAR_FLAG_STENCIL; + stencilClear = + renderPassBuilder->GetRenderPassDepthStencilDescriptor() + ->StencilBeginningAccess.Clear.ClearValue.DepthStencil.Stencil; } + // TODO(kainino@chromium.org): investigate: should the Dawn clear + // stencil type be uint8_t? if (clearFlags) { - D3D12_CPU_DESCRIPTOR_HANDLE handle = args.dsv; - // TODO(kainino@chromium.org): investigate: should the Dawn clear - // stencil type be uint8_t? - uint8_t clearStencil = static_cast(attachmentInfo.clearStencil); commandList->ClearDepthStencilView( - handle, clearFlags, attachmentInfo.clearDepth, clearStencil, 0, nullptr); + renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor, + clearFlags, depthClear, stencilClear, 0, nullptr); } } } - // Set up render targets - { - if (args.dsv.ptr) { - commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, &args.dsv); - } else { - commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, nullptr); - } + commandList->OMSetRenderTargets( + renderPassBuilder->GetColorAttachmentCount(), renderPassBuilder->GetRenderTargetViews(), + FALSE, + renderPassBuilder->HasDepth() + ? &renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor + : nullptr); + } + + MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* commandContext, + BindGroupStateTracker* bindingTracker, + BeginRenderPassCmd* renderPass, + const bool passHasUAV) { + Device* device = ToBackend(GetDevice()); + const bool useRenderPass = device->IsToggleEnabled(Toggle::UseD3D12RenderPass); + + // renderPassBuilder must be scoped to RecordRenderPass because any underlying + // D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS structs must remain + // valid until after EndRenderPass() has been called. + RenderPassBuilder renderPassBuilder(passHasUAV); + + DAWN_TRY(SetupRenderPass(commandContext, renderPass, &renderPassBuilder)); + + // Use D3D12's native render pass API if it's available, otherwise emulate the + // beginning and ending access operations. + if (useRenderPass) { + commandContext->GetCommandList4()->BeginRenderPass( + renderPassBuilder.GetColorAttachmentCount(), + renderPassBuilder.GetRenderPassRenderTargetDescriptors(), + renderPassBuilder.HasDepth() + ? renderPassBuilder.GetRenderPassDepthStencilDescriptor() + : nullptr, + renderPassBuilder.GetRenderPassFlags()); + } else { + EmulateBeginRenderPass(commandContext, &renderPassBuilder); } + ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); + // Set up default dynamic state { uint32_t width = renderPass->width; @@ -796,119 +1165,186 @@ namespace dawn_native { namespace d3d12 { RenderPipeline* lastPipeline = nullptr; PipelineLayout* lastLayout = nullptr; - VertexBuffersInfo vertexBuffersInfo = {}; + VertexBufferTracker vertexBufferTracker = {}; + IndexBufferTracker indexBufferTracker = {}; - Command type; - while (mCommands.NextCommandId(&type)) { + auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) -> MaybeError { switch (type) { - case Command::EndRenderPass: { - mCommands.NextCommand(); - - // TODO(brandon1.jones@intel.com): avoid calling this function and enable MSAA - // resolve in D3D12 render pass on the platforms that support this feature. - if (renderPass->sampleCount > 1) { - ResolveMultisampledRenderPass(commandList, renderPass); - } - return; - } break; - case Command::Draw: { - DrawCmd* draw = mCommands.NextCommand(); + DrawCmd* draw = iter->NextCommand(); - FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); + DAWN_TRY(bindingTracker->Apply(commandContext)); + vertexBufferTracker.Apply(commandList, lastPipeline); commandList->DrawInstanced(draw->vertexCount, draw->instanceCount, draw->firstVertex, draw->firstInstance); - } break; + break; + } case Command::DrawIndexed: { - DrawIndexedCmd* draw = mCommands.NextCommand(); + DrawIndexedCmd* draw = iter->NextCommand(); - FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); + DAWN_TRY(bindingTracker->Apply(commandContext)); + indexBufferTracker.Apply(commandList); + vertexBufferTracker.Apply(commandList, lastPipeline); commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount, draw->firstIndex, draw->baseVertex, draw->firstInstance); - } break; + break; + } case Command::DrawIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); - FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); + DAWN_TRY(bindingTracker->Apply(commandContext)); + vertexBufferTracker.Apply(commandList, lastPipeline); Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); ComPtr signature = ToBackend(GetDevice())->GetDrawIndirectSignature(); - commandList->ExecuteIndirect(signature.Get(), 1, - buffer->GetD3D12Resource().Get(), + commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource(), draw->indirectOffset, nullptr, 0); - } break; + break; + } case Command::DrawIndexedIndirect: { - DrawIndexedIndirectCmd* draw = mCommands.NextCommand(); + DrawIndexedIndirectCmd* draw = iter->NextCommand(); - FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline); + DAWN_TRY(bindingTracker->Apply(commandContext)); + indexBufferTracker.Apply(commandList); + vertexBufferTracker.Apply(commandList, lastPipeline); Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); ComPtr signature = ToBackend(GetDevice())->GetDrawIndexedIndirectSignature(); - commandList->ExecuteIndirect(signature.Get(), 1, - buffer->GetD3D12Resource().Get(), + commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource(), draw->indirectOffset, nullptr, 0); - } break; + break; + } case Command::InsertDebugMarker: { - InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); - const char* label = mCommands.NextData(cmd->length + 1); + InsertDebugMarkerCmd* cmd = iter->NextCommand(); + const char* label = iter->NextData(cmd->length + 1); - if (ToBackend(GetDevice())->GetFunctions()->isPIXEventRuntimeLoaded()) { + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { // PIX color is 1 byte per channel in ARGB format constexpr uint64_t kPIXBlackColor = 0xff000000; ToBackend(GetDevice()) ->GetFunctions() - ->pixSetMarkerOnCommandList(commandList.Get(), kPIXBlackColor, label); + ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label); } - } break; + break; + } case Command::PopDebugGroup: { - mCommands.NextCommand(); + iter->NextCommand(); - if (ToBackend(GetDevice())->GetFunctions()->isPIXEventRuntimeLoaded()) { + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { ToBackend(GetDevice()) ->GetFunctions() - ->pixEndEventOnCommandList(commandList.Get()); + ->pixEndEventOnCommandList(commandList); } - } break; + break; + } case Command::PushDebugGroup: { - PushDebugGroupCmd* cmd = mCommands.NextCommand(); - const char* label = mCommands.NextData(cmd->length + 1); + PushDebugGroupCmd* cmd = iter->NextCommand(); + const char* label = iter->NextData(cmd->length + 1); - if (ToBackend(GetDevice())->GetFunctions()->isPIXEventRuntimeLoaded()) { + if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { // PIX color is 1 byte per channel in ARGB format constexpr uint64_t kPIXBlackColor = 0xff000000; ToBackend(GetDevice()) ->GetFunctions() - ->pixBeginEventOnCommandList(commandList.Get(), kPIXBlackColor, label); + ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label); } - } break; + break; + } case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mCommands.NextCommand(); + SetRenderPipelineCmd* cmd = iter->NextCommand(); RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); PipelineLayout* layout = ToBackend(pipeline->GetLayout()); - commandList->SetGraphicsRootSignature(layout->GetRootSignature().Get()); - commandList->SetPipelineState(pipeline->GetPipelineState().Get()); + commandList->SetGraphicsRootSignature(layout->GetRootSignature()); + commandList->SetPipelineState(pipeline->GetPipelineState()); commandList->IASetPrimitiveTopology(pipeline->GetD3D12PrimitiveTopology()); - bindingTracker->SetInheritedBindGroups(commandList, lastLayout, layout); + bindingTracker->OnSetPipeline(pipeline); + indexBufferTracker.OnSetPipeline(pipeline); lastPipeline = pipeline; lastLayout = layout; - } break; + break; + } + + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = iter->NextCommand(); + BindGroup* group = ToBackend(cmd->group.Get()); + uint32_t* dynamicOffsets = nullptr; + + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); + } + + bindingTracker->OnSetBindGroup(cmd->index, group, cmd->dynamicOffsetCount, + dynamicOffsets); + break; + } + + case Command::SetIndexBuffer: { + SetIndexBufferCmd* cmd = iter->NextCommand(); + + indexBufferTracker.OnSetIndexBuffer(ToBackend(cmd->buffer.Get()), cmd->offset, + cmd->size); + break; + } + + case Command::SetVertexBuffer: { + SetVertexBufferCmd* cmd = iter->NextCommand(); + + vertexBufferTracker.OnSetVertexBuffer(cmd->slot, ToBackend(cmd->buffer.Get()), + cmd->offset, cmd->size); + break; + } + + default: + UNREACHABLE(); + break; + } + return {}; + }; + + Command type; + while (mCommands.NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + mCommands.NextCommand(); + if (useRenderPass) { + commandContext->GetCommandList4()->EndRenderPass(); + } else if (renderPass->attachmentState->GetSampleCount() > 1) { + ResolveMultisampledRenderPass(commandContext, renderPass); + } + return {}; + } case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); commandList->OMSetStencilRef(cmd->reference); - } break; + break; + } + + case Command::SetViewport: { + SetViewportCmd* cmd = mCommands.NextCommand(); + D3D12_VIEWPORT viewport; + viewport.TopLeftX = cmd->x; + viewport.TopLeftY = cmd->y; + viewport.Width = cmd->width; + viewport.Height = cmd->height; + viewport.MinDepth = cmd->minDepth; + viewport.MaxDepth = cmd->maxDepth; + + commandList->RSSetViewports(1, &viewport); + break; + } case Command::SetScissorRect: { SetScissorRectCmd* cmd = mCommands.NextCommand(); @@ -919,58 +1355,39 @@ namespace dawn_native { namespace d3d12 { rect.bottom = cmd->y + cmd->height; commandList->RSSetScissorRects(1, &rect); - } break; + break; + } case Command::SetBlendColor: { SetBlendColorCmd* cmd = mCommands.NextCommand(); commandList->OMSetBlendFactor(static_cast(&cmd->color.r)); - } break; - - case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mCommands.NextCommand(); - BindGroup* group = ToBackend(cmd->group.Get()); - bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index); - } break; + break; + } - case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mCommands.NextCommand(); - - Buffer* buffer = ToBackend(cmd->buffer.Get()); - D3D12_INDEX_BUFFER_VIEW bufferView; - bufferView.BufferLocation = buffer->GetVA() + cmd->offset; - bufferView.SizeInBytes = buffer->GetSize() - cmd->offset; - // TODO(cwallez@chromium.org): Make index buffers lazily applied, right now - // this will break if the pipeline is changed for one with a different index - // format after SetIndexBuffer - bufferView.Format = - DXGIIndexFormat(lastPipeline->GetVertexInputDescriptor()->indexFormat); - - commandList->IASetIndexBuffer(&bufferView); - } break; - - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mCommands.NextCommand(); - auto buffers = mCommands.NextData>(cmd->count); - auto offsets = mCommands.NextData(cmd->count); - - vertexBuffersInfo.startSlot = - std::min(vertexBuffersInfo.startSlot, cmd->startSlot); - vertexBuffersInfo.endSlot = - std::max(vertexBuffersInfo.endSlot, cmd->startSlot + cmd->count); + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = mCommands.NextCommand(); + auto bundles = mCommands.NextData>(cmd->count); for (uint32_t i = 0; i < cmd->count; ++i) { - Buffer* buffer = ToBackend(buffers[i].Get()); - auto* d3d12BufferView = - &vertexBuffersInfo.d3d12BufferViews[cmd->startSlot + i]; - d3d12BufferView->BufferLocation = buffer->GetVA() + offsets[i]; - d3d12BufferView->SizeInBytes = buffer->GetSize() - offsets[i]; - // The bufferView stride is set based on the input state before a draw. + CommandIterator* iter = bundles[i]->GetCommands(); + iter->Reset(); + while (iter->NextCommandId(&type)) { + DAWN_TRY(EncodeRenderBundleCommand(iter, type)); + } } - } break; + break; + } - default: { UNREACHABLE(); } break; + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } + + default: { + DAWN_TRY(EncodeRenderBundleCommand(&mCommands, type)); + break; + } } } + return {}; } - }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/CommandBufferD3D12.h b/third_party/dawn/src/dawn_native/d3d12/CommandBufferD3D12.h index ed508858b48..cc53fd54cf9 100644 --- a/third_party/dawn/src/dawn_native/d3d12/CommandBufferD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/CommandBufferD3D12.h @@ -18,9 +18,8 @@ #include "common/Constants.h" #include "dawn_native/CommandAllocator.h" #include "dawn_native/CommandBuffer.h" - +#include "dawn_native/Error.h" #include "dawn_native/d3d12/Forward.h" -#include "dawn_native/d3d12/d3d12_platform.h" #include @@ -31,38 +30,31 @@ namespace dawn_native { namespace dawn_native { namespace d3d12 { class BindGroupStateTracker; + class CommandRecordingContext; class Device; class RenderPassDescriptorHeapTracker; + class RenderPassBuilder; class RenderPipeline; - struct VertexBuffersInfo { - // startSlot and endSlot indicate the range of dirty vertex buffers. - // If there are multiple calls to SetVertexBuffers, the start and end - // represent the union of the dirty ranges (the union may have non-dirty - // data in the middle of the range). - const RenderPipeline* lastRenderPipeline = nullptr; - uint32_t startSlot = kMaxVertexBuffers; - uint32_t endSlot = 0; - std::array d3d12BufferViews = {}; - }; - - class CommandBuffer : public CommandBufferBase { + class CommandBuffer final : public CommandBufferBase { public: - CommandBuffer(Device* device, CommandEncoderBase* encoder); - ~CommandBuffer(); + CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor); - void RecordCommands(ComPtr commandList, uint32_t indexInSubmit); + MaybeError RecordCommands(CommandRecordingContext* commandContext); private: - void FlushSetVertexBuffers(ComPtr commandList, - VertexBuffersInfo* vertexBuffersInfo, - const RenderPipeline* lastRenderPipeline); - void RecordComputePass(ComPtr commandList, - BindGroupStateTracker* bindingTracker); - void RecordRenderPass(ComPtr commandList, - BindGroupStateTracker* bindingTracker, - RenderPassDescriptorHeapTracker* renderPassTracker, - BeginRenderPassCmd* renderPass); + ~CommandBuffer() override; + MaybeError RecordComputePass(CommandRecordingContext* commandContext, + BindGroupStateTracker* bindingTracker); + MaybeError RecordRenderPass(CommandRecordingContext* commandContext, + BindGroupStateTracker* bindingTracker, + BeginRenderPassCmd* renderPass, + bool passHasUAV); + MaybeError SetupRenderPass(CommandRecordingContext* commandContext, + BeginRenderPassCmd* renderPass, + RenderPassBuilder* renderPassBuilder); + void EmulateBeginRenderPass(CommandRecordingContext* commandContext, + const RenderPassBuilder* renderPassBuilder) const; CommandIterator mCommands; }; diff --git a/third_party/dawn/src/dawn_native/d3d12/CommandRecordingContext.cpp b/third_party/dawn/src/dawn_native/d3d12/CommandRecordingContext.cpp new file mode 100644 index 00000000000..d652d878e6b --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/CommandRecordingContext.cpp @@ -0,0 +1,121 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "dawn_native/d3d12/CommandRecordingContext.h" +#include "dawn_native/d3d12/CommandAllocatorManager.h" +#include "dawn_native/d3d12/D3D12Error.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/HeapD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" + +namespace dawn_native { namespace d3d12 { + + void CommandRecordingContext::AddToSharedTextureList(Texture* texture) { + ASSERT(IsOpen()); + mSharedTextures.insert(texture); + } + + MaybeError CommandRecordingContext::Open(ID3D12Device* d3d12Device, + CommandAllocatorManager* commandAllocationManager) { + ASSERT(!IsOpen()); + ID3D12CommandAllocator* commandAllocator; + DAWN_TRY_ASSIGN(commandAllocator, commandAllocationManager->ReserveCommandAllocator()); + if (mD3d12CommandList != nullptr) { + MaybeError error = CheckHRESULT(mD3d12CommandList->Reset(commandAllocator, nullptr), + "D3D12 resetting command list"); + if (error.IsError()) { + mD3d12CommandList.Reset(); + DAWN_TRY(std::move(error)); + } + } else { + ComPtr d3d12GraphicsCommandList; + DAWN_TRY(CheckHRESULT( + d3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator, + nullptr, IID_PPV_ARGS(&d3d12GraphicsCommandList)), + "D3D12 creating direct command list")); + mD3d12CommandList = std::move(d3d12GraphicsCommandList); + // Store a cast to ID3D12GraphicsCommandList4. This is required to use the D3D12 render + // pass APIs introduced in Windows build 1809. + mD3d12CommandList.As(&mD3d12CommandList4); + } + + mIsOpen = true; + + return {}; + } + + MaybeError CommandRecordingContext::ExecuteCommandList(Device* device) { + if (IsOpen()) { + // Shared textures must be transitioned to common state after the last usage in order + // for them to be used by other APIs like D3D11. We ensure this by transitioning to the + // common state right before command list submission. TransitionUsageNow itself ensures + // no unnecessary transitions happen if the resources is already in the common state. + for (Texture* texture : mSharedTextures) { + texture->TrackAllUsageAndTransitionNow(this, D3D12_RESOURCE_STATE_COMMON); + } + + MaybeError error = + CheckHRESULT(mD3d12CommandList->Close(), "D3D12 closing pending command list"); + if (error.IsError()) { + Release(); + DAWN_TRY(std::move(error)); + } + DAWN_TRY(device->GetResidencyManager()->EnsureHeapsAreResident( + mHeapsPendingUsage.data(), mHeapsPendingUsage.size())); + + ID3D12CommandList* d3d12CommandList = GetCommandList(); + device->GetCommandQueue()->ExecuteCommandLists(1, &d3d12CommandList); + + mIsOpen = false; + mSharedTextures.clear(); + mHeapsPendingUsage.clear(); + } + return {}; + } + + void CommandRecordingContext::TrackHeapUsage(Heap* heap, Serial serial) { + // Before tracking the heap, check the last serial it was recorded on to ensure we aren't + // tracking it more than once. + if (heap->GetLastUsage() < serial) { + heap->SetLastUsage(serial); + mHeapsPendingUsage.push_back(heap); + } + } + + ID3D12GraphicsCommandList* CommandRecordingContext::GetCommandList() const { + ASSERT(mD3d12CommandList != nullptr); + ASSERT(IsOpen()); + return mD3d12CommandList.Get(); + } + + // This function will fail on Windows versions prior to 1809. Support must be queried through + // the device before calling. + ID3D12GraphicsCommandList4* CommandRecordingContext::GetCommandList4() const { + ASSERT(IsOpen()); + ASSERT(mD3d12CommandList.Get() != nullptr); + return mD3d12CommandList4.Get(); + } + + void CommandRecordingContext::Release() { + mD3d12CommandList.Reset(); + mD3d12CommandList4.Reset(); + mIsOpen = false; + mSharedTextures.clear(); + mHeapsPendingUsage.clear(); + } + + bool CommandRecordingContext::IsOpen() const { + return mIsOpen; + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/CommandRecordingContext.h b/third_party/dawn/src/dawn_native/d3d12/CommandRecordingContext.h new file mode 100644 index 00000000000..932fa4d7bfb --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/CommandRecordingContext.h @@ -0,0 +1,51 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef DAWNNATIVE_D3D12_COMMANDRECORDINGCONTEXT_H_ +#define DAWNNATIVE_D3D12_COMMANDRECORDINGCONTEXT_H_ + +#include "dawn_native/Error.h" +#include "dawn_native/d3d12/TextureD3D12.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +#include + +namespace dawn_native { namespace d3d12 { + class CommandAllocatorManager; + class Texture; + + class CommandRecordingContext { + public: + void AddToSharedTextureList(Texture* texture); + MaybeError Open(ID3D12Device* d3d12Device, + CommandAllocatorManager* commandAllocationManager); + + ID3D12GraphicsCommandList* GetCommandList() const; + ID3D12GraphicsCommandList4* GetCommandList4() const; + void Release(); + bool IsOpen() const; + + MaybeError ExecuteCommandList(Device* device); + + void TrackHeapUsage(Heap* heap, Serial serial); + + private: + ComPtr mD3d12CommandList; + ComPtr mD3d12CommandList4; + bool mIsOpen = false; + std::set mSharedTextures; + std::vector mHeapsPendingUsage; + }; +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_COMMANDRECORDINGCONTEXT_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/ComputePipelineD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/ComputePipelineD3D12.cpp index d70846ea099..0c9fc6f6b00 100644 --- a/third_party/dawn/src/dawn_native/d3d12/ComputePipelineD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/ComputePipelineD3D12.cpp @@ -19,11 +19,20 @@ #include "dawn_native/d3d12/PipelineLayoutD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" #include "dawn_native/d3d12/ShaderModuleD3D12.h" +#include "dawn_native/d3d12/UtilsD3D12.h" namespace dawn_native { namespace d3d12 { - ComputePipeline::ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor) - : ComputePipelineBase(device, descriptor) { + ResultOrError ComputePipeline::Create( + Device* device, + const ComputePipelineDescriptor* descriptor) { + Ref pipeline = AcquireRef(new ComputePipeline(device, descriptor)); + DAWN_TRY(pipeline->Initialize(descriptor)); + return pipeline.Detach(); + } + + MaybeError ComputePipeline::Initialize(const ComputePipelineDescriptor* descriptor) { + Device* device = ToBackend(GetDevice()); uint32_t compileFlags = 0; #if defined(_DEBUG) // Enable better shader debugging with the graphics debugging tools. @@ -32,35 +41,44 @@ namespace dawn_native { namespace d3d12 { // SPRIV-cross does matrix multiplication expecting row major matrices compileFlags |= D3DCOMPILE_PACK_MATRIX_ROW_MAJOR; - const ShaderModule* module = ToBackend(descriptor->computeStage->module); - const std::string& hlslSource = module->GetHLSLSource(ToBackend(GetLayout())); + ShaderModule* module = ToBackend(descriptor->computeStage.module); + std::string hlslSource; + DAWN_TRY_ASSIGN(hlslSource, module->GetHLSLSource(ToBackend(GetLayout()))); - ComPtr compiledShader; - ComPtr errors; + D3D12_COMPUTE_PIPELINE_STATE_DESC d3dDesc = {}; + d3dDesc.pRootSignature = ToBackend(GetLayout())->GetRootSignature(); - const PlatformFunctions* functions = device->GetFunctions(); - if (FAILED(functions->d3dCompile(hlslSource.c_str(), hlslSource.length(), nullptr, nullptr, - nullptr, descriptor->computeStage->entryPoint, "cs_5_1", - compileFlags, 0, &compiledShader, &errors))) { - printf("%s\n", reinterpret_cast(errors->GetBufferPointer())); - ASSERT(false); - } + ComPtr compiledDXCShader; + ComPtr compiledFXCShader; - D3D12_COMPUTE_PIPELINE_STATE_DESC d3dDesc = {}; - d3dDesc.pRootSignature = ToBackend(GetLayout())->GetRootSignature().Get(); - d3dDesc.CS.pShaderBytecode = compiledShader->GetBufferPointer(); - d3dDesc.CS.BytecodeLength = compiledShader->GetBufferSize(); + if (device->IsToggleEnabled(Toggle::UseDXC)) { + DAWN_TRY_ASSIGN( + compiledDXCShader, + module->CompileShaderDXC(SingleShaderStage::Compute, hlslSource, + descriptor->computeStage.entryPoint, compileFlags)); + + d3dDesc.CS.pShaderBytecode = compiledDXCShader->GetBufferPointer(); + d3dDesc.CS.BytecodeLength = compiledDXCShader->GetBufferSize(); + } else { + DAWN_TRY_ASSIGN( + compiledFXCShader, + module->CompileShaderFXC(SingleShaderStage::Compute, hlslSource, + descriptor->computeStage.entryPoint, compileFlags)); + d3dDesc.CS.pShaderBytecode = compiledFXCShader->GetBufferPointer(); + d3dDesc.CS.BytecodeLength = compiledFXCShader->GetBufferSize(); + } device->GetD3D12Device()->CreateComputePipelineState(&d3dDesc, IID_PPV_ARGS(&mPipelineState)); + return {}; } ComputePipeline::~ComputePipeline() { ToBackend(GetDevice())->ReferenceUntilUnused(mPipelineState); } - ComPtr ComputePipeline::GetPipelineState() { - return mPipelineState; + ID3D12PipelineState* ComputePipeline::GetPipelineState() const { + return mPipelineState.Get(); } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/ComputePipelineD3D12.h b/third_party/dawn/src/dawn_native/d3d12/ComputePipelineD3D12.h index 7b1af9f6705..7d05f0b3c1c 100644 --- a/third_party/dawn/src/dawn_native/d3d12/ComputePipelineD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/ComputePipelineD3D12.h @@ -23,14 +23,18 @@ namespace dawn_native { namespace d3d12 { class Device; - class ComputePipeline : public ComputePipelineBase { + class ComputePipeline final : public ComputePipelineBase { public: - ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor); - ~ComputePipeline(); + static ResultOrError Create(Device* device, + const ComputePipelineDescriptor* descriptor); + ComputePipeline() = delete; - ComPtr GetPipelineState(); + ID3D12PipelineState* GetPipelineState() const; private: + ~ComputePipeline() override; + using ComputePipelineBase::ComputePipelineBase; + MaybeError Initialize(const ComputePipelineDescriptor* descriptor); ComPtr mPipelineState; }; diff --git a/third_party/dawn/src/dawn_native/d3d12/D3D12Backend.cpp b/third_party/dawn/src/dawn_native/d3d12/D3D12Backend.cpp index 36e766151dc..71965994265 100644 --- a/third_party/dawn/src/dawn_native/d3d12/D3D12Backend.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/D3D12Backend.cpp @@ -20,23 +20,56 @@ #include "common/SwapChainUtils.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/NativeSwapChainImplD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" +#include "dawn_native/d3d12/TextureD3D12.h" namespace dawn_native { namespace d3d12 { - DawnSwapChainImplementation CreateNativeSwapChainImpl(DawnDevice device, HWND window) { + ComPtr GetD3D12Device(WGPUDevice device) { + Device* backendDevice = reinterpret_cast(device); + + return backendDevice->GetD3D12Device(); + } + + DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device, HWND window) { Device* backendDevice = reinterpret_cast(device); DawnSwapChainImplementation impl; impl = CreateSwapChainImplementation(new NativeSwapChainImpl(backendDevice, window)); - impl.textureUsage = DAWN_TEXTURE_USAGE_BIT_PRESENT; + impl.textureUsage = WGPUTextureUsage_Present; return impl; } - DawnTextureFormat GetNativeSwapChainPreferredFormat( + WGPUTextureFormat GetNativeSwapChainPreferredFormat( const DawnSwapChainImplementation* swapChain) { NativeSwapChainImpl* impl = reinterpret_cast(swapChain->userData); - return static_cast(impl->GetPreferredFormat()); + return static_cast(impl->GetPreferredFormat()); + } + + ExternalImageDescriptorDXGISharedHandle::ExternalImageDescriptorDXGISharedHandle() + : ExternalImageDescriptor(ExternalImageDescriptorType::DXGISharedHandle) { + } + + uint64_t SetExternalMemoryReservation(WGPUDevice device, + uint64_t requestedReservationSize, + MemorySegment memorySegment) { + Device* backendDevice = reinterpret_cast(device); + + return backendDevice->GetResidencyManager()->SetExternalMemoryReservation( + memorySegment, requestedReservationSize); } + WGPUTexture WrapSharedHandle(WGPUDevice device, + const ExternalImageDescriptorDXGISharedHandle* descriptor) { + Device* backendDevice = reinterpret_cast(device); + Ref texture = backendDevice->WrapSharedHandle( + descriptor, descriptor->sharedHandle, descriptor->acquireMutexKey, + descriptor->isSwapChainTexture); + return reinterpret_cast(texture.Detach()); + } + + AdapterDiscoveryOptions::AdapterDiscoveryOptions(ComPtr adapter) + : AdapterDiscoveryOptionsBase(WGPUBackendType_D3D12), dxgiAdapter(std::move(adapter)) { + } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/D3D12Error.cpp b/third_party/dawn/src/dawn_native/d3d12/D3D12Error.cpp new file mode 100644 index 00000000000..efc9fe09bad --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/D3D12Error.cpp @@ -0,0 +1,51 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/D3D12Error.h" + +#include +#include +#include + +namespace dawn_native { namespace d3d12 { + MaybeError CheckHRESULTImpl(HRESULT result, const char* context) { + if (DAWN_LIKELY(SUCCEEDED(result))) { + return {}; + } + + std::ostringstream messageStream; + messageStream << context << " failed with "; + if (result == E_FAKE_ERROR_FOR_TESTING) { + messageStream << "E_FAKE_ERROR_FOR_TESTING"; + } else { + messageStream << "0x" << std::uppercase << std::setfill('0') << std::setw(8) << std::hex + << result; + } + + if (result == DXGI_ERROR_DEVICE_REMOVED) { + return DAWN_DEVICE_LOST_ERROR(messageStream.str()); + } else { + return DAWN_INTERNAL_ERROR(messageStream.str()); + } + } + + MaybeError CheckOutOfMemoryHRESULTImpl(HRESULT result, const char* context) { + if (result == E_OUTOFMEMORY || result == E_FAKE_OUTOFMEMORY_ERROR_FOR_TESTING) { + return DAWN_OUT_OF_MEMORY_ERROR(context); + } + + return CheckHRESULTImpl(result, context); + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/D3D12Error.h b/third_party/dawn/src/dawn_native/d3d12/D3D12Error.h new file mode 100644 index 00000000000..ade06fe62e6 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/D3D12Error.h @@ -0,0 +1,45 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_D3D12ERROR_H_ +#define DAWNNATIVE_D3D12_D3D12ERROR_H_ + +#include +#include "dawn_native/Error.h" +#include "dawn_native/ErrorInjector.h" + +namespace dawn_native { namespace d3d12 { + + constexpr HRESULT E_FAKE_ERROR_FOR_TESTING = MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFF); + constexpr HRESULT E_FAKE_OUTOFMEMORY_ERROR_FOR_TESTING = + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0xFE); + + // Returns a success only if result of HResult is success + MaybeError CheckHRESULTImpl(HRESULT result, const char* context); + + // Uses CheckRESULT but returns OOM specific error when recoverable. + MaybeError CheckOutOfMemoryHRESULTImpl(HRESULT result, const char* context); + +#define CheckHRESULT(resultIn, contextIn) \ + ::dawn_native::d3d12::CheckHRESULTImpl( \ + INJECT_ERROR_OR_RUN(resultIn, E_FAKE_ERROR_FOR_TESTING), contextIn) +#define CheckOutOfMemoryHRESULT(resultIn, contextIn) \ + ::dawn_native::d3d12::CheckOutOfMemoryHRESULTImpl( \ + INJECT_ERROR_OR_RUN(resultIn, E_FAKE_OUTOFMEMORY_ERROR_FOR_TESTING, \ + E_FAKE_ERROR_FOR_TESTING), \ + contextIn) + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_D3D12ERROR_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/D3D12Info.cpp b/third_party/dawn/src/dawn_native/d3d12/D3D12Info.cpp index ac5e5269ec0..2ca6429b0cd 100644 --- a/third_party/dawn/src/dawn_native/d3d12/D3D12Info.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/D3D12Info.cpp @@ -14,9 +14,10 @@ #include "dawn_native/d3d12/D3D12Info.h" -#include "dawn_native/D3D12/AdapterD3D12.h" -#include "dawn_native/D3D12/BackendD3D12.h" - +#include "common/GPUInfo.h" +#include "dawn_native/d3d12/AdapterD3D12.h" +#include "dawn_native/d3d12/BackendD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/PlatformFunctions.h" namespace dawn_native { namespace d3d12 { @@ -24,21 +25,97 @@ namespace dawn_native { namespace d3d12 { ResultOrError GatherDeviceInfo(const Adapter& adapter) { D3D12DeviceInfo info = {}; - // Gather info about device memory - { - // Newer builds replace D3D_FEATURE_DATA_ARCHITECTURE with - // D3D_FEATURE_DATA_ARCHITECTURE1. However, D3D_FEATURE_DATA_ARCHITECTURE can be used - // for backwards compat. - // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_feature - D3D12_FEATURE_DATA_ARCHITECTURE arch = {}; - if (FAILED(adapter.GetDevice()->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, &arch, - sizeof(arch)))) { - return DAWN_CONTEXT_LOST_ERROR("CheckFeatureSupport failed"); + // Newer builds replace D3D_FEATURE_DATA_ARCHITECTURE with + // D3D_FEATURE_DATA_ARCHITECTURE1. However, D3D_FEATURE_DATA_ARCHITECTURE can be used + // for backwards compat. + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_feature + D3D12_FEATURE_DATA_ARCHITECTURE arch = {}; + DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE, + &arch, sizeof(arch)), + "ID3D12Device::CheckFeatureSupport")); + + info.isUMA = arch.UMA; + + D3D12_FEATURE_DATA_D3D12_OPTIONS options = {}; + DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS, + &options, sizeof(options)), + "ID3D12Device::CheckFeatureSupport")); + + info.resourceHeapTier = options.ResourceHeapTier; + + // Windows builds 1809 and above can use the D3D12 render pass API. If we query + // CheckFeatureSupport for D3D12_FEATURE_D3D12_OPTIONS5 successfully, then we can use + // the render pass API. + info.supportsRenderPass = false; + D3D12_FEATURE_DATA_D3D12_OPTIONS5 featureOptions5 = {}; + if (SUCCEEDED(adapter.GetDevice()->CheckFeatureSupport( + D3D12_FEATURE_D3D12_OPTIONS5, &featureOptions5, sizeof(featureOptions5)))) { + // Performance regressions been observed when using a render pass on Intel graphics with + // RENDER_PASS_TIER_1 available, so fall back to a software emulated render pass on + // these platforms. + if (featureOptions5.RenderPassesTier < D3D12_RENDER_PASS_TIER_1 || + !gpu_info::IsIntel(adapter.GetPCIInfo().vendorId)) { + info.supportsRenderPass = true; } + } + + D3D12_FEATURE_DATA_SHADER_MODEL knownShaderModels[] = {{D3D_SHADER_MODEL_6_2}, + {D3D_SHADER_MODEL_6_1}, + {D3D_SHADER_MODEL_6_0}, + {D3D_SHADER_MODEL_5_1}}; + for (D3D12_FEATURE_DATA_SHADER_MODEL shaderModel : knownShaderModels) { + if (SUCCEEDED(adapter.GetDevice()->CheckFeatureSupport( + D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof(shaderModel)))) { + if (shaderModel.HighestShaderModel < D3D_SHADER_MODEL_5_1) { + return DAWN_INTERNAL_ERROR( + "Driver could not support Shader Model 5.1 or higher"); + } + + switch (shaderModel.HighestShaderModel) { + case D3D_SHADER_MODEL_6_2: { + info.shaderModel = 62; + info.shaderProfiles[SingleShaderStage::Vertex] = L"vs_6_2"; + info.shaderProfiles[SingleShaderStage::Fragment] = L"ps_6_2"; + info.shaderProfiles[SingleShaderStage::Compute] = L"cs_6_2"; - info.isUMA = arch.UMA; + D3D12_FEATURE_DATA_D3D12_OPTIONS4 featureData4 = {}; + if (SUCCEEDED(adapter.GetDevice()->CheckFeatureSupport( + D3D12_FEATURE_D3D12_OPTIONS4, &featureData4, + sizeof(featureData4)))) { + info.supportsShaderFloat16 = + shaderModel.HighestShaderModel >= D3D_SHADER_MODEL_6_2 && + featureData4.Native16BitShaderOpsSupported; + } + break; + } + case D3D_SHADER_MODEL_6_1: { + info.shaderModel = 61; + info.shaderProfiles[SingleShaderStage::Vertex] = L"vs_6_1"; + info.shaderProfiles[SingleShaderStage::Fragment] = L"ps_6_1"; + info.shaderProfiles[SingleShaderStage::Compute] = L"cs_6_1"; + break; + } + case D3D_SHADER_MODEL_6_0: { + info.shaderModel = 60; + info.shaderProfiles[SingleShaderStage::Vertex] = L"vs_6_0"; + info.shaderProfiles[SingleShaderStage::Fragment] = L"ps_6_0"; + info.shaderProfiles[SingleShaderStage::Compute] = L"cs_6_0"; + break; + } + default: { + info.shaderModel = 51; + info.shaderProfiles[SingleShaderStage::Vertex] = L"vs_5_1"; + info.shaderProfiles[SingleShaderStage::Fragment] = L"ps_5_1"; + info.shaderProfiles[SingleShaderStage::Compute] = L"cs_5_1"; + break; + } + } + + // Successfully find the maximum supported shader model. + break; + } } - return info; + return std::move(info); } -}} // namespace dawn_native::d3d12 \ No newline at end of file +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/D3D12Info.h b/third_party/dawn/src/dawn_native/d3d12/D3D12Info.h index 11be2d31ba9..51134df380b 100644 --- a/third_party/dawn/src/dawn_native/d3d12/D3D12Info.h +++ b/third_party/dawn/src/dawn_native/d3d12/D3D12Info.h @@ -16,6 +16,7 @@ #define DAWNNATIVE_D3D12_D3D12INFO_H_ #include "dawn_native/Error.h" +#include "dawn_native/PerStage.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { @@ -24,9 +25,16 @@ namespace dawn_native { namespace d3d12 { struct D3D12DeviceInfo { bool isUMA; + uint32_t resourceHeapTier; + bool supportsRenderPass; + bool supportsShaderFloat16; + // shaderModel indicates the maximum supported shader model, for example, the value 62 + // indicates that current driver supports the maximum shader model is D3D_SHADER_MODEL_6_2. + uint32_t shaderModel; + PerStage shaderProfiles; }; ResultOrError GatherDeviceInfo(const Adapter& adapter); }} // namespace dawn_native::d3d12 -#endif // DAWNNATIVE_D3D12_D3D12INFO_H_ \ No newline at end of file +#endif // DAWNNATIVE_D3D12_D3D12INFO_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/DeviceD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/DeviceD3D12.cpp index 80971692476..7b73ccfdb1d 100644 --- a/third_party/dawn/src/dawn_native/d3d12/DeviceD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/DeviceD3D12.cpp @@ -16,7 +16,8 @@ #include "common/Assert.h" #include "dawn_native/BackendConnection.h" -#include "dawn_native/DynamicUploader.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/Instance.h" #include "dawn_native/d3d12/AdapterD3D12.h" #include "dawn_native/d3d12/BackendD3D12.h" #include "dawn_native/d3d12/BindGroupD3D12.h" @@ -25,32 +26,43 @@ #include "dawn_native/d3d12/CommandAllocatorManager.h" #include "dawn_native/d3d12/CommandBufferD3D12.h" #include "dawn_native/d3d12/ComputePipelineD3D12.h" -#include "dawn_native/d3d12/DescriptorHeapAllocator.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/PipelineLayoutD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" +#include "dawn_native/d3d12/QuerySetD3D12.h" #include "dawn_native/d3d12/QueueD3D12.h" #include "dawn_native/d3d12/RenderPipelineD3D12.h" -#include "dawn_native/d3d12/ResourceAllocator.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" +#include "dawn_native/d3d12/ResourceAllocatorManagerD3D12.h" #include "dawn_native/d3d12/SamplerD3D12.h" +#include "dawn_native/d3d12/SamplerHeapCacheD3D12.h" #include "dawn_native/d3d12/ShaderModuleD3D12.h" +#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" #include "dawn_native/d3d12/StagingBufferD3D12.h" +#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" #include "dawn_native/d3d12/SwapChainD3D12.h" #include "dawn_native/d3d12/TextureD3D12.h" +#include + namespace dawn_native { namespace d3d12 { - void ASSERT_SUCCESS(HRESULT hr) { - ASSERT(SUCCEEDED(hr)); - } + // TODO(dawn:155): Figure out these values. + static constexpr uint16_t kShaderVisibleDescriptorHeapSize = 1024; + static constexpr uint8_t kAttachmentDescriptorHeapSize = 64; - Device::Device(Adapter* adapter, const DeviceDescriptor* descriptor) - : DeviceBase(adapter, descriptor) { - if (descriptor != nullptr) { - ApplyToggleOverrides(descriptor); - } + static constexpr uint64_t kMaxDebugMessagesToPrint = 5; + + // static + ResultOrError Device::Create(Adapter* adapter, const DeviceDescriptor* descriptor) { + Ref device = AcquireRef(new Device(adapter, descriptor)); + DAWN_TRY(device->Initialize()); + return device.Detach(); } MaybeError Device::Initialize() { + InitTogglesFromDriver(); + mD3d12Device = ToBackend(GetAdapter())->GetDevice(); ASSERT(mD3d12Device != nullptr); @@ -59,20 +71,57 @@ namespace dawn_native { namespace d3d12 { D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; - ASSERT_SUCCESS(mD3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue))); + DAWN_TRY( + CheckHRESULT(mD3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)), + "D3D12 create command queue")); + + // If PIX is not attached, the QueryInterface fails. Hence, no need to check the return + // value. + mCommandQueue.As(&mD3d12SharingContract); + + DAWN_TRY( + CheckHRESULT(mD3d12Device->CreateFence(GetLastSubmittedCommandSerial(), + D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&mFence)), + "D3D12 create fence")); - ASSERT_SUCCESS(mD3d12Device->CreateFence(mLastSubmittedSerial, D3D12_FENCE_FLAG_NONE, - IID_PPV_ARGS(&mFence))); mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); ASSERT(mFenceEvent != nullptr); // Initialize backend services mCommandAllocatorManager = std::make_unique(this); - mDescriptorHeapAllocator = std::make_unique(this); - mMapRequestTracker = std::make_unique(this); - mResourceAllocator = std::make_unique(this); - NextSerial(); + // Zero sized allocator is never requested and does not need to exist. + for (uint32_t countIndex = 0; countIndex < kNumViewDescriptorAllocators; countIndex++) { + mViewAllocators[countIndex + 1] = std::make_unique( + this, 1u << countIndex, kShaderVisibleDescriptorHeapSize, + D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + } + + for (uint32_t countIndex = 0; countIndex < kNumSamplerDescriptorAllocators; countIndex++) { + mSamplerAllocators[countIndex + 1] = std::make_unique( + this, 1u << countIndex, kShaderVisibleDescriptorHeapSize, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + } + + mRenderTargetViewAllocator = std::make_unique( + this, 1, kAttachmentDescriptorHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + + mDepthStencilViewAllocator = std::make_unique( + this, 1, kAttachmentDescriptorHeapSize, D3D12_DESCRIPTOR_HEAP_TYPE_DSV); + + mSamplerHeapCache = std::make_unique(this); + + mResidencyManager = std::make_unique(this); + mResourceAllocatorManager = std::make_unique(this); + + // ShaderVisibleDescriptorAllocators use the ResidencyManager and must be initialized after. + DAWN_TRY_ASSIGN( + mSamplerShaderVisibleDescriptorAllocator, + ShaderVisibleDescriptorAllocator::Create(this, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER)); + + DAWN_TRY_ASSIGN( + mViewShaderVisibleDescriptorAllocator, + ShaderVisibleDescriptorAllocator::Create(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); // Initialize indirect commands D3D12_INDIRECT_ARGUMENT_DESC argumentDesc = {}; @@ -83,59 +132,44 @@ namespace dawn_native { namespace d3d12 { programDesc.NumArgumentDescs = 1; programDesc.pArgumentDescs = &argumentDesc; - ToBackend(GetDevice()) - ->GetD3D12Device() - ->CreateCommandSignature(&programDesc, NULL, IID_PPV_ARGS(&mDispatchIndirectSignature)); + GetD3D12Device()->CreateCommandSignature(&programDesc, NULL, + IID_PPV_ARGS(&mDispatchIndirectSignature)); argumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW; programDesc.ByteStride = 4 * sizeof(uint32_t); - ToBackend(GetDevice()) - ->GetD3D12Device() - ->CreateCommandSignature(&programDesc, NULL, IID_PPV_ARGS(&mDrawIndirectSignature)); + GetD3D12Device()->CreateCommandSignature(&programDesc, NULL, + IID_PPV_ARGS(&mDrawIndirectSignature)); argumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED; programDesc.ByteStride = 5 * sizeof(uint32_t); - ToBackend(GetDevice()) - ->GetD3D12Device() - ->CreateCommandSignature(&programDesc, NULL, - IID_PPV_ARGS(&mDrawIndexedIndirectSignature)); + GetD3D12Device()->CreateCommandSignature(&programDesc, NULL, + IID_PPV_ARGS(&mDrawIndexedIndirectSignature)); + DAWN_TRY(DeviceBase::Initialize(new Queue(this))); + // Device shouldn't be used until after DeviceBase::Initialize so we must wait until after + // device initialization to call NextSerial + DAWN_TRY(NextSerial()); return {}; } Device::~Device() { - // Immediately forget about all pending commands - if (mPendingCommands.open) { - mPendingCommands.commandList->Close(); - mPendingCommands.open = false; - mPendingCommands.commandList = nullptr; - } - NextSerial(); - WaitForSerial(mLastSubmittedSerial); // Wait for all in-flight commands to finish executing - TickImpl(); // Call tick one last time so resources are cleaned up - - // Free services explicitly so that they can free D3D12 resources before destruction of the - // device. - mDynamicUploader = nullptr; - - // Releasing the uploader enqueues buffers to be released. - // Call Tick() again to clear them before releasing the allocator. - mResourceAllocator->Tick(mCompletedSerial); - - ASSERT(mUsedComObjectRefs.Empty()); - ASSERT(mPendingCommands.commandList == nullptr); + ShutDownBase(); } - ComPtr Device::GetD3D12Device() const { - return mD3d12Device; + ID3D12Device* Device::GetD3D12Device() const { + return mD3d12Device.Get(); } ComPtr Device::GetCommandQueue() const { return mCommandQueue; } + ID3D12SharingContract* Device::GetSharingContract() const { + return mD3d12SharingContract.Get(); + } + ComPtr Device::GetDispatchIndirectSignature() const { return mDispatchIndirectSignature; } @@ -148,155 +182,139 @@ namespace dawn_native { namespace d3d12 { return mDrawIndexedIndirectSignature; } - DescriptorHeapAllocator* Device::GetDescriptorHeapAllocator() const { - return mDescriptorHeapAllocator.get(); - } - ComPtr Device::GetFactory() const { return ToBackend(GetAdapter())->GetBackend()->GetFactory(); } - const PlatformFunctions* Device::GetFunctions() const { - return ToBackend(GetAdapter())->GetBackend()->GetFunctions(); + ResultOrError Device::GetOrCreateDxcLibrary() const { + return ToBackend(GetAdapter())->GetBackend()->GetOrCreateDxcLibrary(); } - MapRequestTracker* Device::GetMapRequestTracker() const { - return mMapRequestTracker.get(); + ResultOrError Device::GetOrCreateDxcCompiler() const { + return ToBackend(GetAdapter())->GetBackend()->GetOrCreateDxcCompiler(); } - ResourceAllocator* Device::GetResourceAllocator() const { - return mResourceAllocator.get(); + const PlatformFunctions* Device::GetFunctions() const { + return ToBackend(GetAdapter())->GetBackend()->GetFunctions(); } - void Device::OpenCommandList(ComPtr* commandList) { - ComPtr& cmdList = *commandList; - if (!cmdList) { - ASSERT_SUCCESS(mD3d12Device->CreateCommandList( - 0, D3D12_COMMAND_LIST_TYPE_DIRECT, - mCommandAllocatorManager->ReserveCommandAllocator().Get(), nullptr, - IID_PPV_ARGS(&cmdList))); - } else { - ASSERT_SUCCESS( - cmdList->Reset(mCommandAllocatorManager->ReserveCommandAllocator().Get(), nullptr)); - } + CommandAllocatorManager* Device::GetCommandAllocatorManager() const { + return mCommandAllocatorManager.get(); + } + + ResidencyManager* Device::GetResidencyManager() const { + return mResidencyManager.get(); } - ComPtr Device::GetPendingCommandList() { + ResultOrError Device::GetPendingCommandContext() { // Callers of GetPendingCommandList do so to record commands. Only reserve a command // allocator when it is needed so we don't submit empty command lists - if (!mPendingCommands.open) { - OpenCommandList(&mPendingCommands.commandList); - mPendingCommands.open = true; + if (!mPendingCommands.IsOpen()) { + DAWN_TRY(mPendingCommands.Open(mD3d12Device.Get(), mCommandAllocatorManager.get())); } - return mPendingCommands.commandList; - } - - Serial Device::GetCompletedCommandSerial() const { - return mCompletedSerial; - } - - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; + return &mPendingCommands; } - void Device::TickImpl() { + MaybeError Device::TickImpl() { // Perform cleanup operations to free unused objects - mCompletedSerial = mFence->GetCompletedValue(); + Serial completedSerial = GetCompletedCommandSerial(); - // Uploader should tick before the resource allocator - // as it enqueues resources to be released. - mDynamicUploader->Tick(mCompletedSerial); + mResourceAllocatorManager->Tick(completedSerial); + DAWN_TRY(mCommandAllocatorManager->Tick(completedSerial)); + mViewShaderVisibleDescriptorAllocator->Tick(completedSerial); + mSamplerShaderVisibleDescriptorAllocator->Tick(completedSerial); + mRenderTargetViewAllocator->Tick(completedSerial); + mDepthStencilViewAllocator->Tick(completedSerial); + mUsedComObjectRefs.ClearUpTo(completedSerial); + DAWN_TRY(ExecutePendingCommandContext()); + DAWN_TRY(NextSerial()); - mResourceAllocator->Tick(mCompletedSerial); - mCommandAllocatorManager->Tick(mCompletedSerial); - mDescriptorHeapAllocator->Tick(mCompletedSerial); - mMapRequestTracker->Tick(mCompletedSerial); - mUsedComObjectRefs.ClearUpTo(mCompletedSerial); - ExecuteCommandLists({}); - NextSerial(); + DAWN_TRY(CheckDebugLayerAndGenerateErrors()); + + return {}; } - void Device::NextSerial() { - mLastSubmittedSerial++; - ASSERT_SUCCESS(mCommandQueue->Signal(mFence.Get(), mLastSubmittedSerial)); + MaybeError Device::NextSerial() { + IncrementLastSubmittedCommandSerial(); + + return CheckHRESULT(mCommandQueue->Signal(mFence.Get(), GetLastSubmittedCommandSerial()), + "D3D12 command queue signal fence"); } - void Device::WaitForSerial(uint64_t serial) { - mCompletedSerial = mFence->GetCompletedValue(); - if (mCompletedSerial < serial) { - ASSERT_SUCCESS(mFence->SetEventOnCompletion(serial, mFenceEvent)); + MaybeError Device::WaitForSerial(uint64_t serial) { + CheckPassedSerials(); + if (GetCompletedCommandSerial() < serial) { + DAWN_TRY(CheckHRESULT(mFence->SetEventOnCompletion(serial, mFenceEvent), + "D3D12 set event on completion")); WaitForSingleObject(mFenceEvent, INFINITE); + CheckPassedSerials(); } + return {}; + } + + Serial Device::CheckAndUpdateCompletedSerials() { + return mFence->GetCompletedValue(); } void Device::ReferenceUntilUnused(ComPtr object) { mUsedComObjectRefs.Enqueue(object, GetPendingCommandSerial()); } - void Device::ExecuteCommandLists(std::initializer_list commandLists) { - // If there are pending commands, prepend them to ExecuteCommandLists - if (mPendingCommands.open) { - std::vector lists(commandLists.size() + 1); - mPendingCommands.commandList->Close(); - mPendingCommands.open = false; - lists[0] = mPendingCommands.commandList.Get(); - std::copy(commandLists.begin(), commandLists.end(), lists.begin() + 1); - mCommandQueue->ExecuteCommandLists(static_cast(commandLists.size() + 1), - lists.data()); - mPendingCommands.commandList = nullptr; - } else { - std::vector lists(commandLists); - mCommandQueue->ExecuteCommandLists(static_cast(commandLists.size()), - lists.data()); - } + MaybeError Device::ExecutePendingCommandContext() { + return mPendingCommands.ExecuteCommandList(this); } ResultOrError Device::CreateBindGroupImpl( const BindGroupDescriptor* descriptor) { - return new BindGroup(this, descriptor); + return BindGroup::Create(this, descriptor); } ResultOrError Device::CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) { return new BindGroupLayout(this, descriptor); } - ResultOrError Device::CreateBufferImpl(const BufferDescriptor* descriptor) { - return new Buffer(this, descriptor); + ResultOrError> Device::CreateBufferImpl(const BufferDescriptor* descriptor) { + Ref buffer = AcquireRef(new Buffer(this, descriptor)); + DAWN_TRY(buffer->Initialize()); + return std::move(buffer); } - CommandBufferBase* Device::CreateCommandBuffer(CommandEncoderBase* encoder) { - return new CommandBuffer(this, encoder); + CommandBufferBase* Device::CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) { + return new CommandBuffer(encoder, descriptor); } ResultOrError Device::CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) { - return new ComputePipeline(this, descriptor); + return ComputePipeline::Create(this, descriptor); } ResultOrError Device::CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) { - return new PipelineLayout(this, descriptor); + return PipelineLayout::Create(this, descriptor); } - ResultOrError Device::CreateQueueImpl() { - return new Queue(this); + ResultOrError Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) { + return QuerySet::Create(this, descriptor); } ResultOrError Device::CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) { - return new RenderPipeline(this, descriptor); + return RenderPipeline::Create(this, descriptor); } ResultOrError Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) { return new Sampler(this, descriptor); } ResultOrError Device::CreateShaderModuleImpl( const ShaderModuleDescriptor* descriptor) { - return new ShaderModule(this, descriptor); + return ShaderModule::Create(this, descriptor); } ResultOrError Device::CreateSwapChainImpl( const SwapChainDescriptor* descriptor) { return new SwapChain(this, descriptor); } - ResultOrError Device::CreateTextureImpl(const TextureDescriptor* descriptor) { - return new Texture(this, descriptor); + ResultOrError Device::CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) { + return DAWN_VALIDATION_ERROR("New swapchains not implemented."); + } + ResultOrError> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { + return Texture::Create(this, descriptor); } ResultOrError Device::CreateTextureViewImpl( TextureBase* texture, @@ -307,6 +325,7 @@ namespace dawn_native { namespace d3d12 { ResultOrError> Device::CreateStagingBuffer(size_t size) { std::unique_ptr stagingBuffer = std::make_unique(size, this); + DAWN_TRY(stagingBuffer->Initialize()); return std::move(stagingBuffer); } @@ -315,14 +334,254 @@ namespace dawn_native { namespace d3d12 { BufferBase* destination, uint64_t destinationOffset, uint64_t size) { - ToBackend(destination) - ->TransitionUsageNow(GetPendingCommandList(), dawn::BufferUsageBit::TransferDst); + CommandRecordingContext* commandRecordingContext; + DAWN_TRY_ASSIGN(commandRecordingContext, GetPendingCommandContext()); + + Buffer* dstBuffer = ToBackend(destination); + + DAWN_TRY(dstBuffer->EnsureDataInitializedAsDestination(commandRecordingContext, + destinationOffset, size)); + + CopyFromStagingToBufferImpl(commandRecordingContext, source, sourceOffset, destination, + destinationOffset, size); - GetPendingCommandList()->CopyBufferRegion( - ToBackend(destination)->GetD3D12Resource().Get(), destinationOffset, - ToBackend(source)->GetResource(), sourceOffset, size); + return {}; + } + + void Device::CopyFromStagingToBufferImpl(CommandRecordingContext* commandContext, + StagingBufferBase* source, + uint64_t sourceOffset, + BufferBase* destination, + uint64_t destinationOffset, + uint64_t size) { + ASSERT(commandContext != nullptr); + Buffer* dstBuffer = ToBackend(destination); + StagingBuffer* srcBuffer = ToBackend(source); + dstBuffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst); + + commandContext->GetCommandList()->CopyBufferRegion( + dstBuffer->GetD3D12Resource(), destinationOffset, srcBuffer->GetResource(), + sourceOffset, size); + } + + void Device::DeallocateMemory(ResourceHeapAllocation& allocation) { + mResourceAllocatorManager->DeallocateMemory(allocation); + } + + ResultOrError Device::AllocateMemory( + D3D12_HEAP_TYPE heapType, + const D3D12_RESOURCE_DESC& resourceDescriptor, + D3D12_RESOURCE_STATES initialUsage) { + return mResourceAllocatorManager->AllocateMemory(heapType, resourceDescriptor, + initialUsage); + } + + Ref Device::WrapSharedHandle(const ExternalImageDescriptor* descriptor, + HANDLE sharedHandle, + uint64_t acquireMutexKey, + bool isSwapChainTexture) { + Ref dawnTexture; + if (ConsumedError(Texture::Create(this, descriptor, sharedHandle, acquireMutexKey, + isSwapChainTexture), + &dawnTexture)) + return nullptr; + + return dawnTexture; + } + + // We use IDXGIKeyedMutexes to synchronize access between D3D11 and D3D12. D3D11/12 fences + // are a viable alternative but are, unfortunately, not available on all versions of Windows + // 10. Since D3D12 does not directly support keyed mutexes, we need to wrap the D3D12 + // resource using 11on12 and QueryInterface the D3D11 representation for the keyed mutex. + ResultOrError> Device::CreateKeyedMutexForTexture( + ID3D12Resource* d3d12Resource) { + if (mD3d11On12Device == nullptr) { + ComPtr d3d11Device; + ComPtr d3d11DeviceContext; + D3D_FEATURE_LEVEL d3dFeatureLevel; + IUnknown* const iUnknownQueue = mCommandQueue.Get(); + DAWN_TRY(CheckHRESULT(GetFunctions()->d3d11on12CreateDevice( + mD3d12Device.Get(), 0, nullptr, 0, &iUnknownQueue, 1, 1, + &d3d11Device, &d3d11DeviceContext, &d3dFeatureLevel), + "D3D12 11on12 device create")); + + ComPtr d3d11on12Device; + DAWN_TRY(CheckHRESULT(d3d11Device.As(&d3d11on12Device), + "D3D12 QueryInterface ID3D11Device to ID3D11On12Device")); + + ComPtr d3d11DeviceContext2; + DAWN_TRY( + CheckHRESULT(d3d11DeviceContext.As(&d3d11DeviceContext2), + "D3D12 QueryInterface ID3D11DeviceContext to ID3D11DeviceContext2")); + + mD3d11On12DeviceContext = std::move(d3d11DeviceContext2); + mD3d11On12Device = std::move(d3d11on12Device); + } + + ComPtr d3d11Texture; + D3D11_RESOURCE_FLAGS resourceFlags; + resourceFlags.BindFlags = 0; + resourceFlags.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + resourceFlags.CPUAccessFlags = 0; + resourceFlags.StructureByteStride = 0; + DAWN_TRY(CheckHRESULT(mD3d11On12Device->CreateWrappedResource( + d3d12Resource, &resourceFlags, D3D12_RESOURCE_STATE_COMMON, + D3D12_RESOURCE_STATE_COMMON, IID_PPV_ARGS(&d3d11Texture)), + "D3D12 creating a wrapped resource")); + + ComPtr dxgiKeyedMutex; + DAWN_TRY(CheckHRESULT(d3d11Texture.As(&dxgiKeyedMutex), + "D3D12 QueryInterface ID3D11Texture2D to IDXGIKeyedMutex")); + + return std::move(dxgiKeyedMutex); + } + + void Device::ReleaseKeyedMutexForTexture(ComPtr dxgiKeyedMutex) { + ComPtr d3d11Resource; + HRESULT hr = dxgiKeyedMutex.As(&d3d11Resource); + if (FAILED(hr)) { + return; + } + + ID3D11Resource* d3d11ResourceRaw = d3d11Resource.Get(); + mD3d11On12Device->ReleaseWrappedResources(&d3d11ResourceRaw, 1); + + d3d11Resource.Reset(); + dxgiKeyedMutex.Reset(); + + // 11on12 has a bug where D3D12 resources used only for keyed shared mutexes + // are not released until work is submitted to the device context and flushed. + // The most minimal work we can get away with is issuing a TiledResourceBarrier. + + // ID3D11DeviceContext2 is available in Win8.1 and above. This suffices for a + // D3D12 backend since both D3D12 and 11on12 first appeared in Windows 10. + mD3d11On12DeviceContext->TiledResourceBarrier(nullptr, nullptr); + mD3d11On12DeviceContext->Flush(); + } + + const D3D12DeviceInfo& Device::GetDeviceInfo() const { + return ToBackend(GetAdapter())->GetDeviceInfo(); + } + + void Device::InitTogglesFromDriver() { + const bool useResourceHeapTier2 = (GetDeviceInfo().resourceHeapTier >= 2); + SetToggle(Toggle::UseD3D12ResourceHeapTier2, useResourceHeapTier2); + SetToggle(Toggle::UseD3D12RenderPass, GetDeviceInfo().supportsRenderPass); + SetToggle(Toggle::UseD3D12ResidencyManagement, true); + SetToggle(Toggle::UseDXC, false); + + // By default use the maximum shader-visible heap size allowed. + SetToggle(Toggle::UseD3D12SmallShaderVisibleHeapForTesting, false); + } + + MaybeError Device::WaitForIdleForDestruction() { + // Immediately forget about all pending commands + mPendingCommands.Release(); + + DAWN_TRY(NextSerial()); + // Wait for all in-flight commands to finish executing + DAWN_TRY(WaitForSerial(GetLastSubmittedCommandSerial())); return {}; } + MaybeError Device::CheckDebugLayerAndGenerateErrors() { + if (!GetAdapter()->GetInstance()->IsBackendValidationEnabled()) { + return {}; + } + + ComPtr infoQueue; + ASSERT_SUCCESS(mD3d12Device.As(&infoQueue)); + uint64_t totalErrors = infoQueue->GetNumStoredMessagesAllowedByRetrievalFilter(); + + // Check if any errors have occurred otherwise we would be creating an empty error. Note + // that we use GetNumStoredMessagesAllowedByRetrievalFilter instead of GetNumStoredMessages + // because we only convert WARNINGS or higher messages to dawn errors. + if (totalErrors == 0) { + return {}; + } + + std::ostringstream messages; + uint64_t errorsToPrint = std::min(kMaxDebugMessagesToPrint, totalErrors); + for (uint64_t i = 0; i < errorsToPrint; ++i) { + SIZE_T messageLength = 0; + HRESULT hr = infoQueue->GetMessage(i, nullptr, &messageLength); + if (FAILED(hr)) { + messages << " ID3D12InfoQueue::GetMessage failed with " << hr << '\n'; + continue; + } + + std::unique_ptr messageData(new uint8_t[messageLength]); + D3D12_MESSAGE* message = reinterpret_cast(messageData.get()); + hr = infoQueue->GetMessage(i, message, &messageLength); + if (FAILED(hr)) { + messages << " ID3D12InfoQueue::GetMessage failed with " << hr << '\n'; + continue; + } + + messages << message->pDescription << " (" << message->ID << ")\n"; + } + if (errorsToPrint < totalErrors) { + messages << (totalErrors - errorsToPrint) << " messages silenced\n"; + } + // We only print up to the first kMaxDebugMessagesToPrint errors + infoQueue->ClearStoredMessages(); + + return DAWN_INTERNAL_ERROR(messages.str()); + } + + void Device::ShutDownImpl() { + ASSERT(GetState() == State::Disconnected); + + // Immediately forget about all pending commands for the case where device is lost on its + // own and WaitForIdleForDestruction isn't called. + mPendingCommands.Release(); + + if (mFenceEvent != nullptr) { + ::CloseHandle(mFenceEvent); + } + + // We need to handle clearing up com object refs that were enqeued after TickImpl + mUsedComObjectRefs.ClearUpTo(GetCompletedCommandSerial()); + + ASSERT(mUsedComObjectRefs.Empty()); + ASSERT(!mPendingCommands.IsOpen()); + } + + ShaderVisibleDescriptorAllocator* Device::GetViewShaderVisibleDescriptorAllocator() const { + return mViewShaderVisibleDescriptorAllocator.get(); + } + + ShaderVisibleDescriptorAllocator* Device::GetSamplerShaderVisibleDescriptorAllocator() const { + return mSamplerShaderVisibleDescriptorAllocator.get(); + } + + StagingDescriptorAllocator* Device::GetViewStagingDescriptorAllocator( + uint32_t descriptorCount) const { + ASSERT(descriptorCount <= kMaxViewDescriptorsPerBindGroup); + // This is Log2 of the next power of two, plus 1. + uint32_t allocatorIndex = descriptorCount == 0 ? 0 : Log2Ceil(descriptorCount) + 1; + return mViewAllocators[allocatorIndex].get(); + } + + StagingDescriptorAllocator* Device::GetSamplerStagingDescriptorAllocator( + uint32_t descriptorCount) const { + ASSERT(descriptorCount <= kMaxSamplerDescriptorsPerBindGroup); + // This is Log2 of the next power of two, plus 1. + uint32_t allocatorIndex = descriptorCount == 0 ? 0 : Log2Ceil(descriptorCount) + 1; + return mSamplerAllocators[allocatorIndex].get(); + } + + StagingDescriptorAllocator* Device::GetRenderTargetViewAllocator() const { + return mRenderTargetViewAllocator.get(); + } + + StagingDescriptorAllocator* Device::GetDepthStencilViewAllocator() const { + return mDepthStencilViewAllocator.get(); + } + + SamplerHeapCache* Device::GetSamplerHeapCache() { + return mSamplerHeapCache.get(); + } + }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/DeviceD3D12.h b/third_party/dawn/src/dawn_native/d3d12/DeviceD3D12.h index 311f1784053..d4cb0819bec 100644 --- a/third_party/dawn/src/dawn_native/d3d12/DeviceD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/DeviceD3D12.h @@ -17,10 +17,14 @@ #include "dawn_native/dawn_platform.h" +#include "common/Constants.h" #include "common/SerialQueue.h" +#include "dawn_native/BindingInfo.h" #include "dawn_native/Device.h" +#include "dawn_native/d3d12/CommandRecordingContext.h" +#include "dawn_native/d3d12/D3D12Info.h" #include "dawn_native/d3d12/Forward.h" -#include "dawn_native/d3d12/d3d12_platform.h" +#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h" #include @@ -28,50 +32,58 @@ namespace dawn_native { namespace d3d12 { class CommandAllocatorManager; class DescriptorHeapAllocator; - class MapRequestTracker; class PlatformFunctions; - class ResourceAllocator; - - void ASSERT_SUCCESS(HRESULT hr); + class ResidencyManager; + class ResourceAllocatorManager; + class SamplerHeapCache; + class ShaderVisibleDescriptorAllocator; + class StagingDescriptorAllocator; + +#define ASSERT_SUCCESS(hr) \ + do { \ + HRESULT succeeded = hr; \ + ASSERT(SUCCEEDED(succeeded)); \ + } while (0) // Definition of backend types class Device : public DeviceBase { public: - Device(Adapter* adapter, const DeviceDescriptor* descriptor); - ~Device(); + static ResultOrError Create(Adapter* adapter, const DeviceDescriptor* descriptor); + ~Device() override; MaybeError Initialize(); - CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) override; + CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) override; - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; - void TickImpl() override; + MaybeError TickImpl() override; - ComPtr GetD3D12Device() const; + ID3D12Device* GetD3D12Device() const; ComPtr GetCommandQueue() const; + ID3D12SharingContract* GetSharingContract() const; ComPtr GetDispatchIndirectSignature() const; ComPtr GetDrawIndirectSignature() const; ComPtr GetDrawIndexedIndirectSignature() const; - DescriptorHeapAllocator* GetDescriptorHeapAllocator() const; - MapRequestTracker* GetMapRequestTracker() const; - ResourceAllocator* GetResourceAllocator() const; + CommandAllocatorManager* GetCommandAllocatorManager() const; + ResidencyManager* GetResidencyManager() const; const PlatformFunctions* GetFunctions() const; ComPtr GetFactory() const; + ResultOrError GetOrCreateDxcLibrary() const; + ResultOrError GetOrCreateDxcCompiler() const; + + ResultOrError GetPendingCommandContext(); - void OpenCommandList(ComPtr* commandList); - ComPtr GetPendingCommandList(); - Serial GetPendingCommandSerial() const override; + const D3D12DeviceInfo& GetDeviceInfo() const; - void NextSerial(); - void WaitForSerial(Serial serial); + MaybeError NextSerial(); + MaybeError WaitForSerial(Serial serial); void ReferenceUntilUnused(ComPtr object); - void ExecuteCommandLists(std::initializer_list commandLists); + MaybeError ExecutePendingCommandContext(); ResultOrError> CreateStagingBuffer(size_t size) override; MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, @@ -80,17 +92,61 @@ namespace dawn_native { namespace d3d12 { uint64_t destinationOffset, uint64_t size) override; + void CopyFromStagingToBufferImpl(CommandRecordingContext* commandContext, + StagingBufferBase* source, + uint64_t sourceOffset, + BufferBase* destination, + uint64_t destinationOffset, + uint64_t size); + + ResultOrError AllocateMemory( + D3D12_HEAP_TYPE heapType, + const D3D12_RESOURCE_DESC& resourceDescriptor, + D3D12_RESOURCE_STATES initialUsage); + + void DeallocateMemory(ResourceHeapAllocation& allocation); + + ShaderVisibleDescriptorAllocator* GetViewShaderVisibleDescriptorAllocator() const; + ShaderVisibleDescriptorAllocator* GetSamplerShaderVisibleDescriptorAllocator() const; + + // Returns nullptr when descriptor count is zero. + StagingDescriptorAllocator* GetViewStagingDescriptorAllocator( + uint32_t descriptorCount) const; + + StagingDescriptorAllocator* GetSamplerStagingDescriptorAllocator( + uint32_t descriptorCount) const; + + SamplerHeapCache* GetSamplerHeapCache(); + + StagingDescriptorAllocator* GetRenderTargetViewAllocator() const; + + StagingDescriptorAllocator* GetDepthStencilViewAllocator() const; + + Ref WrapSharedHandle(const ExternalImageDescriptor* descriptor, + HANDLE sharedHandle, + uint64_t acquireMutexKey, + bool isSwapChainTexture); + ResultOrError> CreateKeyedMutexForTexture( + ID3D12Resource* d3d12Resource); + void ReleaseKeyedMutexForTexture(ComPtr dxgiKeyedMutex); + + void InitTogglesFromDriver(); + private: + using DeviceBase::DeviceBase; + ResultOrError CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override; ResultOrError CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) override; - ResultOrError CreateBufferImpl(const BufferDescriptor* descriptor) override; + ResultOrError> CreateBufferImpl( + const BufferDescriptor* descriptor) override; ResultOrError CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) override; ResultOrError CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) override; - ResultOrError CreateQueueImpl() override; + ResultOrError CreateQuerySetImpl( + const QuerySetDescriptor* descriptor) override; ResultOrError CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) override; ResultOrError CreateSamplerImpl(const SamplerDescriptor* descriptor) override; @@ -98,36 +154,76 @@ namespace dawn_native { namespace d3d12 { const ShaderModuleDescriptor* descriptor) override; ResultOrError CreateSwapChainImpl( const SwapChainDescriptor* descriptor) override; - ResultOrError CreateTextureImpl(const TextureDescriptor* descriptor) override; + ResultOrError CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) override; + ResultOrError> CreateTextureImpl( + const TextureDescriptor* descriptor) override; ResultOrError CreateTextureViewImpl( TextureBase* texture, const TextureViewDescriptor* descriptor) override; - Serial mCompletedSerial = 0; - Serial mLastSubmittedSerial = 0; + void ShutDownImpl() override; + MaybeError WaitForIdleForDestruction() override; + + MaybeError CheckDebugLayerAndGenerateErrors(); + ComPtr mFence; - HANDLE mFenceEvent; + HANDLE mFenceEvent = nullptr; + Serial CheckAndUpdateCompletedSerials() override; ComPtr mD3d12Device; // Device is owned by adapter and will not be outlived. ComPtr mCommandQueue; + ComPtr mD3d12SharingContract; + + // 11on12 device and device context corresponding to mCommandQueue + ComPtr mD3d11On12Device; + ComPtr mD3d11On12DeviceContext; ComPtr mDispatchIndirectSignature; ComPtr mDrawIndirectSignature; ComPtr mDrawIndexedIndirectSignature; - struct PendingCommandList { - ComPtr commandList; - bool open = false; - } mPendingCommands; + CommandRecordingContext mPendingCommands; SerialQueue> mUsedComObjectRefs; std::unique_ptr mCommandAllocatorManager; - std::unique_ptr mDescriptorHeapAllocator; - std::unique_ptr mMapRequestTracker; - std::unique_ptr mResourceAllocator; + std::unique_ptr mResourceAllocatorManager; + std::unique_ptr mResidencyManager; + + static constexpr uint32_t kMaxSamplerDescriptorsPerBindGroup = + 3 * kMaxSamplersPerShaderStage; + static constexpr uint32_t kMaxViewDescriptorsPerBindGroup = + kMaxBindingsPerPipelineLayout - kMaxSamplerDescriptorsPerBindGroup; + + static constexpr uint32_t kNumSamplerDescriptorAllocators = + ConstexprLog2Ceil(kMaxSamplerDescriptorsPerBindGroup) + 1; + static constexpr uint32_t kNumViewDescriptorAllocators = + ConstexprLog2Ceil(kMaxViewDescriptorsPerBindGroup) + 1; + + // Index corresponds to Log2Ceil(descriptorCount) where descriptorCount is in + // the range [0, kMaxSamplerDescriptorsPerBindGroup]. + std::array, kNumViewDescriptorAllocators + 1> + mViewAllocators; + + // Index corresponds to Log2Ceil(descriptorCount) where descriptorCount is in + // the range [0, kMaxViewDescriptorsPerBindGroup]. + std::array, kNumSamplerDescriptorAllocators + 1> + mSamplerAllocators; + + std::unique_ptr mRenderTargetViewAllocator; + + std::unique_ptr mDepthStencilViewAllocator; + + std::unique_ptr mViewShaderVisibleDescriptorAllocator; + + std::unique_ptr mSamplerShaderVisibleDescriptorAllocator; - dawn_native::PCIInfo mPCIInfo; + // Sampler cache needs to be destroyed before the CPU sampler allocator to ensure the final + // release is called. + std::unique_ptr mSamplerHeapCache; }; }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/Forward.h b/third_party/dawn/src/dawn_native/d3d12/Forward.h index ade12e3ac86..4e5368b63a5 100644 --- a/third_party/dawn/src/dawn_native/d3d12/Forward.h +++ b/third_party/dawn/src/dawn_native/d3d12/Forward.h @@ -26,7 +26,9 @@ namespace dawn_native { namespace d3d12 { class CommandBuffer; class ComputePipeline; class Device; + class Heap; class PipelineLayout; + class QuerySet; class Queue; class RenderPipeline; class Sampler; @@ -45,8 +47,10 @@ namespace dawn_native { namespace d3d12 { using ComputePipelineType = ComputePipeline; using DeviceType = Device; using PipelineLayoutType = PipelineLayout; + using QuerySetType = QuerySet; using QueueType = Queue; using RenderPipelineType = RenderPipeline; + using ResourceHeapType = Heap; using SamplerType = Sampler; using ShaderModuleType = ShaderModule; using StagingBufferType = StagingBuffer; diff --git a/third_party/dawn/src/dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.cpp new file mode 100644 index 00000000000..3d3ba20fad3 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.cpp @@ -0,0 +1,39 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h" + +namespace dawn_native { namespace d3d12 { + + GPUDescriptorHeapAllocation::GPUDescriptorHeapAllocation( + D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor, + Serial lastUsageSerial, + Serial heapSerial) + : mBaseDescriptor(baseDescriptor), + mLastUsageSerial(lastUsageSerial), + mHeapSerial(heapSerial) { + } + + D3D12_GPU_DESCRIPTOR_HANDLE GPUDescriptorHeapAllocation::GetBaseDescriptor() const { + return mBaseDescriptor; + } + + Serial GPUDescriptorHeapAllocation::GetLastUsageSerial() const { + return mLastUsageSerial; + } + + Serial GPUDescriptorHeapAllocation::GetHeapSerial() const { + return mHeapSerial; + } +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h b/third_party/dawn/src/dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h new file mode 100644 index 00000000000..08930d79fcf --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h @@ -0,0 +1,43 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_GPUDESCRIPTORHEAPALLOCATION_H_ +#define DAWNNATIVE_D3D12_GPUDESCRIPTORHEAPALLOCATION_H_ + +#include "common/Serial.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + + // Wrapper for a handle into a GPU-only descriptor heap. + class GPUDescriptorHeapAllocation { + public: + GPUDescriptorHeapAllocation() = default; + GPUDescriptorHeapAllocation(D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor, + Serial lastUsageSerial, + Serial heapSerial); + + D3D12_GPU_DESCRIPTOR_HANDLE GetBaseDescriptor() const; + Serial GetLastUsageSerial() const; + Serial GetHeapSerial() const; + + private: + D3D12_GPU_DESCRIPTOR_HANDLE mBaseDescriptor = {0}; + Serial mLastUsageSerial = 0; + Serial mHeapSerial = 0; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_CPUDESCRIPTORHEAPALLOCATION_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/HeapAllocatorD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/HeapAllocatorD3D12.cpp new file mode 100644 index 00000000000..756c9071cfe --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/HeapAllocatorD3D12.cpp @@ -0,0 +1,71 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/HeapAllocatorD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/HeapD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" + +namespace dawn_native { namespace d3d12 { + + HeapAllocator::HeapAllocator(Device* device, + D3D12_HEAP_TYPE heapType, + D3D12_HEAP_FLAGS heapFlags, + MemorySegment memorySegment) + : mDevice(device), + mHeapType(heapType), + mHeapFlags(heapFlags), + mMemorySegment(memorySegment) { + } + + ResultOrError> HeapAllocator::AllocateResourceHeap( + uint64_t size) { + D3D12_HEAP_DESC heapDesc; + heapDesc.SizeInBytes = size; + heapDesc.Properties.Type = mHeapType; + heapDesc.Properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapDesc.Properties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapDesc.Properties.CreationNodeMask = 0; + heapDesc.Properties.VisibleNodeMask = 0; + // It is preferred to use a size that is a multiple of the alignment. + // However, MSAA heaps are always aligned to 4MB instead of 64KB. This means + // if the heap size is too small, the VMM would fragment. + // TODO(bryan.bernhart@intel.com): Consider having MSAA vs non-MSAA heaps. + heapDesc.Alignment = D3D12_DEFAULT_MSAA_RESOURCE_PLACEMENT_ALIGNMENT; + heapDesc.Flags = mHeapFlags; + + // CreateHeap will implicitly make the created heap resident. We must ensure enough free + // memory exists before allocating to avoid an out-of-memory error when overcommitted. + DAWN_TRY(mDevice->GetResidencyManager()->EnsureCanAllocate(size, mMemorySegment)); + + ComPtr d3d12Heap; + DAWN_TRY(CheckOutOfMemoryHRESULT( + mDevice->GetD3D12Device()->CreateHeap(&heapDesc, IID_PPV_ARGS(&d3d12Heap)), + "ID3D12Device::CreateHeap")); + + std::unique_ptr heapBase = + std::make_unique(std::move(d3d12Heap), mMemorySegment, size); + + // Calling CreateHeap implicitly calls MakeResident on the new heap. We must track this to + // avoid calling MakeResident a second time. + mDevice->GetResidencyManager()->TrackResidentAllocation(ToBackend(heapBase.get())); + return std::move(heapBase); + } + + void HeapAllocator::DeallocateResourceHeap(std::unique_ptr heap) { + mDevice->ReferenceUntilUnused(static_cast(heap.get())->GetD3D12Heap()); + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/HeapAllocatorD3D12.h b/third_party/dawn/src/dawn_native/d3d12/HeapAllocatorD3D12.h new file mode 100644 index 00000000000..53254c5e9fd --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/HeapAllocatorD3D12.h @@ -0,0 +1,48 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_HEAPALLOCATORD3D12_H_ +#define DAWNNATIVE_D3D12_HEAPALLOCATORD3D12_H_ + +#include "dawn_native/D3D12Backend.h" +#include "dawn_native/ResourceHeapAllocator.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + + class Device; + + // Wrapper to allocate a D3D12 heap. + class HeapAllocator : public ResourceHeapAllocator { + public: + HeapAllocator(Device* device, + D3D12_HEAP_TYPE heapType, + D3D12_HEAP_FLAGS heapFlags, + MemorySegment memorySegment); + ~HeapAllocator() override = default; + + ResultOrError> AllocateResourceHeap( + uint64_t size) override; + void DeallocateResourceHeap(std::unique_ptr allocation) override; + + private: + Device* mDevice; + D3D12_HEAP_TYPE mHeapType; + D3D12_HEAP_FLAGS mHeapFlags; + MemorySegment mMemorySegment; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_HEAPALLOCATORD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/HeapD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/HeapD3D12.cpp new file mode 100644 index 00000000000..7bb4e323a67 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/HeapD3D12.cpp @@ -0,0 +1,31 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/HeapD3D12.h" + +namespace dawn_native { namespace d3d12 { + Heap::Heap(ComPtr d3d12Pageable, MemorySegment memorySegment, uint64_t size) + : Pageable(std::move(d3d12Pageable), memorySegment, size) { + mD3d12Pageable.As(&mD3d12Heap); + } + + // This function should only be used when mD3D12Pageable was initialized from a + // ID3D12Pageable that was initially created as an ID3D12Heap (i.e. SubAllocation). If the + // ID3D12Pageable was initially created as an ID3D12Resource (i.e. DirectAllocation), then + // use GetD3D12Pageable(). + ID3D12Heap* Heap::GetD3D12Heap() const { + return mD3d12Heap.Get(); + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/HeapD3D12.h b/third_party/dawn/src/dawn_native/d3d12/HeapD3D12.h new file mode 100644 index 00000000000..b59c6449dec --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/HeapD3D12.h @@ -0,0 +1,40 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_HEAPD3D12_H_ +#define DAWNNATIVE_D3D12_HEAPD3D12_H_ + +#include "dawn_native/ResourceHeap.h" +#include "dawn_native/d3d12/PageableD3D12.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + + class Device; + + // This class is used to represent ID3D12Heap allocations, as well as an implicit heap + // representing a directly allocated resource. It inherits from Pageable because each Heap must + // be represented in the ResidencyManager. + class Heap : public ResourceHeapBase, public Pageable { + public: + Heap(ComPtr d3d12Pageable, MemorySegment memorySegment, uint64_t size); + + ID3D12Heap* GetD3D12Heap() const; + + private: + ComPtr mD3d12Heap; + }; +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_HEAPD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp index 17120f6cb60..9170e8ed864 100644 --- a/third_party/dawn/src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/NativeSwapChainImplD3D12.cpp @@ -21,15 +21,15 @@ namespace dawn_native { namespace d3d12 { namespace { - DXGI_USAGE D3D12SwapChainBufferUsage(DawnTextureUsageBit allowedUsages) { + DXGI_USAGE D3D12SwapChainBufferUsage(WGPUTextureUsage allowedUsages) { DXGI_USAGE usage = DXGI_CPU_ACCESS_NONE; - if (allowedUsages & DAWN_TEXTURE_USAGE_BIT_SAMPLED) { + if (allowedUsages & WGPUTextureUsage_Sampled) { usage |= DXGI_USAGE_SHADER_INPUT; } - if (allowedUsages & DAWN_TEXTURE_USAGE_BIT_STORAGE) { + if (allowedUsages & WGPUTextureUsage_Storage) { usage |= DXGI_USAGE_UNORDERED_ACCESS; } - if (allowedUsages & DAWN_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT) { + if (allowedUsages & WGPUTextureUsage_OutputAttachment) { usage |= DXGI_USAGE_RENDER_TARGET_OUTPUT; } return usage; @@ -39,7 +39,7 @@ namespace dawn_native { namespace d3d12 { } // anonymous namespace NativeSwapChainImpl::NativeSwapChainImpl(Device* device, HWND window) - : mWindow(window), mDevice(device) { + : mWindow(window), mDevice(device), mInterval(1) { } NativeSwapChainImpl::~NativeSwapChainImpl() { @@ -48,17 +48,19 @@ namespace dawn_native { namespace d3d12 { void NativeSwapChainImpl::Init(DawnWSIContextD3D12* /*context*/) { } - DawnSwapChainError NativeSwapChainImpl::Configure(DawnTextureFormat format, - DawnTextureUsageBit usage, + DawnSwapChainError NativeSwapChainImpl::Configure(WGPUTextureFormat format, + WGPUTextureUsage usage, uint32_t width, uint32_t height) { ASSERT(width > 0); ASSERT(height > 0); - ASSERT(format == static_cast(GetPreferredFormat())); + ASSERT(format == static_cast(GetPreferredFormat())); ComPtr factory = mDevice->GetFactory(); ComPtr queue = mDevice->GetCommandQueue(); + mInterval = mDevice->IsToggleEnabled(Toggle::TurnOffVsync) == true ? 0 : 1; + // Create the D3D12 swapchain, assuming only two buffers for now DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; swapChainDesc.Width = width; @@ -95,7 +97,7 @@ namespace dawn_native { namespace d3d12 { // TODO(cwallez@chromium.org) Currently we force the CPU to wait for the GPU to be finished // with the buffer. Ideally the synchronization should be all done on the GPU. - mDevice->WaitForSerial(mBufferSerials[mCurrentBuffer]); + ASSERT(mDevice->WaitForSerial(mBufferSerials[mCurrentBuffer]).IsSuccess()); return DAWN_SWAP_CHAIN_NO_ERROR; } @@ -103,16 +105,16 @@ namespace dawn_native { namespace d3d12 { DawnSwapChainError NativeSwapChainImpl::Present() { // This assumes the texture has already been transition to the PRESENT state. - ASSERT_SUCCESS(mSwapChain->Present(1, 0)); + ASSERT_SUCCESS(mSwapChain->Present(mInterval, 0)); // TODO(cwallez@chromium.org): Make the serial ticking implicit. - mDevice->NextSerial(); + ASSERT(mDevice->NextSerial().IsSuccess()); mBufferSerials[mCurrentBuffer] = mDevice->GetPendingCommandSerial(); return DAWN_SWAP_CHAIN_NO_ERROR; } - dawn::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const { - return dawn::TextureFormat::R8G8B8A8Unorm; + wgpu::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const { + return wgpu::TextureFormat::RGBA8Unorm; } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/NativeSwapChainImplD3D12.h b/third_party/dawn/src/dawn_native/d3d12/NativeSwapChainImplD3D12.h index f2fa847b97f..aaa8d85ee59 100644 --- a/third_party/dawn/src/dawn_native/d3d12/NativeSwapChainImplD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/NativeSwapChainImplD3D12.h @@ -34,18 +34,19 @@ namespace dawn_native { namespace d3d12 { ~NativeSwapChainImpl(); void Init(DawnWSIContextD3D12* context); - DawnSwapChainError Configure(DawnTextureFormat format, - DawnTextureUsageBit, + DawnSwapChainError Configure(WGPUTextureFormat format, + WGPUTextureUsage, uint32_t width, uint32_t height); DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture); DawnSwapChainError Present(); - dawn::TextureFormat GetPreferredFormat() const; + wgpu::TextureFormat GetPreferredFormat() const; private: HWND mWindow = nullptr; Device* mDevice = nullptr; + UINT mInterval; ComPtr mSwapChain = nullptr; std::vector> mBuffers; diff --git a/third_party/dawn/src/dawn_native/d3d12/PageableD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/PageableD3D12.cpp new file mode 100644 index 00000000000..5c5ef9c0f0d --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/PageableD3D12.cpp @@ -0,0 +1,76 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/PageableD3D12.h" + +namespace dawn_native { namespace d3d12 { + Pageable::Pageable(ComPtr d3d12Pageable, + MemorySegment memorySegment, + uint64_t size) + : mD3d12Pageable(std::move(d3d12Pageable)), mMemorySegment(memorySegment), mSize(size) { + } + + // When a pageable is destroyed, it no longer resides in resident memory, so we must evict + // it from the LRU cache. If this heap is not manually removed from the LRU-cache, the + // ResidencyManager will attempt to use it after it has been deallocated. + Pageable::~Pageable() { + if (IsInResidencyLRUCache()) { + RemoveFromList(); + } + } + + ID3D12Pageable* Pageable::GetD3D12Pageable() const { + return mD3d12Pageable.Get(); + } + + Serial Pageable::GetLastUsage() const { + return mLastUsage; + } + + void Pageable::SetLastUsage(Serial serial) { + mLastUsage = serial; + } + + uint64_t Pageable::GetLastSubmission() const { + return mLastSubmission; + } + + void Pageable::SetLastSubmission(Serial serial) { + mLastSubmission = serial; + } + + MemorySegment Pageable::GetMemorySegment() const { + return mMemorySegment; + } + + uint64_t Pageable::GetSize() const { + return mSize; + } + + bool Pageable::IsInResidencyLRUCache() const { + return IsInList(); + } + + void Pageable::IncrementResidencyLock() { + mResidencyLockRefCount++; + } + + void Pageable::DecrementResidencyLock() { + mResidencyLockRefCount--; + } + + bool Pageable::IsResidencyLocked() const { + return mResidencyLockRefCount != 0; + } +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/PageableD3D12.h b/third_party/dawn/src/dawn_native/d3d12/PageableD3D12.h new file mode 100644 index 00000000000..6b07adb4d44 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/PageableD3D12.h @@ -0,0 +1,80 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_PAGEABLED3D12_H_ +#define DAWNNATIVE_D3D12_PAGEABLED3D12_H_ + +#include "common/LinkedList.h" +#include "common/Serial.h" +#include "dawn_native/D3D12Backend.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + // This class is used to represent ID3D12Pageable allocations, and also serves as a node within + // the ResidencyManager's LRU cache. This node is inserted into the LRU-cache when it is first + // allocated, and any time it is scheduled to be used by the GPU. This node is removed from the + // LRU cache when it is evicted from resident memory due to budget constraints, or when the + // pageable allocation is released. + class Pageable : public LinkNode { + public: + Pageable(ComPtr d3d12Pageable, MemorySegment memorySegment, uint64_t size); + ~Pageable(); + + ID3D12Pageable* GetD3D12Pageable() const; + + // We set mLastRecordingSerial to denote the serial this pageable was last recorded to be + // used. We must check this serial against the current serial when recording usages to + // ensure we do not process residency for this pageable multiple times. + Serial GetLastUsage() const; + void SetLastUsage(Serial serial); + + // The residency manager must know the last serial that any portion of the pageable was + // submitted to be used so that we can ensure this pageable stays resident in memory at + // least until that serial has completed. + uint64_t GetLastSubmission() const; + void SetLastSubmission(Serial serial); + + MemorySegment GetMemorySegment() const; + + uint64_t GetSize() const; + + bool IsInResidencyLRUCache() const; + + // In some scenarios, such as async buffer mapping or descriptor heaps, we must lock + // residency to ensure the pageable cannot be evicted. Because multiple buffers may be + // mapped in a single heap, we must track the number of resources currently locked. + void IncrementResidencyLock(); + void DecrementResidencyLock(); + bool IsResidencyLocked() const; + + protected: + ComPtr mD3d12Pageable; + + private: + // mLastUsage denotes the last time this pageable was recorded for use. + Serial mLastUsage = 0; + // mLastSubmission denotes the last time this pageable was submitted to the GPU. Note that + // although this variable often contains the same value as mLastUsage, it can differ in some + // situations. When some asynchronous APIs (like WriteBuffer) are called, mLastUsage is + // updated upon the call, but the backend operation is deferred until the next submission + // to the GPU. This makes mLastSubmission unique from mLastUsage, and allows us to + // accurately identify when a pageable can be evicted. + Serial mLastSubmission = 0; + MemorySegment mMemorySegment; + uint32_t mResidencyLockRefCount = 0; + uint64_t mSize = 0; + }; +}} // namespace dawn_native::d3d12 + +#endif diff --git a/third_party/dawn/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp index e63d9eaeacb..8eed07ed2c9 100644 --- a/third_party/dawn/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/PipelineLayoutD3D12.cpp @@ -17,34 +17,69 @@ #include "common/Assert.h" #include "common/BitSetIterator.h" #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" using Microsoft::WRL::ComPtr; namespace dawn_native { namespace d3d12 { + namespace { + D3D12_SHADER_VISIBILITY ShaderVisibilityType(wgpu::ShaderStage visibility) { + ASSERT(visibility != wgpu::ShaderStage::None); - PipelineLayout::PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor) - : PipelineLayoutBase(device, descriptor) { - D3D12_ROOT_PARAMETER rootParameters[kMaxBindGroups * 2]; + if (visibility == wgpu::ShaderStage::Vertex) { + return D3D12_SHADER_VISIBILITY_VERTEX; + } + + if (visibility == wgpu::ShaderStage::Fragment) { + return D3D12_SHADER_VISIBILITY_PIXEL; + } - // A root parameter is one of these types - union { - D3D12_ROOT_DESCRIPTOR_TABLE DescriptorTable; - D3D12_ROOT_CONSTANTS Constants; - D3D12_ROOT_DESCRIPTOR Descriptor; - } rootParameterValues[kMaxBindGroups * 2]; - // samplers must be in a separate descriptor table so we need at most twice as many tables - // as bind groups + // For compute or any two combination of stages, visibility must be ALL + return D3D12_SHADER_VISIBILITY_ALL; + } + + D3D12_ROOT_PARAMETER_TYPE RootParameterType(wgpu::BindingType type) { + switch (type) { + case wgpu::BindingType::UniformBuffer: + return D3D12_ROOT_PARAMETER_TYPE_CBV; + case wgpu::BindingType::StorageBuffer: + return D3D12_ROOT_PARAMETER_TYPE_UAV; + case wgpu::BindingType::ReadonlyStorageBuffer: + return D3D12_ROOT_PARAMETER_TYPE_SRV; + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::StorageTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + UNREACHABLE(); + } + } + } // anonymous namespace + + ResultOrError PipelineLayout::Create( + Device* device, + const PipelineLayoutDescriptor* descriptor) { + Ref layout = AcquireRef(new PipelineLayout(device, descriptor)); + DAWN_TRY(layout->Initialize()); + return layout.Detach(); + } + + MaybeError PipelineLayout::Initialize() { + Device* device = ToBackend(GetDevice()); + // Parameters are D3D12_ROOT_PARAMETER_TYPE which is either a root table, constant, or + // descriptor. + std::vector rootParameters; // Ranges are D3D12_DESCRIPTOR_RANGE_TYPE_(SRV|UAV|CBV|SAMPLER) // They are grouped together so each bind group has at most 4 ranges D3D12_DESCRIPTOR_RANGE ranges[kMaxBindGroups * 4]; - uint32_t parameterIndex = 0; uint32_t rangeIndex = 0; - for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) { + for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) { const BindGroupLayout* bindGroupLayout = ToBackend(GetBindGroupLayout(group)); // Set the root descriptor table parameter and copy ranges. Ranges are offset by the @@ -56,36 +91,74 @@ namespace dawn_native { namespace d3d12 { return false; } - auto& rootParameter = rootParameters[parameterIndex]; + D3D12_ROOT_PARAMETER rootParameter = {}; rootParameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; - rootParameter.DescriptorTable = rootParameterValues[parameterIndex].DescriptorTable; rootParameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; rootParameter.DescriptorTable.NumDescriptorRanges = rangeCount; rootParameter.DescriptorTable.pDescriptorRanges = &ranges[rangeIndex]; for (uint32_t i = 0; i < rangeCount; ++i) { ranges[rangeIndex] = descriptorRanges[i]; - ranges[rangeIndex].RegisterSpace = group; + ranges[rangeIndex].RegisterSpace = static_cast(group); rangeIndex++; } + rootParameters.emplace_back(rootParameter); + return true; }; if (SetRootDescriptorTable(bindGroupLayout->GetCbvUavSrvDescriptorTableSize(), bindGroupLayout->GetCbvUavSrvDescriptorRanges())) { - mCbvUavSrvRootParameterInfo[group] = parameterIndex++; + mCbvUavSrvRootParameterInfo[group] = rootParameters.size() - 1; } if (SetRootDescriptorTable(bindGroupLayout->GetSamplerDescriptorTableSize(), bindGroupLayout->GetSamplerDescriptorRanges())) { - mSamplerRootParameterInfo[group] = parameterIndex++; + mSamplerRootParameterInfo[group] = rootParameters.size() - 1; + } + + // Get calculated shader register for root descriptors + const auto& shaderRegisters = bindGroupLayout->GetBindingOffsets(); + + // Init root descriptors in root signatures for dynamic buffer bindings. + // These are packed at the beginning of the layout binding info. + for (BindingIndex dynamicBindingIndex{0}; + dynamicBindingIndex < bindGroupLayout->GetDynamicBufferCount(); + ++dynamicBindingIndex) { + const BindingInfo& bindingInfo = + bindGroupLayout->GetBindingInfo(dynamicBindingIndex); + + if (bindingInfo.visibility == wgpu::ShaderStage::None) { + // Skip dynamic buffers that are not visible. D3D12 does not have None + // visibility. + continue; + } + + D3D12_ROOT_PARAMETER rootParameter = {}; + + // Setup root descriptor. + D3D12_ROOT_DESCRIPTOR rootDescriptor; + rootDescriptor.ShaderRegister = shaderRegisters[dynamicBindingIndex]; + rootDescriptor.RegisterSpace = static_cast(group); + + // Set root descriptors in root signatures. + rootParameter.Descriptor = rootDescriptor; + mDynamicRootParameterIndices[group][dynamicBindingIndex] = rootParameters.size(); + + // Set parameter types according to bind group layout descriptor. + rootParameter.ParameterType = RootParameterType(bindingInfo.type); + + // Set visibilities according to bind group layout descriptor. + rootParameter.ShaderVisibility = ShaderVisibilityType(bindingInfo.visibility); + + rootParameters.emplace_back(rootParameter); } } D3D12_ROOT_SIGNATURE_DESC rootSignatureDescriptor; - rootSignatureDescriptor.NumParameters = parameterIndex; - rootSignatureDescriptor.pParameters = rootParameters; + rootSignatureDescriptor.NumParameters = rootParameters.size(); + rootSignatureDescriptor.pParameters = rootParameters.data(); rootSignatureDescriptor.NumStaticSamplers = 0; rootSignatureDescriptor.pStaticSamplers = nullptr; rootSignatureDescriptor.Flags = @@ -93,24 +166,38 @@ namespace dawn_native { namespace d3d12 { ComPtr signature; ComPtr error; - ASSERT_SUCCESS(device->GetFunctions()->d3d12SerializeRootSignature( - &rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error)); - ASSERT_SUCCESS(device->GetD3D12Device()->CreateRootSignature( - 0, signature->GetBufferPointer(), signature->GetBufferSize(), - IID_PPV_ARGS(&mRootSignature))); + DAWN_TRY(CheckHRESULT( + device->GetFunctions()->d3d12SerializeRootSignature( + &rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error), + "D3D12 serialize root signature")); + DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateRootSignature( + 0, signature->GetBufferPointer(), signature->GetBufferSize(), + IID_PPV_ARGS(&mRootSignature)), + "D3D12 create root signature")); + return {}; } - uint32_t PipelineLayout::GetCbvUavSrvRootParameterIndex(uint32_t group) const { - ASSERT(group < kMaxBindGroups); + uint32_t PipelineLayout::GetCbvUavSrvRootParameterIndex(BindGroupIndex group) const { + ASSERT(group < kMaxBindGroupsTyped); return mCbvUavSrvRootParameterInfo[group]; } - uint32_t PipelineLayout::GetSamplerRootParameterIndex(uint32_t group) const { - ASSERT(group < kMaxBindGroups); + uint32_t PipelineLayout::GetSamplerRootParameterIndex(BindGroupIndex group) const { + ASSERT(group < kMaxBindGroupsTyped); return mSamplerRootParameterInfo[group]; } - ComPtr PipelineLayout::GetRootSignature() { - return mRootSignature; + ID3D12RootSignature* PipelineLayout::GetRootSignature() const { + return mRootSignature.Get(); + } + + uint32_t PipelineLayout::GetDynamicRootParameterIndex(BindGroupIndex group, + BindingIndex bindingIndex) const { + ASSERT(group < kMaxBindGroupsTyped); + ASSERT(bindingIndex < kMaxDynamicBuffersPerPipelineLayoutTyped); + ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).hasDynamicOffset); + ASSERT(GetBindGroupLayout(group)->GetBindingInfo(bindingIndex).visibility != + wgpu::ShaderStage::None); + return mDynamicRootParameterIndices[group][bindingIndex]; } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/PipelineLayoutD3D12.h b/third_party/dawn/src/dawn_native/d3d12/PipelineLayoutD3D12.h index 90c1802a4cc..6116cd81988 100644 --- a/third_party/dawn/src/dawn_native/d3d12/PipelineLayoutD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/PipelineLayoutD3D12.h @@ -15,27 +15,39 @@ #ifndef DAWNNATIVE_D3D12_PIPELINELAYOUTD3D12_H_ #define DAWNNATIVE_D3D12_PIPELINELAYOUTD3D12_H_ +#include "common/ityp_array.h" +#include "dawn_native/BindingInfo.h" #include "dawn_native/PipelineLayout.h" - #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { class Device; - class PipelineLayout : public PipelineLayoutBase { + class PipelineLayout final : public PipelineLayoutBase { public: - PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor); + static ResultOrError Create(Device* device, + const PipelineLayoutDescriptor* descriptor); - uint32_t GetCbvUavSrvRootParameterIndex(uint32_t group) const; - uint32_t GetSamplerRootParameterIndex(uint32_t group) const; + uint32_t GetCbvUavSrvRootParameterIndex(BindGroupIndex group) const; + uint32_t GetSamplerRootParameterIndex(BindGroupIndex group) const; - ComPtr GetRootSignature(); + // Returns the index of the root parameter reserved for a dynamic buffer binding + uint32_t GetDynamicRootParameterIndex(BindGroupIndex group, + BindingIndex bindingIndex) const; - private: - std::array mCbvUavSrvRootParameterInfo; - std::array mSamplerRootParameterInfo; + ID3D12RootSignature* GetRootSignature() const; + private: + ~PipelineLayout() override = default; + using PipelineLayoutBase::PipelineLayoutBase; + MaybeError Initialize(); + ityp::array mCbvUavSrvRootParameterInfo; + ityp::array mSamplerRootParameterInfo; + ityp::array, + kMaxBindGroups> + mDynamicRootParameterIndices; ComPtr mRootSignature; }; diff --git a/third_party/dawn/src/dawn_native/d3d12/PlatformFunctions.cpp b/third_party/dawn/src/dawn_native/d3d12/PlatformFunctions.cpp index d9137365f6a..467e8d79efb 100644 --- a/third_party/dawn/src/dawn_native/d3d12/PlatformFunctions.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/PlatformFunctions.cpp @@ -26,7 +26,10 @@ namespace dawn_native { namespace d3d12 { MaybeError PlatformFunctions::LoadFunctions() { DAWN_TRY(LoadD3D12()); DAWN_TRY(LoadDXGI()); - DAWN_TRY(LoadD3DCompiler()); + LoadDXIL(); + LoadDXCompiler(); + DAWN_TRY(LoadFXCompiler()); + DAWN_TRY(LoadD3D11()); LoadPIXRuntime(); return {}; } @@ -44,7 +47,17 @@ namespace dawn_native { namespace d3d12 { "D3D12SerializeVersionedRootSignature", &error) || !mD3D12Lib.GetProc(&d3d12CreateVersionedRootSignatureDeserializer, "D3D12CreateVersionedRootSignatureDeserializer", &error)) { - return DAWN_CONTEXT_LOST_ERROR(error.c_str()); + return DAWN_INTERNAL_ERROR(error.c_str()); + } + + return {}; + } + + MaybeError PlatformFunctions::LoadD3D11() { + std::string error; + if (!mD3D11Lib.Open("d3d11.dll", &error) || + !mD3D11Lib.GetProc(&d3d11on12CreateDevice, "D3D11On12CreateDevice", &error)) { + return DAWN_INTERNAL_ERROR(error.c_str()); } return {}; @@ -55,26 +68,44 @@ namespace dawn_native { namespace d3d12 { if (!mDXGILib.Open("dxgi.dll", &error) || !mDXGILib.GetProc(&dxgiGetDebugInterface1, "DXGIGetDebugInterface1", &error) || !mDXGILib.GetProc(&createDxgiFactory2, "CreateDXGIFactory2", &error)) { - return DAWN_CONTEXT_LOST_ERROR(error.c_str()); + return DAWN_INTERNAL_ERROR(error.c_str()); } return {}; } - MaybeError PlatformFunctions::LoadD3DCompiler() { + void PlatformFunctions::LoadDXIL() { + if (!mDXILLib.Open("dxil.dll", nullptr)) { + mDXILLib.Close(); + } + } + + void PlatformFunctions::LoadDXCompiler() { + // DXIL must be loaded before DXC, otherwise shader signing is unavailable + if (!mDXCompilerLib.Open("dxcompiler.dll", nullptr) || + !mDXCompilerLib.GetProc(&dxcCreateInstance, "DxcCreateInstance", nullptr)) { + mDXCompilerLib.Close(); + } + } + + MaybeError PlatformFunctions::LoadFXCompiler() { std::string error; - if (!mD3DCompilerLib.Open("d3dcompiler_47.dll", &error) || - !mD3DCompilerLib.GetProc(&d3dCompile, "D3DCompile", &error)) { - return DAWN_CONTEXT_LOST_ERROR(error.c_str()); + if (!mFXCompilerLib.Open("d3dcompiler_47.dll", &error) || + !mFXCompilerLib.GetProc(&d3dCompile, "D3DCompile", &error)) { + return DAWN_INTERNAL_ERROR(error.c_str()); } return {}; } - bool PlatformFunctions::isPIXEventRuntimeLoaded() const { + bool PlatformFunctions::IsPIXEventRuntimeLoaded() const { return mPIXEventRuntimeLib.Valid(); } + bool PlatformFunctions::IsDXCAvailable() const { + return mDXILLib.Valid() && mDXCompilerLib.Valid(); + } + void PlatformFunctions::LoadPIXRuntime() { if (!mPIXEventRuntimeLib.Open("WinPixEventRuntime.dll") || !mPIXEventRuntimeLib.GetProc(&pixBeginEventOnCommandList, diff --git a/third_party/dawn/src/dawn_native/d3d12/PlatformFunctions.h b/third_party/dawn/src/dawn_native/d3d12/PlatformFunctions.h index ec51ba18c5c..7b2851b2d9f 100644 --- a/third_party/dawn/src/dawn_native/d3d12/PlatformFunctions.h +++ b/third_party/dawn/src/dawn_native/d3d12/PlatformFunctions.h @@ -35,7 +35,8 @@ namespace dawn_native { namespace d3d12 { ~PlatformFunctions(); MaybeError LoadFunctions(); - bool isPIXEventRuntimeLoaded() const; + bool IsPIXEventRuntimeLoaded() const; + bool IsDXCAvailable() const; // Functions from d3d12.dll PFN_D3D12_CREATE_DEVICE d3d12CreateDevice = nullptr; @@ -58,6 +59,12 @@ namespace dawn_native { namespace d3d12 { _COM_Outptr_ void** ppFactory); PFN_CREATE_DXGI_FACTORY2 createDxgiFactory2 = nullptr; + // Functions from dxcompiler.dll + using PFN_DXC_CREATE_INSTANCE = HRESULT(WINAPI*)(REFCLSID rclsid, + REFIID riid, + _COM_Outptr_ void** ppCompiler); + PFN_DXC_CREATE_INSTANCE dxcCreateInstance = nullptr; + // Functions from d3d3compiler.dll pD3DCompile d3dCompile = nullptr; @@ -77,15 +84,24 @@ namespace dawn_native { namespace d3d12 { PFN_SET_MARKER_ON_COMMAND_LIST pixSetMarkerOnCommandList = nullptr; + // Functions from D3D11.dll + PFN_D3D11ON12_CREATE_DEVICE d3d11on12CreateDevice = nullptr; + private: MaybeError LoadD3D12(); + MaybeError LoadD3D11(); MaybeError LoadDXGI(); - MaybeError LoadD3DCompiler(); + void LoadDXIL(); + void LoadDXCompiler(); + MaybeError LoadFXCompiler(); void LoadPIXRuntime(); DynamicLib mD3D12Lib; + DynamicLib mD3D11Lib; DynamicLib mDXGILib; - DynamicLib mD3DCompilerLib; + DynamicLib mDXILLib; + DynamicLib mDXCompilerLib; + DynamicLib mFXCompilerLib; DynamicLib mPIXEventRuntimeLib; }; diff --git a/third_party/dawn/src/dawn_native/d3d12/QuerySetD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/QuerySetD3D12.cpp new file mode 100644 index 00000000000..e4a6a4191c0 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/QuerySetD3D12.cpp @@ -0,0 +1,66 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/QuerySetD3D12.h" + +#include "dawn_native/d3d12/D3D12Error.h" +#include "dawn_native/d3d12/DeviceD3D12.h" + +namespace dawn_native { namespace d3d12 { + + // static + ResultOrError QuerySet::Create(Device* device, + const QuerySetDescriptor* descriptor) { + Ref querySet = AcquireRef(new QuerySet(device, descriptor)); + DAWN_TRY(querySet->Initialize()); + return querySet.Detach(); + } + + MaybeError QuerySet::Initialize() { + D3D12_QUERY_HEAP_DESC queryHeapDesc = {}; + switch (GetQueryType()) { + case wgpu::QueryType::Occlusion: + queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION; + break; + case wgpu::QueryType::PipelineStatistics: + queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_PIPELINE_STATISTICS; + break; + case wgpu::QueryType::Timestamp: + queryHeapDesc.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; + break; + default: + UNREACHABLE(); + break; + } + queryHeapDesc.Count = GetQueryCount(); + + ID3D12Device* d3d12Device = ToBackend(GetDevice())->GetD3D12Device(); + return CheckOutOfMemoryHRESULT( + d3d12Device->CreateQueryHeap(&queryHeapDesc, IID_PPV_ARGS(&mQueryHeap)), + "ID3D12Device::CreateQueryHeap"); + } + + ID3D12QueryHeap* QuerySet::GetQueryHeap() const { + return mQueryHeap.Get(); + } + + QuerySet::~QuerySet() { + DestroyInternal(); + } + + void QuerySet::DestroyImpl() { + ToBackend(GetDevice())->ReferenceUntilUnused(mQueryHeap); + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/QuerySetD3D12.h b/third_party/dawn/src/dawn_native/d3d12/QuerySetD3D12.h new file mode 100644 index 00000000000..7b24cceb67d --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/QuerySetD3D12.h @@ -0,0 +1,45 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_QUERYSETD3D12_H_ +#define DAWNNATIVE_D3D12_QUERYSETD3D12_H_ + +#include "dawn_native/QuerySet.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + + class Device; + + class QuerySet : public QuerySetBase { + public: + static ResultOrError Create(Device* device, + const QuerySetDescriptor* descriptor); + + ID3D12QueryHeap* GetQueryHeap() const; + + private: + ~QuerySet() override; + using QuerySetBase::QuerySetBase; + MaybeError Initialize(); + + // Dawn API + void DestroyImpl() override; + + ComPtr mQueryHeap; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_QUERYSETD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/QueueD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/QueueD3D12.cpp index 063942d8c6d..710e41f052c 100644 --- a/third_party/dawn/src/dawn_native/d3d12/QueueD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/QueueD3D12.cpp @@ -15,27 +15,36 @@ #include "dawn_native/d3d12/QueueD3D12.h" #include "dawn_native/d3d12/CommandBufferD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_platform/DawnPlatform.h" +#include "dawn_platform/tracing/TraceEvent.h" namespace dawn_native { namespace d3d12 { Queue::Queue(Device* device) : QueueBase(device) { } - void Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { + MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { Device* device = ToBackend(GetDevice()); device->Tick(); - device->OpenCommandList(&mCommandList); + CommandRecordingContext* commandContext; + DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext()); + + TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, + "CommandBufferD3D12::RecordCommands"); for (uint32_t i = 0; i < commandCount; ++i) { - ToBackend(commands[i])->RecordCommands(mCommandList, i); + DAWN_TRY(ToBackend(commands[i])->RecordCommands(commandContext)); } - ASSERT_SUCCESS(mCommandList->Close()); + TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, + "CommandBufferD3D12::RecordCommands"); - device->ExecuteCommandLists({mCommandList.Get()}); + DAWN_TRY(device->ExecutePendingCommandContext()); - device->NextSerial(); + DAWN_TRY(device->NextSerial()); + return {}; } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/QueueD3D12.h b/third_party/dawn/src/dawn_native/d3d12/QueueD3D12.h index 117d6eeb972..f211d0bf587 100644 --- a/third_party/dawn/src/dawn_native/d3d12/QueueD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/QueueD3D12.h @@ -17,6 +17,7 @@ #include "dawn_native/Queue.h" +#include "dawn_native/d3d12/CommandRecordingContext.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { @@ -24,14 +25,12 @@ namespace dawn_native { namespace d3d12 { class Device; class CommandBuffer; - class Queue : public QueueBase { + class Queue final : public QueueBase { public: Queue(Device* device); private: - void SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; - - ComPtr mCommandList; + MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; }; }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp new file mode 100644 index 00000000000..66558cb7efb --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp @@ -0,0 +1,237 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/RenderPassBuilderD3D12.h" + +#include "dawn_native/Format.h" +#include "dawn_native/d3d12/CommandBufferD3D12.h" +#include "dawn_native/d3d12/Forward.h" +#include "dawn_native/d3d12/TextureD3D12.h" + +#include "dawn_native/dawn_platform.h" + +namespace dawn_native { namespace d3d12 { + + namespace { + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE D3D12BeginningAccessType(wgpu::LoadOp loadOp) { + switch (loadOp) { + case wgpu::LoadOp::Clear: + return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR; + case wgpu::LoadOp::Load: + return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE; + default: + UNREACHABLE(); + } + } + + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE D3D12EndingAccessType(wgpu::StoreOp storeOp) { + switch (storeOp) { + case wgpu::StoreOp::Clear: + return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD; + case wgpu::StoreOp::Store: + return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE; + default: + UNREACHABLE(); + } + } + + D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS D3D12EndingAccessResolveParameters( + wgpu::StoreOp storeOp, + TextureView* resolveSource, + TextureView* resolveDestination) { + D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS resolveParameters; + + resolveParameters.Format = resolveDestination->GetD3D12Format(); + resolveParameters.pSrcResource = + ToBackend(resolveSource->GetTexture())->GetD3D12Resource(); + resolveParameters.pDstResource = + ToBackend(resolveDestination->GetTexture())->GetD3D12Resource(); + + // Clear or preserve the resolve source. + if (storeOp == wgpu::StoreOp::Clear) { + resolveParameters.PreserveResolveSource = false; + } else if (storeOp == wgpu::StoreOp::Store) { + resolveParameters.PreserveResolveSource = true; + } + + // RESOLVE_MODE_AVERAGE is only valid for non-integer formats. + // TODO: Investigate and determine how integer format resolves should work in WebGPU. + switch (resolveDestination->GetFormat().type) { + case Format::Type::Sint: + case Format::Type::Uint: + resolveParameters.ResolveMode = D3D12_RESOLVE_MODE_MAX; + break; + default: + resolveParameters.ResolveMode = D3D12_RESOLVE_MODE_AVERAGE; + break; + } + + resolveParameters.SubresourceCount = 1; + + return resolveParameters; + } + + D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS + D3D12EndingAccessResolveSubresourceParameters(TextureView* resolveDestination) { + D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS subresourceParameters; + Texture* resolveDestinationTexture = ToBackend(resolveDestination->GetTexture()); + + subresourceParameters.DstX = 0; + subresourceParameters.DstY = 0; + subresourceParameters.SrcSubresource = 0; + subresourceParameters.DstSubresource = resolveDestinationTexture->GetSubresourceIndex( + resolveDestination->GetBaseMipLevel(), resolveDestination->GetBaseArrayLayer()); + // Resolving a specified sub-rect is only valid on hardware that supports sample + // positions. This means even {0, 0, width, height} would be invalid if unsupported. To + // avoid this, we assume sub-rect resolves never work by setting them to all zeros or + // "empty" to resolve the entire region. + subresourceParameters.SrcRect = {0, 0, 0, 0}; + + return subresourceParameters; + } + } // anonymous namespace + + RenderPassBuilder::RenderPassBuilder(bool hasUAV) { + if (hasUAV) { + mRenderPassFlags = D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES; + } + } + + void RenderPassBuilder::SetRenderTargetView(uint32_t attachmentIndex, + D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor) { + ASSERT(mColorAttachmentCount < kMaxColorAttachments); + mRenderTargetViews[attachmentIndex] = baseDescriptor; + mRenderPassRenderTargetDescriptors[attachmentIndex].cpuDescriptor = baseDescriptor; + mColorAttachmentCount++; + } + + void RenderPassBuilder::SetDepthStencilView(D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor) { + mRenderPassDepthStencilDesc.cpuDescriptor = baseDescriptor; + } + + uint32_t RenderPassBuilder::GetColorAttachmentCount() const { + return mColorAttachmentCount; + } + + bool RenderPassBuilder::HasDepth() const { + return mHasDepth; + } + + const D3D12_RENDER_PASS_RENDER_TARGET_DESC* + RenderPassBuilder::GetRenderPassRenderTargetDescriptors() const { + return mRenderPassRenderTargetDescriptors.data(); + } + + const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC* + RenderPassBuilder::GetRenderPassDepthStencilDescriptor() const { + return &mRenderPassDepthStencilDesc; + } + + D3D12_RENDER_PASS_FLAGS RenderPassBuilder::GetRenderPassFlags() const { + return mRenderPassFlags; + } + + const D3D12_CPU_DESCRIPTOR_HANDLE* RenderPassBuilder::GetRenderTargetViews() const { + return mRenderTargetViews.data(); + } + + void RenderPassBuilder::SetRenderTargetBeginningAccess(uint32_t attachment, + wgpu::LoadOp loadOp, + dawn_native::Color clearColor, + DXGI_FORMAT format) { + mRenderPassRenderTargetDescriptors[attachment].BeginningAccess.Type = + D3D12BeginningAccessType(loadOp); + if (loadOp == wgpu::LoadOp::Clear) { + mRenderPassRenderTargetDescriptors[attachment] + .BeginningAccess.Clear.ClearValue.Color[0] = clearColor.r; + mRenderPassRenderTargetDescriptors[attachment] + .BeginningAccess.Clear.ClearValue.Color[1] = clearColor.g; + mRenderPassRenderTargetDescriptors[attachment] + .BeginningAccess.Clear.ClearValue.Color[2] = clearColor.b; + mRenderPassRenderTargetDescriptors[attachment] + .BeginningAccess.Clear.ClearValue.Color[3] = clearColor.a; + mRenderPassRenderTargetDescriptors[attachment].BeginningAccess.Clear.ClearValue.Format = + format; + } + } + + void RenderPassBuilder::SetRenderTargetEndingAccess(uint32_t attachment, + wgpu::StoreOp storeOp) { + mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Type = + D3D12EndingAccessType(storeOp); + } + + void RenderPassBuilder::SetRenderTargetEndingAccessResolve(uint32_t attachment, + wgpu::StoreOp storeOp, + TextureView* resolveSource, + TextureView* resolveDestination) { + mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Type = + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_RESOLVE; + mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Resolve = + D3D12EndingAccessResolveParameters(storeOp, resolveSource, resolveDestination); + + mSubresourceParams[attachment] = + D3D12EndingAccessResolveSubresourceParameters(resolveDestination); + + mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Resolve.pSubresourceParameters = + &mSubresourceParams[attachment]; + } + + void RenderPassBuilder::SetDepthAccess(wgpu::LoadOp loadOp, + wgpu::StoreOp storeOp, + float clearDepth, + DXGI_FORMAT format) { + mHasDepth = true; + mRenderPassDepthStencilDesc.DepthBeginningAccess.Type = D3D12BeginningAccessType(loadOp); + if (loadOp == wgpu::LoadOp::Clear) { + mRenderPassDepthStencilDesc.DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth = + clearDepth; + mRenderPassDepthStencilDesc.DepthBeginningAccess.Clear.ClearValue.Format = format; + } + mRenderPassDepthStencilDesc.DepthEndingAccess.Type = D3D12EndingAccessType(storeOp); + } + + void RenderPassBuilder::SetStencilAccess(wgpu::LoadOp loadOp, + wgpu::StoreOp storeOp, + uint8_t clearStencil, + DXGI_FORMAT format) { + mRenderPassDepthStencilDesc.StencilBeginningAccess.Type = D3D12BeginningAccessType(loadOp); + if (loadOp == wgpu::LoadOp::Clear) { + mRenderPassDepthStencilDesc.StencilBeginningAccess.Clear.ClearValue.DepthStencil + .Stencil = clearStencil; + mRenderPassDepthStencilDesc.StencilBeginningAccess.Clear.ClearValue.Format = format; + } + mRenderPassDepthStencilDesc.StencilEndingAccess.Type = D3D12EndingAccessType(storeOp); + } + + void RenderPassBuilder::SetDepthNoAccess() { + mRenderPassDepthStencilDesc.DepthBeginningAccess.Type = + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS; + mRenderPassDepthStencilDesc.DepthEndingAccess.Type = + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS; + } + + void RenderPassBuilder::SetDepthStencilNoAccess() { + SetDepthNoAccess(); + SetStencilNoAccess(); + } + + void RenderPassBuilder::SetStencilNoAccess() { + mRenderPassDepthStencilDesc.StencilBeginningAccess.Type = + D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS; + mRenderPassDepthStencilDesc.StencilEndingAccess.Type = + D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS; + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/RenderPassBuilderD3D12.h b/third_party/dawn/src/dawn_native/d3d12/RenderPassBuilderD3D12.h new file mode 100644 index 00000000000..cf61a182a09 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/RenderPassBuilderD3D12.h @@ -0,0 +1,91 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_ +#define DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_ + +#include "common/Constants.h" +#include "dawn_native/d3d12/d3d12_platform.h" +#include "dawn_native/dawn_platform.h" + +#include + +namespace dawn_native { namespace d3d12 { + + class TextureView; + + // RenderPassBuilder stores parameters related to render pass load and store operations. + // When the D3D12 render pass API is available, the needed descriptors can be fetched + // directly from the RenderPassBuilder. When the D3D12 render pass API is not available, the + // descriptors are still fetched and any information necessary to emulate the load and store + // operations is extracted from the descriptors. + class RenderPassBuilder { + public: + RenderPassBuilder(bool hasUAV); + + uint32_t GetColorAttachmentCount() const; + + // Returns descriptors that are fed directly to BeginRenderPass, or are used as parameter + // storage if D3D12 render pass API is unavailable. + const D3D12_RENDER_PASS_RENDER_TARGET_DESC* GetRenderPassRenderTargetDescriptors() const; + const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC* GetRenderPassDepthStencilDescriptor() const; + + D3D12_RENDER_PASS_FLAGS GetRenderPassFlags() const; + + // Returns attachment RTVs to use with OMSetRenderTargets. + const D3D12_CPU_DESCRIPTOR_HANDLE* GetRenderTargetViews() const; + + bool HasDepth() const; + + // Functions that set the appropriate values in the render pass descriptors. + void SetDepthAccess(wgpu::LoadOp loadOp, + wgpu::StoreOp storeOp, + float clearDepth, + DXGI_FORMAT format); + void SetDepthNoAccess(); + void SetDepthStencilNoAccess(); + void SetRenderTargetBeginningAccess(uint32_t attachment, + wgpu::LoadOp loadOp, + dawn_native::Color clearColor, + DXGI_FORMAT format); + void SetRenderTargetEndingAccess(uint32_t attachment, wgpu::StoreOp storeOp); + void SetRenderTargetEndingAccessResolve(uint32_t attachment, + wgpu::StoreOp storeOp, + TextureView* resolveSource, + TextureView* resolveDestination); + void SetStencilAccess(wgpu::LoadOp loadOp, + wgpu::StoreOp storeOp, + uint8_t clearStencil, + DXGI_FORMAT format); + void SetStencilNoAccess(); + + void SetRenderTargetView(uint32_t attachmentIndex, + D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor); + void SetDepthStencilView(D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor); + + private: + uint32_t mColorAttachmentCount = 0; + bool mHasDepth = false; + D3D12_RENDER_PASS_FLAGS mRenderPassFlags = D3D12_RENDER_PASS_FLAG_NONE; + D3D12_RENDER_PASS_DEPTH_STENCIL_DESC mRenderPassDepthStencilDesc; + std::array + mRenderPassRenderTargetDescriptors; + std::array mRenderTargetViews; + std::array + mSubresourceParams; + }; +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/RenderPipelineD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/RenderPipelineD3D12.cpp index 9bac4959dd4..4190164a35e 100644 --- a/third_party/dawn/src/dawn_native/d3d12/RenderPipelineD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/RenderPipelineD3D12.cpp @@ -15,6 +15,8 @@ #include "dawn_native/d3d12/RenderPipelineD3D12.h" #include "common/Assert.h" +#include "common/Log.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/PipelineLayoutD3D12.h" #include "dawn_native/d3d12/PlatformFunctions.h" @@ -27,95 +29,95 @@ namespace dawn_native { namespace d3d12 { namespace { - DXGI_FORMAT VertexFormatType(dawn::VertexFormat format) { + DXGI_FORMAT VertexFormatType(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar2: + case wgpu::VertexFormat::UChar2: return DXGI_FORMAT_R8G8_UINT; - case dawn::VertexFormat::UChar4: + case wgpu::VertexFormat::UChar4: return DXGI_FORMAT_R8G8B8A8_UINT; - case dawn::VertexFormat::Char2: + case wgpu::VertexFormat::Char2: return DXGI_FORMAT_R8G8_SINT; - case dawn::VertexFormat::Char4: + case wgpu::VertexFormat::Char4: return DXGI_FORMAT_R8G8B8A8_SINT; - case dawn::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar2Norm: return DXGI_FORMAT_R8G8_UNORM; - case dawn::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::UChar4Norm: return DXGI_FORMAT_R8G8B8A8_UNORM; - case dawn::VertexFormat::Char2Norm: + case wgpu::VertexFormat::Char2Norm: return DXGI_FORMAT_R8G8_SNORM; - case dawn::VertexFormat::Char4Norm: + case wgpu::VertexFormat::Char4Norm: return DXGI_FORMAT_R8G8B8A8_SNORM; - case dawn::VertexFormat::UShort2: + case wgpu::VertexFormat::UShort2: return DXGI_FORMAT_R16G16_UINT; - case dawn::VertexFormat::UShort4: + case wgpu::VertexFormat::UShort4: return DXGI_FORMAT_R16G16B16A16_UINT; - case dawn::VertexFormat::Short2: + case wgpu::VertexFormat::Short2: return DXGI_FORMAT_R16G16_SINT; - case dawn::VertexFormat::Short4: + case wgpu::VertexFormat::Short4: return DXGI_FORMAT_R16G16B16A16_SINT; - case dawn::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort2Norm: return DXGI_FORMAT_R16G16_UNORM; - case dawn::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::UShort4Norm: return DXGI_FORMAT_R16G16B16A16_UNORM; - case dawn::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Short2Norm: return DXGI_FORMAT_R16G16_SNORM; - case dawn::VertexFormat::Short4Norm: + case wgpu::VertexFormat::Short4Norm: return DXGI_FORMAT_R16G16B16A16_SNORM; - case dawn::VertexFormat::Half2: + case wgpu::VertexFormat::Half2: return DXGI_FORMAT_R16G16_FLOAT; - case dawn::VertexFormat::Half4: + case wgpu::VertexFormat::Half4: return DXGI_FORMAT_R16G16B16A16_FLOAT; - case dawn::VertexFormat::Float: + case wgpu::VertexFormat::Float: return DXGI_FORMAT_R32_FLOAT; - case dawn::VertexFormat::Float2: + case wgpu::VertexFormat::Float2: return DXGI_FORMAT_R32G32_FLOAT; - case dawn::VertexFormat::Float3: + case wgpu::VertexFormat::Float3: return DXGI_FORMAT_R32G32B32_FLOAT; - case dawn::VertexFormat::Float4: + case wgpu::VertexFormat::Float4: return DXGI_FORMAT_R32G32B32A32_FLOAT; - case dawn::VertexFormat::UInt: + case wgpu::VertexFormat::UInt: return DXGI_FORMAT_R32_UINT; - case dawn::VertexFormat::UInt2: + case wgpu::VertexFormat::UInt2: return DXGI_FORMAT_R32G32_UINT; - case dawn::VertexFormat::UInt3: + case wgpu::VertexFormat::UInt3: return DXGI_FORMAT_R32G32B32_UINT; - case dawn::VertexFormat::UInt4: + case wgpu::VertexFormat::UInt4: return DXGI_FORMAT_R32G32B32A32_UINT; - case dawn::VertexFormat::Int: + case wgpu::VertexFormat::Int: return DXGI_FORMAT_R32_SINT; - case dawn::VertexFormat::Int2: + case wgpu::VertexFormat::Int2: return DXGI_FORMAT_R32G32_SINT; - case dawn::VertexFormat::Int3: + case wgpu::VertexFormat::Int3: return DXGI_FORMAT_R32G32B32_SINT; - case dawn::VertexFormat::Int4: + case wgpu::VertexFormat::Int4: return DXGI_FORMAT_R32G32B32A32_SINT; default: UNREACHABLE(); } } - D3D12_INPUT_CLASSIFICATION InputStepModeFunction(dawn::InputStepMode mode) { + D3D12_INPUT_CLASSIFICATION InputStepModeFunction(wgpu::InputStepMode mode) { switch (mode) { - case dawn::InputStepMode::Vertex: + case wgpu::InputStepMode::Vertex: return D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; - case dawn::InputStepMode::Instance: + case wgpu::InputStepMode::Instance: return D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA; default: UNREACHABLE(); } } - D3D12_PRIMITIVE_TOPOLOGY D3D12PrimitiveTopology(dawn::PrimitiveTopology primitiveTopology) { + D3D12_PRIMITIVE_TOPOLOGY D3D12PrimitiveTopology(wgpu::PrimitiveTopology primitiveTopology) { switch (primitiveTopology) { - case dawn::PrimitiveTopology::PointList: + case wgpu::PrimitiveTopology::PointList: return D3D_PRIMITIVE_TOPOLOGY_POINTLIST; - case dawn::PrimitiveTopology::LineList: + case wgpu::PrimitiveTopology::LineList: return D3D_PRIMITIVE_TOPOLOGY_LINELIST; - case dawn::PrimitiveTopology::LineStrip: + case wgpu::PrimitiveTopology::LineStrip: return D3D_PRIMITIVE_TOPOLOGY_LINESTRIP; - case dawn::PrimitiveTopology::TriangleList: + case wgpu::PrimitiveTopology::TriangleList: return D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; - case dawn::PrimitiveTopology::TriangleStrip: + case wgpu::PrimitiveTopology::TriangleStrip: return D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; default: UNREACHABLE(); @@ -123,82 +125,95 @@ namespace dawn_native { namespace d3d12 { } D3D12_PRIMITIVE_TOPOLOGY_TYPE D3D12PrimitiveTopologyType( - dawn::PrimitiveTopology primitiveTopology) { + wgpu::PrimitiveTopology primitiveTopology) { switch (primitiveTopology) { - case dawn::PrimitiveTopology::PointList: + case wgpu::PrimitiveTopology::PointList: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_POINT; - case dawn::PrimitiveTopology::LineList: - case dawn::PrimitiveTopology::LineStrip: + case wgpu::PrimitiveTopology::LineList: + case wgpu::PrimitiveTopology::LineStrip: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; - case dawn::PrimitiveTopology::TriangleList: - case dawn::PrimitiveTopology::TriangleStrip: + case wgpu::PrimitiveTopology::TriangleList: + case wgpu::PrimitiveTopology::TriangleStrip: return D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; default: UNREACHABLE(); } } - D3D12_BLEND D3D12Blend(dawn::BlendFactor factor) { + D3D12_CULL_MODE D3D12CullMode(wgpu::CullMode mode) { + switch (mode) { + case wgpu::CullMode::None: + return D3D12_CULL_MODE_NONE; + case wgpu::CullMode::Front: + return D3D12_CULL_MODE_FRONT; + case wgpu::CullMode::Back: + return D3D12_CULL_MODE_BACK; + default: + UNREACHABLE(); + } + } + + D3D12_BLEND D3D12Blend(wgpu::BlendFactor factor) { switch (factor) { - case dawn::BlendFactor::Zero: + case wgpu::BlendFactor::Zero: return D3D12_BLEND_ZERO; - case dawn::BlendFactor::One: + case wgpu::BlendFactor::One: return D3D12_BLEND_ONE; - case dawn::BlendFactor::SrcColor: + case wgpu::BlendFactor::SrcColor: return D3D12_BLEND_SRC_COLOR; - case dawn::BlendFactor::OneMinusSrcColor: + case wgpu::BlendFactor::OneMinusSrcColor: return D3D12_BLEND_INV_SRC_COLOR; - case dawn::BlendFactor::SrcAlpha: + case wgpu::BlendFactor::SrcAlpha: return D3D12_BLEND_SRC_ALPHA; - case dawn::BlendFactor::OneMinusSrcAlpha: + case wgpu::BlendFactor::OneMinusSrcAlpha: return D3D12_BLEND_INV_SRC_ALPHA; - case dawn::BlendFactor::DstColor: + case wgpu::BlendFactor::DstColor: return D3D12_BLEND_DEST_COLOR; - case dawn::BlendFactor::OneMinusDstColor: + case wgpu::BlendFactor::OneMinusDstColor: return D3D12_BLEND_INV_DEST_COLOR; - case dawn::BlendFactor::DstAlpha: + case wgpu::BlendFactor::DstAlpha: return D3D12_BLEND_DEST_ALPHA; - case dawn::BlendFactor::OneMinusDstAlpha: + case wgpu::BlendFactor::OneMinusDstAlpha: return D3D12_BLEND_INV_DEST_ALPHA; - case dawn::BlendFactor::SrcAlphaSaturated: + case wgpu::BlendFactor::SrcAlphaSaturated: return D3D12_BLEND_SRC_ALPHA_SAT; - case dawn::BlendFactor::BlendColor: + case wgpu::BlendFactor::BlendColor: return D3D12_BLEND_BLEND_FACTOR; - case dawn::BlendFactor::OneMinusBlendColor: + case wgpu::BlendFactor::OneMinusBlendColor: return D3D12_BLEND_INV_BLEND_FACTOR; default: UNREACHABLE(); } } - D3D12_BLEND_OP D3D12BlendOperation(dawn::BlendOperation operation) { + D3D12_BLEND_OP D3D12BlendOperation(wgpu::BlendOperation operation) { switch (operation) { - case dawn::BlendOperation::Add: + case wgpu::BlendOperation::Add: return D3D12_BLEND_OP_ADD; - case dawn::BlendOperation::Subtract: + case wgpu::BlendOperation::Subtract: return D3D12_BLEND_OP_SUBTRACT; - case dawn::BlendOperation::ReverseSubtract: + case wgpu::BlendOperation::ReverseSubtract: return D3D12_BLEND_OP_REV_SUBTRACT; - case dawn::BlendOperation::Min: + case wgpu::BlendOperation::Min: return D3D12_BLEND_OP_MIN; - case dawn::BlendOperation::Max: + case wgpu::BlendOperation::Max: return D3D12_BLEND_OP_MAX; default: UNREACHABLE(); } } - uint8_t D3D12RenderTargetWriteMask(dawn::ColorWriteMask writeMask) { - static_assert(static_cast(dawn::ColorWriteMask::Red) == + uint8_t D3D12RenderTargetWriteMask(wgpu::ColorWriteMask writeMask) { + static_assert(static_cast(wgpu::ColorWriteMask::Red) == D3D12_COLOR_WRITE_ENABLE_RED, "ColorWriteMask values must match"); - static_assert(static_cast(dawn::ColorWriteMask::Green) == + static_assert(static_cast(wgpu::ColorWriteMask::Green) == D3D12_COLOR_WRITE_ENABLE_GREEN, "ColorWriteMask values must match"); - static_assert(static_cast(dawn::ColorWriteMask::Blue) == + static_assert(static_cast(wgpu::ColorWriteMask::Blue) == D3D12_COLOR_WRITE_ENABLE_BLUE, "ColorWriteMask values must match"); - static_assert(static_cast(dawn::ColorWriteMask::Alpha) == + static_assert(static_cast(wgpu::ColorWriteMask::Alpha) == D3D12_COLOR_WRITE_ENABLE_ALPHA, "ColorWriteMask values must match"); return static_cast(writeMask); @@ -219,23 +234,23 @@ namespace dawn_native { namespace d3d12 { return blendDesc; } - D3D12_STENCIL_OP StencilOp(dawn::StencilOperation op) { + D3D12_STENCIL_OP StencilOp(wgpu::StencilOperation op) { switch (op) { - case dawn::StencilOperation::Keep: + case wgpu::StencilOperation::Keep: return D3D12_STENCIL_OP_KEEP; - case dawn::StencilOperation::Zero: + case wgpu::StencilOperation::Zero: return D3D12_STENCIL_OP_ZERO; - case dawn::StencilOperation::Replace: + case wgpu::StencilOperation::Replace: return D3D12_STENCIL_OP_REPLACE; - case dawn::StencilOperation::IncrementClamp: + case wgpu::StencilOperation::IncrementClamp: return D3D12_STENCIL_OP_INCR_SAT; - case dawn::StencilOperation::DecrementClamp: + case wgpu::StencilOperation::DecrementClamp: return D3D12_STENCIL_OP_DECR_SAT; - case dawn::StencilOperation::Invert: + case wgpu::StencilOperation::Invert: return D3D12_STENCIL_OP_INVERT; - case dawn::StencilOperation::IncrementWrap: + case wgpu::StencilOperation::IncrementWrap: return D3D12_STENCIL_OP_INCR; - case dawn::StencilOperation::DecrementWrap: + case wgpu::StencilOperation::DecrementWrap: return D3D12_STENCIL_OP_DECR; default: UNREACHABLE(); @@ -275,9 +290,16 @@ namespace dawn_native { namespace d3d12 { } // anonymous namespace - RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor) - : RenderPipelineBase(device, descriptor), - mD3d12PrimitiveTopology(D3D12PrimitiveTopology(GetPrimitiveTopology())) { + ResultOrError RenderPipeline::Create( + Device* device, + const RenderPipelineDescriptor* descriptor) { + Ref pipeline = AcquireRef(new RenderPipeline(device, descriptor)); + DAWN_TRY(pipeline->Initialize(descriptor)); + return pipeline.Detach(); + } + + MaybeError RenderPipeline::Initialize(const RenderPipelineDescriptor* descriptor) { + Device* device = ToBackend(GetDevice()); uint32_t compileFlags = 0; #if defined(_DEBUG) // Enable better shader debugging with the graphics debugging tools. @@ -288,63 +310,57 @@ namespace dawn_native { namespace d3d12 { D3D12_GRAPHICS_PIPELINE_STATE_DESC descriptorD3D12 = {}; - PerStage> compiledShader; - ComPtr errors; + PerStage entryPoints; + entryPoints[SingleShaderStage::Vertex] = descriptor->vertexStage.entryPoint; + entryPoints[SingleShaderStage::Fragment] = descriptor->fragmentStage->entryPoint; + + PerStage modules; + modules[SingleShaderStage::Vertex] = ToBackend(descriptor->vertexStage.module); + modules[SingleShaderStage::Fragment] = ToBackend(descriptor->fragmentStage->module); - dawn::ShaderStageBit renderStages = - dawn::ShaderStageBit::Vertex | dawn::ShaderStageBit::Fragment; + PerStage shaders; + shaders[SingleShaderStage::Vertex] = &descriptorD3D12.VS; + shaders[SingleShaderStage::Fragment] = &descriptorD3D12.PS; + + PerStage> compiledFXCShader; + PerStage> compiledDXCShader; + + wgpu::ShaderStage renderStages = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment; for (auto stage : IterateStages(renderStages)) { - const ShaderModule* module = nullptr; - const char* entryPoint = nullptr; - const char* compileTarget = nullptr; - D3D12_SHADER_BYTECODE* shader = nullptr; - switch (stage) { - case dawn::ShaderStage::Vertex: - module = ToBackend(descriptor->vertexStage->module); - entryPoint = descriptor->vertexStage->entryPoint; - shader = &descriptorD3D12.VS; - compileTarget = "vs_5_1"; - break; - case dawn::ShaderStage::Fragment: - module = ToBackend(descriptor->fragmentStage->module); - entryPoint = descriptor->fragmentStage->entryPoint; - shader = &descriptorD3D12.PS; - compileTarget = "ps_5_1"; - break; - default: - UNREACHABLE(); - break; - } + std::string hlslSource; + DAWN_TRY_ASSIGN(hlslSource, modules[stage]->GetHLSLSource(ToBackend(GetLayout()))); - const std::string hlslSource = module->GetHLSLSource(ToBackend(GetLayout())); + if (device->IsToggleEnabled(Toggle::UseDXC)) { + DAWN_TRY_ASSIGN(compiledDXCShader[stage], + modules[stage]->CompileShaderDXC(stage, hlslSource, + entryPoints[stage], compileFlags)); - const PlatformFunctions* functions = device->GetFunctions(); - if (FAILED(functions->d3dCompile(hlslSource.c_str(), hlslSource.length(), nullptr, - nullptr, nullptr, entryPoint, compileTarget, - compileFlags, 0, &compiledShader[stage], &errors))) { - printf("%s\n", reinterpret_cast(errors->GetBufferPointer())); - ASSERT(false); - } + shaders[stage]->pShaderBytecode = compiledDXCShader[stage]->GetBufferPointer(); + shaders[stage]->BytecodeLength = compiledDXCShader[stage]->GetBufferSize(); + } else { + DAWN_TRY_ASSIGN(compiledFXCShader[stage], + modules[stage]->CompileShaderFXC(stage, hlslSource, + entryPoints[stage], compileFlags)); - if (shader != nullptr) { - shader->pShaderBytecode = compiledShader[stage]->GetBufferPointer(); - shader->BytecodeLength = compiledShader[stage]->GetBufferSize(); + shaders[stage]->pShaderBytecode = compiledFXCShader[stage]->GetBufferPointer(); + shaders[stage]->BytecodeLength = compiledFXCShader[stage]->GetBufferSize(); } } PipelineLayout* layout = ToBackend(GetLayout()); - descriptorD3D12.pRootSignature = layout->GetRootSignature().Get(); + descriptorD3D12.pRootSignature = layout->GetRootSignature(); // D3D12 logs warnings if any empty input state is used std::array inputElementDescriptors; - if (GetAttributesSetMask().any()) { + if (GetAttributeLocationsUsed().any()) { descriptorD3D12.InputLayout = ComputeInputLayout(&inputElementDescriptors); } descriptorD3D12.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; - descriptorD3D12.RasterizerState.CullMode = D3D12_CULL_MODE_NONE; - descriptorD3D12.RasterizerState.FrontCounterClockwise = FALSE; + descriptorD3D12.RasterizerState.CullMode = D3D12CullMode(GetCullMode()); + descriptorD3D12.RasterizerState.FrontCounterClockwise = + (GetFrontFace() == wgpu::FrontFace::CCW) ? TRUE : FALSE; descriptorD3D12.RasterizerState.DepthBias = D3D12_DEFAULT_DEPTH_BIAS; descriptorD3D12.RasterizerState.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP; descriptorD3D12.RasterizerState.SlopeScaledDepthBias = @@ -373,13 +389,17 @@ namespace dawn_native { namespace d3d12 { descriptorD3D12.DepthStencilState = ComputeDepthStencilDesc(GetDepthStencilStateDescriptor()); - descriptorD3D12.SampleMask = UINT_MAX; + descriptorD3D12.SampleMask = GetSampleMask(); descriptorD3D12.PrimitiveTopologyType = D3D12PrimitiveTopologyType(GetPrimitiveTopology()); descriptorD3D12.SampleDesc.Count = GetSampleCount(); descriptorD3D12.SampleDesc.Quality = 0; - ASSERT_SUCCESS(device->GetD3D12Device()->CreateGraphicsPipelineState( - &descriptorD3D12, IID_PPV_ARGS(&mPipelineState))); + mD3d12PrimitiveTopology = D3D12PrimitiveTopology(GetPrimitiveTopology()); + + DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateGraphicsPipelineState( + &descriptorD3D12, IID_PPV_ARGS(&mPipelineState)), + "D3D12 create graphics pipeline state")); + return {}; } RenderPipeline::~RenderPipeline() { @@ -390,14 +410,14 @@ namespace dawn_native { namespace d3d12 { return mD3d12PrimitiveTopology; } - ComPtr RenderPipeline::GetPipelineState() { - return mPipelineState; + ID3D12PipelineState* RenderPipeline::GetPipelineState() const { + return mPipelineState.Get(); } D3D12_INPUT_LAYOUT_DESC RenderPipeline::ComputeInputLayout( std::array* inputElementDescriptors) { unsigned int count = 0; - for (auto i : IterateBitSet(GetAttributesSetMask())) { + for (auto i : IterateBitSet(GetAttributeLocationsUsed())) { D3D12_INPUT_ELEMENT_DESC& inputElementDescriptor = (*inputElementDescriptors)[count++]; const VertexAttributeInfo& attribute = GetAttribute(i); @@ -407,9 +427,9 @@ namespace dawn_native { namespace d3d12 { inputElementDescriptor.SemanticName = "TEXCOORD"; inputElementDescriptor.SemanticIndex = static_cast(i); inputElementDescriptor.Format = VertexFormatType(attribute.format); - inputElementDescriptor.InputSlot = attribute.inputSlot; + inputElementDescriptor.InputSlot = attribute.vertexBufferSlot; - const VertexBufferInfo& input = GetInput(attribute.inputSlot); + const VertexBufferInfo& input = GetVertexBuffer(attribute.vertexBufferSlot); inputElementDescriptor.AlignedByteOffset = attribute.offset; inputElementDescriptor.InputSlotClass = InputStepModeFunction(input.stepMode); diff --git a/third_party/dawn/src/dawn_native/d3d12/RenderPipelineD3D12.h b/third_party/dawn/src/dawn_native/d3d12/RenderPipelineD3D12.h index b9c9029eef0..50f0decac71 100644 --- a/third_party/dawn/src/dawn_native/d3d12/RenderPipelineD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/RenderPipelineD3D12.h @@ -23,15 +23,19 @@ namespace dawn_native { namespace d3d12 { class Device; - class RenderPipeline : public RenderPipelineBase { + class RenderPipeline final : public RenderPipelineBase { public: - RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor); - ~RenderPipeline(); + static ResultOrError Create(Device* device, + const RenderPipelineDescriptor* descriptor); + RenderPipeline() = delete; D3D12_PRIMITIVE_TOPOLOGY GetD3D12PrimitiveTopology() const; - ComPtr GetPipelineState(); + ID3D12PipelineState* GetPipelineState() const; private: + ~RenderPipeline() override; + using RenderPipelineBase::RenderPipelineBase; + MaybeError Initialize(const RenderPipelineDescriptor* descriptor); D3D12_INPUT_LAYOUT_DESC ComputeInputLayout( std::array* inputElementDescriptors); diff --git a/third_party/dawn/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp new file mode 100644 index 00000000000..e66296e4e26 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/ResidencyManagerD3D12.cpp @@ -0,0 +1,370 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" + +#include "dawn_native/d3d12/AdapterD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/Forward.h" +#include "dawn_native/d3d12/HeapD3D12.h" + +namespace dawn_native { namespace d3d12 { + + ResidencyManager::ResidencyManager(Device* device) + : mDevice(device), + mResidencyManagementEnabled( + device->IsToggleEnabled(Toggle::UseD3D12ResidencyManagement)) { + UpdateVideoMemoryInfo(); + } + + // Increments number of locks on a heap to ensure the heap remains resident. + MaybeError ResidencyManager::LockAllocation(Pageable* pageable) { + if (!mResidencyManagementEnabled) { + return {}; + } + + // If the heap isn't already resident, make it resident. + if (!pageable->IsInResidencyLRUCache() && !pageable->IsResidencyLocked()) { + ID3D12Pageable* d3d12Pageable = pageable->GetD3D12Pageable(); + uint64_t size = pageable->GetSize(); + + DAWN_TRY(MakeAllocationsResident(GetMemorySegmentInfo(pageable->GetMemorySegment()), + size, 1, &d3d12Pageable)); + } + + // Since we can't evict the heap, it's unnecessary to track the heap in the LRU Cache. + if (pageable->IsInResidencyLRUCache()) { + pageable->RemoveFromList(); + } + + pageable->IncrementResidencyLock(); + + return {}; + } + + // Decrements number of locks on a heap. When the number of locks becomes zero, the heap is + // inserted into the LRU cache and becomes eligible for eviction. + void ResidencyManager::UnlockAllocation(Pageable* pageable) { + if (!mResidencyManagementEnabled) { + return; + } + + ASSERT(pageable->IsResidencyLocked()); + ASSERT(!pageable->IsInResidencyLRUCache()); + pageable->DecrementResidencyLock(); + + // If another lock still exists on the heap, nothing further should be done. + if (pageable->IsResidencyLocked()) { + return; + } + + // When all locks have been removed, the resource remains resident and becomes tracked in + // the corresponding LRU. + TrackResidentAllocation(pageable); + } + + // Returns the appropriate MemorySegmentInfo for a given MemorySegment. + ResidencyManager::MemorySegmentInfo* ResidencyManager::GetMemorySegmentInfo( + MemorySegment memorySegment) { + switch (memorySegment) { + case MemorySegment::Local: + return &mVideoMemoryInfo.local; + case MemorySegment::NonLocal: + ASSERT(!mDevice->GetDeviceInfo().isUMA); + return &mVideoMemoryInfo.nonLocal; + default: + UNREACHABLE(); + } + } + + // Allows an application component external to Dawn to cap Dawn's residency budgets to prevent + // competition for device memory. Returns the amount of memory reserved, which may be less + // that the requested reservation when under pressure. + uint64_t ResidencyManager::SetExternalMemoryReservation(MemorySegment segment, + uint64_t requestedReservationSize) { + MemorySegmentInfo* segmentInfo = GetMemorySegmentInfo(segment); + + segmentInfo->externalRequest = requestedReservationSize; + + UpdateMemorySegmentInfo(segmentInfo); + + return segmentInfo->externalReservation; + } + + void ResidencyManager::UpdateVideoMemoryInfo() { + UpdateMemorySegmentInfo(&mVideoMemoryInfo.local); + if (!mDevice->GetDeviceInfo().isUMA) { + UpdateMemorySegmentInfo(&mVideoMemoryInfo.nonLocal); + } + } + + void ResidencyManager::UpdateMemorySegmentInfo(MemorySegmentInfo* segmentInfo) { + DXGI_QUERY_VIDEO_MEMORY_INFO queryVideoMemoryInfo; + + ToBackend(mDevice->GetAdapter()) + ->GetHardwareAdapter() + ->QueryVideoMemoryInfo(0, segmentInfo->dxgiSegment, &queryVideoMemoryInfo); + + // The video memory budget provided by QueryVideoMemoryInfo is defined by the operating + // system, and may be lower than expected in certain scenarios. Under memory pressure, we + // cap the external reservation to half the available budget, which prevents the external + // component from consuming a disproportionate share of memory and ensures that Dawn can + // continue to make forward progress. Note the choice to halve memory is arbitrarily chosen + // and subject to future experimentation. + segmentInfo->externalReservation = + std::min(queryVideoMemoryInfo.Budget / 2, segmentInfo->externalRequest); + + segmentInfo->usage = queryVideoMemoryInfo.CurrentUsage - segmentInfo->externalReservation; + + // If we're restricting the budget for testing, leave the budget as is. + if (mRestrictBudgetForTesting) { + return; + } + + // We cap Dawn's budget to 95% of the provided budget. Leaving some budget unused + // decreases fluctuations in the operating-system-defined budget, which improves stability + // for both Dawn and other applications on the system. Note the value of 95% is arbitrarily + // chosen and subject to future experimentation. + static constexpr float kBudgetCap = 0.95; + segmentInfo->budget = + (queryVideoMemoryInfo.Budget - segmentInfo->externalReservation) * kBudgetCap; + } + + // Removes a heap from the LRU and returns the least recently used heap when possible. Returns + // nullptr when nothing further can be evicted. + ResultOrError ResidencyManager::RemoveSingleEntryFromLRU( + MemorySegmentInfo* memorySegment) { + // If the LRU is empty, return nullptr to allow execution to continue. Note that fully + // emptying the LRU is undesirable, because it can mean either 1) the LRU is not accurately + // accounting for Dawn's GPU allocations, or 2) a component external to Dawn is using all of + // the process budget and starving Dawn, which will cause thrash. + if (memorySegment->lruCache.empty()) { + return nullptr; + } + + Pageable* pageable = memorySegment->lruCache.head()->value(); + + Serial lastSubmissionSerial = pageable->GetLastSubmission(); + + // If the next candidate for eviction was inserted into the LRU during the current serial, + // it is because more memory is being used in a single command list than is available. + // In this scenario, we cannot make any more resources resident and thrashing must occur. + if (lastSubmissionSerial == mDevice->GetPendingCommandSerial()) { + return nullptr; + } + + // We must ensure that any previous use of a resource has completed before the resource can + // be evicted. + if (lastSubmissionSerial > mDevice->GetCompletedCommandSerial()) { + DAWN_TRY(mDevice->WaitForSerial(lastSubmissionSerial)); + } + + pageable->RemoveFromList(); + return pageable; + } + + MaybeError ResidencyManager::EnsureCanAllocate(uint64_t allocationSize, + MemorySegment memorySegment) { + if (!mResidencyManagementEnabled) { + return {}; + } + + uint64_t bytesEvicted; + DAWN_TRY_ASSIGN(bytesEvicted, + EnsureCanMakeResident(allocationSize, GetMemorySegmentInfo(memorySegment))); + + return {}; + } + + // Any time we need to make something resident, we must check that we have enough free memory to + // make the new object resident while also staying within budget. If there isn't enough + // memory, we should evict until there is. Returns the number of bytes evicted. + ResultOrError ResidencyManager::EnsureCanMakeResident( + uint64_t sizeToMakeResident, + MemorySegmentInfo* memorySegment) { + ASSERT(mResidencyManagementEnabled); + + UpdateMemorySegmentInfo(memorySegment); + + uint64_t memoryUsageAfterMakeResident = sizeToMakeResident + memorySegment->usage; + + // Return when we can call MakeResident and remain under budget. + if (memoryUsageAfterMakeResident < memorySegment->budget) { + return 0; + } + + std::vector resourcesToEvict; + uint64_t sizeNeededToBeUnderBudget = memoryUsageAfterMakeResident - memorySegment->budget; + uint64_t sizeEvicted = 0; + while (sizeEvicted < sizeNeededToBeUnderBudget) { + Pageable* pageable; + DAWN_TRY_ASSIGN(pageable, RemoveSingleEntryFromLRU(memorySegment)); + + // If no heap was returned, then nothing more can be evicted. + if (pageable == nullptr) { + break; + } + + sizeEvicted += pageable->GetSize(); + resourcesToEvict.push_back(pageable->GetD3D12Pageable()); + } + + if (resourcesToEvict.size() > 0) { + DAWN_TRY(CheckHRESULT( + mDevice->GetD3D12Device()->Evict(resourcesToEvict.size(), resourcesToEvict.data()), + "Evicting resident heaps to free memory")); + } + + return sizeEvicted; + } + + // Given a list of heaps that are pending usage, this function will estimate memory needed, + // evict resources until enough space is available, then make resident any heaps scheduled for + // usage. + MaybeError ResidencyManager::EnsureHeapsAreResident(Heap** heaps, size_t heapCount) { + if (!mResidencyManagementEnabled) { + return {}; + } + + std::vector localHeapsToMakeResident; + std::vector nonLocalHeapsToMakeResident; + uint64_t localSizeToMakeResident = 0; + uint64_t nonLocalSizeToMakeResident = 0; + + Serial pendingCommandSerial = mDevice->GetPendingCommandSerial(); + for (size_t i = 0; i < heapCount; i++) { + Heap* heap = heaps[i]; + + // Heaps that are locked resident are not tracked in the LRU cache. + if (heap->IsResidencyLocked()) { + continue; + } + + if (heap->IsInResidencyLRUCache()) { + // If the heap is already in the LRU, we must remove it and append again below to + // update its position in the LRU. + heap->RemoveFromList(); + } else { + if (heap->GetMemorySegment() == MemorySegment::Local) { + localSizeToMakeResident += heap->GetSize(); + localHeapsToMakeResident.push_back(heap->GetD3D12Pageable()); + } else { + nonLocalSizeToMakeResident += heap->GetSize(); + nonLocalHeapsToMakeResident.push_back(heap->GetD3D12Pageable()); + } + } + + // If we submit a command list to the GPU, we must ensure that heaps referenced by that + // command list stay resident at least until that command list has finished execution. + // Setting this serial unnecessarily can leave the LRU in a state where nothing is + // eligible for eviction, even though some evictions may be possible. + heap->SetLastSubmission(pendingCommandSerial); + + // Insert the heap into the appropriate LRU. + TrackResidentAllocation(heap); + } + + if (localSizeToMakeResident > 0) { + return MakeAllocationsResident(&mVideoMemoryInfo.local, localSizeToMakeResident, + localHeapsToMakeResident.size(), + localHeapsToMakeResident.data()); + } + + if (nonLocalSizeToMakeResident > 0) { + ASSERT(!mDevice->GetDeviceInfo().isUMA); + return MakeAllocationsResident(&mVideoMemoryInfo.nonLocal, nonLocalSizeToMakeResident, + nonLocalHeapsToMakeResident.size(), + nonLocalHeapsToMakeResident.data()); + } + + return {}; + } + + MaybeError ResidencyManager::MakeAllocationsResident(MemorySegmentInfo* segment, + uint64_t sizeToMakeResident, + uint64_t numberOfObjectsToMakeResident, + ID3D12Pageable** allocations) { + uint64_t bytesEvicted; + DAWN_TRY_ASSIGN(bytesEvicted, EnsureCanMakeResident(sizeToMakeResident, segment)); + + // Note that MakeResident is a synchronous function and can add a significant + // overhead to command recording. In the future, it may be possible to decrease this + // overhead by using MakeResident on a secondary thread, or by instead making use of + // the EnqueueMakeResident function (which is not available on all Windows 10 + // platforms). + HRESULT hr = + mDevice->GetD3D12Device()->MakeResident(numberOfObjectsToMakeResident, allocations); + + // A MakeResident call can fail if there's not enough available memory. This + // could occur when there's significant fragmentation or if the allocation size + // estimates are incorrect. We may be able to continue execution by evicting some + // more memory and calling MakeResident again. + while (FAILED(hr)) { + constexpr uint32_t kAdditonalSizeToEvict = 50000000; // 50MB + + uint64_t sizeEvicted = 0; + + DAWN_TRY_ASSIGN(sizeEvicted, EnsureCanMakeResident(kAdditonalSizeToEvict, segment)); + + // If nothing can be evicted after MakeResident has failed, we cannot continue + // execution and must throw a fatal error. + if (sizeEvicted == 0) { + return DAWN_OUT_OF_MEMORY_ERROR( + "MakeResident has failed due to excessive video memory usage."); + } + + hr = + mDevice->GetD3D12Device()->MakeResident(numberOfObjectsToMakeResident, allocations); + } + + return {}; + } + + // Inserts a heap at the bottom of the LRU. The passed heap must be resident or scheduled to + // become resident within the current serial. Failing to call this function when an allocation + // is implicitly made resident will cause the residency manager to view the allocation as + // non-resident and call MakeResident - which will make D3D12's internal residency refcount on + // the allocation out of sync with Dawn. + void ResidencyManager::TrackResidentAllocation(Pageable* pageable) { + if (!mResidencyManagementEnabled) { + return; + } + + ASSERT(pageable->IsInList() == false); + GetMemorySegmentInfo(pageable->GetMemorySegment())->lruCache.Append(pageable); + } + + // Places an artifical cap on Dawn's budget so we can test in a predictable manner. If used, + // this function must be called before any resources have been created. + void ResidencyManager::RestrictBudgetForTesting(uint64_t artificialBudgetCap) { + ASSERT(mVideoMemoryInfo.local.lruCache.empty()); + ASSERT(mVideoMemoryInfo.nonLocal.lruCache.empty()); + ASSERT(!mRestrictBudgetForTesting); + + mRestrictBudgetForTesting = true; + UpdateVideoMemoryInfo(); + + // Dawn has a non-zero memory usage even before any resources have been created, and this + // value can vary depending on the environment Dawn is running in. By adding this in + // addition to the artificial budget cap, we can create a predictable and reproducible + // budget for testing. + mVideoMemoryInfo.local.budget = mVideoMemoryInfo.local.usage + artificialBudgetCap; + if (!mDevice->GetDeviceInfo().isUMA) { + mVideoMemoryInfo.nonLocal.budget = + mVideoMemoryInfo.nonLocal.usage + artificialBudgetCap; + } + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/ResidencyManagerD3D12.h b/third_party/dawn/src/dawn_native/d3d12/ResidencyManagerD3D12.h new file mode 100644 index 00000000000..a79a4fca6a2 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/ResidencyManagerD3D12.h @@ -0,0 +1,83 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_RESIDENCYMANAGERD3D12_H_ +#define DAWNNATIVE_D3D12_RESIDENCYMANAGERD3D12_H_ + +#include "common/LinkedList.h" +#include "common/Serial.h" +#include "dawn_native/D3D12Backend.h" +#include "dawn_native/Error.h" +#include "dawn_native/dawn_platform.h" + +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + + class Device; + class Heap; + class Pageable; + + class ResidencyManager { + public: + ResidencyManager(Device* device); + + MaybeError LockAllocation(Pageable* pageable); + void UnlockAllocation(Pageable* pageable); + + MaybeError EnsureCanAllocate(uint64_t allocationSize, MemorySegment memorySegment); + MaybeError EnsureHeapsAreResident(Heap** heaps, size_t heapCount); + + uint64_t SetExternalMemoryReservation(MemorySegment segment, + uint64_t requestedReservationSize); + + void TrackResidentAllocation(Pageable* pageable); + + void RestrictBudgetForTesting(uint64_t artificialBudgetCap); + + private: + struct MemorySegmentInfo { + const DXGI_MEMORY_SEGMENT_GROUP dxgiSegment; + LinkedList lruCache = {}; + uint64_t budget = 0; + uint64_t usage = 0; + uint64_t externalReservation = 0; + uint64_t externalRequest = 0; + }; + + struct VideoMemoryInfo { + MemorySegmentInfo local = {DXGI_MEMORY_SEGMENT_GROUP_LOCAL}; + MemorySegmentInfo nonLocal = {DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL}; + }; + + MemorySegmentInfo* GetMemorySegmentInfo(MemorySegment memorySegment); + ResultOrError EnsureCanMakeResident(uint64_t allocationSize, + MemorySegmentInfo* memorySegment); + ResultOrError RemoveSingleEntryFromLRU(MemorySegmentInfo* memorySegment); + MaybeError MakeAllocationsResident(MemorySegmentInfo* segment, + uint64_t sizeToMakeResident, + uint64_t numberOfObjectsToMakeResident, + ID3D12Pageable** allocations); + void UpdateVideoMemoryInfo(); + void UpdateMemorySegmentInfo(MemorySegmentInfo* segmentInfo); + + Device* mDevice; + bool mResidencyManagementEnabled = false; + bool mRestrictBudgetForTesting = false; + VideoMemoryInfo mVideoMemoryInfo = {}; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_RESIDENCYMANAGERD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp new file mode 100644 index 00000000000..e8d9af3db82 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp @@ -0,0 +1,399 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/ResourceAllocatorManagerD3D12.h" + +#include "dawn_native/d3d12/D3D12Error.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/HeapAllocatorD3D12.h" +#include "dawn_native/d3d12/HeapD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" +#include "dawn_native/d3d12/UtilsD3D12.h" + +namespace dawn_native { namespace d3d12 { + namespace { + MemorySegment GetMemorySegment(Device* device, D3D12_HEAP_TYPE heapType) { + if (device->GetDeviceInfo().isUMA) { + return MemorySegment::Local; + } + + D3D12_HEAP_PROPERTIES heapProperties = + device->GetD3D12Device()->GetCustomHeapProperties(0, heapType); + + if (heapProperties.MemoryPoolPreference == D3D12_MEMORY_POOL_L1) { + return MemorySegment::Local; + } + + return MemorySegment::NonLocal; + } + + D3D12_HEAP_TYPE GetD3D12HeapType(ResourceHeapKind resourceHeapKind) { + switch (resourceHeapKind) { + case Readback_OnlyBuffers: + case Readback_AllBuffersAndTextures: + return D3D12_HEAP_TYPE_READBACK; + case Default_AllBuffersAndTextures: + case Default_OnlyBuffers: + case Default_OnlyNonRenderableOrDepthTextures: + case Default_OnlyRenderableOrDepthTextures: + return D3D12_HEAP_TYPE_DEFAULT; + case Upload_OnlyBuffers: + case Upload_AllBuffersAndTextures: + return D3D12_HEAP_TYPE_UPLOAD; + default: + UNREACHABLE(); + } + } + + D3D12_HEAP_FLAGS GetD3D12HeapFlags(ResourceHeapKind resourceHeapKind) { + switch (resourceHeapKind) { + case Default_AllBuffersAndTextures: + case Readback_AllBuffersAndTextures: + case Upload_AllBuffersAndTextures: + return D3D12_HEAP_FLAG_ALLOW_ALL_BUFFERS_AND_TEXTURES; + case Default_OnlyBuffers: + case Readback_OnlyBuffers: + case Upload_OnlyBuffers: + return D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS; + case Default_OnlyNonRenderableOrDepthTextures: + return D3D12_HEAP_FLAG_ALLOW_ONLY_NON_RT_DS_TEXTURES; + case Default_OnlyRenderableOrDepthTextures: + return D3D12_HEAP_FLAG_ALLOW_ONLY_RT_DS_TEXTURES; + default: + UNREACHABLE(); + } + } + + ResourceHeapKind GetResourceHeapKind(D3D12_RESOURCE_DIMENSION dimension, + D3D12_HEAP_TYPE heapType, + D3D12_RESOURCE_FLAGS flags, + uint32_t resourceHeapTier) { + if (resourceHeapTier >= 2) { + switch (heapType) { + case D3D12_HEAP_TYPE_UPLOAD: + return Upload_AllBuffersAndTextures; + case D3D12_HEAP_TYPE_DEFAULT: + return Default_AllBuffersAndTextures; + case D3D12_HEAP_TYPE_READBACK: + return Readback_AllBuffersAndTextures; + default: + UNREACHABLE(); + } + } + + switch (dimension) { + case D3D12_RESOURCE_DIMENSION_BUFFER: { + switch (heapType) { + case D3D12_HEAP_TYPE_UPLOAD: + return Upload_OnlyBuffers; + case D3D12_HEAP_TYPE_DEFAULT: + return Default_OnlyBuffers; + case D3D12_HEAP_TYPE_READBACK: + return Readback_OnlyBuffers; + default: + UNREACHABLE(); + } + break; + } + case D3D12_RESOURCE_DIMENSION_TEXTURE1D: + case D3D12_RESOURCE_DIMENSION_TEXTURE2D: + case D3D12_RESOURCE_DIMENSION_TEXTURE3D: { + switch (heapType) { + case D3D12_HEAP_TYPE_DEFAULT: { + if ((flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) || + (flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET)) { + return Default_OnlyRenderableOrDepthTextures; + } + return Default_OnlyNonRenderableOrDepthTextures; + } + + default: + UNREACHABLE(); + } + break; + } + default: + UNREACHABLE(); + } + } + + uint64_t GetResourcePlacementAlignment(ResourceHeapKind resourceHeapKind, + uint32_t sampleCount, + uint64_t requestedAlignment) { + switch (resourceHeapKind) { + // Small resources can take advantage of smaller alignments. For example, + // if the most detailed mip can fit under 64KB, 4KB alignments can be used. + // Must be non-depth or without render-target to use small resource alignment. + // This also applies to MSAA textures (4MB => 64KB). + // + // Note: Only known to be used for small textures; however, MSDN suggests + // it could be extended for more cases. If so, this could default to always + // attempt small resource placement. + // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ns-d3d12-d3d12_resource_desc + case Default_OnlyNonRenderableOrDepthTextures: + return (sampleCount > 1) ? D3D12_SMALL_MSAA_RESOURCE_PLACEMENT_ALIGNMENT + : D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT; + default: + return requestedAlignment; + } + } + + bool IsClearValueOptimizable(const D3D12_RESOURCE_DESC& resourceDescriptor) { + // Optimized clear color cannot be set on buffers, non-render-target/depth-stencil + // textures, or typeless resources + // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createcommittedresource + // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource + return !IsTypeless(resourceDescriptor.Format) && + resourceDescriptor.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER && + (resourceDescriptor.Flags & (D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET | + D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL)) != 0; + } + + } // namespace + + ResourceAllocatorManager::ResourceAllocatorManager(Device* device) : mDevice(device) { + mResourceHeapTier = (mDevice->IsToggleEnabled(Toggle::UseD3D12ResourceHeapTier2)) + ? mDevice->GetDeviceInfo().resourceHeapTier + : 1; + + for (uint32_t i = 0; i < ResourceHeapKind::EnumCount; i++) { + const ResourceHeapKind resourceHeapKind = static_cast(i); + mHeapAllocators[i] = std::make_unique( + mDevice, GetD3D12HeapType(resourceHeapKind), GetD3D12HeapFlags(resourceHeapKind), + GetMemorySegment(device, GetD3D12HeapType(resourceHeapKind))); + mSubAllocatedResourceAllocators[i] = std::make_unique( + kMaxHeapSize, kMinHeapSize, mHeapAllocators[i].get()); + } + } + + ResultOrError ResourceAllocatorManager::AllocateMemory( + D3D12_HEAP_TYPE heapType, + const D3D12_RESOURCE_DESC& resourceDescriptor, + D3D12_RESOURCE_STATES initialUsage) { + // In order to suppress a warning in the D3D12 debug layer, we need to specify an + // optimized clear value. As there are no negative consequences when picking a mismatched + // clear value, we use zero as the optimized clear value. This also enables fast clears on + // some architectures. + D3D12_CLEAR_VALUE zero{}; + D3D12_CLEAR_VALUE* optimizedClearValue = nullptr; + if (IsClearValueOptimizable(resourceDescriptor)) { + zero.Format = resourceDescriptor.Format; + optimizedClearValue = &zero; + } + + // TODO(bryan.bernhart@intel.com): Conditionally disable sub-allocation. + // For very large resources, there is no benefit to suballocate. + // For very small resources, it is inefficent to suballocate given the min. heap + // size could be much larger then the resource allocation. + // Attempt to satisfy the request using sub-allocation (placed resource in a heap). + ResourceHeapAllocation subAllocation; + DAWN_TRY_ASSIGN(subAllocation, CreatePlacedResource(heapType, resourceDescriptor, + optimizedClearValue, initialUsage)); + if (subAllocation.GetInfo().mMethod != AllocationMethod::kInvalid) { + return std::move(subAllocation); + } + + // If sub-allocation fails, fall-back to direct allocation (committed resource). + ResourceHeapAllocation directAllocation; + DAWN_TRY_ASSIGN(directAllocation, + CreateCommittedResource(heapType, resourceDescriptor, optimizedClearValue, + initialUsage)); + if (directAllocation.GetInfo().mMethod != AllocationMethod::kInvalid) { + return std::move(directAllocation); + } + + // If direct allocation fails, the system is probably out of memory. + return DAWN_OUT_OF_MEMORY_ERROR("Allocation failed"); + } + + void ResourceAllocatorManager::Tick(Serial completedSerial) { + for (ResourceHeapAllocation& allocation : + mAllocationsToDelete.IterateUpTo(completedSerial)) { + if (allocation.GetInfo().mMethod == AllocationMethod::kSubAllocated) { + FreeMemory(allocation); + } + } + mAllocationsToDelete.ClearUpTo(completedSerial); + } + + void ResourceAllocatorManager::DeallocateMemory(ResourceHeapAllocation& allocation) { + if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) { + return; + } + + mAllocationsToDelete.Enqueue(allocation, mDevice->GetPendingCommandSerial()); + + // Directly allocated ResourceHeapAllocations are created with a heap object that must be + // manually deleted upon deallocation. See ResourceAllocatorManager::CreateCommittedResource + // for more information. + if (allocation.GetInfo().mMethod == AllocationMethod::kDirect) { + delete allocation.GetResourceHeap(); + } + + // Invalidate the allocation immediately in case one accidentally + // calls DeallocateMemory again using the same allocation. + allocation.Invalidate(); + + ASSERT(allocation.GetD3D12Resource() == nullptr); + } + + void ResourceAllocatorManager::FreeMemory(ResourceHeapAllocation& allocation) { + ASSERT(allocation.GetInfo().mMethod == AllocationMethod::kSubAllocated); + + D3D12_HEAP_PROPERTIES heapProp; + allocation.GetD3D12Resource()->GetHeapProperties(&heapProp, nullptr); + + const D3D12_RESOURCE_DESC resourceDescriptor = allocation.GetD3D12Resource()->GetDesc(); + + const size_t resourceHeapKindIndex = + GetResourceHeapKind(resourceDescriptor.Dimension, heapProp.Type, + resourceDescriptor.Flags, mResourceHeapTier); + + mSubAllocatedResourceAllocators[resourceHeapKindIndex]->Deallocate(allocation); + } + + ResultOrError ResourceAllocatorManager::CreatePlacedResource( + D3D12_HEAP_TYPE heapType, + const D3D12_RESOURCE_DESC& requestedResourceDescriptor, + const D3D12_CLEAR_VALUE* optimizedClearValue, + D3D12_RESOURCE_STATES initialUsage) { + const ResourceHeapKind resourceHeapKind = + GetResourceHeapKind(requestedResourceDescriptor.Dimension, heapType, + requestedResourceDescriptor.Flags, mResourceHeapTier); + + D3D12_RESOURCE_DESC resourceDescriptor = requestedResourceDescriptor; + resourceDescriptor.Alignment = GetResourcePlacementAlignment( + resourceHeapKind, requestedResourceDescriptor.SampleDesc.Count, + requestedResourceDescriptor.Alignment); + + D3D12_RESOURCE_ALLOCATION_INFO resourceInfo = + mDevice->GetD3D12Device()->GetResourceAllocationInfo(0, 1, &resourceDescriptor); + + // If the requested resource alignment was rejected, let D3D tell us what the + // required alignment is for this resource. + if (resourceDescriptor.Alignment != resourceInfo.Alignment) { + resourceDescriptor.Alignment = 0; + resourceInfo = + mDevice->GetD3D12Device()->GetResourceAllocationInfo(0, 1, &resourceDescriptor); + } + + // If d3d tells us the resource size is invalid, treat the error as OOM. + // Otherwise, creating the resource could cause a device loss (too large). + // This is because NextPowerOfTwo(UINT64_MAX) overflows and proceeds to + // incorrectly allocate a mismatched size. + if (resourceInfo.SizeInBytes == 0 || + resourceInfo.SizeInBytes == std::numeric_limits::max()) { + return DAWN_OUT_OF_MEMORY_ERROR("Resource allocation size was invalid."); + } + + BuddyMemoryAllocator* allocator = + mSubAllocatedResourceAllocators[static_cast(resourceHeapKind)].get(); + + ResourceMemoryAllocation allocation; + DAWN_TRY_ASSIGN(allocation, + allocator->Allocate(resourceInfo.SizeInBytes, resourceInfo.Alignment)); + if (allocation.GetInfo().mMethod == AllocationMethod::kInvalid) { + return ResourceHeapAllocation{}; // invalid + } + + Heap* heap = ToBackend(allocation.GetResourceHeap()); + + // Before calling CreatePlacedResource, we must ensure the target heap is resident. + // CreatePlacedResource will fail if it is not. + DAWN_TRY(mDevice->GetResidencyManager()->LockAllocation(heap)); + + // With placed resources, a single heap can be reused. + // The resource placed at an offset is only reclaimed + // upon Tick or after the last command list using the resource has completed + // on the GPU. This means the same physical memory is not reused + // within the same command-list and does not require additional synchronization (aliasing + // barrier). + // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12device-createplacedresource + ComPtr placedResource; + DAWN_TRY(CheckOutOfMemoryHRESULT( + mDevice->GetD3D12Device()->CreatePlacedResource( + heap->GetD3D12Heap(), allocation.GetOffset(), &resourceDescriptor, initialUsage, + optimizedClearValue, IID_PPV_ARGS(&placedResource)), + "ID3D12Device::CreatePlacedResource")); + + // After CreatePlacedResource has finished, the heap can be unlocked from residency. This + // will insert it into the residency LRU. + mDevice->GetResidencyManager()->UnlockAllocation(heap); + + return ResourceHeapAllocation{allocation.GetInfo(), allocation.GetOffset(), + std::move(placedResource), heap}; + } + + ResultOrError ResourceAllocatorManager::CreateCommittedResource( + D3D12_HEAP_TYPE heapType, + const D3D12_RESOURCE_DESC& resourceDescriptor, + const D3D12_CLEAR_VALUE* optimizedClearValue, + D3D12_RESOURCE_STATES initialUsage) { + D3D12_HEAP_PROPERTIES heapProperties; + heapProperties.Type = heapType; + heapProperties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN; + heapProperties.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN; + heapProperties.CreationNodeMask = 0; + heapProperties.VisibleNodeMask = 0; + + // If d3d tells us the resource size is invalid, treat the error as OOM. + // Otherwise, creating the resource could cause a device loss (too large). + // This is because NextPowerOfTwo(UINT64_MAX) overflows and proceeds to + // incorrectly allocate a mismatched size. + D3D12_RESOURCE_ALLOCATION_INFO resourceInfo = + mDevice->GetD3D12Device()->GetResourceAllocationInfo(0, 1, &resourceDescriptor); + if (resourceInfo.SizeInBytes == 0 || + resourceInfo.SizeInBytes == std::numeric_limits::max()) { + return DAWN_OUT_OF_MEMORY_ERROR("Resource allocation size was invalid."); + } + + if (resourceInfo.SizeInBytes > kMaxHeapSize) { + return ResourceHeapAllocation{}; // Invalid + } + + // CreateCommittedResource will implicitly make the created resource resident. We must + // ensure enough free memory exists before allocating to avoid an out-of-memory error when + // overcommitted. + DAWN_TRY(mDevice->GetResidencyManager()->EnsureCanAllocate( + resourceInfo.SizeInBytes, GetMemorySegment(mDevice, heapType))); + + // Note: Heap flags are inferred by the resource descriptor and do not need to be explicitly + // provided to CreateCommittedResource. + ComPtr committedResource; + DAWN_TRY(CheckOutOfMemoryHRESULT( + mDevice->GetD3D12Device()->CreateCommittedResource( + &heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDescriptor, initialUsage, + optimizedClearValue, IID_PPV_ARGS(&committedResource)), + "ID3D12Device::CreateCommittedResource")); + + // When using CreateCommittedResource, D3D12 creates an implicit heap that contains the + // resource allocation. Because Dawn's memory residency management occurs at the resource + // heap granularity, every directly allocated ResourceHeapAllocation also stores a Heap + // object. This object is created manually, and must be deleted manually upon deallocation + // of the committed resource. + Heap* heap = new Heap(committedResource, GetMemorySegment(mDevice, heapType), + resourceInfo.SizeInBytes); + + // Calling CreateCommittedResource implicitly calls MakeResident on the resource. We must + // track this to avoid calling MakeResident a second time. + mDevice->GetResidencyManager()->TrackResidentAllocation(heap); + + AllocationInfo info; + info.mMethod = AllocationMethod::kDirect; + + return ResourceHeapAllocation{info, + /*offset*/ 0, std::move(committedResource), heap}; + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h b/third_party/dawn/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h new file mode 100644 index 00000000000..2e7cb39abbe --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.h @@ -0,0 +1,100 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_RESOURCEALLOCATORMANAGERD3D12_H_ +#define DAWNNATIVE_D3D12_RESOURCEALLOCATORMANAGERD3D12_H_ + +#include "common/SerialQueue.h" +#include "dawn_native/BuddyMemoryAllocator.h" +#include "dawn_native/d3d12/HeapAllocatorD3D12.h" +#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h" + +#include + +namespace dawn_native { namespace d3d12 { + + class Device; + + // Resource heap types + flags combinations are named after the D3D constants. + // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_heap_flags + // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_heap_type + enum ResourceHeapKind { + + // Resource heap tier 2 + // Allows resource heaps to contain all buffer and textures types. + // This enables better heap re-use by avoiding the need for separate heaps and + // also reduces fragmentation. + Readback_AllBuffersAndTextures, + Upload_AllBuffersAndTextures, + Default_AllBuffersAndTextures, + + // Resource heap tier 1 + // Resource heaps only support types from a single resource category. + Readback_OnlyBuffers, + Upload_OnlyBuffers, + Default_OnlyBuffers, + + Default_OnlyNonRenderableOrDepthTextures, + Default_OnlyRenderableOrDepthTextures, + + EnumCount, + InvalidEnum = EnumCount, + }; + + // Manages a list of resource allocators used by the device to create resources using + // multiple allocation methods. + class ResourceAllocatorManager { + public: + ResourceAllocatorManager(Device* device); + + ResultOrError AllocateMemory( + D3D12_HEAP_TYPE heapType, + const D3D12_RESOURCE_DESC& resourceDescriptor, + D3D12_RESOURCE_STATES initialUsage); + + void DeallocateMemory(ResourceHeapAllocation& allocation); + + void Tick(Serial lastCompletedSerial); + + private: + void FreeMemory(ResourceHeapAllocation& allocation); + + ResultOrError CreatePlacedResource( + D3D12_HEAP_TYPE heapType, + const D3D12_RESOURCE_DESC& requestedResourceDescriptor, + const D3D12_CLEAR_VALUE* optimizedClearValue, + D3D12_RESOURCE_STATES initialUsage); + + ResultOrError CreateCommittedResource( + D3D12_HEAP_TYPE heapType, + const D3D12_RESOURCE_DESC& resourceDescriptor, + const D3D12_CLEAR_VALUE* optimizedClearValue, + D3D12_RESOURCE_STATES initialUsage); + + Device* mDevice; + uint32_t mResourceHeapTier; + + static constexpr uint64_t kMaxHeapSize = 32ll * 1024ll * 1024ll * 1024ll; // 32GB + static constexpr uint64_t kMinHeapSize = 4ll * 1024ll * 1024ll; // 4MB + + std::array, ResourceHeapKind::EnumCount> + mSubAllocatedResourceAllocators; + std::array, ResourceHeapKind::EnumCount> mHeapAllocators; + + SerialQueue mAllocationsToDelete; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_RESOURCEALLOCATORMANAGERD3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp new file mode 100644 index 00000000000..edf21c1e06d --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.cpp @@ -0,0 +1,42 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h" + +#include "dawn_native/d3d12/HeapD3D12.h" + +#include + +namespace dawn_native { namespace d3d12 { + ResourceHeapAllocation::ResourceHeapAllocation(const AllocationInfo& info, + uint64_t offset, + ComPtr resource, + Heap* heap) + : ResourceMemoryAllocation(info, offset, heap), mResource(std::move(resource)) { + ASSERT((info.mMethod == AllocationMethod::kExternal) == (heap == nullptr)); + } + + void ResourceHeapAllocation::Invalidate() { + ResourceMemoryAllocation::Invalidate(); + mResource.Reset(); + } + + ID3D12Resource* ResourceHeapAllocation::GetD3D12Resource() const { + return mResource.Get(); + } + + D3D12_GPU_VIRTUAL_ADDRESS ResourceHeapAllocation::GetGPUPointer() const { + return mResource->GetGPUVirtualAddress(); + } +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h b/third_party/dawn/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h new file mode 100644 index 00000000000..ace1a7efa00 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/ResourceHeapAllocationD3D12.h @@ -0,0 +1,45 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_RESOURCEHEAPALLOCATIOND3D12_H_ +#define DAWNNATIVE_D3D12_RESOURCEHEAPALLOCATIOND3D12_H_ + +#include "dawn_native/ResourceMemoryAllocation.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +namespace dawn_native { namespace d3d12 { + + class Heap; + + class ResourceHeapAllocation : public ResourceMemoryAllocation { + public: + ResourceHeapAllocation() = default; + ResourceHeapAllocation(const AllocationInfo& info, + uint64_t offset, + ComPtr resource, + Heap* heap); + ~ResourceHeapAllocation() override = default; + + void Invalidate() override; + + ID3D12Resource* GetD3D12Resource() const; + D3D12_GPU_VIRTUAL_ADDRESS GetGPUPointer() const; + + private: + ComPtr mResource; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_RESOURCEHEAPALLOCATIOND3D12_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/SamplerD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/SamplerD3D12.cpp index 68afa1ac8a7..931a3b78d8d 100644 --- a/third_party/dawn/src/dawn_native/d3d12/SamplerD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/SamplerD3D12.cpp @@ -20,13 +20,13 @@ namespace dawn_native { namespace d3d12 { namespace { - D3D12_TEXTURE_ADDRESS_MODE AddressMode(dawn::AddressMode mode) { + D3D12_TEXTURE_ADDRESS_MODE AddressMode(wgpu::AddressMode mode) { switch (mode) { - case dawn::AddressMode::Repeat: + case wgpu::AddressMode::Repeat: return D3D12_TEXTURE_ADDRESS_MODE_WRAP; - case dawn::AddressMode::MirroredRepeat: + case wgpu::AddressMode::MirrorRepeat: return D3D12_TEXTURE_ADDRESS_MODE_MIRROR; - case dawn::AddressMode::ClampToEdge: + case wgpu::AddressMode::ClampToEdge: return D3D12_TEXTURE_ADDRESS_MODE_CLAMP; default: UNREACHABLE(); @@ -36,54 +36,63 @@ namespace dawn_native { namespace d3d12 { Sampler::Sampler(Device* device, const SamplerDescriptor* descriptor) : SamplerBase(device, descriptor) { - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn770367(v=vs.85).aspx - // hex value, decimal value, min linear, mag linear, mip linear - // D3D12_FILTER_MIN_MAG_MIP_POINT = 0 0 0 0 0 - // D3D12_FILTER_MIN_MAG_POINT_MIP_LINEAR = 0x1 1 0 0 1 - // D3D12_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT = 0x4 4 0 1 0 - // D3D12_FILTER_MIN_POINT_MAG_MIP_LINEAR = 0x5 5 0 1 1 - // D3D12_FILTER_MIN_LINEAR_MAG_MIP_POINT = 0x10 16 1 0 0 - // D3D12_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR = 0x11 17 1 0 1 - // D3D12_FILTER_MIN_MAG_LINEAR_MIP_POINT = 0x14 20 1 1 0 - // D3D12_FILTER_MIN_MAG_MIP_LINEAR = 0x15 21 1 1 1 - - // if mip mode is linear, add 1 - // if mag mode is linear, add 4 - // if min mode is linear, add 16 - - uint8_t mode = 0; - + D3D12_FILTER_TYPE minFilter; switch (descriptor->minFilter) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: + minFilter = D3D12_FILTER_TYPE_POINT; + break; + case wgpu::FilterMode::Linear: + minFilter = D3D12_FILTER_TYPE_LINEAR; break; - case dawn::FilterMode::Linear: - mode += 16; + default: + UNREACHABLE(); break; } + D3D12_FILTER_TYPE magFilter; switch (descriptor->magFilter) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: + magFilter = D3D12_FILTER_TYPE_POINT; break; - case dawn::FilterMode::Linear: - mode += 4; + case wgpu::FilterMode::Linear: + magFilter = D3D12_FILTER_TYPE_LINEAR; + break; + default: + UNREACHABLE(); break; } + D3D12_FILTER_TYPE mipmapFilter; switch (descriptor->mipmapFilter) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: + mipmapFilter = D3D12_FILTER_TYPE_POINT; + break; + case wgpu::FilterMode::Linear: + mipmapFilter = D3D12_FILTER_TYPE_LINEAR; break; - case dawn::FilterMode::Linear: - mode += 1; + default: + UNREACHABLE(); break; } - mSamplerDesc.Filter = static_cast(mode); + D3D12_FILTER_REDUCTION_TYPE reduction = + descriptor->compare == wgpu::CompareFunction::Undefined + ? D3D12_FILTER_REDUCTION_TYPE_STANDARD + : D3D12_FILTER_REDUCTION_TYPE_COMPARISON; + + mSamplerDesc.Filter = + D3D12_ENCODE_BASIC_FILTER(minFilter, magFilter, mipmapFilter, reduction); mSamplerDesc.AddressU = AddressMode(descriptor->addressModeU); mSamplerDesc.AddressV = AddressMode(descriptor->addressModeV); mSamplerDesc.AddressW = AddressMode(descriptor->addressModeW); mSamplerDesc.MipLODBias = 0.f; mSamplerDesc.MaxAnisotropy = 1; - mSamplerDesc.ComparisonFunc = ToD3D12ComparisonFunc(descriptor->compareFunction); + if (descriptor->compare != wgpu::CompareFunction::Undefined) { + mSamplerDesc.ComparisonFunc = ToD3D12ComparisonFunc(descriptor->compare); + } else { + // Still set the function so it's not garbage. + mSamplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_NEVER; + } mSamplerDesc.MinLOD = descriptor->lodMinClamp; mSamplerDesc.MaxLOD = descriptor->lodMaxClamp; } diff --git a/third_party/dawn/src/dawn_native/d3d12/SamplerD3D12.h b/third_party/dawn/src/dawn_native/d3d12/SamplerD3D12.h index 0c52716b44e..59a7d8370a4 100644 --- a/third_party/dawn/src/dawn_native/d3d12/SamplerD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/SamplerD3D12.h @@ -23,13 +23,14 @@ namespace dawn_native { namespace d3d12 { class Device; - class Sampler : public SamplerBase { + class Sampler final : public SamplerBase { public: Sampler(Device* device, const SamplerDescriptor* descriptor); const D3D12_SAMPLER_DESC& GetSamplerDescriptor() const; private: + ~Sampler() override = default; D3D12_SAMPLER_DESC mSamplerDesc = {}; }; diff --git a/third_party/dawn/src/dawn_native/d3d12/SamplerHeapCacheD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/SamplerHeapCacheD3D12.cpp new file mode 100644 index 00000000000..8ba3aab3197 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/SamplerHeapCacheD3D12.cpp @@ -0,0 +1,167 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/SamplerHeapCacheD3D12.h" + +#include "common/Assert.h" +#include "common/HashUtils.h" +#include "dawn_native/d3d12/BindGroupD3D12.h" +#include "dawn_native/d3d12/BindGroupLayoutD3D12.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/Forward.h" +#include "dawn_native/d3d12/SamplerD3D12.h" +#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" +#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" + +namespace dawn_native { namespace d3d12 { + + SamplerHeapCacheEntry::SamplerHeapCacheEntry(std::vector samplers) + : mSamplers(std::move(samplers)) { + } + + SamplerHeapCacheEntry::SamplerHeapCacheEntry(SamplerHeapCache* cache, + StagingDescriptorAllocator* allocator, + std::vector samplers, + CPUDescriptorHeapAllocation allocation) + : mCPUAllocation(std::move(allocation)), + mSamplers(std::move(samplers)), + mAllocator(allocator), + mCache(cache) { + ASSERT(mCache != nullptr); + ASSERT(mCPUAllocation.IsValid()); + ASSERT(!mSamplers.empty()); + } + + std::vector&& SamplerHeapCacheEntry::AcquireSamplers() { + return std::move(mSamplers); + } + + SamplerHeapCacheEntry::~SamplerHeapCacheEntry() { + // If this is a blueprint then the CPU allocation cannot exist and has no entry to remove. + if (mCPUAllocation.IsValid()) { + mCache->RemoveCacheEntry(this); + mAllocator->Deallocate(&mCPUAllocation); + } + + ASSERT(!mCPUAllocation.IsValid()); + } + + bool SamplerHeapCacheEntry::Populate(Device* device, + ShaderVisibleDescriptorAllocator* allocator) { + if (allocator->IsAllocationStillValid(mGPUAllocation)) { + return true; + } + + ASSERT(!mSamplers.empty()); + + // Attempt to allocate descriptors for the currently bound shader-visible heaps. + // If either failed, return early to re-allocate and switch the heaps. + const uint32_t descriptorCount = mSamplers.size(); + D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor; + if (!allocator->AllocateGPUDescriptors(descriptorCount, device->GetPendingCommandSerial(), + &baseCPUDescriptor, &mGPUAllocation)) { + return false; + } + + // CPU bindgroups are sparsely allocated across CPU heaps. Instead of doing + // simple copies per bindgroup, a single non-simple copy could be issued. + // TODO(dawn:155): Consider doing this optimization. + device->GetD3D12Device()->CopyDescriptorsSimple(descriptorCount, baseCPUDescriptor, + mCPUAllocation.GetBaseDescriptor(), + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + + return true; + } + + D3D12_GPU_DESCRIPTOR_HANDLE SamplerHeapCacheEntry::GetBaseDescriptor() const { + return mGPUAllocation.GetBaseDescriptor(); + } + + ResultOrError> SamplerHeapCache::GetOrCreate( + const BindGroup* group, + StagingDescriptorAllocator* samplerAllocator) { + const BindGroupLayout* bgl = ToBackend(group->GetLayout()); + + // If a previously created bindgroup used the same samplers, the backing sampler heap + // allocation can be reused. The packed list of samplers acts as the key to lookup the + // allocation in a cache. + // TODO(dawn:155): Avoid re-allocating the vector each lookup. + std::vector samplers; + samplers.reserve(bgl->GetSamplerDescriptorCount()); + + for (BindingIndex bindingIndex = bgl->GetDynamicBufferCount(); + bindingIndex < bgl->GetBindingCount(); ++bindingIndex) { + const BindingInfo& bindingInfo = bgl->GetBindingInfo(bindingIndex); + if (bindingInfo.type == wgpu::BindingType::Sampler || + bindingInfo.type == wgpu::BindingType::ComparisonSampler) { + samplers.push_back(ToBackend(group->GetBindingAsSampler(bindingIndex))); + } + } + + // Check the cache if there exists a sampler heap allocation that corresponds to the + // samplers. + SamplerHeapCacheEntry blueprint(std::move(samplers)); + auto iter = mCache.find(&blueprint); + if (iter != mCache.end()) { + return Ref(*iter); + } + + // Steal the sampler vector back from the blueprint to avoid creating a new copy for the + // real entry below. + samplers = std::move(blueprint.AcquireSamplers()); + + CPUDescriptorHeapAllocation allocation; + DAWN_TRY_ASSIGN(allocation, samplerAllocator->AllocateCPUDescriptors()); + + const uint32_t samplerSizeIncrement = samplerAllocator->GetSizeIncrement(); + ID3D12Device* d3d12Device = mDevice->GetD3D12Device(); + + for (uint32_t i = 0; i < samplers.size(); ++i) { + const auto& samplerDesc = samplers[i]->GetSamplerDescriptor(); + d3d12Device->CreateSampler(&samplerDesc, + allocation.OffsetFrom(samplerSizeIncrement, i)); + } + + Ref entry = AcquireRef(new SamplerHeapCacheEntry( + this, samplerAllocator, std::move(samplers), std::move(allocation))); + mCache.insert(entry.Get()); + return std::move(entry); + } + + SamplerHeapCache::SamplerHeapCache(Device* device) : mDevice(device) { + } + + SamplerHeapCache::~SamplerHeapCache() { + ASSERT(mCache.empty()); + } + + void SamplerHeapCache::RemoveCacheEntry(SamplerHeapCacheEntry* entry) { + ASSERT(entry->GetRefCountForTesting() == 0); + size_t removedCount = mCache.erase(entry); + ASSERT(removedCount == 1); + } + + size_t SamplerHeapCacheEntry::HashFunc::operator()(const SamplerHeapCacheEntry* entry) const { + size_t hash = 0; + for (const Sampler* sampler : entry->mSamplers) { + HashCombine(&hash, sampler); + } + return hash; + } + + bool SamplerHeapCacheEntry::EqualityFunc::operator()(const SamplerHeapCacheEntry* a, + const SamplerHeapCacheEntry* b) const { + return a->mSamplers == b->mSamplers; + } +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/SamplerHeapCacheD3D12.h b/third_party/dawn/src/dawn_native/d3d12/SamplerHeapCacheD3D12.h new file mode 100644 index 00000000000..2e5a2d4237a --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/SamplerHeapCacheD3D12.h @@ -0,0 +1,108 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_SAMPLERHEAPCACHE_H_ +#define DAWNNATIVE_D3D12_SAMPLERHEAPCACHE_H_ + +#include "common/RefCounted.h" +#include "dawn_native/BindingInfo.h" +#include "dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.h" +#include "dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h" + +#include +#include + +// |SamplerHeapCacheEntry| maintains a cache of sampler descriptor heap allocations. +// Each entry represents one or more sampler descriptors that co-exist in a CPU and +// GPU descriptor heap. The CPU-side allocation is deallocated once the final reference +// has been released while the GPU-side allocation is deallocated when the GPU is finished. +// +// The BindGroupLayout hands out these entries upon constructing the bindgroup. If the entry is not +// invalid, it will allocate and initialize so it may be reused by another bindgroup. +// +// The cache is primary needed for the GPU sampler heap, which is much smaller than the view heap +// and switches incur expensive pipeline flushes. +namespace dawn_native { namespace d3d12 { + + class BindGroup; + class Device; + class Sampler; + class SamplerHeapCache; + class StagingDescriptorAllocator; + class ShaderVisibleDescriptorAllocator; + + // Wraps sampler descriptor heap allocations in a cache. + class SamplerHeapCacheEntry : public RefCounted { + public: + SamplerHeapCacheEntry() = default; + SamplerHeapCacheEntry(std::vector samplers); + SamplerHeapCacheEntry(SamplerHeapCache* cache, + StagingDescriptorAllocator* allocator, + std::vector samplers, + CPUDescriptorHeapAllocation allocation); + ~SamplerHeapCacheEntry() override; + + D3D12_GPU_DESCRIPTOR_HANDLE GetBaseDescriptor() const; + + std::vector&& AcquireSamplers(); + + bool Populate(Device* device, ShaderVisibleDescriptorAllocator* allocator); + + // Functors necessary for the unordered_map-based cache. + struct HashFunc { + size_t operator()(const SamplerHeapCacheEntry* entry) const; + }; + + struct EqualityFunc { + bool operator()(const SamplerHeapCacheEntry* a, const SamplerHeapCacheEntry* b) const; + }; + + private: + CPUDescriptorHeapAllocation mCPUAllocation; + GPUDescriptorHeapAllocation mGPUAllocation; + + // Storing raw pointer because the sampler object will be already hashed + // by the device and will already be unique. + std::vector mSamplers; + + StagingDescriptorAllocator* mAllocator = nullptr; + SamplerHeapCache* mCache = nullptr; + }; + + // Cache descriptor heap allocations so that we don't create duplicate ones for every + // BindGroup. + class SamplerHeapCache { + public: + SamplerHeapCache(Device* device); + ~SamplerHeapCache(); + + ResultOrError> GetOrCreate( + const BindGroup* group, + StagingDescriptorAllocator* samplerAllocator); + + void RemoveCacheEntry(SamplerHeapCacheEntry* entry); + + private: + Device* mDevice; + + using Cache = std::unordered_set; + + Cache mCache; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_SAMPLERHEAPCACHE_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/ShaderModuleD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/ShaderModuleD3D12.cpp index 2973bb244da..0bdbafa12cb 100644 --- a/third_party/dawn/src/dawn_native/d3d12/ShaderModuleD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/ShaderModuleD3D12.cpp @@ -16,48 +16,288 @@ #include "common/Assert.h" #include "common/BitSetIterator.h" +#include "common/Log.h" #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/PipelineLayoutD3D12.h" +#include "dawn_native/d3d12/PlatformFunctions.h" +#include "dawn_native/d3d12/UtilsD3D12.h" -#include +#include + +#include namespace dawn_native { namespace d3d12 { + namespace { + std::vector GetDXCArguments(uint32_t compileFlags, bool enable16BitTypes) { + std::vector arguments; + if (compileFlags & D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY) { + arguments.push_back(L"/Gec"); + } + if (compileFlags & D3DCOMPILE_IEEE_STRICTNESS) { + arguments.push_back(L"/Gis"); + } + if (compileFlags & D3DCOMPILE_OPTIMIZATION_LEVEL2) { + switch (compileFlags & D3DCOMPILE_OPTIMIZATION_LEVEL2) { + case D3DCOMPILE_OPTIMIZATION_LEVEL0: + arguments.push_back(L"/O0"); + break; + case D3DCOMPILE_OPTIMIZATION_LEVEL2: + arguments.push_back(L"/O2"); + break; + case D3DCOMPILE_OPTIMIZATION_LEVEL3: + arguments.push_back(L"/O3"); + break; + } + } + if (compileFlags & D3DCOMPILE_DEBUG) { + arguments.push_back(L"/Zi"); + } + if (compileFlags & D3DCOMPILE_PACK_MATRIX_ROW_MAJOR) { + arguments.push_back(L"/Zpr"); + } + if (compileFlags & D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR) { + arguments.push_back(L"/Zpc"); + } + if (compileFlags & D3DCOMPILE_AVOID_FLOW_CONTROL) { + arguments.push_back(L"/Gfa"); + } + if (compileFlags & D3DCOMPILE_PREFER_FLOW_CONTROL) { + arguments.push_back(L"/Gfp"); + } + if (compileFlags & D3DCOMPILE_RESOURCES_MAY_ALIAS) { + arguments.push_back(L"/res_may_alias"); + } + + if (enable16BitTypes) { + // enable-16bit-types are only allowed in -HV 2018 (default) + arguments.push_back(L"/enable-16bit-types"); + } else { + // Enable FXC backward compatibility by setting the language version to 2016 + arguments.push_back(L"-HV"); + arguments.push_back(L"2016"); + } + return arguments; + } + + } // anonymous namespace + + // static + ResultOrError ShaderModule::Create(Device* device, + const ShaderModuleDescriptor* descriptor) { + Ref module = AcquireRef(new ShaderModule(device, descriptor)); + DAWN_TRY(module->Initialize()); + return module.Detach(); + } + ShaderModule::ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor) : ShaderModuleBase(device, descriptor) { - mSpirv.assign(descriptor->code, descriptor->code + descriptor->codeSize); - spirv_cross::CompilerHLSL compiler(mSpirv); - ExtractSpirvInfo(compiler); } - const std::string ShaderModule::GetHLSLSource(PipelineLayout* layout) const { - spirv_cross::CompilerHLSL compiler(mSpirv); + MaybeError ShaderModule::Initialize() { + DAWN_TRY(InitializeBase()); + const std::vector& spirv = GetSpirv(); + + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc::CompileOptions options = GetCompileOptions(); - // If these options are changed, the values in DawnSPIRVCrossHLSLFastFuzzer.cpp need to be - // updated. - spirv_cross::CompilerGLSL::Options options_glsl; - options_glsl.vertex.flip_vert_y = true; - compiler.set_common_options(options_glsl); + options.SetForceZeroInitializedVariables(true); + if (GetDevice()->IsExtensionEnabled(Extension::ShaderFloat16)) { + options.SetHLSLShaderModel(ToBackend(GetDevice())->GetDeviceInfo().shaderModel); + options.SetHLSLEnable16BitTypes(true); + } else { + options.SetHLSLShaderModel(51); + } + // PointCoord and PointSize are not supported in HLSL + // TODO (hao.x.li@intel.com): The point_coord_compat and point_size_compat are + // required temporarily for https://bugs.chromium.org/p/dawn/issues/detail?id=146, + // but should be removed once WebGPU requires there is no gl_PointSize builtin. + // See https://github.com/gpuweb/gpuweb/issues/332 + options.SetHLSLPointCoordCompat(true); + options.SetHLSLPointSizeCompat(true); + options.SetHLSLNonWritableUAVTextureAsSRV(true); - spirv_cross::CompilerHLSL::Options options_hlsl; - options_hlsl.shader_model = 51; - compiler.set_hlsl_options(options_hlsl); + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.InitializeForHlsl(spirv.data(), spirv.size(), options), + "Unable to initialize instance of spvc")); + + spirv_cross::Compiler* compiler; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetCompiler(reinterpret_cast(&compiler)), + "Unable to get cross compiler")); + DAWN_TRY(ExtractSpirvInfo(*compiler)); + } else { + spirv_cross::CompilerHLSL compiler(spirv); + DAWN_TRY(ExtractSpirvInfo(compiler)); + } + return {}; + } + + ResultOrError ShaderModule::GetHLSLSource(PipelineLayout* layout) { + ASSERT(!IsError()); + const std::vector& spirv = GetSpirv(); + + std::unique_ptr compilerImpl; + spirv_cross::CompilerHLSL* compiler = nullptr; + if (!GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + // If these options are changed, the values in DawnSPIRVCrossHLSLFastFuzzer.cpp need to + // be updated. + spirv_cross::CompilerGLSL::Options options_glsl; + // Force all uninitialized variables to be 0, otherwise they will fail to compile + // by FXC. + options_glsl.force_zero_initialized_variables = true; + + spirv_cross::CompilerHLSL::Options options_hlsl; + if (GetDevice()->IsExtensionEnabled(Extension::ShaderFloat16)) { + options_hlsl.shader_model = ToBackend(GetDevice())->GetDeviceInfo().shaderModel; + options_hlsl.enable_16bit_types = true; + } else { + options_hlsl.shader_model = 51; + } + // PointCoord and PointSize are not supported in HLSL + // TODO (hao.x.li@intel.com): The point_coord_compat and point_size_compat are + // required temporarily for https://bugs.chromium.org/p/dawn/issues/detail?id=146, + // but should be removed once WebGPU requires there is no gl_PointSize builtin. + // See https://github.com/gpuweb/gpuweb/issues/332 + options_hlsl.point_coord_compat = true; + options_hlsl.point_size_compat = true; + options_hlsl.nonwritable_uav_texture_as_srv = true; + + compilerImpl = std::make_unique(spirv); + compiler = compilerImpl.get(); + compiler->set_common_options(options_glsl); + compiler->set_hlsl_options(options_hlsl); + } const ModuleBindingInfo& moduleBindingInfo = GetBindingInfo(); - for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { - const auto& bindingOffsets = - ToBackend(layout->GetBindGroupLayout(group))->GetBindingOffsets(); + for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { + const BindGroupLayout* bgl = ToBackend(layout->GetBindGroupLayout(group)); + const auto& bindingOffsets = bgl->GetBindingOffsets(); const auto& groupBindingInfo = moduleBindingInfo[group]; - for (uint32_t binding = 0; binding < groupBindingInfo.size(); ++binding) { - const BindingInfo& bindingInfo = groupBindingInfo[binding]; - if (bindingInfo.used) { - uint32_t bindingOffset = bindingOffsets[binding]; - compiler.set_decoration(bindingInfo.id, spv::DecorationBinding, bindingOffset); + for (const auto& it : groupBindingInfo) { + const ShaderBindingInfo& bindingInfo = it.second; + BindingNumber bindingNumber = it.first; + BindingIndex bindingIndex = bgl->GetBindingIndex(bindingNumber); + + // Declaring a read-only storage buffer in HLSL but specifying a storage buffer in + // the BGL produces the wrong output. Force read-only storage buffer bindings to + // be treated as UAV instead of SRV. + const bool forceStorageBufferAsUAV = + (bindingInfo.type == wgpu::BindingType::ReadonlyStorageBuffer && + bgl->GetBindingInfo(bindingIndex).type == wgpu::BindingType::StorageBuffer); + + uint32_t bindingOffset = bindingOffsets[bindingIndex]; + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.SetDecoration(bindingInfo.id, SHADERC_SPVC_DECORATION_BINDING, + bindingOffset), + "Unable to set decorating binding before generating HLSL shader w/ " + "spvc")); + if (forceStorageBufferAsUAV) { + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.SetHLSLForceStorageBufferAsUAV( + static_cast(group), static_cast(bindingNumber)), + "Unable to force read-only storage buffer as UAV w/ spvc")); + } + } else { + compiler->set_decoration(bindingInfo.id, spv::DecorationBinding, bindingOffset); + if (forceStorageBufferAsUAV) { + compiler->set_hlsl_force_storage_buffer_as_uav( + static_cast(group), static_cast(bindingNumber)); + } } } } - return compiler.compile(); + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc::CompilationResult result; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.CompileShader(&result), + "Unable to generate HLSL shader w/ spvc")); + std::string result_string; + DAWN_TRY(CheckSpvcSuccess(result.GetStringOutput(&result_string), + "Unable to get HLSL shader text")); + return std::move(result_string); + } else { + return compiler->compile(); + } + } + + ResultOrError> ShaderModule::CompileShaderDXC(SingleShaderStage stage, + const std::string& hlslSource, + const char* entryPoint, + uint32_t compileFlags) { + IDxcLibrary* dxcLibrary; + DAWN_TRY_ASSIGN(dxcLibrary, ToBackend(GetDevice())->GetOrCreateDxcLibrary()); + + ComPtr sourceBlob; + DAWN_TRY(CheckHRESULT(dxcLibrary->CreateBlobWithEncodingOnHeapCopy( + hlslSource.c_str(), hlslSource.length(), CP_UTF8, &sourceBlob), + "DXC create blob")); + + IDxcCompiler* dxcCompiler; + DAWN_TRY_ASSIGN(dxcCompiler, ToBackend(GetDevice())->GetOrCreateDxcCompiler()); + + std::wstring entryPointW; + DAWN_TRY_ASSIGN(entryPointW, ConvertStringToWstring(entryPoint)); + + std::vector arguments = GetDXCArguments( + compileFlags, GetDevice()->IsExtensionEnabled(Extension::ShaderFloat16)); + + ComPtr result; + DAWN_TRY( + CheckHRESULT(dxcCompiler->Compile( + sourceBlob.Get(), nullptr, entryPointW.c_str(), + ToBackend(GetDevice())->GetDeviceInfo().shaderProfiles[stage].c_str(), + arguments.data(), arguments.size(), nullptr, 0, nullptr, &result), + "DXC compile")); + + HRESULT hr; + DAWN_TRY(CheckHRESULT(result->GetStatus(&hr), "DXC get status")); + + if (FAILED(hr)) { + ComPtr errors; + DAWN_TRY(CheckHRESULT(result->GetErrorBuffer(&errors), "DXC get error buffer")); + + std::string message = std::string("DXC compile failed with ") + + static_cast(errors->GetBufferPointer()); + return DAWN_INTERNAL_ERROR(message); + } + + ComPtr compiledShader; + DAWN_TRY(CheckHRESULT(result->GetResult(&compiledShader), "DXC get result")); + return std::move(compiledShader); + } + + ResultOrError> ShaderModule::CompileShaderFXC(SingleShaderStage stage, + const std::string& hlslSource, + const char* entryPoint, + uint32_t compileFlags) { + const char* targetProfile = nullptr; + switch (stage) { + case SingleShaderStage::Vertex: + targetProfile = "vs_5_1"; + break; + case SingleShaderStage::Fragment: + targetProfile = "ps_5_1"; + break; + case SingleShaderStage::Compute: + targetProfile = "cs_5_1"; + break; + } + + ComPtr compiledShader; + ComPtr errors; + + const PlatformFunctions* functions = ToBackend(GetDevice())->GetFunctions(); + if (FAILED(functions->d3dCompile(hlslSource.c_str(), hlslSource.length(), nullptr, nullptr, + nullptr, entryPoint, targetProfile, compileFlags, 0, + &compiledShader, &errors))) { + std::string message = std::string("D3D compile failed with ") + + static_cast(errors->GetBufferPointer()); + return DAWN_INTERNAL_ERROR(message); + } + + return std::move(compiledShader); } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/ShaderModuleD3D12.h b/third_party/dawn/src/dawn_native/d3d12/ShaderModuleD3D12.h index 7cafd1cb218..c64e8ce6610 100644 --- a/third_party/dawn/src/dawn_native/d3d12/ShaderModuleD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/ShaderModuleD3D12.h @@ -17,19 +17,33 @@ #include "dawn_native/ShaderModule.h" +#include "dawn_native/d3d12/d3d12_platform.h" + namespace dawn_native { namespace d3d12 { class Device; class PipelineLayout; - class ShaderModule : public ShaderModuleBase { + class ShaderModule final : public ShaderModuleBase { public: - ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor); + static ResultOrError Create(Device* device, + const ShaderModuleDescriptor* descriptor); - const std::string GetHLSLSource(PipelineLayout* layout) const; + ResultOrError GetHLSLSource(PipelineLayout* layout); + + ResultOrError> CompileShaderDXC(SingleShaderStage stage, + const std::string& hlslSource, + const char* entryPoint, + uint32_t compileFlags); + ResultOrError> CompileShaderFXC(SingleShaderStage stage, + const std::string& hlslSource, + const char* entryPoint, + uint32_t compileFlags); private: - std::vector mSpirv; + ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor); + ~ShaderModule() override = default; + MaybeError Initialize(); }; }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp new file mode 100644 index 00000000000..ca30889e2a2 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.cpp @@ -0,0 +1,212 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/GPUDescriptorHeapAllocationD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" + +namespace dawn_native { namespace d3d12 { + + // Thresholds should be adjusted (lower == faster) to avoid tests taking too long to complete. + static constexpr const uint32_t kShaderVisibleSmallHeapSizes[] = {1024, 512}; + + uint32_t GetD3D12ShaderVisibleHeapSize(D3D12_DESCRIPTOR_HEAP_TYPE heapType, bool useSmallSize) { + if (useSmallSize) { + return kShaderVisibleSmallHeapSizes[heapType]; + } + + switch (heapType) { + case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV: + return D3D12_MAX_SHADER_VISIBLE_DESCRIPTOR_HEAP_SIZE_TIER_1; + case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER: + return D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE; + default: + UNREACHABLE(); + } + } + + D3D12_DESCRIPTOR_HEAP_FLAGS GetD3D12HeapFlags(D3D12_DESCRIPTOR_HEAP_TYPE heapType) { + switch (heapType) { + case D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV: + case D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER: + return D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; + default: + UNREACHABLE(); + } + } + + // static + ResultOrError> + ShaderVisibleDescriptorAllocator::Create(Device* device, D3D12_DESCRIPTOR_HEAP_TYPE heapType) { + std::unique_ptr allocator = + std::make_unique(device, heapType); + DAWN_TRY(allocator->AllocateAndSwitchShaderVisibleHeap()); + return std::move(allocator); + } + + ShaderVisibleDescriptorAllocator::ShaderVisibleDescriptorAllocator( + Device* device, + D3D12_DESCRIPTOR_HEAP_TYPE heapType) + : mHeapType(heapType), + mDevice(device), + mSizeIncrement(device->GetD3D12Device()->GetDescriptorHandleIncrementSize(heapType)) { + ASSERT(heapType == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV || + heapType == D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + } + + bool ShaderVisibleDescriptorAllocator::AllocateGPUDescriptors( + uint32_t descriptorCount, + Serial pendingSerial, + D3D12_CPU_DESCRIPTOR_HANDLE* baseCPUDescriptor, + GPUDescriptorHeapAllocation* allocation) { + ASSERT(mHeap != nullptr); + const uint64_t startOffset = mAllocator.Allocate(descriptorCount, pendingSerial); + if (startOffset == RingBufferAllocator::kInvalidOffset) { + return false; + } + + ID3D12DescriptorHeap* descriptorHeap = mHeap->GetD3D12DescriptorHeap(); + + const uint64_t heapOffset = mSizeIncrement * startOffset; + + // Check for 32-bit overflow since CPU heap start handle uses size_t. + const size_t cpuHeapStartPtr = descriptorHeap->GetCPUDescriptorHandleForHeapStart().ptr; + + ASSERT(heapOffset <= std::numeric_limits::max() - cpuHeapStartPtr); + + *baseCPUDescriptor = {cpuHeapStartPtr + static_cast(heapOffset)}; + + const D3D12_GPU_DESCRIPTOR_HANDLE baseGPUDescriptor = { + descriptorHeap->GetGPUDescriptorHandleForHeapStart().ptr + heapOffset}; + + // Record both the device and heap serials to determine later if the allocations are + // still valid. + *allocation = GPUDescriptorHeapAllocation{baseGPUDescriptor, pendingSerial, mHeapSerial}; + + return true; + } + + ID3D12DescriptorHeap* ShaderVisibleDescriptorAllocator::GetShaderVisibleHeap() const { + return mHeap->GetD3D12DescriptorHeap(); + } + + void ShaderVisibleDescriptorAllocator::Tick(uint64_t completedSerial) { + mAllocator.Deallocate(completedSerial); + } + + // Creates a GPU descriptor heap that manages descriptors in a FIFO queue. + MaybeError ShaderVisibleDescriptorAllocator::AllocateAndSwitchShaderVisibleHeap() { + std::unique_ptr descriptorHeap; + // Return the switched out heap to the pool and retrieve the oldest heap that is no longer + // used by GPU. This maintains a heap buffer to avoid frequently re-creating heaps for heavy + // users. + // TODO(dawn:256): Consider periodically triming to avoid OOM. + if (mHeap != nullptr) { + mDevice->GetResidencyManager()->UnlockAllocation(mHeap.get()); + mPool.push_back({mDevice->GetPendingCommandSerial(), std::move(mHeap)}); + } + + // Recycle existing heap if possible. + if (!mPool.empty() && mPool.front().heapSerial <= mDevice->GetCompletedCommandSerial()) { + descriptorHeap = std::move(mPool.front().heap); + mPool.pop_front(); + } + + // TODO(bryan.bernhart@intel.com): Allocating to max heap size wastes memory + // should the developer not allocate any bindings for the heap type. + // Consider dynamically re-sizing GPU heaps. + const uint32_t descriptorCount = GetD3D12ShaderVisibleHeapSize( + mHeapType, mDevice->IsToggleEnabled(Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); + + if (descriptorHeap == nullptr) { + // The size in bytes of a descriptor heap is best calculated by the increment size + // multiplied by the number of descriptors. In practice, this is only an estimate and + // the actual size may vary depending on the driver. + const uint64_t kSize = mSizeIncrement * descriptorCount; + + DAWN_TRY( + mDevice->GetResidencyManager()->EnsureCanAllocate(kSize, MemorySegment::Local)); + + ComPtr d3d12DescriptorHeap; + D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor; + heapDescriptor.Type = mHeapType; + heapDescriptor.NumDescriptors = descriptorCount; + heapDescriptor.Flags = GetD3D12HeapFlags(mHeapType); + heapDescriptor.NodeMask = 0; + DAWN_TRY( + CheckOutOfMemoryHRESULT(mDevice->GetD3D12Device()->CreateDescriptorHeap( + &heapDescriptor, IID_PPV_ARGS(&d3d12DescriptorHeap)), + "ID3D12Device::CreateDescriptorHeap")); + descriptorHeap = std::make_unique( + std::move(d3d12DescriptorHeap), kSize); + // We must track the allocation in the LRU when it is created, otherwise the residency + // manager will see the allocation as non-resident in the later call to LockAllocation. + mDevice->GetResidencyManager()->TrackResidentAllocation(descriptorHeap.get()); + } + + DAWN_TRY(mDevice->GetResidencyManager()->LockAllocation(descriptorHeap.get())); + // Create a FIFO buffer from the recently created heap. + mHeap = std::move(descriptorHeap); + mAllocator = RingBufferAllocator(descriptorCount); + + // Invalidate all bindgroup allocations on previously bound heaps by incrementing the heap + // serial. When a bindgroup attempts to re-populate, it will compare with its recorded + // heap serial. + mHeapSerial++; + + return {}; + } + + Serial ShaderVisibleDescriptorAllocator::GetShaderVisibleHeapSerialForTesting() const { + return mHeapSerial; + } + + uint64_t ShaderVisibleDescriptorAllocator::GetShaderVisibleHeapSizeForTesting() const { + return mAllocator.GetSize(); + } + + uint64_t ShaderVisibleDescriptorAllocator::GetShaderVisiblePoolSizeForTesting() const { + return mPool.size(); + } + + bool ShaderVisibleDescriptorAllocator::IsShaderVisibleHeapLockedResidentForTesting() const { + return mHeap->IsResidencyLocked(); + } + + bool ShaderVisibleDescriptorAllocator::IsLastShaderVisibleHeapInLRUForTesting() const { + ASSERT(!mPool.empty()); + return mPool.back().heap->IsInResidencyLRUCache(); + } + + bool ShaderVisibleDescriptorAllocator::IsAllocationStillValid( + const GPUDescriptorHeapAllocation& allocation) const { + // Consider valid if allocated for the pending submit and the shader visible heaps + // have not switched over. + return (allocation.GetLastUsageSerial() > mDevice->GetCompletedCommandSerial() && + allocation.GetHeapSerial() == mHeapSerial); + } + + ShaderVisibleDescriptorHeap::ShaderVisibleDescriptorHeap( + ComPtr d3d12DescriptorHeap, + uint64_t size) + : Pageable(d3d12DescriptorHeap, MemorySegment::Local, size), + mD3d12DescriptorHeap(std::move(d3d12DescriptorHeap)) { + } + + ID3D12DescriptorHeap* ShaderVisibleDescriptorHeap::GetD3D12DescriptorHeap() const { + return mD3d12DescriptorHeap.Get(); + } +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h b/third_party/dawn/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h new file mode 100644 index 00000000000..564eb95e79b --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h @@ -0,0 +1,97 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_SHADERVISIBLEDESCRIPTORALLOCATOR_H_ +#define DAWNNATIVE_D3D12_SHADERVISIBLEDESCRIPTORALLOCATOR_H_ + +#include "dawn_native/Error.h" +#include "dawn_native/RingBufferAllocator.h" +#include "dawn_native/d3d12/PageableD3D12.h" +#include "dawn_native/d3d12/d3d12_platform.h" + +#include + +// |ShaderVisibleDescriptorAllocator| allocates a variable-sized block of descriptors from a GPU +// descriptor heap pool. +// Internally, it manages a list of heaps using a ringbuffer block allocator. The heap is in one +// of two states: switched in or out. Only a switched in heap can be bound to the pipeline. If +// the heap is full, the caller must switch-in a new heap before re-allocating and the old one +// is returned to the pool. +namespace dawn_native { namespace d3d12 { + + class Device; + class GPUDescriptorHeapAllocation; + + class ShaderVisibleDescriptorHeap : public Pageable { + public: + ShaderVisibleDescriptorHeap(ComPtr d3d12DescriptorHeap, + uint64_t size); + ID3D12DescriptorHeap* GetD3D12DescriptorHeap() const; + + private: + ComPtr mD3d12DescriptorHeap; + }; + + class ShaderVisibleDescriptorAllocator { + public: + static ResultOrError> Create( + Device* device, + D3D12_DESCRIPTOR_HEAP_TYPE heapType); + + ShaderVisibleDescriptorAllocator(Device* device, D3D12_DESCRIPTOR_HEAP_TYPE heapType); + + // Returns true if the allocation was successful, when false is returned the current heap is + // full and AllocateAndSwitchShaderVisibleHeap() must be called. + bool AllocateGPUDescriptors(uint32_t descriptorCount, + Serial pendingSerial, + D3D12_CPU_DESCRIPTOR_HANDLE* baseCPUDescriptor, + GPUDescriptorHeapAllocation* allocation); + + void Tick(uint64_t completedSerial); + + ID3D12DescriptorHeap* GetShaderVisibleHeap() const; + MaybeError AllocateAndSwitchShaderVisibleHeap(); + + // For testing purposes only. + Serial GetShaderVisibleHeapSerialForTesting() const; + uint64_t GetShaderVisibleHeapSizeForTesting() const; + uint64_t GetShaderVisiblePoolSizeForTesting() const; + bool IsShaderVisibleHeapLockedResidentForTesting() const; + bool IsLastShaderVisibleHeapInLRUForTesting() const; + + bool IsAllocationStillValid(const GPUDescriptorHeapAllocation& allocation) const; + + private: + struct SerialDescriptorHeap { + Serial heapSerial; + std::unique_ptr heap; + }; + + std::unique_ptr mHeap; + RingBufferAllocator mAllocator; + std::list mPool; + D3D12_DESCRIPTOR_HEAP_TYPE mHeapType; + + Device* mDevice; + + // The serial value of 0 means the shader-visible heaps have not been allocated. + // This value is never returned in the GPUDescriptorHeapAllocation after + // AllocateGPUDescriptors() is called. + Serial mHeapSerial = 0; + + uint32_t mSizeIncrement; + }; +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_SHADERVISIBLEDESCRIPTORALLOCATOR_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/StagingBufferD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/StagingBufferD3D12.cpp index da5db485394..11c612ba82f 100644 --- a/third_party/dawn/src/dawn_native/d3d12/StagingBufferD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/StagingBufferD3D12.cpp @@ -13,8 +13,10 @@ // limitations under the License. #include "dawn_native/d3d12/StagingBufferD3D12.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" -#include "dawn_native/d3d12/ResourceAllocator.h" +#include "dawn_native/d3d12/HeapD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" namespace dawn_native { namespace d3d12 { @@ -36,28 +38,37 @@ namespace dawn_native { namespace d3d12 { resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE; - mUploadHeap = mDevice->GetResourceAllocator()->Allocate( - D3D12_HEAP_TYPE_UPLOAD, resourceDescriptor, D3D12_RESOURCE_STATE_GENERIC_READ); + DAWN_TRY_ASSIGN(mUploadHeap, + mDevice->AllocateMemory(D3D12_HEAP_TYPE_UPLOAD, resourceDescriptor, + D3D12_RESOURCE_STATE_GENERIC_READ)); - // TODO(bryan.bernhart@intel.com): Record the GPU pointer for generic non-upload usage. + // The mapped buffer can be accessed at any time, so it must be locked to ensure it is never + // evicted. This buffer should already have been made resident when it was created. + DAWN_TRY(mDevice->GetResidencyManager()->LockAllocation( + ToBackend(mUploadHeap.GetResourceHeap()))); - if (FAILED(mUploadHeap->Map(0, nullptr, &mMappedPointer))) { - return DAWN_CONTEXT_LOST_ERROR("Unable to map staging buffer."); - } - - return {}; + return CheckHRESULT(GetResource()->Map(0, nullptr, &mMappedPointer), "ID3D12Resource::Map"); } StagingBuffer::~StagingBuffer() { + // Always check if the allocation is valid before Unmap. + // The resource would not exist had it failed to allocate. + if (mUploadHeap.GetInfo().mMethod == AllocationMethod::kInvalid) { + return; + } + + // The underlying heap was locked in residency upon creation. We must unlock it when this + // buffer becomes unmapped. + mDevice->GetResidencyManager()->UnlockAllocation(ToBackend(mUploadHeap.GetResourceHeap())); + // Invalidate the CPU virtual address & flush cache (if needed). - mUploadHeap->Unmap(0, nullptr); + GetResource()->Unmap(0, nullptr); mMappedPointer = nullptr; - mDevice->GetResourceAllocator()->Release(mUploadHeap); + mDevice->DeallocateMemory(mUploadHeap); } ID3D12Resource* StagingBuffer::GetResource() const { - return mUploadHeap.Get(); + return mUploadHeap.GetD3D12Resource(); } - -}} // namespace dawn_native::d3d12 \ No newline at end of file +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/StagingBufferD3D12.h b/third_party/dawn/src/dawn_native/d3d12/StagingBufferD3D12.h index b689df4a956..291400d996c 100644 --- a/third_party/dawn/src/dawn_native/d3d12/StagingBufferD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/StagingBufferD3D12.h @@ -16,16 +16,18 @@ #define DAWNNATIVE_STAGINGBUFFERD3D12_H_ #include "dawn_native/StagingBuffer.h" +#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { + class CommandRecordingContext; class Device; class StagingBuffer : public StagingBufferBase { public: StagingBuffer(size_t size, Device* device); - ~StagingBuffer(); + ~StagingBuffer() override; ID3D12Resource* GetResource() const; @@ -33,7 +35,7 @@ namespace dawn_native { namespace d3d12 { private: Device* mDevice; - ComPtr mUploadHeap; + ResourceHeapAllocation mUploadHeap; }; }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/StagingDescriptorAllocatorD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/StagingDescriptorAllocatorD3D12.cpp new file mode 100644 index 00000000000..92a47412e96 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/StagingDescriptorAllocatorD3D12.cpp @@ -0,0 +1,152 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Math.h" + +#include "dawn_native/d3d12/D3D12Error.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" + +namespace dawn_native { namespace d3d12 { + + StagingDescriptorAllocator::StagingDescriptorAllocator(Device* device, + uint32_t descriptorCount, + uint32_t heapSize, + D3D12_DESCRIPTOR_HEAP_TYPE heapType) + : mDevice(device), + mSizeIncrement(device->GetD3D12Device()->GetDescriptorHandleIncrementSize(heapType)), + mBlockSize(descriptorCount * mSizeIncrement), + mHeapSize(RoundUp(heapSize, descriptorCount)), + mHeapType(heapType) { + ASSERT(descriptorCount <= heapSize); + } + + StagingDescriptorAllocator::~StagingDescriptorAllocator() { + const Index freeBlockIndicesSize = GetFreeBlockIndicesSize(); + for (auto& buffer : mPool) { + ASSERT(buffer.freeBlockIndices.size() == freeBlockIndicesSize); + } + ASSERT(mAvailableHeaps.size() == mPool.size()); + } + + ResultOrError + StagingDescriptorAllocator::AllocateCPUDescriptors() { + if (mAvailableHeaps.empty()) { + DAWN_TRY(AllocateCPUHeap()); + } + + ASSERT(!mAvailableHeaps.empty()); + + const uint32_t heapIndex = mAvailableHeaps.back(); + NonShaderVisibleBuffer& buffer = mPool[heapIndex]; + + ASSERT(!buffer.freeBlockIndices.empty()); + + const Index blockIndex = buffer.freeBlockIndices.back(); + + buffer.freeBlockIndices.pop_back(); + + if (buffer.freeBlockIndices.empty()) { + mAvailableHeaps.pop_back(); + } + + const D3D12_CPU_DESCRIPTOR_HANDLE baseCPUDescriptor = { + buffer.heap->GetCPUDescriptorHandleForHeapStart().ptr + (blockIndex * mBlockSize)}; + + return CPUDescriptorHeapAllocation{baseCPUDescriptor, heapIndex}; + } + + MaybeError StagingDescriptorAllocator::AllocateCPUHeap() { + D3D12_DESCRIPTOR_HEAP_DESC heapDescriptor; + heapDescriptor.Type = mHeapType; + heapDescriptor.NumDescriptors = mHeapSize; + heapDescriptor.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; + heapDescriptor.NodeMask = 0; + + ComPtr heap; + DAWN_TRY(CheckHRESULT( + mDevice->GetD3D12Device()->CreateDescriptorHeap(&heapDescriptor, IID_PPV_ARGS(&heap)), + "ID3D12Device::CreateDescriptorHeap")); + + NonShaderVisibleBuffer newBuffer; + newBuffer.heap = std::move(heap); + + const Index freeBlockIndicesSize = GetFreeBlockIndicesSize(); + newBuffer.freeBlockIndices.reserve(freeBlockIndicesSize); + + for (Index blockIndex = 0; blockIndex < freeBlockIndicesSize; blockIndex++) { + newBuffer.freeBlockIndices.push_back(blockIndex); + } + + mAvailableHeaps.push_back(mPool.size()); + mPool.emplace_back(std::move(newBuffer)); + + return {}; + } + + void StagingDescriptorAllocator::Deallocate(CPUDescriptorHeapAllocation* allocation) { + ASSERT(allocation->IsValid()); + + const uint32_t heapIndex = allocation->GetHeapIndex(); + + ASSERT(heapIndex < mPool.size()); + + // Insert the deallocated block back into the free-list. Order does not matter. However, + // having blocks be non-contigious could slow down future allocations due to poor cache + // locality. + // TODO(dawn:155): Consider more optimization. + std::vector& freeBlockIndices = mPool[heapIndex].freeBlockIndices; + if (freeBlockIndices.empty()) { + mAvailableHeaps.emplace_back(heapIndex); + } + + const D3D12_CPU_DESCRIPTOR_HANDLE heapStart = + mPool[heapIndex].heap->GetCPUDescriptorHandleForHeapStart(); + + const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = allocation->OffsetFrom(0, 0); + + const Index blockIndex = (baseDescriptor.ptr - heapStart.ptr) / mBlockSize; + + freeBlockIndices.emplace_back(blockIndex); + + // Invalidate the handle in case the developer accidentally uses it again. + allocation->Invalidate(); + } + + uint32_t StagingDescriptorAllocator::GetSizeIncrement() const { + return mSizeIncrement; + } + + StagingDescriptorAllocator::Index StagingDescriptorAllocator::GetFreeBlockIndicesSize() const { + return ((mHeapSize * mSizeIncrement) / mBlockSize); + } + + ResultOrError + StagingDescriptorAllocator::AllocateTransientCPUDescriptors() { + CPUDescriptorHeapAllocation allocation; + DAWN_TRY_ASSIGN(allocation, AllocateCPUDescriptors()); + mAllocationsToDelete.Enqueue(allocation, mDevice->GetPendingCommandSerial()); + return allocation; + } + + void StagingDescriptorAllocator::Tick(Serial completedSerial) { + for (CPUDescriptorHeapAllocation& allocation : + mAllocationsToDelete.IterateUpTo(completedSerial)) { + Deallocate(&allocation); + } + + mAllocationsToDelete.ClearUpTo(completedSerial); + } + +}} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h b/third_party/dawn/src/dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h new file mode 100644 index 00000000000..519920b4655 --- /dev/null +++ b/third_party/dawn/src/dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h @@ -0,0 +1,85 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_D3D12_STAGINGDESCRIPTORALLOCATOR_H_ +#define DAWNNATIVE_D3D12_STAGINGDESCRIPTORALLOCATOR_H_ + +#include "dawn_native/Error.h" + +#include "dawn_native/d3d12/CPUDescriptorHeapAllocationD3D12.h" + +#include + +// |StagingDescriptorAllocator| allocates a fixed-size block of descriptors from a CPU +// descriptor heap pool. +// Internally, it manages a list of heaps using a fixed-size block allocator. The fixed-size +// block allocator is backed by a list of free blocks (free-list). The heap is in one of two +// states: AVAILABLE or not. To allocate, the next free block is removed from the free-list +// and the corresponding heap offset is returned. The AVAILABLE heap always has room for +// at-least one free block. If no AVAILABLE heap exists, a new heap is created and inserted +// back into the pool to be immediately used. To deallocate, the block corresponding to the +// offset is inserted back into the free-list. +namespace dawn_native { namespace d3d12 { + + class Device; + + class StagingDescriptorAllocator { + public: + StagingDescriptorAllocator() = default; + StagingDescriptorAllocator(Device* device, + uint32_t descriptorCount, + uint32_t heapSize, + D3D12_DESCRIPTOR_HEAP_TYPE heapType); + ~StagingDescriptorAllocator(); + + ResultOrError AllocateCPUDescriptors(); + + // Will call Deallocate when the serial is passed. + ResultOrError AllocateTransientCPUDescriptors(); + + void Deallocate(CPUDescriptorHeapAllocation* allocation); + + uint32_t GetSizeIncrement() const; + + void Tick(Serial completedSerial); + + private: + using Index = uint16_t; + + struct NonShaderVisibleBuffer { + ComPtr heap; + std::vector freeBlockIndices; + }; + + MaybeError AllocateCPUHeap(); + + Index GetFreeBlockIndicesSize() const; + + std::vector mAvailableHeaps; // Indices into the pool. + std::vector mPool; + + Device* mDevice; + + uint32_t mSizeIncrement; // Size of the descriptor (in bytes). + uint32_t mBlockSize; // Size of the block of descriptors (in bytes). + uint32_t mHeapSize; // Size of the heap (in number of descriptors). + + D3D12_DESCRIPTOR_HEAP_TYPE mHeapType; + + SerialQueue mAllocationsToDelete; + }; + +}} // namespace dawn_native::d3d12 + +#endif // DAWNNATIVE_D3D12_STAGINGDESCRIPTORALLOCATOR_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/SwapChainD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/SwapChainD3D12.cpp index f02a79e30f1..74798059dfd 100644 --- a/third_party/dawn/src/dawn_native/d3d12/SwapChainD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/SwapChainD3D12.cpp @@ -22,14 +22,14 @@ namespace dawn_native { namespace d3d12 { SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor) - : SwapChainBase(device, descriptor) { + : OldSwapChainBase(device, descriptor) { const auto& im = GetImplementation(); DawnWSIContextD3D12 wsiContext = {}; - wsiContext.device = reinterpret_cast(GetDevice()); + wsiContext.device = reinterpret_cast(GetDevice()); im.Init(im.userData, &wsiContext); - ASSERT(im.textureUsage != DAWN_TEXTURE_USAGE_BIT_NONE); - mTextureUsage = static_cast(im.textureUsage); + ASSERT(im.textureUsage != WGPUTextureUsage_None); + mTextureUsage = static_cast(im.textureUsage); } SwapChain::~SwapChain() { @@ -40,21 +40,28 @@ namespace dawn_native { namespace d3d12 { DawnSwapChainNextTexture next = {}; DawnSwapChainError error = im.GetNextTexture(im.userData, &next); if (error) { - GetDevice()->HandleError(error); + GetDevice()->HandleError(InternalErrorType::Internal, error); return nullptr; } - ID3D12Resource* nativeTexture = static_cast(next.texture.ptr); - return new Texture(ToBackend(GetDevice()), descriptor, nativeTexture); + ComPtr d3d12Texture = static_cast(next.texture.ptr); + return new Texture(ToBackend(GetDevice()), descriptor, std::move(d3d12Texture)); } - void SwapChain::OnBeforePresent(TextureBase* texture) { + MaybeError SwapChain::OnBeforePresent(TextureViewBase* view) { Device* device = ToBackend(GetDevice()); + CommandRecordingContext* commandContext; + DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext()); + // Perform the necessary transition for the texture to be presented. - ToBackend(texture)->TransitionUsageNow(device->GetPendingCommandList(), mTextureUsage); + ToBackend(view->GetTexture()) + ->TrackUsageAndTransitionNow(commandContext, mTextureUsage, + view->GetSubresourceRange()); + + DAWN_TRY(device->ExecutePendingCommandContext()); - device->ExecuteCommandLists({}); + return {}; } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/SwapChainD3D12.h b/third_party/dawn/src/dawn_native/d3d12/SwapChainD3D12.h index 1ca8ded3f3f..6938e20adad 100644 --- a/third_party/dawn/src/dawn_native/d3d12/SwapChainD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/SwapChainD3D12.h @@ -21,16 +21,16 @@ namespace dawn_native { namespace d3d12 { class Device; - class SwapChain : public SwapChainBase { + class SwapChain final : public OldSwapChainBase { public: SwapChain(Device* device, const SwapChainDescriptor* descriptor); - ~SwapChain(); protected: + ~SwapChain() override; TextureBase* GetNextTextureImpl(const TextureDescriptor* descriptor) override; - void OnBeforePresent(TextureBase* texture) override; + MaybeError OnBeforePresent(TextureViewBase* view) override; - dawn::TextureUsageBit mTextureUsage; + wgpu::TextureUsage mTextureUsage; }; }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/TextureCopySplitter.cpp b/third_party/dawn/src/dawn_native/d3d12/TextureCopySplitter.cpp index 201c4634b0f..07da049c716 100644 --- a/third_party/dawn/src/dawn_native/d3d12/TextureCopySplitter.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/TextureCopySplitter.cpp @@ -15,36 +15,37 @@ #include "dawn_native/d3d12/TextureCopySplitter.h" #include "common/Assert.h" +#include "dawn_native/Format.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { namespace { - void ComputeTexelOffsets(uint32_t offset, - uint32_t rowPitch, - uint32_t slicePitch, - uint32_t texelSize, - Origin3D* texelOffset) { - uint32_t byteOffsetX = offset % rowPitch; + Origin3D ComputeTexelOffsets(const Format& format, + uint32_t offset, + uint32_t bytesPerRow, + uint32_t slicePitch) { + ASSERT(bytesPerRow != 0); + ASSERT(slicePitch != 0); + uint32_t byteOffsetX = offset % bytesPerRow; offset -= byteOffsetX; uint32_t byteOffsetY = offset % slicePitch; uint32_t byteOffsetZ = offset - byteOffsetY; - texelOffset->x = byteOffsetX / texelSize; - texelOffset->y = byteOffsetY / rowPitch; - texelOffset->z = byteOffsetZ / slicePitch; + return {byteOffsetX / format.blockByteSize * format.blockWidth, + byteOffsetY / bytesPerRow * format.blockHeight, byteOffsetZ / slicePitch}; } } // namespace - TextureCopySplit ComputeTextureCopySplit(Origin3D origin, - Extent3D copySize, - uint32_t texelSize, - uint64_t offset, - uint32_t rowPitch, - uint32_t imageHeight) { - TextureCopySplit copy; + Texture2DCopySplit ComputeTextureCopySplit(Origin3D origin, + Extent3D copySize, + const Format& format, + uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage) { + Texture2DCopySplit copy; - ASSERT(rowPitch % texelSize == 0); + ASSERT(bytesPerRow % format.blockByteSize == 0); uint64_t alignedOffset = offset & ~static_cast(D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT - 1); @@ -70,16 +71,16 @@ namespace dawn_native { namespace d3d12 { ASSERT(alignedOffset < offset); ASSERT(offset - alignedOffset < D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - Origin3D texelOffset; - ComputeTexelOffsets(static_cast(offset - alignedOffset), rowPitch, - rowPitch * imageHeight, texelSize, &texelOffset); + uint32_t slicePitch = bytesPerRow * (rowsPerImage / format.blockHeight); + Origin3D texelOffset = ComputeTexelOffsets( + format, static_cast(offset - alignedOffset), bytesPerRow, slicePitch); - uint32_t rowPitchInTexels = rowPitch / texelSize; - - if (copySize.width + texelOffset.x <= rowPitchInTexels) { - // The region's rows fit inside the row pitch. In this case, extend the width of the + uint32_t copyBytesPerRowPitch = copySize.width / format.blockWidth * format.blockByteSize; + uint32_t byteOffsetInRowPitch = texelOffset.x / format.blockWidth * format.blockByteSize; + if (copyBytesPerRowPitch + byteOffsetInRowPitch <= bytesPerRow) { + // The region's rows fit inside the bytes per row. In this case, extend the width of the // PlacedFootprint and copy the buffer with an offset location - // |<--------------- row pitch --------------->| + // |<------------- bytes per row ------------->| // // |-------------------------------------------| // | | @@ -108,14 +109,14 @@ namespace dawn_native { namespace d3d12 { copy.copies[0].bufferOffset = texelOffset; copy.copies[0].bufferSize.width = copySize.width + texelOffset.x; - copy.copies[0].bufferSize.height = imageHeight + texelOffset.y; + copy.copies[0].bufferSize.height = rowsPerImage + texelOffset.y; copy.copies[0].bufferSize.depth = copySize.depth + texelOffset.z; return copy; } - // The region's rows straddle the row pitch. Split the copy into two copies - // |<--------------- row pitch --------------->| + // The region's rows straddle the bytes per row. Split the copy into two copies + // |<------------- bytes per row ------------->| // // |-------------------------------------------| // | | @@ -152,14 +153,15 @@ namespace dawn_native { namespace d3d12 { copy.copies[0].textureOffset = origin; - ASSERT(rowPitchInTexels > texelOffset.x); - copy.copies[0].copySize.width = rowPitchInTexels - texelOffset.x; + ASSERT(bytesPerRow > byteOffsetInRowPitch); + uint32_t texelsPerRow = bytesPerRow / format.blockByteSize * format.blockWidth; + copy.copies[0].copySize.width = texelsPerRow - texelOffset.x; copy.copies[0].copySize.height = copySize.height; copy.copies[0].copySize.depth = copySize.depth; copy.copies[0].bufferOffset = texelOffset; - copy.copies[0].bufferSize.width = rowPitchInTexels; - copy.copies[0].bufferSize.height = imageHeight + texelOffset.y; + copy.copies[0].bufferSize.width = texelsPerRow; + copy.copies[0].bufferSize.height = rowsPerImage + texelOffset.y; copy.copies[0].bufferSize.depth = copySize.depth + texelOffset.z; copy.copies[1].textureOffset.x = origin.x + copy.copies[0].copySize.width; @@ -172,13 +174,59 @@ namespace dawn_native { namespace d3d12 { copy.copies[1].copySize.depth = copySize.depth; copy.copies[1].bufferOffset.x = 0; - copy.copies[1].bufferOffset.y = texelOffset.y + 1; + copy.copies[1].bufferOffset.y = texelOffset.y + format.blockHeight; copy.copies[1].bufferOffset.z = texelOffset.z; copy.copies[1].bufferSize.width = copy.copies[1].copySize.width; - copy.copies[1].bufferSize.height = imageHeight + texelOffset.y + 1; + copy.copies[1].bufferSize.height = rowsPerImage + texelOffset.y + format.blockHeight; copy.copies[1].bufferSize.depth = copySize.depth + texelOffset.z; return copy; } + TextureCopySplits ComputeTextureCopySplits(Origin3D origin, + Extent3D copySize, + const Format& format, + uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage) { + TextureCopySplits copies; + + const uint64_t bytesPerSlice = bytesPerRow * (rowsPerImage / format.blockHeight); + + // The function ComputeTextureCopySplit() decides how to split the copy based on: + // - the alignment of the buffer offset with D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT (512) + // - the alignment of the buffer offset with D3D12_TEXTURE_DATA_PITCH_ALIGNMENT (256) + // Each slice of a 2D array or 3D copy might need to be split, but because of the WebGPU + // constraint that "bytesPerRow" must be a multiple of 256, all odd (resp. all even) slices + // will be at an offset multiple of 512 of each other, which means they will all result in + // the same 2D split. Thus we can just compute the copy splits for the first and second + // slices, and reuse them for the remaining slices by adding the related offset of each + // slice. Moreover, if "rowsPerImage" is even, both the first and second copy layers can + // share the same copy split, so in this situation we just need to compute copy split once + // and reuse it for all the slices. + const dawn_native::Extent3D copyOneLayerSize = {copySize.width, copySize.height, 1}; + const dawn_native::Origin3D copyFirstLayerOrigin = {origin.x, origin.y, 0}; + + copies.copies2D[0] = ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize, format, + offset, bytesPerRow, rowsPerImage); + + // When the copy only refers one texture 2D array layer copies.copies2D[1] will never be + // used so we can safely early return here. + if (copySize.depth == 1) { + return copies; + } + + if (bytesPerSlice % D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT == 0) { + copies.copies2D[1] = copies.copies2D[0]; + copies.copies2D[1].offset += bytesPerSlice; + } else { + const uint64_t bufferOffsetNextLayer = offset + bytesPerSlice; + copies.copies2D[1] = + ComputeTextureCopySplit(copyFirstLayerOrigin, copyOneLayerSize, format, + bufferOffsetNextLayer, bytesPerRow, rowsPerImage); + } + + return copies; + } + }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/TextureCopySplitter.h b/third_party/dawn/src/dawn_native/d3d12/TextureCopySplitter.h index e70a0e15766..f9f4b363d5c 100644 --- a/third_party/dawn/src/dawn_native/d3d12/TextureCopySplitter.h +++ b/third_party/dawn/src/dawn_native/d3d12/TextureCopySplitter.h @@ -19,9 +19,15 @@ #include +namespace dawn_native { + + struct Format; + +} // namespace dawn_native + namespace dawn_native { namespace d3d12 { - struct TextureCopySplit { + struct Texture2DCopySplit { static constexpr unsigned int kMaxTextureCopyRegions = 2; struct CopyInfo { @@ -37,12 +43,25 @@ namespace dawn_native { namespace d3d12 { std::array copies; }; - TextureCopySplit ComputeTextureCopySplit(Origin3D origin, - Extent3D copySize, - uint32_t texelSize, - uint64_t offset, - uint32_t rowPitch, - uint32_t imageHeight); + struct TextureCopySplits { + static constexpr uint32_t kMaxTextureCopySplits = 2; + + std::array copies2D; + }; + + Texture2DCopySplit ComputeTextureCopySplit(Origin3D origin, + Extent3D copySize, + const Format& format, + uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage); + + TextureCopySplits ComputeTextureCopySplits(Origin3D origin, + Extent3D copySize, + const Format& format, + uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage); }} // namespace dawn_native::d3d12 #endif // DAWNNATIVE_D3D12_TEXTURECOPYSPLITTER_H_ diff --git a/third_party/dawn/src/dawn_native/d3d12/TextureD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/TextureD3D12.cpp index a8153b55f9d..b85f72a7157 100644 --- a/third_party/dawn/src/dawn_native/d3d12/TextureD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/TextureD3D12.cpp @@ -14,37 +14,49 @@ #include "dawn_native/d3d12/TextureD3D12.h" -#include "dawn_native/d3d12/DescriptorHeapAllocator.h" +#include "common/Constants.h" +#include "common/Math.h" +#include "dawn_native/DynamicUploader.h" +#include "dawn_native/Error.h" +#include "dawn_native/d3d12/BufferD3D12.h" +#include "dawn_native/d3d12/CommandRecordingContext.h" +#include "dawn_native/d3d12/D3D12Error.h" #include "dawn_native/d3d12/DeviceD3D12.h" -#include "dawn_native/d3d12/ResourceAllocator.h" +#include "dawn_native/d3d12/HeapD3D12.h" +#include "dawn_native/d3d12/ResourceAllocatorManagerD3D12.h" +#include "dawn_native/d3d12/StagingBufferD3D12.h" +#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" +#include "dawn_native/d3d12/TextureCopySplitter.h" +#include "dawn_native/d3d12/UtilsD3D12.h" namespace dawn_native { namespace d3d12 { namespace { - D3D12_RESOURCE_STATES D3D12TextureUsage(dawn::TextureUsageBit usage, - dawn::TextureFormat format) { + D3D12_RESOURCE_STATES D3D12TextureUsage(wgpu::TextureUsage usage, const Format& format) { D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON; - // Present is an exclusive flag. - if (usage & dawn::TextureUsageBit::Present) { + if (usage & kPresentTextureUsage) { + // The present usage is only used internally by the swapchain and is never used in + // combination with other usages. + ASSERT(usage == kPresentTextureUsage); return D3D12_RESOURCE_STATE_PRESENT; } - if (usage & dawn::TextureUsageBit::TransferSrc) { + if (usage & wgpu::TextureUsage::CopySrc) { resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE; } - if (usage & dawn::TextureUsageBit::TransferDst) { + if (usage & wgpu::TextureUsage::CopyDst) { resourceState |= D3D12_RESOURCE_STATE_COPY_DEST; } - if (usage & dawn::TextureUsageBit::Sampled) { + if (usage & (wgpu::TextureUsage::Sampled | kReadonlyStorageTexture)) { resourceState |= (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE); } - if (usage & dawn::TextureUsageBit::Storage) { + if (usage & wgpu::TextureUsage::Storage) { resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS; } - if (usage & dawn::TextureUsageBit::OutputAttachment) { - if (TextureFormatHasDepth(format) || TextureFormatHasStencil(format)) { + if (usage & wgpu::TextureUsage::OutputAttachment) { + if (format.HasDepthOrStencil()) { resourceState |= D3D12_RESOURCE_STATE_DEPTH_WRITE; } else { resourceState |= D3D12_RESOURCE_STATE_RENDER_TARGET; @@ -54,21 +66,26 @@ namespace dawn_native { namespace d3d12 { return resourceState; } - D3D12_RESOURCE_FLAGS D3D12ResourceFlags(dawn::TextureUsageBit usage, - dawn::TextureFormat format, + D3D12_RESOURCE_FLAGS D3D12ResourceFlags(wgpu::TextureUsage usage, + const Format& format, bool isMultisampledTexture) { D3D12_RESOURCE_FLAGS flags = D3D12_RESOURCE_FLAG_NONE; - if (usage & dawn::TextureUsageBit::Storage) { + if (usage & wgpu::TextureUsage::Storage) { flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; } // A multisampled resource must have either D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET or // D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL set in D3D12_RESOURCE_DESC::Flags. - // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_resource - // _desc - if ((usage & dawn::TextureUsageBit::OutputAttachment) || isMultisampledTexture) { - if (TextureFormatHasDepth(format) || TextureFormatHasStencil(format)) { + // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_resource_desc + // Currently all textures are zero-initialized via the render-target path so always add + // the render target flag, except for compressed textures for which the render-target + // flag is invalid. + // TODO(natlee@microsoft.com, jiawei.shao@intel.com): do not require render target for + // lazy clearing. + if ((usage & wgpu::TextureUsage::OutputAttachment) || isMultisampledTexture || + !format.isCompressed) { + if (format.HasDepthOrStencil()) { flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; } else { flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET; @@ -80,42 +97,354 @@ namespace dawn_native { namespace d3d12 { return flags; } - D3D12_RESOURCE_DIMENSION D3D12TextureDimension(dawn::TextureDimension dimension) { + D3D12_RESOURCE_DIMENSION D3D12TextureDimension(wgpu::TextureDimension dimension) { switch (dimension) { - case dawn::TextureDimension::e2D: + case wgpu::TextureDimension::e2D: return D3D12_RESOURCE_DIMENSION_TEXTURE2D; default: UNREACHABLE(); } } + DXGI_FORMAT D3D12TypelessTextureFormat(wgpu::TextureFormat format) { + switch (format) { + case wgpu::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Snorm: + case wgpu::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Sint: + return DXGI_FORMAT_R8_TYPELESS; + + case wgpu::TextureFormat::R16Uint: + case wgpu::TextureFormat::R16Sint: + case wgpu::TextureFormat::R16Float: + return DXGI_FORMAT_R16_TYPELESS; + + case wgpu::TextureFormat::RG8Unorm: + case wgpu::TextureFormat::RG8Snorm: + case wgpu::TextureFormat::RG8Uint: + case wgpu::TextureFormat::RG8Sint: + return DXGI_FORMAT_R8G8_TYPELESS; + + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::R32Float: + return DXGI_FORMAT_R32_TYPELESS; + + case wgpu::TextureFormat::RG16Uint: + case wgpu::TextureFormat::RG16Sint: + case wgpu::TextureFormat::RG16Float: + return DXGI_FORMAT_R16G16_TYPELESS; + + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8UnormSrgb: + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA8Sint: + return DXGI_FORMAT_R8G8B8A8_TYPELESS; + + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + return DXGI_FORMAT_B8G8R8A8_TYPELESS; + + case wgpu::TextureFormat::RGB10A2Unorm: + return DXGI_FORMAT_R10G10B10A2_TYPELESS; + + case wgpu::TextureFormat::RG11B10Float: + return DXGI_FORMAT_R11G11B10_FLOAT; + + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RG32Float: + return DXGI_FORMAT_R32G32_TYPELESS; + + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA16Float: + return DXGI_FORMAT_R16G16B16A16_TYPELESS; + + case wgpu::TextureFormat::RGBA32Uint: + case wgpu::TextureFormat::RGBA32Sint: + case wgpu::TextureFormat::RGBA32Float: + return DXGI_FORMAT_R32G32B32A32_TYPELESS; + + case wgpu::TextureFormat::Depth32Float: + case wgpu::TextureFormat::Depth24Plus: + return DXGI_FORMAT_R32_TYPELESS; + + case wgpu::TextureFormat::Depth24PlusStencil8: + return DXGI_FORMAT_X32_TYPELESS_G8X24_UINT; + + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + return DXGI_FORMAT_BC1_TYPELESS; + + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + return DXGI_FORMAT_BC2_TYPELESS; + + case wgpu::TextureFormat::BC3RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + return DXGI_FORMAT_BC3_TYPELESS; + + case wgpu::TextureFormat::BC4RSnorm: + case wgpu::TextureFormat::BC4RUnorm: + return DXGI_FORMAT_BC4_TYPELESS; + + case wgpu::TextureFormat::BC5RGSnorm: + case wgpu::TextureFormat::BC5RGUnorm: + return DXGI_FORMAT_BC5_TYPELESS; + + case wgpu::TextureFormat::BC6HRGBSfloat: + case wgpu::TextureFormat::BC6HRGBUfloat: + return DXGI_FORMAT_BC6H_TYPELESS; + + case wgpu::TextureFormat::BC7RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return DXGI_FORMAT_BC7_TYPELESS; + + default: + UNREACHABLE(); + } + } + } // namespace - DXGI_FORMAT D3D12TextureFormat(dawn::TextureFormat format) { + DXGI_FORMAT D3D12TextureFormat(wgpu::TextureFormat format) { switch (format) { - case dawn::TextureFormat::R8G8B8A8Unorm: - return DXGI_FORMAT_R8G8B8A8_UNORM; - case dawn::TextureFormat::R8G8Unorm: - return DXGI_FORMAT_R8G8_UNORM; - case dawn::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Unorm: return DXGI_FORMAT_R8_UNORM; - case dawn::TextureFormat::R8G8B8A8Uint: - return DXGI_FORMAT_R8G8B8A8_UINT; - case dawn::TextureFormat::R8G8Uint: - return DXGI_FORMAT_R8G8_UINT; - case dawn::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Snorm: + return DXGI_FORMAT_R8_SNORM; + case wgpu::TextureFormat::R8Uint: return DXGI_FORMAT_R8_UINT; - case dawn::TextureFormat::B8G8R8A8Unorm: + case wgpu::TextureFormat::R8Sint: + return DXGI_FORMAT_R8_SINT; + + case wgpu::TextureFormat::R16Uint: + return DXGI_FORMAT_R16_UINT; + case wgpu::TextureFormat::R16Sint: + return DXGI_FORMAT_R16_SINT; + case wgpu::TextureFormat::R16Float: + return DXGI_FORMAT_R16_FLOAT; + case wgpu::TextureFormat::RG8Unorm: + return DXGI_FORMAT_R8G8_UNORM; + case wgpu::TextureFormat::RG8Snorm: + return DXGI_FORMAT_R8G8_SNORM; + case wgpu::TextureFormat::RG8Uint: + return DXGI_FORMAT_R8G8_UINT; + case wgpu::TextureFormat::RG8Sint: + return DXGI_FORMAT_R8G8_SINT; + + case wgpu::TextureFormat::R32Uint: + return DXGI_FORMAT_R32_UINT; + case wgpu::TextureFormat::R32Sint: + return DXGI_FORMAT_R32_SINT; + case wgpu::TextureFormat::R32Float: + return DXGI_FORMAT_R32_FLOAT; + case wgpu::TextureFormat::RG16Uint: + return DXGI_FORMAT_R16G16_UINT; + case wgpu::TextureFormat::RG16Sint: + return DXGI_FORMAT_R16G16_SINT; + case wgpu::TextureFormat::RG16Float: + return DXGI_FORMAT_R16G16_FLOAT; + case wgpu::TextureFormat::RGBA8Unorm: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case wgpu::TextureFormat::RGBA8UnormSrgb: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + case wgpu::TextureFormat::RGBA8Snorm: + return DXGI_FORMAT_R8G8B8A8_SNORM; + case wgpu::TextureFormat::RGBA8Uint: + return DXGI_FORMAT_R8G8B8A8_UINT; + case wgpu::TextureFormat::RGBA8Sint: + return DXGI_FORMAT_R8G8B8A8_SINT; + case wgpu::TextureFormat::BGRA8Unorm: return DXGI_FORMAT_B8G8R8A8_UNORM; - case dawn::TextureFormat::D32FloatS8Uint: + case wgpu::TextureFormat::BGRA8UnormSrgb: + return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + case wgpu::TextureFormat::RGB10A2Unorm: + return DXGI_FORMAT_R10G10B10A2_UNORM; + case wgpu::TextureFormat::RG11B10Float: + return DXGI_FORMAT_R11G11B10_FLOAT; + + case wgpu::TextureFormat::RG32Uint: + return DXGI_FORMAT_R32G32_UINT; + case wgpu::TextureFormat::RG32Sint: + return DXGI_FORMAT_R32G32_SINT; + case wgpu::TextureFormat::RG32Float: + return DXGI_FORMAT_R32G32_FLOAT; + case wgpu::TextureFormat::RGBA16Uint: + return DXGI_FORMAT_R16G16B16A16_UINT; + case wgpu::TextureFormat::RGBA16Sint: + return DXGI_FORMAT_R16G16B16A16_SINT; + case wgpu::TextureFormat::RGBA16Float: + return DXGI_FORMAT_R16G16B16A16_FLOAT; + + case wgpu::TextureFormat::RGBA32Uint: + return DXGI_FORMAT_R32G32B32A32_UINT; + case wgpu::TextureFormat::RGBA32Sint: + return DXGI_FORMAT_R32G32B32A32_SINT; + case wgpu::TextureFormat::RGBA32Float: + return DXGI_FORMAT_R32G32B32A32_FLOAT; + + case wgpu::TextureFormat::Depth32Float: + return DXGI_FORMAT_D32_FLOAT; + case wgpu::TextureFormat::Depth24Plus: + return DXGI_FORMAT_D32_FLOAT; + case wgpu::TextureFormat::Depth24PlusStencil8: return DXGI_FORMAT_D32_FLOAT_S8X24_UINT; + + case wgpu::TextureFormat::BC1RGBAUnorm: + return DXGI_FORMAT_BC1_UNORM; + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + return DXGI_FORMAT_BC1_UNORM_SRGB; + case wgpu::TextureFormat::BC2RGBAUnorm: + return DXGI_FORMAT_BC2_UNORM; + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + return DXGI_FORMAT_BC2_UNORM_SRGB; + case wgpu::TextureFormat::BC3RGBAUnorm: + return DXGI_FORMAT_BC3_UNORM; + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + return DXGI_FORMAT_BC3_UNORM_SRGB; + case wgpu::TextureFormat::BC4RSnorm: + return DXGI_FORMAT_BC4_SNORM; + case wgpu::TextureFormat::BC4RUnorm: + return DXGI_FORMAT_BC4_UNORM; + case wgpu::TextureFormat::BC5RGSnorm: + return DXGI_FORMAT_BC5_SNORM; + case wgpu::TextureFormat::BC5RGUnorm: + return DXGI_FORMAT_BC5_UNORM; + case wgpu::TextureFormat::BC6HRGBSfloat: + return DXGI_FORMAT_BC6H_SF16; + case wgpu::TextureFormat::BC6HRGBUfloat: + return DXGI_FORMAT_BC6H_UF16; + case wgpu::TextureFormat::BC7RGBAUnorm: + return DXGI_FORMAT_BC7_UNORM; + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return DXGI_FORMAT_BC7_UNORM_SRGB; + default: UNREACHABLE(); } } - Texture::Texture(Device* device, const TextureDescriptor* descriptor) - : TextureBase(device, descriptor, TextureState::OwnedInternal) { + MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor) { + if (descriptor->dimension != wgpu::TextureDimension::e2D) { + return DAWN_VALIDATION_ERROR("Texture must be 2D"); + } + + if (descriptor->mipLevelCount != 1) { + return DAWN_VALIDATION_ERROR("Mip level count must be 1"); + } + + if (descriptor->size.depth != 1) { + return DAWN_VALIDATION_ERROR("Depth must be 1"); + } + + if (descriptor->sampleCount != 1) { + return DAWN_VALIDATION_ERROR("Sample count must be 1"); + } + + return {}; + } + + MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource, + const TextureDescriptor* dawnDescriptor) { + const D3D12_RESOURCE_DESC d3dDescriptor = d3d12Resource->GetDesc(); + if ((dawnDescriptor->size.width != d3dDescriptor.Width) || + (dawnDescriptor->size.height != d3dDescriptor.Height) || + (dawnDescriptor->size.depth != 1)) { + return DAWN_VALIDATION_ERROR("D3D12 texture size doesn't match descriptor"); + } + + const DXGI_FORMAT dxgiFormatFromDescriptor = D3D12TextureFormat(dawnDescriptor->format); + if (dxgiFormatFromDescriptor != d3dDescriptor.Format) { + return DAWN_VALIDATION_ERROR( + "D3D12 texture format must be compatible with descriptor format."); + } + + if (d3dDescriptor.MipLevels != 1) { + return DAWN_VALIDATION_ERROR("D3D12 texture number of miplevels must be 1."); + } + + if (d3dDescriptor.DepthOrArraySize != 1) { + return DAWN_VALIDATION_ERROR("D3D12 texture array size must be 1."); + } + + // Shared textures cannot be multi-sample so no need to check those. + ASSERT(d3dDescriptor.SampleDesc.Count == 1); + ASSERT(d3dDescriptor.SampleDesc.Quality == 0); + + return {}; + } + + ResultOrError> Texture::Create(Device* device, + const TextureDescriptor* descriptor) { + Ref dawnTexture = + AcquireRef(new Texture(device, descriptor, TextureState::OwnedInternal)); + DAWN_TRY(dawnTexture->InitializeAsInternalTexture()); + return std::move(dawnTexture); + } + + ResultOrError> Texture::Create(Device* device, + const ExternalImageDescriptor* descriptor, + HANDLE sharedHandle, + uint64_t acquireMutexKey, + bool isSwapChainTexture) { + const TextureDescriptor* textureDescriptor = + reinterpret_cast(descriptor->cTextureDescriptor); + + // TODO(dawn:22): Remove once migration from GPUTextureDescriptor.arrayLayerCount to + // GPUTextureDescriptor.size.depth is done. + TextureDescriptor fixedDescriptor; + DAWN_TRY_ASSIGN(fixedDescriptor, FixTextureDescriptor(device, textureDescriptor)); + textureDescriptor = &fixedDescriptor; + + Ref dawnTexture = + AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedExternal)); + DAWN_TRY(dawnTexture->InitializeAsExternalTexture(textureDescriptor, sharedHandle, + acquireMutexKey, isSwapChainTexture)); + dawnTexture->SetIsSubresourceContentInitialized(descriptor->isCleared, + dawnTexture->GetAllSubresources()); + return std::move(dawnTexture); + } + + MaybeError Texture::InitializeAsExternalTexture(const TextureDescriptor* descriptor, + HANDLE sharedHandle, + uint64_t acquireMutexKey, + bool isSwapChainTexture) { + Device* dawnDevice = ToBackend(GetDevice()); + DAWN_TRY(ValidateTextureDescriptor(dawnDevice, descriptor)); + DAWN_TRY(ValidateTextureDescriptorCanBeWrapped(descriptor)); + + ComPtr d3d12Resource; + DAWN_TRY(CheckHRESULT(dawnDevice->GetD3D12Device()->OpenSharedHandle( + sharedHandle, IID_PPV_ARGS(&d3d12Resource)), + "D3D12 opening shared handle")); + + DAWN_TRY(ValidateD3D12TextureCanBeWrapped(d3d12Resource.Get(), descriptor)); + + ComPtr dxgiKeyedMutex; + DAWN_TRY_ASSIGN(dxgiKeyedMutex, + dawnDevice->CreateKeyedMutexForTexture(d3d12Resource.Get())); + + DAWN_TRY(CheckHRESULT(dxgiKeyedMutex->AcquireSync(acquireMutexKey, INFINITE), + "D3D12 acquiring shared mutex")); + + mAcquireMutexKey = acquireMutexKey; + mDxgiKeyedMutex = std::move(dxgiKeyedMutex); + mSwapChainTexture = isSwapChainTexture; + + AllocationInfo info; + info.mMethod = AllocationMethod::kExternal; + // When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the + // texture is owned externally. The texture's owning entity must remain responsible for + // memory management. + mResourceAllocation = {info, 0, std::move(d3d12Resource), nullptr}; + + return {}; + } + + MaybeError Texture::InitializeAsInternalTexture() { D3D12_RESOURCE_DESC resourceDescriptor; resourceDescriptor.Dimension = D3D12TextureDimension(GetDimension()); resourceDescriptor.Alignment = 0; @@ -123,132 +452,332 @@ namespace dawn_native { namespace d3d12 { const Extent3D& size = GetSize(); resourceDescriptor.Width = size.width; resourceDescriptor.Height = size.height; + resourceDescriptor.DepthOrArraySize = size.depth; + + // This will need to be much more nuanced when WebGPU has + // texture view compatibility rules. + bool needsTypelessFormat = GetFormat().format == wgpu::TextureFormat::Depth32Float && + (GetUsage() & wgpu::TextureUsage::Sampled) != 0; + + DXGI_FORMAT dxgiFormat = needsTypelessFormat + ? D3D12TypelessTextureFormat(GetFormat().format) + : D3D12TextureFormat(GetFormat().format); - resourceDescriptor.DepthOrArraySize = GetDepthOrArraySize(); resourceDescriptor.MipLevels = static_cast(GetNumMipLevels()); - resourceDescriptor.Format = D3D12TextureFormat(GetFormat()); - resourceDescriptor.SampleDesc.Count = descriptor->sampleCount; + resourceDescriptor.Format = dxgiFormat; + resourceDescriptor.SampleDesc.Count = GetSampleCount(); // TODO(bryan.bernhart@intel.com): investigate how to specify standard MSAA sample pattern. resourceDescriptor.SampleDesc.Quality = 0; resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; resourceDescriptor.Flags = D3D12ResourceFlags(GetUsage(), GetFormat(), IsMultisampledTexture()); - mResource = ToBackend(GetDevice()) - ->GetResourceAllocator() - ->Allocate(D3D12_HEAP_TYPE_DEFAULT, resourceDescriptor, - D3D12_RESOURCE_STATE_COMMON); - mResourcePtr = mResource.Get(); + DAWN_TRY_ASSIGN(mResourceAllocation, + ToBackend(GetDevice()) + ->AllocateMemory(D3D12_HEAP_TYPE_DEFAULT, resourceDescriptor, + D3D12_RESOURCE_STATE_COMMON)); + + Device* device = ToBackend(GetDevice()); if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { - TransitionUsageNow(device->GetPendingCommandList(), D3D12_RESOURCE_STATE_RENDER_TARGET); - uint32_t arrayLayerCount = GetArrayLayers(); - - DescriptorHeapAllocator* descriptorHeapAllocator = device->GetDescriptorHeapAllocator(); - DescriptorHeapHandle rtvHeap = - descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1); - D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetCPUHandle(0); - - const float clearColor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - // TODO(natlee@microsoft.com): clear all array layers for 2D array textures - for (int i = 0; i < resourceDescriptor.MipLevels; i++) { - D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = GetRTVDescriptor(i, arrayLayerCount, 0); - device->GetD3D12Device()->CreateRenderTargetView(mResourcePtr, &rtvDesc, rtvHandle); - device->GetPendingCommandList()->ClearRenderTargetView(rtvHandle, clearColor, 0, - nullptr); - } + CommandRecordingContext* commandContext; + DAWN_TRY_ASSIGN(commandContext, device->GetPendingCommandContext()); + + DAWN_TRY(ClearTexture(commandContext, GetAllSubresources(), + TextureBase::ClearValue::NonZero)); } + + return {}; + } + + Texture::Texture(Device* device, const TextureDescriptor* descriptor, TextureState state) + : TextureBase(device, descriptor, state), + mSubresourceStateAndDecay( + GetSubresourceCount(), + {D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON, UINT64_MAX, false}) { } - // With this constructor, the lifetime of the ID3D12Resource is externally managed. Texture::Texture(Device* device, const TextureDescriptor* descriptor, - ID3D12Resource* nativeTexture) - : TextureBase(device, descriptor, TextureState::OwnedExternal), - mResourcePtr(nativeTexture) { + ComPtr nativeTexture) + : Texture(device, descriptor, TextureState::OwnedExternal) { + AllocationInfo info; + info.mMethod = AllocationMethod::kExternal; + // When creating the ResourceHeapAllocation, the resource heap is set to nullptr because the + // texture is owned externally. The texture's owning entity must remain responsible for + // memory management. + mResourceAllocation = {info, 0, std::move(nativeTexture), nullptr}; + + SetIsSubresourceContentInitialized(true, GetAllSubresources()); } Texture::~Texture() { DestroyInternal(); } - bool Texture::CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier, - dawn::TextureUsageBit newUsage) const { - return CreateD3D12ResourceBarrierIfNeeded(barrier, - D3D12TextureUsage(newUsage, GetFormat())); - } + void Texture::DestroyImpl() { + Device* device = ToBackend(GetDevice()); - bool Texture::CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier, - D3D12_RESOURCE_STATES newState) const { - // Avoid transitioning the texture when it isn't needed. - // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point. - if (mLastState == newState) { - return false; + // In PIX's D3D12-only mode, there is no way to determine frame boundaries + // for WebGPU since Dawn does not manage DXGI swap chains. Without assistance, + // PIX will wait forever for a present that never happens. + // If we know we're dealing with a swapbuffer texture, inform PIX we've + // "presented" the texture so it can determine frame boundaries and use its + // contents for the UI. + if (mSwapChainTexture) { + ID3D12SharingContract* d3dSharingContract = device->GetSharingContract(); + if (d3dSharingContract != nullptr) { + d3dSharingContract->Present(mResourceAllocation.GetD3D12Resource(), 0, 0); + } } - barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier->Transition.pResource = mResourcePtr; - barrier->Transition.StateBefore = mLastState; - barrier->Transition.StateAfter = newState; - barrier->Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - - return true; - } + device->DeallocateMemory(mResourceAllocation); - void Texture::DestroyImpl() { - // If we own the resource, release it. - ToBackend(GetDevice())->GetResourceAllocator()->Release(mResource); - mResource = nullptr; + if (mDxgiKeyedMutex != nullptr) { + mDxgiKeyedMutex->ReleaseSync(mAcquireMutexKey + 1); + device->ReleaseKeyedMutexForTexture(std::move(mDxgiKeyedMutex)); + } } DXGI_FORMAT Texture::GetD3D12Format() const { - return D3D12TextureFormat(GetFormat()); + return D3D12TextureFormat(GetFormat().format); } ID3D12Resource* Texture::GetD3D12Resource() const { - return mResourcePtr; + return mResourceAllocation.GetD3D12Resource(); } - UINT16 Texture::GetDepthOrArraySize() { - switch (GetDimension()) { - case dawn::TextureDimension::e2D: - return static_cast(GetArrayLayers()); - default: - UNREACHABLE(); - } + void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, + wgpu::TextureUsage usage, + const SubresourceRange& range) { + TrackUsageAndTransitionNow(commandContext, D3D12TextureUsage(usage, GetFormat()), range); } - void Texture::SetUsage(dawn::TextureUsageBit newUsage) { - mLastState = D3D12TextureUsage(newUsage, GetFormat()); + void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext, + wgpu::TextureUsage usage) { + TrackUsageAndTransitionNow(commandContext, D3D12TextureUsage(usage, GetFormat()), + GetAllSubresources()); } - void Texture::TransitionUsageNow(ComPtr commandList, - dawn::TextureUsageBit usage) { - TransitionUsageNow(commandList, D3D12TextureUsage(usage, GetFormat())); + void Texture::TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext, + D3D12_RESOURCE_STATES newState) { + TrackUsageAndTransitionNow(commandContext, newState, GetAllSubresources()); } - void Texture::TransitionUsageNow(ComPtr commandList, - D3D12_RESOURCE_STATES newState) { + void Texture::TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, + D3D12_RESOURCE_STATES newState, + const SubresourceRange& range) { + if (mResourceAllocation.GetInfo().mMethod != AllocationMethod::kExternal) { + // Track the underlying heap to ensure residency. + Heap* heap = ToBackend(mResourceAllocation.GetResourceHeap()); + commandContext->TrackHeapUsage(heap, GetDevice()->GetPendingCommandSerial()); + } + + std::vector barriers; + barriers.reserve(range.levelCount * range.layerCount); + + TransitionUsageAndGetResourceBarrier(commandContext, &barriers, newState, range); + if (barriers.size()) { + commandContext->GetCommandList()->ResourceBarrier(barriers.size(), barriers.data()); + } + } + + void Texture::TransitionSingleOrAllSubresources(std::vector* barriers, + uint32_t index, + D3D12_RESOURCE_STATES newState, + const Serial pendingCommandSerial, + bool allSubresources) { + StateAndDecay* state = &mSubresourceStateAndDecay[index]; + // Reuse the subresource(s) directly and avoid transition when it isn't needed, and + // return false. + // TODO(cwallez@chromium.org): Need some form of UAV barriers at some point. + if (state->lastState == newState) { + return; + } + + D3D12_RESOURCE_STATES lastState = state->lastState; + + // The COMMON state represents a state where no write operations can be pending, and + // where all pixels are uncompressed. This makes it possible to transition to and + // from some states without synchronization (i.e. without an explicit + // ResourceBarrier call). Textures can be implicitly promoted to 1) a single write + // state, or 2) multiple read states. Textures will implicitly decay to the COMMON + // state when all of the following are true: 1) the texture is accessed on a command + // list, 2) the ExecuteCommandLists call that uses that command list has ended, and + // 3) the texture was promoted implicitly to a read-only state and is still in that + // state. + // https://docs.microsoft.com/en-us/windows/desktop/direct3d12/using-resource-barriers-to-synchronize-resource-states-in-direct3d-12#implicit-state-transitions + + // To track implicit decays, we must record the pending serial on which that + // transition will occur. When that texture is used again, the previously recorded + // serial must be compared to the last completed serial to determine if the texture + // has implicity decayed to the common state. + if (state->isValidToDecay && pendingCommandSerial > state->lastDecaySerial) { + lastState = D3D12_RESOURCE_STATE_COMMON; + } + + // Update the tracked state. + state->lastState = newState; + + // Destination states that qualify for an implicit promotion for a + // non-simultaneous-access texture: NON_PIXEL_SHADER_RESOURCE, + // PIXEL_SHADER_RESOURCE, COPY_SRC, COPY_DEST. + { + static constexpr D3D12_RESOURCE_STATES kD3D12PromotableReadOnlyStates = + D3D12_RESOURCE_STATE_COPY_SOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | + D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE; + + if (lastState == D3D12_RESOURCE_STATE_COMMON) { + if (newState == (newState & kD3D12PromotableReadOnlyStates)) { + // Implicit texture state decays can only occur when the texture was implicitly + // transitioned to a read-only state. isValidToDecay is needed to differentiate + // between resources that were implictly or explicitly transitioned to a + // read-only state. + state->isValidToDecay = true; + state->lastDecaySerial = pendingCommandSerial; + return; + } else if (newState == D3D12_RESOURCE_STATE_COPY_DEST) { + state->isValidToDecay = false; + return; + } + } + } + D3D12_RESOURCE_BARRIER barrier; - if (CreateD3D12ResourceBarrierIfNeeded(&barrier, newState)) { - commandList->ResourceBarrier(1, &barrier); + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = GetD3D12Resource(); + barrier.Transition.StateBefore = lastState; + barrier.Transition.StateAfter = newState; + barrier.Transition.Subresource = + allSubresources ? D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES : index; + barriers->push_back(barrier); + // TODO(yunchao.he@intel.com): support subresource for depth/stencil. Depth stencil + // texture has different plane slices. While the current implementation only has differernt + // mip slices and array slices for subresources. + // This is a hack because Dawn doesn't handle subresource of multiplanar resources + // correctly. We force the transition to be the same for all planes to match what the + // frontend validation checks for. This hack might be incorrect for stencil-only texture + // because we always set transition barrier for depth plane. + if (!allSubresources && newState == D3D12_RESOURCE_STATE_DEPTH_WRITE && + GetFormat().HasStencil()) { + D3D12_RESOURCE_BARRIER barrierStencil = barrier; + barrierStencil.Transition.Subresource += GetArrayLayers() * GetNumMipLevels(); + barriers->push_back(barrierStencil); + } + + state->isValidToDecay = false; + } + + void Texture::HandleTransitionSpecialCases(CommandRecordingContext* commandContext) { + // Textures with keyed mutexes can be written from other graphics queues. Hence, they + // must be acquired before command list submission to ensure work from the other queues + // has finished. See Device::ExecuteCommandContext. + if (mDxgiKeyedMutex != nullptr) { + commandContext->AddToSharedTextureList(this); + } + } + + void Texture::TransitionUsageAndGetResourceBarrier( + CommandRecordingContext* commandContext, + std::vector* barriers, + D3D12_RESOURCE_STATES newState, + const SubresourceRange& range) { + HandleTransitionSpecialCases(commandContext); + + const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial(); + uint32_t subresourceCount = GetSubresourceCount(); + + // This transitions assume it is a 2D texture + ASSERT(GetDimension() == wgpu::TextureDimension::e2D); + + // If the usages transitions can cover all subresources, and old usages of all subresources + // are the same, then we can use one barrier to do state transition for all subresources. + // Note that if the texture has only one mip level and one array slice, it will fall into + // this category. + bool areAllSubresourcesCovered = range.levelCount * range.layerCount == subresourceCount; + if (mSameLastUsagesAcrossSubresources && areAllSubresourcesCovered) { + TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true); + + // TODO(yunchao.he@intel.com): compress and decompress if all subresources have the + // same states. We may need to retain mSubresourceStateAndDecay[0] only. + for (uint32_t i = 1; i < subresourceCount; ++i) { + mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0]; + } + + return; + } + for (uint32_t arrayLayer = 0; arrayLayer < range.layerCount; ++arrayLayer) { + for (uint32_t mipLevel = 0; mipLevel < range.levelCount; ++mipLevel) { + uint32_t index = GetSubresourceIndex(range.baseMipLevel + mipLevel, + range.baseArrayLayer + arrayLayer); + + TransitionSingleOrAllSubresources(barriers, index, newState, pendingCommandSerial, + false); + } + } + mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered; + } + + void Texture::TrackUsageAndGetResourceBarrierForPass( + CommandRecordingContext* commandContext, + std::vector* barriers, + const PassTextureUsage& textureUsages) { + HandleTransitionSpecialCases(commandContext); + + const Serial pendingCommandSerial = ToBackend(GetDevice())->GetPendingCommandSerial(); + uint32_t subresourceCount = GetSubresourceCount(); + ASSERT(textureUsages.subresourceUsages.size() == subresourceCount); + // This transitions assume it is a 2D texture + ASSERT(GetDimension() == wgpu::TextureDimension::e2D); + + // If new usages of all subresources are the same and old usages of all subresources are + // the same too, we can use one barrier to do state transition for all subresources. + // Note that if the texture has only one mip level and one array slice, it will fall into + // this category. + if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) { + D3D12_RESOURCE_STATES newState = D3D12TextureUsage(textureUsages.usage, GetFormat()); + TransitionSingleOrAllSubresources(barriers, 0, newState, pendingCommandSerial, true); + + // TODO(yunchao.he@intel.com): compress and decompress if all subresources have the + // same states. We may need to retain mSubresourceStateAndDecay[0] only. + for (uint32_t i = 1; i < subresourceCount; ++i) { + mSubresourceStateAndDecay[i] = mSubresourceStateAndDecay[0]; + } + + return; } - mLastState = newState; + for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) { + for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) { + uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer); + + // Skip if this subresource is not used during the current pass + if (textureUsages.subresourceUsages[index] == wgpu::TextureUsage::None) { + continue; + } + + D3D12_RESOURCE_STATES newState = + D3D12TextureUsage(textureUsages.subresourceUsages[index], GetFormat()); + + TransitionSingleOrAllSubresources(barriers, index, newState, pendingCommandSerial, + false); + } + } + mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources; } - D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipSlice, - uint32_t arrayLayers, - uint32_t baseArrayLayer) const { - ASSERT(GetDimension() == dawn::TextureDimension::e2D); + D3D12_RENDER_TARGET_VIEW_DESC Texture::GetRTVDescriptor(uint32_t mipLevel, + uint32_t baseArrayLayer, + uint32_t layerCount) const { + ASSERT(GetDimension() == wgpu::TextureDimension::e2D); D3D12_RENDER_TARGET_VIEW_DESC rtvDesc; rtvDesc.Format = GetD3D12Format(); if (IsMultisampledTexture()) { ASSERT(GetNumMipLevels() == 1); - ASSERT(arrayLayers == 1); + ASSERT(layerCount == 1); ASSERT(baseArrayLayer == 0); - ASSERT(mipSlice == 0); + ASSERT(mipLevel == 0); rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS; } else { // Currently we always use D3D12_TEX2D_ARRAY_RTV because we cannot specify base array @@ -259,55 +788,259 @@ namespace dawn_native { namespace d3d12 { // _rtv rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; rtvDesc.Texture2DArray.FirstArraySlice = baseArrayLayer; - rtvDesc.Texture2DArray.ArraySize = arrayLayers; - rtvDesc.Texture2DArray.MipSlice = mipSlice; + rtvDesc.Texture2DArray.ArraySize = layerCount; + rtvDesc.Texture2DArray.MipSlice = mipLevel; rtvDesc.Texture2DArray.PlaneSlice = 0; } return rtvDesc; } + D3D12_DEPTH_STENCIL_VIEW_DESC Texture::GetDSVDescriptor(uint32_t mipLevel, + uint32_t baseArrayLayer, + uint32_t layerCount) const { + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; + dsvDesc.Format = GetD3D12Format(); + dsvDesc.Flags = D3D12_DSV_FLAG_NONE; + + if (IsMultisampledTexture()) { + ASSERT(GetNumMipLevels() == 1); + ASSERT(layerCount == 1); + ASSERT(baseArrayLayer == 0); + ASSERT(mipLevel == 0); + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; + } else { + dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; + dsvDesc.Texture2DArray.FirstArraySlice = baseArrayLayer; + dsvDesc.Texture2DArray.ArraySize = layerCount; + dsvDesc.Texture2DArray.MipSlice = mipLevel; + } + + return dsvDesc; + } + + MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext, + const SubresourceRange& range, + TextureBase::ClearValue clearValue) { + // TODO(jiawei.shao@intel.com): initialize the textures in compressed formats with copies. + if (GetFormat().isCompressed) { + SetIsSubresourceContentInitialized(true, range); + return {}; + } + + ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); + + Device* device = ToBackend(GetDevice()); + + uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1; + float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f; + + if (GetFormat().isRenderable) { + if (GetFormat().HasDepthOrStencil()) { + TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_DEPTH_WRITE, range); + + D3D12_CLEAR_FLAGS clearFlags = {}; + + for (uint32_t level = range.baseMipLevel; + level < range.baseMipLevel + range.levelCount; ++level) { + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, layer))) { + // Skip lazy clears if already initialized. + continue; + } + + CPUDescriptorHeapAllocation dsvHandle; + DAWN_TRY_ASSIGN(dsvHandle, device->GetDepthStencilViewAllocator() + ->AllocateTransientCPUDescriptors()); + const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = + dsvHandle.GetBaseDescriptor(); + D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc = GetDSVDescriptor(level, layer, 1); + device->GetD3D12Device()->CreateDepthStencilView(GetD3D12Resource(), + &dsvDesc, baseDescriptor); + + if (GetFormat().HasDepth()) { + clearFlags |= D3D12_CLEAR_FLAG_DEPTH; + } + if (GetFormat().HasStencil()) { + clearFlags |= D3D12_CLEAR_FLAG_STENCIL; + } + + commandList->ClearDepthStencilView(baseDescriptor, clearFlags, fClearColor, + clearColor, 0, nullptr); + } + } + } else { + TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RENDER_TARGET, + range); + + const float clearColorRGBA[4] = {fClearColor, fClearColor, fClearColor, + fClearColor}; + + for (uint32_t level = range.baseMipLevel; + level < range.baseMipLevel + range.levelCount; ++level) { + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, layer))) { + // Skip lazy clears if already initialized. + continue; + } + + CPUDescriptorHeapAllocation rtvHeap; + DAWN_TRY_ASSIGN(rtvHeap, device->GetRenderTargetViewAllocator() + ->AllocateTransientCPUDescriptors()); + const D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap.GetBaseDescriptor(); + + D3D12_RENDER_TARGET_VIEW_DESC rtvDesc = GetRTVDescriptor(level, layer, 1); + device->GetD3D12Device()->CreateRenderTargetView(GetD3D12Resource(), + &rtvDesc, rtvHandle); + commandList->ClearRenderTargetView(rtvHandle, clearColorRGBA, 0, nullptr); + } + } + } + } else { + // TODO(natlee@microsoft.com): test compressed textures are cleared + // create temp buffer with clear color to copy to the texture image + uint32_t bytesPerRow = + Align((GetWidth() / GetFormat().blockWidth) * GetFormat().blockByteSize, + kTextureBytesPerRowAlignment); + uint64_t bufferSize64 = bytesPerRow * (GetHeight() / GetFormat().blockHeight); + if (bufferSize64 > std::numeric_limits::max()) { + return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer."); + } + uint32_t bufferSize = static_cast(bufferSize64); + DynamicUploader* uploader = device->GetDynamicUploader(); + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, + uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); + memset(uploadHandle.mappedBuffer, clearColor, bufferSize); + + TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_COPY_DEST, range); + + for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; + ++level) { + // compute d3d12 texture copy locations for texture and buffer + Extent3D copySize = GetMipLevelVirtualSize(level); + + uint32_t rowsPerImage = GetHeight(); + Texture2DCopySplit copySplit = + ComputeTextureCopySplit({0, 0, 0}, copySize, GetFormat(), + uploadHandle.startOffset, bytesPerRow, rowsPerImage); + + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, layer))) { + // Skip lazy clears if already initialized. + continue; + } + + D3D12_TEXTURE_COPY_LOCATION textureLocation = + ComputeTextureCopyLocationForTexture(this, level, layer); + for (uint32_t i = 0; i < copySplit.count; ++i) { + Texture2DCopySplit::CopyInfo& info = copySplit.copies[i]; + + D3D12_TEXTURE_COPY_LOCATION bufferLocation = + ComputeBufferLocationForCopyTextureRegion( + this, ToBackend(uploadHandle.stagingBuffer)->GetResource(), + info.bufferSize, copySplit.offset, bytesPerRow); + D3D12_BOX sourceRegion = + ComputeD3D12BoxFromOffsetAndSize(info.bufferOffset, info.copySize); + + // copy the buffer filled with clear color to the texture + commandList->CopyTextureRegion(&textureLocation, info.textureOffset.x, + info.textureOffset.y, info.textureOffset.z, + &bufferLocation, &sourceRegion); + } + } + } + } + if (clearValue == TextureBase::ClearValue::Zero) { + SetIsSubresourceContentInitialized(true, range); + GetDevice()->IncrementLazyClearCountForTesting(); + } + return {}; + } + + void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext, + const SubresourceRange& range) { + if (!ToBackend(GetDevice())->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { + return; + } + if (!IsSubresourceContentInitialized(range)) { + // If subresource has not been initialized, clear it to black as it could contain + // dirty bits from recycled memory + GetDevice()->ConsumedError( + ClearTexture(commandContext, range, TextureBase::ClearValue::Zero)); + } + } + TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) : TextureViewBase(texture, descriptor) { mSrvDesc.Format = D3D12TextureFormat(descriptor->format); + if (descriptor->format == wgpu::TextureFormat::Depth32Float) { + // TODO(enga): This will need to be much more nuanced when WebGPU has + // texture view compatibility rules. + mSrvDesc.Format = DXGI_FORMAT_R32_FLOAT; + } mSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; // Currently we always use D3D12_TEX2D_ARRAY_SRV because we cannot specify base array layer // and layer count in D3D12_TEX2D_SRV. For 2D texture views, we treat them as 1-layer 2D // array textures. + // Multisampled textures may only be one array layer, so we use + // D3D12_SRV_DIMENSION_TEXTURE2DMS. // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_srv // https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ns-d3d12-d3d12_tex2d_array_srv // TODO(jiawei.shao@intel.com): support more texture view dimensions. - // TODO(jiawei.shao@intel.com): support creating SRV on multisampled textures. - switch (descriptor->dimension) { - case dawn::TextureViewDimension::e2D: - case dawn::TextureViewDimension::e2DArray: - ASSERT(texture->GetDimension() == dawn::TextureDimension::e2D); - mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; - mSrvDesc.Texture2DArray.ArraySize = descriptor->arrayLayerCount; - mSrvDesc.Texture2DArray.FirstArraySlice = descriptor->baseArrayLayer; - mSrvDesc.Texture2DArray.MipLevels = descriptor->mipLevelCount; - mSrvDesc.Texture2DArray.MostDetailedMip = descriptor->baseMipLevel; - mSrvDesc.Texture2DArray.PlaneSlice = 0; - mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0; - break; - case dawn::TextureViewDimension::Cube: - case dawn::TextureViewDimension::CubeArray: - ASSERT(texture->GetDimension() == dawn::TextureDimension::e2D); - ASSERT(descriptor->arrayLayerCount % 6 == 0); - mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; - mSrvDesc.TextureCubeArray.First2DArrayFace = descriptor->baseArrayLayer; - mSrvDesc.TextureCubeArray.NumCubes = descriptor->arrayLayerCount / 6; - mSrvDesc.TextureCubeArray.MostDetailedMip = descriptor->baseMipLevel; - mSrvDesc.TextureCubeArray.MipLevels = descriptor->mipLevelCount; - mSrvDesc.TextureCubeArray.ResourceMinLODClamp = 0; - break; - default: - UNREACHABLE(); + if (GetTexture()->IsMultisampledTexture()) { + switch (descriptor->dimension) { + case wgpu::TextureViewDimension::e2DArray: + ASSERT(texture->GetArrayLayers() == 1); + DAWN_FALLTHROUGH; + case wgpu::TextureViewDimension::e2D: + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS; + break; + default: + UNREACHABLE(); + } + } else { + switch (descriptor->dimension) { + case wgpu::TextureViewDimension::e2D: + case wgpu::TextureViewDimension::e2DArray: + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + mSrvDesc.Texture2DArray.ArraySize = descriptor->arrayLayerCount; + mSrvDesc.Texture2DArray.FirstArraySlice = descriptor->baseArrayLayer; + mSrvDesc.Texture2DArray.MipLevels = descriptor->mipLevelCount; + mSrvDesc.Texture2DArray.MostDetailedMip = descriptor->baseMipLevel; + mSrvDesc.Texture2DArray.PlaneSlice = 0; + mSrvDesc.Texture2DArray.ResourceMinLODClamp = 0; + break; + case wgpu::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::CubeArray: + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + ASSERT(descriptor->arrayLayerCount % 6 == 0); + mSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURECUBEARRAY; + mSrvDesc.TextureCubeArray.First2DArrayFace = descriptor->baseArrayLayer; + mSrvDesc.TextureCubeArray.NumCubes = descriptor->arrayLayerCount / 6; + mSrvDesc.TextureCubeArray.MostDetailedMip = descriptor->baseMipLevel; + mSrvDesc.TextureCubeArray.MipLevels = descriptor->mipLevelCount; + mSrvDesc.TextureCubeArray.ResourceMinLODClamp = 0; + break; + default: + UNREACHABLE(); + } } } DXGI_FORMAT TextureView::GetD3D12Format() const { - return D3D12TextureFormat(GetFormat()); + return D3D12TextureFormat(GetFormat().format); } const D3D12_SHADER_RESOURCE_VIEW_DESC& TextureView::GetSRVDescriptor() const { @@ -316,26 +1049,26 @@ namespace dawn_native { namespace d3d12 { D3D12_RENDER_TARGET_VIEW_DESC TextureView::GetRTVDescriptor() const { return ToBackend(GetTexture()) - ->GetRTVDescriptor(GetBaseMipLevel(), GetLayerCount(), GetBaseArrayLayer()); + ->GetRTVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount()); } D3D12_DEPTH_STENCIL_VIEW_DESC TextureView::GetDSVDescriptor() const { - D3D12_DEPTH_STENCIL_VIEW_DESC dsvDesc; - dsvDesc.Format = ToBackend(GetTexture())->GetD3D12Format(); - dsvDesc.Flags = D3D12_DSV_FLAG_NONE; - - // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture. - ASSERT(GetTexture()->GetArrayLayers() == 1 && GetTexture()->GetNumMipLevels() == 1 && - GetBaseArrayLayer() == 0 && GetBaseMipLevel() == 0); + ASSERT(GetLevelCount() == 1); + return ToBackend(GetTexture()) + ->GetDSVDescriptor(GetBaseMipLevel(), GetBaseArrayLayer(), GetLayerCount()); + } - if (GetTexture()->IsMultisampledTexture()) { - dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS; - } else { - dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; - dsvDesc.Texture2D.MipSlice = 0; - } + D3D12_UNORDERED_ACCESS_VIEW_DESC TextureView::GetUAVDescriptor() const { + D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc; + uavDesc.Format = GetD3D12Format(); - return dsvDesc; + ASSERT(!GetTexture()->IsMultisampledTexture()); + uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + uavDesc.Texture2DArray.FirstArraySlice = GetBaseArrayLayer(); + uavDesc.Texture2DArray.ArraySize = GetLayerCount(); + uavDesc.Texture2DArray.MipSlice = GetBaseMipLevel(); + uavDesc.Texture2DArray.PlaneSlice = 0; + return uavDesc; } }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/TextureD3D12.h b/third_party/dawn/src/dawn_native/d3d12/TextureD3D12.h index 1c76f0dfd46..ef17bf40628 100644 --- a/third_party/dawn/src/dawn_native/d3d12/TextureD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/TextureD3D12.h @@ -15,50 +15,109 @@ #ifndef DAWNNATIVE_D3D12_TEXTURED3D12_H_ #define DAWNNATIVE_D3D12_TEXTURED3D12_H_ +#include "common/Serial.h" #include "dawn_native/Texture.h" +#include "dawn_native/DawnNative.h" +#include "dawn_native/PassResourceUsage.h" +#include "dawn_native/d3d12/ResourceHeapAllocationD3D12.h" #include "dawn_native/d3d12/d3d12_platform.h" namespace dawn_native { namespace d3d12 { + class CommandRecordingContext; class Device; - DXGI_FORMAT D3D12TextureFormat(dawn::TextureFormat format); + DXGI_FORMAT D3D12TextureFormat(wgpu::TextureFormat format); + MaybeError ValidateD3D12TextureCanBeWrapped(ID3D12Resource* d3d12Resource, + const TextureDescriptor* descriptor); + MaybeError ValidateTextureDescriptorCanBeWrapped(const TextureDescriptor* descriptor); - class Texture : public TextureBase { + class Texture final : public TextureBase { public: - Texture(Device* device, const TextureDescriptor* descriptor); - Texture(Device* device, const TextureDescriptor* descriptor, ID3D12Resource* nativeTexture); - ~Texture(); - - bool CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier, - dawn::TextureUsageBit newUsage) const; - bool CreateD3D12ResourceBarrierIfNeeded(D3D12_RESOURCE_BARRIER* barrier, - D3D12_RESOURCE_STATES newState) const; + static ResultOrError> Create(Device* device, + const TextureDescriptor* descriptor); + static ResultOrError> Create(Device* device, + const ExternalImageDescriptor* descriptor, + HANDLE sharedHandle, + uint64_t acquireMutexKey, + bool isSwapChainTexture); + Texture(Device* device, + const TextureDescriptor* descriptor, + ComPtr d3d12Texture); + DXGI_FORMAT GetD3D12Format() const; ID3D12Resource* GetD3D12Resource() const; - void SetUsage(dawn::TextureUsageBit newUsage); - void TransitionUsageNow(ComPtr commandList, - dawn::TextureUsageBit usage); - void TransitionUsageNow(ComPtr commandList, - D3D12_RESOURCE_STATES newState); - D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(uint32_t mipSlice, - uint32_t arrayLayers, - uint32_t baseArrayLayer) const; + D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor(uint32_t mipLevel, + uint32_t baseArrayLayer, + uint32_t layerCount) const; + D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor(uint32_t mipLevel, + uint32_t baseArrayLayer, + uint32_t layerCount) const; + void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext, + const SubresourceRange& range); + + void TrackUsageAndGetResourceBarrierForPass(CommandRecordingContext* commandContext, + std::vector* barrier, + const PassTextureUsage& textureUsages); + void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, + wgpu::TextureUsage usage, + const SubresourceRange& range); + void TrackUsageAndTransitionNow(CommandRecordingContext* commandContext, + D3D12_RESOURCE_STATES newState, + const SubresourceRange& range); + void TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext, + wgpu::TextureUsage usage); + void TrackAllUsageAndTransitionNow(CommandRecordingContext* commandContext, + D3D12_RESOURCE_STATES newState); private: - // Dawn API - void DestroyImpl() override; + Texture(Device* device, const TextureDescriptor* descriptor, TextureState state); + ~Texture() override; + using TextureBase::TextureBase; - UINT16 GetDepthOrArraySize(); + MaybeError InitializeAsInternalTexture(); + MaybeError InitializeAsExternalTexture(const TextureDescriptor* descriptor, + HANDLE sharedHandle, + uint64_t acquireMutexKey, + bool isSwapChainTexture); - ComPtr mResource = {}; - ID3D12Resource* mResourcePtr = nullptr; - D3D12_RESOURCE_STATES mLastState = D3D12_RESOURCE_STATES::D3D12_RESOURCE_STATE_COMMON; + // Dawn API + void DestroyImpl() override; + MaybeError ClearTexture(CommandRecordingContext* commandContext, + const SubresourceRange& range, + TextureBase::ClearValue clearValue); + + void TransitionUsageAndGetResourceBarrier(CommandRecordingContext* commandContext, + std::vector* barrier, + D3D12_RESOURCE_STATES newState, + const SubresourceRange& range); + + void TransitionSingleOrAllSubresources(std::vector* barriers, + uint32_t index, + D3D12_RESOURCE_STATES subresourceNewState, + const Serial pendingCommandSerial, + bool allSubresources); + void HandleTransitionSpecialCases(CommandRecordingContext* commandContext); + + bool mSameLastUsagesAcrossSubresources = true; + + struct StateAndDecay { + D3D12_RESOURCE_STATES lastState; + Serial lastDecaySerial; + bool isValidToDecay; + }; + std::vector mSubresourceStateAndDecay; + + ResourceHeapAllocation mResourceAllocation; + bool mSwapChainTexture = false; + + Serial mAcquireMutexKey = 0; + ComPtr mDxgiKeyedMutex; }; - class TextureView : public TextureViewBase { + class TextureView final : public TextureViewBase { public: TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor); @@ -67,6 +126,7 @@ namespace dawn_native { namespace d3d12 { const D3D12_SHADER_RESOURCE_VIEW_DESC& GetSRVDescriptor() const; D3D12_RENDER_TARGET_VIEW_DESC GetRTVDescriptor() const; D3D12_DEPTH_STENCIL_VIEW_DESC GetDSVDescriptor() const; + D3D12_UNORDERED_ACCESS_VIEW_DESC GetUAVDescriptor() const; private: D3D12_SHADER_RESOURCE_VIEW_DESC mSrvDesc; diff --git a/third_party/dawn/src/dawn_native/d3d12/UtilsD3D12.cpp b/third_party/dawn/src/dawn_native/d3d12/UtilsD3D12.cpp index 864605c871e..33a1de703c5 100644 --- a/third_party/dawn/src/dawn_native/d3d12/UtilsD3D12.cpp +++ b/third_party/dawn/src/dawn_native/d3d12/UtilsD3D12.cpp @@ -16,29 +16,123 @@ #include "common/Assert.h" +#include + namespace dawn_native { namespace d3d12 { - D3D12_COMPARISON_FUNC ToD3D12ComparisonFunc(dawn::CompareFunction func) { + ResultOrError ConvertStringToWstring(const char* str) { + size_t len = strlen(str); + if (len == 0) { + return std::wstring(); + } + int numChars = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, len, nullptr, 0); + if (numChars == 0) { + return DAWN_INTERNAL_ERROR("Failed to convert string to wide string"); + } + std::wstring result; + result.resize(numChars); + int numConvertedChars = + MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str, len, &result[0], numChars); + if (numConvertedChars != numChars) { + return DAWN_INTERNAL_ERROR("Failed to convert string to wide string"); + } + return std::move(result); + } + + D3D12_COMPARISON_FUNC ToD3D12ComparisonFunc(wgpu::CompareFunction func) { switch (func) { - case dawn::CompareFunction::Always: - return D3D12_COMPARISON_FUNC_ALWAYS; - case dawn::CompareFunction::Equal: - return D3D12_COMPARISON_FUNC_EQUAL; - case dawn::CompareFunction::Greater: - return D3D12_COMPARISON_FUNC_GREATER; - case dawn::CompareFunction::GreaterEqual: - return D3D12_COMPARISON_FUNC_GREATER_EQUAL; - case dawn::CompareFunction::Less: + case wgpu::CompareFunction::Never: + return D3D12_COMPARISON_FUNC_NEVER; + case wgpu::CompareFunction::Less: return D3D12_COMPARISON_FUNC_LESS; - case dawn::CompareFunction::LessEqual: + case wgpu::CompareFunction::LessEqual: return D3D12_COMPARISON_FUNC_LESS_EQUAL; - case dawn::CompareFunction::Never: - return D3D12_COMPARISON_FUNC_NEVER; - case dawn::CompareFunction::NotEqual: + case wgpu::CompareFunction::Greater: + return D3D12_COMPARISON_FUNC_GREATER; + case wgpu::CompareFunction::GreaterEqual: + return D3D12_COMPARISON_FUNC_GREATER_EQUAL; + case wgpu::CompareFunction::Equal: + return D3D12_COMPARISON_FUNC_EQUAL; + case wgpu::CompareFunction::NotEqual: return D3D12_COMPARISON_FUNC_NOT_EQUAL; + case wgpu::CompareFunction::Always: + return D3D12_COMPARISON_FUNC_ALWAYS; default: UNREACHABLE(); } } + D3D12_TEXTURE_COPY_LOCATION ComputeTextureCopyLocationForTexture(const Texture* texture, + uint32_t level, + uint32_t slice) { + D3D12_TEXTURE_COPY_LOCATION copyLocation; + copyLocation.pResource = texture->GetD3D12Resource(); + copyLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + copyLocation.SubresourceIndex = texture->GetSubresourceIndex(level, slice); + + return copyLocation; + } + + D3D12_TEXTURE_COPY_LOCATION ComputeBufferLocationForCopyTextureRegion( + const Texture* texture, + ID3D12Resource* bufferResource, + const Extent3D& bufferSize, + const uint64_t offset, + const uint32_t rowPitch) { + D3D12_TEXTURE_COPY_LOCATION bufferLocation; + bufferLocation.pResource = bufferResource; + bufferLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + bufferLocation.PlacedFootprint.Offset = offset; + bufferLocation.PlacedFootprint.Footprint.Format = texture->GetD3D12Format(); + bufferLocation.PlacedFootprint.Footprint.Width = bufferSize.width; + bufferLocation.PlacedFootprint.Footprint.Height = bufferSize.height; + bufferLocation.PlacedFootprint.Footprint.Depth = bufferSize.depth; + bufferLocation.PlacedFootprint.Footprint.RowPitch = rowPitch; + return bufferLocation; + } + + D3D12_BOX ComputeD3D12BoxFromOffsetAndSize(const Origin3D& offset, const Extent3D& copySize) { + D3D12_BOX sourceRegion; + sourceRegion.left = offset.x; + sourceRegion.top = offset.y; + sourceRegion.front = offset.z; + sourceRegion.right = offset.x + copySize.width; + sourceRegion.bottom = offset.y + copySize.height; + sourceRegion.back = offset.z + copySize.depth; + return sourceRegion; + } + + bool IsTypeless(DXGI_FORMAT format) { + // List generated from + switch (format) { + case DXGI_FORMAT_R32G32B32A32_TYPELESS: + case DXGI_FORMAT_R32G32B32_TYPELESS: + case DXGI_FORMAT_R16G16B16A16_TYPELESS: + case DXGI_FORMAT_R32G32_TYPELESS: + case DXGI_FORMAT_R32G8X24_TYPELESS: + case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: + case DXGI_FORMAT_R10G10B10A2_TYPELESS: + case DXGI_FORMAT_R8G8B8A8_TYPELESS: + case DXGI_FORMAT_R16G16_TYPELESS: + case DXGI_FORMAT_R32_TYPELESS: + case DXGI_FORMAT_R24G8_TYPELESS: + case DXGI_FORMAT_R24_UNORM_X8_TYPELESS: + case DXGI_FORMAT_R8G8_TYPELESS: + case DXGI_FORMAT_R16_TYPELESS: + case DXGI_FORMAT_R8_TYPELESS: + case DXGI_FORMAT_BC1_TYPELESS: + case DXGI_FORMAT_BC2_TYPELESS: + case DXGI_FORMAT_BC3_TYPELESS: + case DXGI_FORMAT_BC4_TYPELESS: + case DXGI_FORMAT_BC5_TYPELESS: + case DXGI_FORMAT_B8G8R8A8_TYPELESS: + case DXGI_FORMAT_B8G8R8X8_TYPELESS: + case DXGI_FORMAT_BC6H_TYPELESS: + case DXGI_FORMAT_BC7_TYPELESS: + return true; + default: + return false; + } + } + }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/UtilsD3D12.h b/third_party/dawn/src/dawn_native/d3d12/UtilsD3D12.h index f47a360fc45..90842d71874 100644 --- a/third_party/dawn/src/dawn_native/d3d12/UtilsD3D12.h +++ b/third_party/dawn/src/dawn_native/d3d12/UtilsD3D12.h @@ -15,12 +15,31 @@ #ifndef DAWNNATIVE_D3D12_UTILSD3D12_H_ #define DAWNNATIVE_D3D12_UTILSD3D12_H_ +#include "dawn_native/d3d12/BufferD3D12.h" +#include "dawn_native/d3d12/TextureCopySplitter.h" +#include "dawn_native/d3d12/TextureD3D12.h" #include "dawn_native/d3d12/d3d12_platform.h" #include "dawn_native/dawn_platform.h" namespace dawn_native { namespace d3d12 { - D3D12_COMPARISON_FUNC ToD3D12ComparisonFunc(dawn::CompareFunction func); + ResultOrError ConvertStringToWstring(const char* str); + + D3D12_COMPARISON_FUNC ToD3D12ComparisonFunc(wgpu::CompareFunction func); + + D3D12_TEXTURE_COPY_LOCATION ComputeTextureCopyLocationForTexture(const Texture* texture, + uint32_t level, + uint32_t slice); + + D3D12_TEXTURE_COPY_LOCATION ComputeBufferLocationForCopyTextureRegion( + const Texture* texture, + ID3D12Resource* bufferResource, + const Extent3D& bufferSize, + const uint64_t offset, + const uint32_t rowPitch); + D3D12_BOX ComputeD3D12BoxFromOffsetAndSize(const Origin3D& offset, const Extent3D& copySize); + + bool IsTypeless(DXGI_FORMAT format); }} // namespace dawn_native::d3d12 diff --git a/third_party/dawn/src/dawn_native/d3d12/d3d12_platform.h b/third_party/dawn/src/dawn_native/d3d12/d3d12_platform.h index 854f9519f11..1c733c8256e 100644 --- a/third_party/dawn/src/dawn_native/d3d12/d3d12_platform.h +++ b/third_party/dawn/src/dawn_native/d3d12/d3d12_platform.h @@ -15,18 +15,23 @@ #ifndef DAWNNATIVE_D3D12_D3D12PLATFORM_H_ #define DAWNNATIVE_D3D12_D3D12PLATFORM_H_ +// Pre-emptively include windows.h but remove its macros so that they aren't set when declaring the +// COM interfaces. Otherwise ID3D12InfoQueue::GetMessage would be either GetMessageA or GetMessageW +// which causes compilation errors. +#include "common/windows_with_undefs.h" + +#include +#include #include +#include #include #include +// DXProgrammableCapture.h takes a dependency on other platform header +// files, so it must be defined after them. +#include #include using Microsoft::WRL::ComPtr; -// Remove windows.h macros after d3d12's include of windows.h -#include "common/Platform.h" -#if defined(DAWN_PLATFORM_WINDOWS) -# include "common/windows_with_undefs.h" -#endif - #endif // DAWNNATIVE_D3D12_D3D12PLATFORM_H_ diff --git a/third_party/dawn/src/dawn_native/dawn_platform.h b/third_party/dawn/src/dawn_native/dawn_platform.h index d456d326db7..19d47e887b2 100644 --- a/third_party/dawn/src/dawn_native/dawn_platform.h +++ b/third_party/dawn/src/dawn_native/dawn_platform.h @@ -15,11 +15,26 @@ #ifndef DAWNNATIVE_DAWNPLATFORM_H_ #define DAWNNATIVE_DAWNPLATFORM_H_ -// Use cawncpp to have the enum and bitfield definitions -#include +// Use webgpu_cpp to have the enum and bitfield definitions +#include -// Use our autogenerated version of the dawn structures that point to dawn_native object types -// (dawn::Buffer is dawn_native::BufferBase*) -#include +// Use our autogenerated version of the wgpu structures that point to dawn_native object types +// (wgpu::Buffer is dawn_native::BufferBase*) +#include + +namespace dawn_native { + // Add an extra buffer usage (readonly storage buffer usage) and an extra texture usage + // (readonly storage texture usage) for render pass resource tracking + static constexpr wgpu::BufferUsage kReadOnlyStorageBuffer = + static_cast(0x80000000); + static constexpr wgpu::TextureUsage kReadonlyStorageTexture = + static_cast(0x80000000); + + // Add an extra texture usage for textures that will be presented, for use in backends + // that needs to transition to present usage. + // TODO(cwallez@chromium.org): It currently aliases wgpu::TextureUsage::Present, assign it + // some bit when wgpu::TextureUsage::Present is removed. + static constexpr wgpu::TextureUsage kPresentTextureUsage = wgpu::TextureUsage::Present; +} // namespace dawn_native #endif // DAWNNATIVE_DAWNPLATFORM_H_ diff --git a/third_party/dawn/src/dawn_native/metal/BackendMTL.mm b/third_party/dawn/src/dawn_native/metal/BackendMTL.mm index 7e1be797432..a5e7c474fbf 100644 --- a/third_party/dawn/src/dawn_native/metal/BackendMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/BackendMTL.mm @@ -14,76 +14,58 @@ #include "dawn_native/metal/BackendMTL.h" +#include "common/GPUInfo.h" +#include "common/Platform.h" #include "dawn_native/Instance.h" #include "dawn_native/MetalBackend.h" #include "dawn_native/metal/DeviceMTL.h" -#include +#if defined(DAWN_PLATFORM_MACOS) +# import +#endif namespace dawn_native { namespace metal { namespace { - // Since CGDisplayIOServicePort was deprecated in macOS 10.9, we need create - // an alternative function for getting I/O service port from current display. - io_service_t GetDisplayIOServicePort() { - // The matching service port (or 0 if none can be found) - io_service_t servicePort = 0; - - // Create matching dictionary for display service - CFMutableDictionaryRef matchingDict = IOServiceMatching("IODisplayConnect"); - if (matchingDict == nullptr) { - return 0; - } - io_iterator_t iter; - // IOServiceGetMatchingServices look up the default master ports that match a - // matching dictionary, and will consume the reference on the matching dictionary, - // so we don't need to release the dictionary, but the iterator handle should - // be released when its iteration is finished. - if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter) != - kIOReturnSuccess) { - return 0; - } + struct PCIIDs { + uint32_t vendorId; + uint32_t deviceId; + }; + + struct Vendor { + const char* trademark; + uint32_t vendorId; + }; + +#if defined(DAWN_PLATFORM_MACOS) + const Vendor kVendors[] = {{"AMD", gpu_info::kVendorID_AMD}, + {"Radeon", gpu_info::kVendorID_AMD}, + {"Intel", gpu_info::kVendorID_Intel}, + {"Geforce", gpu_info::kVendorID_Nvidia}, + {"Quadro", gpu_info::kVendorID_Nvidia}}; - // Vendor number and product number of current main display - const uint32_t displayVendorNumber = CGDisplayVendorNumber(kCGDirectMainDisplay); - const uint32_t displayProductNumber = CGDisplayModelNumber(kCGDirectMainDisplay); - - io_service_t serv; - while ((serv = IOIteratorNext(iter)) != IO_OBJECT_NULL) { - CFDictionaryRef displayInfo = - IODisplayCreateInfoDictionary(serv, kIODisplayOnlyPreferredName); - - CFNumberRef vendorIDRef, productIDRef; - Boolean success; - // The ownership of CF object follows the 'Get Rule', we don't need to - // release these values - success = CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplayVendorID), - (const void**)&vendorIDRef); - success &= CFDictionaryGetValueIfPresent(displayInfo, CFSTR(kDisplayProductID), - (const void**)&productIDRef); - if (success) { - CFIndex vendorID = 0, productID = 0; - CFNumberGetValue(vendorIDRef, kCFNumberSInt32Type, &vendorID); - CFNumberGetValue(productIDRef, kCFNumberSInt32Type, &productID); - - if (vendorID == displayVendorNumber && productID == displayProductNumber) { - // Check if vendor id and product id match with current display's - // If it does, we find the desired service port - servicePort = serv; - CFRelease(displayInfo); - break; - } + // Find vendor ID from MTLDevice name. + MaybeError GetVendorIdFromVendors(id device, PCIIDs* ids) { + uint32_t vendorId = 0; + const char* deviceName = [device.name UTF8String]; + for (const auto& it : kVendors) { + if (strstr(deviceName, it.trademark) != nullptr) { + vendorId = it.vendorId; + break; } + } - CFRelease(displayInfo); - IOObjectRelease(serv); + if (vendorId == 0) { + return DAWN_INTERNAL_ERROR("Failed to find vendor id with the device"); } - IOObjectRelease(iter); - return servicePort; + + // Set vendor id with 0 + *ids = PCIIDs{vendorId, 0}; + return {}; } - // Get integer property from registry entry. + // Extracts an integer property from a registry entry. uint32_t GetEntryProperty(io_registry_entry_t entry, CFStringRef name) { uint32_t value = 0; @@ -93,24 +75,100 @@ uint32_t GetEntryProperty(io_registry_entry_t entry, CFStringRef name) { entry, kIOServicePlane, name, kCFAllocatorDefault, kIORegistryIterateRecursively | kIORegistryIterateParents)); - if (data != nullptr) { - const uint32_t* valuePtr = - reinterpret_cast(CFDataGetBytePtr(data)); - if (valuePtr) { - value = *valuePtr; - } - - CFRelease(data); + if (data == nullptr) { + return value; } + // CFDataGetBytePtr() is guaranteed to return a read-only pointer + value = *reinterpret_cast(CFDataGetBytePtr(data)); + + CFRelease(data); return value; } + // Queries the IO Registry to find the PCI device and vendor IDs of the MTLDevice. + // The registry entry correponding to [device registryID] doesn't contain the exact PCI ids + // because it corresponds to a driver. However its parent entry corresponds to the device + // itself and has uint32_t "device-id" and "registry-id" keys. For example on a dual-GPU + // MacBook Pro 2017 the IORegistry explorer shows the following tree (simplified here): + // + // - PCI0@0 + // | - AppleACPIPCI + // | | - IGPU@2 (type IOPCIDevice) + // | | | - IntelAccelerator (type IOGraphicsAccelerator2) + // | | - PEG0@1 + // | | | - IOPP + // | | | | - GFX0@0 (type IOPCIDevice) + // | | | | | - AMDRadeonX4000_AMDBaffinGraphicsAccelerator (type IOGraphicsAccelerator2) + // + // [device registryID] is the ID for one of the IOGraphicsAccelerator2 and we can see that + // their parent always is an IOPCIDevice that has properties for the device and vendor IDs. + MaybeError API_AVAILABLE(macos(10.13)) + GetDeviceIORegistryPCIInfo(id device, PCIIDs* ids) { + // Get a matching dictionary for the IOGraphicsAccelerator2 + CFMutableDictionaryRef matchingDict = IORegistryEntryIDMatching([device registryID]); + if (matchingDict == nullptr) { + return DAWN_INTERNAL_ERROR("Failed to create the matching dict for the device"); + } + + // IOServiceGetMatchingService will consume the reference on the matching dictionary, + // so we don't need to release the dictionary. + io_registry_entry_t acceleratorEntry = + IOServiceGetMatchingService(kIOMasterPortDefault, matchingDict); + if (acceleratorEntry == IO_OBJECT_NULL) { + return DAWN_INTERNAL_ERROR( + "Failed to get the IO registry entry for the accelerator"); + } + + // Get the parent entry that will be the IOPCIDevice + io_registry_entry_t deviceEntry = IO_OBJECT_NULL; + if (IORegistryEntryGetParentEntry(acceleratorEntry, kIOServicePlane, &deviceEntry) != + kIOReturnSuccess) { + IOObjectRelease(acceleratorEntry); + return DAWN_INTERNAL_ERROR("Failed to get the IO registry entry for the device"); + } + + ASSERT(deviceEntry != IO_OBJECT_NULL); + IOObjectRelease(acceleratorEntry); + + uint32_t vendorId = GetEntryProperty(deviceEntry, CFSTR("vendor-id")); + uint32_t deviceId = GetEntryProperty(deviceEntry, CFSTR("device-id")); + + *ids = PCIIDs{vendorId, deviceId}; + + IOObjectRelease(deviceEntry); + + return {}; + } + + MaybeError GetDevicePCIInfo(id device, PCIIDs* ids) { + // [device registryID] is introduced on macOS 10.13+, otherwise workaround to get vendor + // id by vendor name on old macOS + if (@available(macos 10.13, *)) { + return GetDeviceIORegistryPCIInfo(device, ids); + } else { + return GetVendorIdFromVendors(device, ids); + } + } + bool IsMetalSupported() { // Metal was first introduced in macOS 10.11 NSOperatingSystemVersion macOS10_11 = {10, 11, 0}; return [NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:macOS10_11]; } +#elif defined(DAWN_PLATFORM_IOS) + MaybeError GetDevicePCIInfo(id device, PCIIDs* ids) { + DAWN_UNUSED(device); + *ids = PCIIDs{0, 0}; + return {}; + } + + bool IsMetalSupported() { + return true; + } +#else +# error "Unsupported Apple platform." +#endif } // anonymous namespace // The Metal backend's Adapter. @@ -118,24 +176,28 @@ bool IsMetalSupported() { class Adapter : public AdapterBase { public: Adapter(InstanceBase* instance, id device) - : AdapterBase(instance, BackendType::Metal), mDevice([device retain]) { + : AdapterBase(instance, wgpu::BackendType::Metal), mDevice([device retain]) { mPCIInfo.name = std::string([mDevice.name UTF8String]); - // Gather the PCI device and vendor IDs based on which device is rendering to the - // main display. This is obviously wrong for systems with multiple devices. - // TODO(cwallez@chromium.org): Once Chromium has the macOS 10.13 SDK rolled, we - // should use MTLDevice.registryID to gather the information. - io_registry_entry_t entry = GetDisplayIOServicePort(); - if (entry != IO_OBJECT_NULL) { - mPCIInfo.vendorId = GetEntryProperty(entry, CFSTR("vendor-id")); - mPCIInfo.deviceId = GetEntryProperty(entry, CFSTR("device-id")); - IOObjectRelease(entry); + + PCIIDs ids; + if (!instance->ConsumedError(GetDevicePCIInfo(device, &ids))) { + mPCIInfo.vendorId = ids.vendorId; + mPCIInfo.deviceId = ids.deviceId; } +#if defined(DAWN_PLATFORM_IOS) + mAdapterType = wgpu::AdapterType::IntegratedGPU; +#elif defined(DAWN_PLATFORM_MACOS) if ([device isLowPower]) { - mDeviceType = DeviceType::IntegratedGPU; + mAdapterType = wgpu::AdapterType::IntegratedGPU; } else { - mDeviceType = DeviceType::DiscreteGPU; + mAdapterType = wgpu::AdapterType::DiscreteGPU; } +#else +# error "Unsupported Apple platform." +#endif + + InitializeSupportedExtensions(); } ~Adapter() override { @@ -144,7 +206,22 @@ bool IsMetalSupported() { private: ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override { - return {new Device(this, mDevice, descriptor)}; + return Device::Create(this, mDevice, descriptor); + } + + void InitializeSupportedExtensions() { +#if defined(DAWN_PLATFORM_MACOS) + if ([mDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v1]) { + mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC); + } + + if (@available(macOS 10.15, *)) { + mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery); + mSupportedExtensions.EnableExtension(Extension::TimestampQuery); + } +#endif + + mSupportedExtensions.EnableExtension(Extension::ShaderFloat16); } id mDevice = nil; @@ -152,21 +229,40 @@ bool IsMetalSupported() { // Implementation of the Metal backend's BackendConnection - Backend::Backend(InstanceBase* instance) : BackendConnection(instance, BackendType::Metal) { + Backend::Backend(InstanceBase* instance) + : BackendConnection(instance, wgpu::BackendType::Metal) { if (GetInstance()->IsBackendValidationEnabled()) { setenv("METAL_DEVICE_WRAPPER_TYPE", "1", 1); } } std::vector> Backend::DiscoverDefaultAdapters() { - NSArray>* devices = MTLCopyAllDevices(); - std::vector> adapters; - for (id device in devices) { - adapters.push_back(std::make_unique(GetInstance(), device)); + BOOL supportedVersion = NO; +#if defined(DAWN_PLATFORM_MACOS) + if (@available(macOS 10.11, *)) { + supportedVersion = YES; + NSArray>* devices = MTLCopyAllDevices(); + + for (id device in devices) { + adapters.push_back(std::make_unique(GetInstance(), device)); + } + + [devices release]; } +#endif - [devices release]; +#if defined(DAWN_PLATFORM_IOS) + if (@available(iOS 8.0, *)) { + supportedVersion = YES; + // iOS only has a single device so MTLCopyAllDevices doesn't exist there. + adapters.push_back( + std::make_unique(GetInstance(), MTLCreateSystemDefaultDevice())); + } +#endif + if (!supportedVersion) { + UNREACHABLE(); + } return adapters; } diff --git a/third_party/dawn/src/dawn_native/metal/BindGroupLayoutMTL.h b/third_party/dawn/src/dawn_native/metal/BindGroupLayoutMTL.h new file mode 100644 index 00000000000..c6777db7a7c --- /dev/null +++ b/third_party/dawn/src/dawn_native/metal/BindGroupLayoutMTL.h @@ -0,0 +1,40 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_METAL_BINDGROUPLAYOUTMTL_H_ +#define DAWNNATIVE_METAL_BINDGROUPLAYOUTMTL_H_ + +#include "common/SlabAllocator.h" +#include "dawn_native/BindGroupLayout.h" + +namespace dawn_native { namespace metal { + + class BindGroup; + class Device; + + class BindGroupLayout final : public BindGroupLayoutBase { + public: + BindGroupLayout(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor); + + BindGroup* AllocateBindGroup(Device* device, const BindGroupDescriptor* descriptor); + void DeallocateBindGroup(BindGroup* bindGroup); + + private: + ~BindGroupLayout() override = default; + SlabAllocator mBindGroupAllocator; + }; + +}} // namespace dawn_native::metal + +#endif // DAWNNATIVE_METAL_BINDGROUPLAYOUTMTL_H_ diff --git a/third_party/dawn/src/dawn_native/metal/BindGroupLayoutMTL.mm b/third_party/dawn/src/dawn_native/metal/BindGroupLayoutMTL.mm new file mode 100644 index 00000000000..70beb5d5374 --- /dev/null +++ b/third_party/dawn/src/dawn_native/metal/BindGroupLayoutMTL.mm @@ -0,0 +1,36 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/metal/BindGroupLayoutMTL.h" + +#include "dawn_native/metal/BindGroupMTL.h" + +namespace dawn_native { namespace metal { + + BindGroupLayout::BindGroupLayout(DeviceBase* device, + const BindGroupLayoutDescriptor* descriptor) + : BindGroupLayoutBase(device, descriptor), + mBindGroupAllocator(MakeFrontendBindGroupAllocator(4096)) { + } + + BindGroup* BindGroupLayout::AllocateBindGroup(Device* device, + const BindGroupDescriptor* descriptor) { + return mBindGroupAllocator.Allocate(device, descriptor); + } + + void BindGroupLayout::DeallocateBindGroup(BindGroup* bindGroup) { + mBindGroupAllocator.Deallocate(bindGroup); + } + +}} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/BindGroupMTL.h b/third_party/dawn/src/dawn_native/metal/BindGroupMTL.h new file mode 100644 index 00000000000..60cbf3162d1 --- /dev/null +++ b/third_party/dawn/src/dawn_native/metal/BindGroupMTL.h @@ -0,0 +1,38 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_METAL_BINDGROUPMTL_H_ +#define DAWNNATIVE_METAL_BINDGROUPMTL_H_ + +#include "common/PlacementAllocated.h" +#include "dawn_native/BindGroup.h" + +namespace dawn_native { namespace metal { + + class BindGroupLayout; + class Device; + + class BindGroup final : public BindGroupBase, public PlacementAllocated { + public: + BindGroup(Device* device, const BindGroupDescriptor* descriptor); + + static BindGroup* Create(Device* device, const BindGroupDescriptor* descriptor); + + private: + ~BindGroup() override; + }; + +}} // namespace dawn_native::metal + +#endif // DAWNNATIVE_METAL_BINDGROUPMTL_H_ diff --git a/third_party/dawn/src/dawn_native/metal/BindGroupMTL.mm b/third_party/dawn/src/dawn_native/metal/BindGroupMTL.mm new file mode 100644 index 00000000000..d8bcd515d97 --- /dev/null +++ b/third_party/dawn/src/dawn_native/metal/BindGroupMTL.mm @@ -0,0 +1,34 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/metal/BindGroupMTL.h" + +#include "dawn_native/metal/BindGroupLayoutMTL.h" +#include "dawn_native/metal/DeviceMTL.h" +namespace dawn_native { namespace metal { + + BindGroup::BindGroup(Device* device, const BindGroupDescriptor* descriptor) + : BindGroupBase(this, device, descriptor) { + } + + BindGroup::~BindGroup() { + ToBackend(GetLayout())->DeallocateBindGroup(this); + } + + // static + BindGroup* BindGroup::Create(Device* device, const BindGroupDescriptor* descriptor) { + return ToBackend(descriptor->layout)->AllocateBindGroup(device, descriptor); + } + +}} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/BufferMTL.h b/third_party/dawn/src/dawn_native/metal/BufferMTL.h index 4708b44b2dd..ad6ed5faac9 100644 --- a/third_party/dawn/src/dawn_native/metal/BufferMTL.h +++ b/third_party/dawn/src/dawn_native/metal/BufferMTL.h @@ -22,47 +22,41 @@ namespace dawn_native { namespace metal { + class CommandRecordingContext; class Device; class Buffer : public BufferBase { public: - Buffer(Device* device, const BufferDescriptor* descriptor); - ~Buffer(); + static ResultOrError> Create(Device* device, + const BufferDescriptor* descriptor); + id GetMTLBuffer() const; - id GetMTLBuffer(); - - void OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite); + void EnsureDataInitialized(CommandRecordingContext* commandContext); + void EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext, + uint64_t offset, + uint64_t size); + void EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext, + const CopyTextureToBufferCmd* copy); private: + using BufferBase::BufferBase; + MaybeError Initialize(); + ~Buffer() override; // Dawn API - void MapReadAsyncImpl(uint32_t serial) override; - void MapWriteAsyncImpl(uint32_t serial) override; + MaybeError MapReadAsyncImpl() override; + MaybeError MapWriteAsyncImpl() override; + MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override; void UnmapImpl() override; void DestroyImpl() override; + void* GetMappedPointerImpl() override; - bool IsMapWritable() const override; - MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; + bool IsMappableAtCreation() const override; + MaybeError MapAtCreationImpl() override; - id mMtlBuffer = nil; - }; - - class MapRequestTracker { - public: - MapRequestTracker(Device* device); - ~MapRequestTracker(); + void InitializeToZero(CommandRecordingContext* commandContext); + void ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue); - void Track(Buffer* buffer, uint32_t mapSerial, bool isWrite); - void Tick(Serial finishedSerial); - - private: - Device* mDevice; - - struct Request { - Ref buffer; - uint32_t mapSerial; - bool isWrite; - }; - SerialQueue mInflightRequests; + id mMtlBuffer = nil; }; }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/BufferMTL.mm b/third_party/dawn/src/dawn_native/metal/BufferMTL.mm index b73ff5f11cf..2c82a7cf721 100644 --- a/third_party/dawn/src/dawn_native/metal/BufferMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/BufferMTL.mm @@ -14,57 +14,130 @@ #include "dawn_native/metal/BufferMTL.h" +#include "common/Math.h" +#include "dawn_native/CommandBuffer.h" +#include "dawn_native/metal/CommandRecordingContext.h" #include "dawn_native/metal/DeviceMTL.h" +#include + namespace dawn_native { namespace metal { + // The size of uniform buffer and storage buffer need to be aligned to 16 bytes which is the + // largest alignment of supported data types + static constexpr uint32_t kMinUniformOrStorageBufferAlignment = 16u; + + // The maximum buffer size if querying the maximum buffer size or recommended working set size + // is not available. This is a somewhat arbitrary limit of 1 GiB. + static constexpr uint32_t kMaxBufferSizeFallback = 1024u * 1024u * 1024u; + + // static + ResultOrError> Buffer::Create(Device* device, const BufferDescriptor* descriptor) { + Ref buffer = AcquireRef(new Buffer(device, descriptor)); + DAWN_TRY(buffer->Initialize()); + return std::move(buffer); + } - Buffer::Buffer(Device* device, const BufferDescriptor* descriptor) - : BufferBase(device, descriptor) { + MaybeError Buffer::Initialize() { MTLResourceOptions storageMode; - if (GetUsage() & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) { + if (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) { storageMode = MTLResourceStorageModeShared; } else { storageMode = MTLResourceStorageModePrivate; } - mMtlBuffer = [device->GetMTLDevice() newBufferWithLength:GetSize() options:storageMode]; + // TODO(cwallez@chromium.org): Have a global "zero" buffer that can do everything instead + // of creating a new 4-byte buffer? + if (GetSize() > std::numeric_limits::max()) { + return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large"); + } + NSUInteger currentSize = static_cast(std::max(GetSize(), uint64_t(4u))); + + // Metal validation layer requires the size of uniform buffer and storage buffer to be no + // less than the size of the buffer block defined in shader, and the overall size of the + // buffer must be aligned to the largest alignment of its members. + if (GetUsage() & (wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage)) { + if (currentSize > + std::numeric_limits::max() - kMinUniformOrStorageBufferAlignment) { + // Alignment would overlow. + return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large"); + } + currentSize = Align(currentSize, kMinUniformOrStorageBufferAlignment); + } + + if (@available(iOS 12, macOS 10.14, *)) { + NSUInteger maxBufferSize = [ToBackend(GetDevice())->GetMTLDevice() maxBufferLength]; + if (currentSize > maxBufferSize) { + return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large"); + } +#if defined(DAWN_PLATFORM_MACOS) + } else if (@available(macOS 10.12, *)) { + // |maxBufferLength| isn't always available on older systems. If available, use + // |recommendedMaxWorkingSetSize| instead. We can probably allocate more than this, + // but don't have a way to discover a better limit. MoltenVK also uses this heuristic. + uint64_t maxWorkingSetSize = + [ToBackend(GetDevice())->GetMTLDevice() recommendedMaxWorkingSetSize]; + if (currentSize > maxWorkingSetSize) { + return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large"); + } +#endif + } else if (currentSize > kMaxBufferSizeFallback) { + return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large"); + } + + mMtlBuffer = [ToBackend(GetDevice())->GetMTLDevice() newBufferWithLength:currentSize + options:storageMode]; + if (mMtlBuffer == nil) { + return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation failed"); + } + + if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { + CommandRecordingContext* commandContext = + ToBackend(GetDevice())->GetPendingCommandContext(); + ClearBuffer(commandContext, uint8_t(1u)); + } + + return {}; } Buffer::~Buffer() { DestroyInternal(); } - id Buffer::GetMTLBuffer() { + id Buffer::GetMTLBuffer() const { return mMtlBuffer; } - void Buffer::OnMapCommandSerialFinished(uint32_t mapSerial, bool isWrite) { - char* data = reinterpret_cast([mMtlBuffer contents]); - if (isWrite) { - CallMapWriteCallback(mapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data, GetSize()); - } else { - CallMapReadCallback(mapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data, GetSize()); - } + bool Buffer::IsMappableAtCreation() const { + // TODO(enga): Handle CPU-visible memory on UMA + return (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0; } - bool Buffer::IsMapWritable() const { - // TODO(enga): Handle CPU-visible memory on UMA - return (GetUsage() & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) != 0; + MaybeError Buffer::MapAtCreationImpl() { + CommandRecordingContext* commandContext = + ToBackend(GetDevice())->GetPendingCommandContext(); + EnsureDataInitialized(commandContext); + + return {}; } - MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { - *mappedPointer = reinterpret_cast([mMtlBuffer contents]); + MaybeError Buffer::MapReadAsyncImpl() { return {}; } - void Buffer::MapReadAsyncImpl(uint32_t serial) { - MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapTracker(); - tracker->Track(this, serial, false); + MaybeError Buffer::MapWriteAsyncImpl() { + return {}; } - void Buffer::MapWriteAsyncImpl(uint32_t serial) { - MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapTracker(); - tracker->Track(this, serial, true); + MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) { + CommandRecordingContext* commandContext = + ToBackend(GetDevice())->GetPendingCommandContext(); + EnsureDataInitialized(commandContext); + + return {}; + } + + void* Buffer::GetMappedPointerImpl() { + return [mMtlBuffer contents]; } void Buffer::UnmapImpl() { @@ -76,29 +149,65 @@ mMtlBuffer = nil; } - MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) { - } + void Buffer::EnsureDataInitialized(CommandRecordingContext* commandContext) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; + } - MapRequestTracker::~MapRequestTracker() { - ASSERT(mInflightRequests.Empty()); + InitializeToZero(commandContext); } - void MapRequestTracker::Track(Buffer* buffer, - uint32_t mapSerial, - bool isWrite) { - Request request; - request.buffer = buffer; - request.mapSerial = mapSerial; - request.isWrite = isWrite; + void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext, + uint64_t offset, + uint64_t size) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; + } - mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial()); + if (IsFullBufferRange(offset, size)) { + SetIsDataInitialized(); + } else { + InitializeToZero(commandContext); + } } - void MapRequestTracker::Tick(Serial finishedSerial) { - for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) { - request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.isWrite); + void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* commandContext, + const CopyTextureToBufferCmd* copy) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; } - mInflightRequests.ClearUpTo(finishedSerial); + + if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) { + SetIsDataInitialized(); + } else { + InitializeToZero(commandContext); + } + } + + void Buffer::InitializeToZero(CommandRecordingContext* commandContext) { + ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)); + ASSERT(!IsDataInitialized()); + + ClearBuffer(commandContext, uint8_t(0u)); + + SetIsDataInitialized(); + GetDevice()->IncrementLazyClearCountForTesting(); + } + + void Buffer::ClearBuffer(CommandRecordingContext* commandContext, uint8_t clearValue) { + ASSERT(commandContext != nullptr); + [commandContext->EnsureBlit() fillBuffer:mMtlBuffer + range:NSMakeRange(0, GetSize()) + value:clearValue]; } }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/CommandBufferMTL.h b/third_party/dawn/src/dawn_native/metal/CommandBufferMTL.h index a127c515acd..cbaf0371863 100644 --- a/third_party/dawn/src/dawn_native/metal/CommandBufferMTL.h +++ b/third_party/dawn/src/dawn_native/metal/CommandBufferMTL.h @@ -17,37 +17,37 @@ #include "dawn_native/CommandAllocator.h" #include "dawn_native/CommandBuffer.h" +#include "dawn_native/Error.h" #import namespace dawn_native { - class CommandEncoderBase; + class CommandEncoder; } namespace dawn_native { namespace metal { + class CommandRecordingContext; class Device; - struct GlobalEncoders; - class CommandBuffer : public CommandBufferBase { + class CommandBuffer final : public CommandBufferBase { public: - CommandBuffer(Device* device, CommandEncoderBase* encoder); - ~CommandBuffer(); + CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor); - void FillCommands(id commandBuffer); + MaybeError FillCommands(CommandRecordingContext* commandContext); private: - void EncodeComputePass(id commandBuffer); - void EncodeRenderPass(id commandBuffer, - MTLRenderPassDescriptor* mtlRenderPass, - GlobalEncoders* globalEncoders, - uint32_t width, - uint32_t height); - - void EncodeRenderPassInternal(id commandBuffer, - MTLRenderPassDescriptor* mtlRenderPass, - uint32_t width, - uint32_t height); + ~CommandBuffer() override; + MaybeError EncodeComputePass(CommandRecordingContext* commandContext); + MaybeError EncodeRenderPass(CommandRecordingContext* commandContext, + MTLRenderPassDescriptor* mtlRenderPass, + uint32_t width, + uint32_t height); + + MaybeError EncodeRenderPassInternal(CommandRecordingContext* commandContext, + MTLRenderPassDescriptor* mtlRenderPass, + uint32_t width, + uint32_t height); CommandIterator mCommands; }; diff --git a/third_party/dawn/src/dawn_native/metal/CommandBufferMTL.mm b/third_party/dawn/src/dawn_native/metal/CommandBufferMTL.mm index 65cf7264fce..74b64612eca 100644 --- a/third_party/dawn/src/dawn_native/metal/CommandBufferMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/CommandBufferMTL.mm @@ -14,9 +14,11 @@ #include "dawn_native/metal/CommandBufferMTL.h" -#include "dawn_native/BindGroup.h" +#include "dawn_native/BindGroupTracker.h" #include "dawn_native/CommandEncoder.h" #include "dawn_native/Commands.h" +#include "dawn_native/RenderBundle.h" +#include "dawn_native/metal/BindGroupMTL.h" #include "dawn_native/metal/BufferMTL.h" #include "dawn_native/metal/ComputePipelineMTL.h" #include "dawn_native/metal/DeviceMTL.h" @@ -24,42 +26,44 @@ #include "dawn_native/metal/RenderPipelineMTL.h" #include "dawn_native/metal/SamplerMTL.h" #include "dawn_native/metal/TextureMTL.h" +#include "dawn_native/metal/UtilsMetal.h" namespace dawn_native { namespace metal { - struct GlobalEncoders { - id blit = nil; - - void Finish() { - if (blit != nil) { - [blit endEncoding]; - blit = nil; // This will be autoreleased. - } - } - - void EnsureBlit(id commandBuffer) { - if (blit == nil) { - blit = [commandBuffer blitCommandEncoder]; - } - } - }; - namespace { + // Allows this file to use MTLStoreActionStoreAndMultismapleResolve because the logic is + // first to compute what the "best" Metal render pass descriptor is, then fix it up if we + // are not on macOS 10.12 (i.e. the EmulateStoreAndMSAAResolve toggle is on). +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + constexpr MTLStoreAction kMTLStoreActionStoreAndMultisampleResolve = + MTLStoreActionStoreAndMultisampleResolve; +#pragma clang diagnostic pop + // Creates an autoreleased MTLRenderPassDescriptor matching desc MTLRenderPassDescriptor* CreateMTLRenderPassDescriptor(BeginRenderPassCmd* renderPass) { MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor]; - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { + for (uint32_t i : + IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { auto& attachmentInfo = renderPass->colorAttachments[i]; - if (attachmentInfo.loadOp == dawn::LoadOp::Clear) { - descriptor.colorAttachments[i].loadAction = MTLLoadActionClear; - descriptor.colorAttachments[i].clearColor = - MTLClearColorMake(attachmentInfo.clearColor.r, attachmentInfo.clearColor.g, - attachmentInfo.clearColor.b, attachmentInfo.clearColor.a); - } else { - descriptor.colorAttachments[i].loadAction = MTLLoadActionLoad; + switch (attachmentInfo.loadOp) { + case wgpu::LoadOp::Clear: + descriptor.colorAttachments[i].loadAction = MTLLoadActionClear; + descriptor.colorAttachments[i].clearColor = MTLClearColorMake( + attachmentInfo.clearColor.r, attachmentInfo.clearColor.g, + attachmentInfo.clearColor.b, attachmentInfo.clearColor.a); + break; + + case wgpu::LoadOp::Load: + descriptor.colorAttachments[i].loadAction = MTLLoadActionLoad; + break; + + default: + UNREACHABLE(); + break; } descriptor.colorAttachments[i].texture = @@ -67,51 +71,109 @@ void EnsureBlit(id commandBuffer) { descriptor.colorAttachments[i].level = attachmentInfo.view->GetBaseMipLevel(); descriptor.colorAttachments[i].slice = attachmentInfo.view->GetBaseArrayLayer(); - if (attachmentInfo.storeOp == dawn::StoreOp::Store) { - if (attachmentInfo.resolveTarget.Get() != nullptr) { - descriptor.colorAttachments[i].resolveTexture = - ToBackend(attachmentInfo.resolveTarget->GetTexture())->GetMTLTexture(); - descriptor.colorAttachments[i].resolveLevel = - attachmentInfo.resolveTarget->GetBaseMipLevel(); - descriptor.colorAttachments[i].resolveSlice = - attachmentInfo.resolveTarget->GetBaseArrayLayer(); - descriptor.colorAttachments[i].storeAction = - MTLStoreActionStoreAndMultisampleResolve; - } else { - descriptor.colorAttachments[i].storeAction = MTLStoreActionStore; - } + bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr; + + switch (attachmentInfo.storeOp) { + case wgpu::StoreOp::Store: + if (hasResolveTarget) { + descriptor.colorAttachments[i].resolveTexture = + ToBackend(attachmentInfo.resolveTarget->GetTexture()) + ->GetMTLTexture(); + descriptor.colorAttachments[i].resolveLevel = + attachmentInfo.resolveTarget->GetBaseMipLevel(); + descriptor.colorAttachments[i].resolveSlice = + attachmentInfo.resolveTarget->GetBaseArrayLayer(); + descriptor.colorAttachments[i].storeAction = + kMTLStoreActionStoreAndMultisampleResolve; + } else { + descriptor.colorAttachments[i].storeAction = MTLStoreActionStore; + } + break; + + case wgpu::StoreOp::Clear: + descriptor.colorAttachments[i].storeAction = MTLStoreActionDontCare; + break; + + default: + UNREACHABLE(); + break; } } - if (renderPass->hasDepthStencilAttachment) { + if (renderPass->attachmentState->HasDepthStencilAttachment()) { auto& attachmentInfo = renderPass->depthStencilAttachment; - // TODO(jiawei.shao@intel.com): support rendering into a layer of a texture. id texture = ToBackend(attachmentInfo.view->GetTexture())->GetMTLTexture(); - dawn::TextureFormat format = attachmentInfo.view->GetTexture()->GetFormat(); + const Format& format = attachmentInfo.view->GetTexture()->GetFormat(); - if (TextureFormatHasDepth(format)) { + if (format.HasDepth()) { descriptor.depthAttachment.texture = texture; - descriptor.depthAttachment.storeAction = MTLStoreActionStore; + descriptor.depthAttachment.level = attachmentInfo.view->GetBaseMipLevel(); + descriptor.depthAttachment.slice = attachmentInfo.view->GetBaseArrayLayer(); + + switch (attachmentInfo.depthStoreOp) { + case wgpu::StoreOp::Store: + descriptor.depthAttachment.storeAction = MTLStoreActionStore; + break; + + case wgpu::StoreOp::Clear: + descriptor.depthAttachment.storeAction = MTLStoreActionDontCare; + break; - if (attachmentInfo.depthLoadOp == dawn::LoadOp::Clear) { - descriptor.depthAttachment.loadAction = MTLLoadActionClear; - descriptor.depthAttachment.clearDepth = attachmentInfo.clearDepth; - } else { - descriptor.depthAttachment.loadAction = MTLLoadActionLoad; + default: + UNREACHABLE(); + break; + } + + switch (attachmentInfo.depthLoadOp) { + case wgpu::LoadOp::Clear: + descriptor.depthAttachment.loadAction = MTLLoadActionClear; + descriptor.depthAttachment.clearDepth = attachmentInfo.clearDepth; + break; + + case wgpu::LoadOp::Load: + descriptor.depthAttachment.loadAction = MTLLoadActionLoad; + break; + + default: + UNREACHABLE(); + break; } } - if (TextureFormatHasStencil(format)) { + if (format.HasStencil()) { descriptor.stencilAttachment.texture = texture; - descriptor.stencilAttachment.storeAction = MTLStoreActionStore; + descriptor.stencilAttachment.level = attachmentInfo.view->GetBaseMipLevel(); + descriptor.stencilAttachment.slice = attachmentInfo.view->GetBaseArrayLayer(); - if (attachmentInfo.stencilLoadOp == dawn::LoadOp::Clear) { - descriptor.stencilAttachment.loadAction = MTLLoadActionClear; - descriptor.stencilAttachment.clearStencil = attachmentInfo.clearStencil; - } else { - descriptor.stencilAttachment.loadAction = MTLLoadActionLoad; + switch (attachmentInfo.stencilStoreOp) { + case wgpu::StoreOp::Store: + descriptor.stencilAttachment.storeAction = MTLStoreActionStore; + break; + + case wgpu::StoreOp::Clear: + descriptor.stencilAttachment.storeAction = MTLStoreActionDontCare; + break; + + default: + UNREACHABLE(); + break; + } + + switch (attachmentInfo.stencilLoadOp) { + case wgpu::LoadOp::Clear: + descriptor.stencilAttachment.loadAction = MTLLoadActionClear; + descriptor.stencilAttachment.clearStencil = attachmentInfo.clearStencil; + break; + + case wgpu::LoadOp::Load: + descriptor.stencilAttachment.loadAction = MTLLoadActionLoad; + break; + + default: + UNREACHABLE(); + break; } } } @@ -121,7 +183,7 @@ void EnsureBlit(id commandBuffer) { // Helper function for Toggle EmulateStoreAndMSAAResolve void ResolveInAnotherRenderPass( - id commandBuffer, + CommandRecordingContext* commandContext, const MTLRenderPassDescriptor* mtlRenderPass, const std::array, kMaxColorAttachments>& resolveTextures) { MTLRenderPassDescriptor* mtlRenderPassForResolve = @@ -143,9 +205,8 @@ void ResolveInAnotherRenderPass( mtlRenderPass.colorAttachments[i].resolveSlice; } - id encoder = - [commandBuffer renderCommandEncoderWithDescriptor:mtlRenderPassForResolve]; - [encoder endEncoding]; + commandContext->BeginRender(mtlRenderPassForResolve); + commandContext->EndRender(); } // Helper functions for Toggle AlwaysResolveIntoZeroLevelAndLayer @@ -170,192 +231,367 @@ void ResolveInAnotherRenderPass( return resolveTexture; } - void CopyIntoTrueResolveTarget(id commandBuffer, + void CopyIntoTrueResolveTarget(CommandRecordingContext* commandContext, id mtlTrueResolveTexture, uint32_t trueResolveLevel, uint32_t trueResolveSlice, id temporaryResolveTexture, uint32_t width, - uint32_t height, - GlobalEncoders* encoders) { - encoders->EnsureBlit(commandBuffer); - [encoders->blit copyFromTexture:temporaryResolveTexture - sourceSlice:0 - sourceLevel:0 - sourceOrigin:MTLOriginMake(0, 0, 0) - sourceSize:MTLSizeMake(width, height, 1) - toTexture:mtlTrueResolveTexture - destinationSlice:trueResolveSlice - destinationLevel:trueResolveLevel - destinationOrigin:MTLOriginMake(0, 0, 0)]; + uint32_t height) { + [commandContext->EnsureBlit() copyFromTexture:temporaryResolveTexture + sourceSlice:0 + sourceLevel:0 + sourceOrigin:MTLOriginMake(0, 0, 0) + sourceSize:MTLSizeMake(width, height, 1) + toTexture:mtlTrueResolveTexture + destinationSlice:trueResolveSlice + destinationLevel:trueResolveLevel + destinationOrigin:MTLOriginMake(0, 0, 0)]; } - // Handles a call to SetBindGroup, directing the commands to the correct encoder. - // There is a single function that takes both encoders to factor code. Other approaches like - // templates wouldn't work because the name of methods are different between the two encoder - // types. - void ApplyBindGroup(uint32_t index, - BindGroup* group, - uint32_t dynamicOffsetCount, - uint64_t* dynamicOffsets, - PipelineLayout* pipelineLayout, - id render, - id compute) { - const auto& layout = group->GetLayout()->GetBindingInfo(); - uint32_t currentDynamicBufferIndex = 0; - - // TODO(kainino@chromium.org): Maintain buffers and offsets arrays in BindGroup - // so that we only have to do one setVertexBuffers and one setFragmentBuffers - // call here. - for (uint32_t bindingIndex : IterateBitSet(layout.mask)) { - auto stage = layout.visibilities[bindingIndex]; - bool hasVertStage = stage & dawn::ShaderStageBit::Vertex && render != nil; - bool hasFragStage = stage & dawn::ShaderStageBit::Fragment && render != nil; - bool hasComputeStage = stage & dawn::ShaderStageBit::Compute && compute != nil; - - uint32_t vertIndex = 0; - uint32_t fragIndex = 0; - uint32_t computeIndex = 0; - - if (hasVertStage) { - vertIndex = pipelineLayout->GetBindingIndexInfo( - dawn::ShaderStage::Vertex)[index][bindingIndex]; - } - if (hasFragStage) { - fragIndex = pipelineLayout->GetBindingIndexInfo( - dawn::ShaderStage::Fragment)[index][bindingIndex]; - } - if (hasComputeStage) { - computeIndex = pipelineLayout->GetBindingIndexInfo( - dawn::ShaderStage::Compute)[index][bindingIndex]; - } - - switch (layout.types[bindingIndex]) { - case dawn::BindingType::UniformBuffer: - case dawn::BindingType::StorageBuffer: { - BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); - const id buffer = ToBackend(binding.buffer)->GetMTLBuffer(); - const NSUInteger offset = binding.offset; - - if (hasVertStage) { - [render setVertexBuffers:&buffer - offsets:&offset - withRange:NSMakeRange(vertIndex, 1)]; - } - if (hasFragStage) { - [render setFragmentBuffers:&buffer - offsets:&offset - withRange:NSMakeRange(fragIndex, 1)]; - } - if (hasComputeStage) { - [compute setBuffers:&buffer - offsets:&offset - withRange:NSMakeRange(computeIndex, 1)]; - } + // Metal uses a physical addressing mode which means buffers in the shading language are + // just pointers to the virtual address of their start. This means there is no way to know + // the length of a buffer to compute the length() of unsized arrays at the end of storage + // buffers. SPIRV-Cross implements the length() of unsized arrays by requiring an extra + // buffer that contains the length of other buffers. This structure that keeps track of the + // length of storage buffers and can apply them to the reserved "buffer length buffer" when + // needed for a draw or a dispatch. + struct StorageBufferLengthTracker { + wgpu::ShaderStage dirtyStages = wgpu::ShaderStage::None; + + // The lengths of buffers are stored as 32bit integers because that is the width the + // MSL code generated by SPIRV-Cross expects. + PerStage> data; + + void Apply(id render, RenderPipeline* pipeline) { + wgpu::ShaderStage stagesToApply = + dirtyStages & pipeline->GetStagesRequiringStorageBufferLength(); + + if (stagesToApply == wgpu::ShaderStage::None) { + return; + } - } break; + if (stagesToApply & wgpu::ShaderStage::Vertex) { + uint32_t bufferCount = ToBackend(pipeline->GetLayout()) + ->GetBufferBindingCount(SingleShaderStage::Vertex); + [render setVertexBytes:data[SingleShaderStage::Vertex].data() + length:sizeof(uint32_t) * bufferCount + atIndex:kBufferLengthBufferSlot]; + } - case dawn::BindingType::Sampler: { - auto sampler = ToBackend(group->GetBindingAsSampler(bindingIndex)); - if (hasVertStage) { - [render setVertexSamplerState:sampler->GetMTLSamplerState() - atIndex:vertIndex]; - } - if (hasFragStage) { - [render setFragmentSamplerState:sampler->GetMTLSamplerState() - atIndex:fragIndex]; - } - if (hasComputeStage) { - [compute setSamplerState:sampler->GetMTLSamplerState() - atIndex:computeIndex]; - } - } break; + if (stagesToApply & wgpu::ShaderStage::Fragment) { + uint32_t bufferCount = ToBackend(pipeline->GetLayout()) + ->GetBufferBindingCount(SingleShaderStage::Fragment); + [render setFragmentBytes:data[SingleShaderStage::Fragment].data() + length:sizeof(uint32_t) * bufferCount + atIndex:kBufferLengthBufferSlot]; + } - case dawn::BindingType::SampledTexture: { - auto textureView = ToBackend(group->GetBindingAsTextureView(bindingIndex)); - if (hasVertStage) { - [render setVertexTexture:textureView->GetMTLTexture() - atIndex:vertIndex]; - } - if (hasFragStage) { - [render setFragmentTexture:textureView->GetMTLTexture() - atIndex:fragIndex]; - } - if (hasComputeStage) { - [compute setTexture:textureView->GetMTLTexture() atIndex:computeIndex]; - } - } break; - - // TODO(shaobo.yan@intel.com): Record bound buffer status to use setBufferOffset - // to achieve better performance. - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: { - ASSERT(currentDynamicBufferIndex < dynamicOffsetCount); - BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); - const id buffer = ToBackend(binding.buffer)->GetMTLBuffer(); - NSUInteger offset = - binding.offset + dynamicOffsets[currentDynamicBufferIndex]; - currentDynamicBufferIndex += 1; - - if (hasVertStage) { - [render setVertexBuffers:&buffer - offsets:&offset - withRange:NSMakeRange(vertIndex, 1)]; + // Only mark clean stages that were actually applied. + dirtyStages ^= stagesToApply; + } + + void Apply(id compute, ComputePipeline* pipeline) { + if (!(dirtyStages & wgpu::ShaderStage::Compute)) { + return; + } + + if (!pipeline->RequiresStorageBufferLength()) { + return; + } + + uint32_t bufferCount = ToBackend(pipeline->GetLayout()) + ->GetBufferBindingCount(SingleShaderStage::Compute); + [compute setBytes:data[SingleShaderStage::Compute].data() + length:sizeof(uint32_t) * bufferCount + atIndex:kBufferLengthBufferSlot]; + + dirtyStages ^= wgpu::ShaderStage::Compute; + } + }; + + // Keeps track of the dirty bind groups so they can be lazily applied when we know the + // pipeline state. + // Bind groups may be inherited because bind groups are packed in the buffer / + // texture tables in contiguous order. + class BindGroupTracker : public BindGroupTrackerBase { + public: + explicit BindGroupTracker(StorageBufferLengthTracker* lengthTracker) + : BindGroupTrackerBase(), mLengthTracker(lengthTracker) { + } + + template + void Apply(Encoder encoder) { + for (BindGroupIndex index : + IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) { + ApplyBindGroup(encoder, index, ToBackend(mBindGroups[index]), + mDynamicOffsetCounts[index], mDynamicOffsets[index].data(), + ToBackend(mPipelineLayout)); + } + DidApply(); + } + + private: + // Handles a call to SetBindGroup, directing the commands to the correct encoder. + // There is a single function that takes both encoders to factor code. Other approaches + // like templates wouldn't work because the name of methods are different between the + // two encoder types. + void ApplyBindGroupImpl(id render, + id compute, + BindGroupIndex index, + BindGroup* group, + uint32_t dynamicOffsetCount, + uint64_t* dynamicOffsets, + PipelineLayout* pipelineLayout) { + uint32_t currentDynamicBufferIndex = 0; + + // TODO(kainino@chromium.org): Maintain buffers and offsets arrays in BindGroup + // so that we only have to do one setVertexBuffers and one setFragmentBuffers + // call here. + for (BindingIndex bindingIndex{0}; + bindingIndex < group->GetLayout()->GetBindingCount(); ++bindingIndex) { + const BindingInfo& bindingInfo = + group->GetLayout()->GetBindingInfo(bindingIndex); + + bool hasVertStage = + bindingInfo.visibility & wgpu::ShaderStage::Vertex && render != nil; + bool hasFragStage = + bindingInfo.visibility & wgpu::ShaderStage::Fragment && render != nil; + bool hasComputeStage = + bindingInfo.visibility & wgpu::ShaderStage::Compute && compute != nil; + + uint32_t vertIndex = 0; + uint32_t fragIndex = 0; + uint32_t computeIndex = 0; + + if (hasVertStage) { + vertIndex = pipelineLayout->GetBindingIndexInfo( + SingleShaderStage::Vertex)[index][bindingIndex]; + } + if (hasFragStage) { + fragIndex = pipelineLayout->GetBindingIndexInfo( + SingleShaderStage::Fragment)[index][bindingIndex]; + } + if (hasComputeStage) { + computeIndex = pipelineLayout->GetBindingIndexInfo( + SingleShaderStage::Compute)[index][bindingIndex]; + } + + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: { + const BufferBinding& binding = + group->GetBindingAsBufferBinding(bindingIndex); + const id buffer = ToBackend(binding.buffer)->GetMTLBuffer(); + NSUInteger offset = binding.offset; + + // TODO(shaobo.yan@intel.com): Record bound buffer status to use + // setBufferOffset to achieve better performance. + if (bindingInfo.hasDynamicOffset) { + offset += dynamicOffsets[currentDynamicBufferIndex]; + currentDynamicBufferIndex++; + } + + if (hasVertStage) { + mLengthTracker->data[SingleShaderStage::Vertex][vertIndex] = + binding.size; + mLengthTracker->dirtyStages |= wgpu::ShaderStage::Vertex; + [render setVertexBuffers:&buffer + offsets:&offset + withRange:NSMakeRange(vertIndex, 1)]; + } + if (hasFragStage) { + mLengthTracker->data[SingleShaderStage::Fragment][fragIndex] = + binding.size; + mLengthTracker->dirtyStages |= wgpu::ShaderStage::Fragment; + [render setFragmentBuffers:&buffer + offsets:&offset + withRange:NSMakeRange(fragIndex, 1)]; + } + if (hasComputeStage) { + mLengthTracker->data[SingleShaderStage::Compute][computeIndex] = + binding.size; + mLengthTracker->dirtyStages |= wgpu::ShaderStage::Compute; + [compute setBuffers:&buffer + offsets:&offset + withRange:NSMakeRange(computeIndex, 1)]; + } + + break; } - if (hasFragStage) { - [render setFragmentBuffers:&buffer - offsets:&offset - withRange:NSMakeRange(fragIndex, 1)]; + + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: { + auto sampler = ToBackend(group->GetBindingAsSampler(bindingIndex)); + if (hasVertStage) { + [render setVertexSamplerState:sampler->GetMTLSamplerState() + atIndex:vertIndex]; + } + if (hasFragStage) { + [render setFragmentSamplerState:sampler->GetMTLSamplerState() + atIndex:fragIndex]; + } + if (hasComputeStage) { + [compute setSamplerState:sampler->GetMTLSamplerState() + atIndex:computeIndex]; + } + break; } - if (hasComputeStage) { - [compute setBuffers:&buffer - offsets:&offset - withRange:NSMakeRange(computeIndex, 1)]; + + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + auto textureView = + ToBackend(group->GetBindingAsTextureView(bindingIndex)); + if (hasVertStage) { + [render setVertexTexture:textureView->GetMTLTexture() + atIndex:vertIndex]; + } + if (hasFragStage) { + [render setFragmentTexture:textureView->GetMTLTexture() + atIndex:fragIndex]; + } + if (hasComputeStage) { + [compute setTexture:textureView->GetMTLTexture() + atIndex:computeIndex]; + } + break; } - } break; + + case wgpu::BindingType::StorageTexture: + UNREACHABLE(); + break; + } } } - } + + template + void ApplyBindGroup(id encoder, Args&&... args) { + ApplyBindGroupImpl(encoder, nil, std::forward(args)...); + } + + template + void ApplyBindGroup(id encoder, Args&&... args) { + ApplyBindGroupImpl(nil, encoder, std::forward(args)...); + } + + StorageBufferLengthTracker* mLengthTracker; + }; + + // Keeps track of the dirty vertex buffer values so they can be lazily applied when we know + // all the relevant state. + class VertexBufferTracker { + public: + void OnSetVertexBuffer(uint32_t slot, Buffer* buffer, uint64_t offset) { + mVertexBuffers[slot] = buffer->GetMTLBuffer(); + mVertexBufferOffsets[slot] = offset; + + // Use 64 bit masks and make sure there are no shift UB + static_assert(kMaxVertexBuffers <= 8 * sizeof(unsigned long long) - 1, ""); + mDirtyVertexBuffers |= 1ull << slot; + } + + void OnSetPipeline(RenderPipeline* lastPipeline, RenderPipeline* pipeline) { + // When a new pipeline is bound we must set all the vertex buffers again because + // they might have been offset by the pipeline layout, and they might be packed + // differently from the previous pipeline. + mDirtyVertexBuffers |= pipeline->GetVertexBufferSlotsUsed(); + } + + void Apply(id encoder, RenderPipeline* pipeline) { + std::bitset vertexBuffersToApply = + mDirtyVertexBuffers & pipeline->GetVertexBufferSlotsUsed(); + + for (uint32_t dawnIndex : IterateBitSet(vertexBuffersToApply)) { + uint32_t metalIndex = pipeline->GetMtlVertexBufferIndex(dawnIndex); + + [encoder setVertexBuffers:&mVertexBuffers[dawnIndex] + offsets:&mVertexBufferOffsets[dawnIndex] + withRange:NSMakeRange(metalIndex, 1)]; + } + + mDirtyVertexBuffers.reset(); + } + + private: + // All the indices in these arrays are Dawn vertex buffer indices + std::bitset mDirtyVertexBuffers; + std::array, kMaxVertexBuffers> mVertexBuffers; + std::array mVertexBufferOffsets; + }; } // anonymous namespace - CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder) - : CommandBufferBase(device, encoder), mCommands(encoder->AcquireCommands()) { + CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) + : CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) { } CommandBuffer::~CommandBuffer() { FreeCommands(&mCommands); } - void CommandBuffer::FillCommands(id commandBuffer) { - GlobalEncoders encoders; + MaybeError CommandBuffer::FillCommands(CommandRecordingContext* commandContext) { + const std::vector& passResourceUsages = GetResourceUsages().perPass; + size_t nextPassNumber = 0; + + auto LazyClearForPass = [](const PassResourceUsage& usages) { + for (size_t i = 0; i < usages.textures.size(); ++i) { + Texture* texture = ToBackend(usages.textures[i]); + // Clear textures that are not output attachments. Output attachments will be + // cleared in CreateMTLRenderPassDescriptor by setting the loadop to clear when the + // texture subresource has not been initialized before the render pass. + if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) { + texture->EnsureSubresourceContentInitialized(texture->GetAllSubresources()); + } + } + }; Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::BeginComputePass: { mCommands.NextCommand(); - encoders.Finish(); - EncodeComputePass(commandBuffer); - } break; + + LazyClearForPass(passResourceUsages[nextPassNumber]); + commandContext->EndBlit(); + + DAWN_TRY(EncodeComputePass(commandContext)); + + nextPassNumber++; + break; + } case Command::BeginRenderPass: { BeginRenderPassCmd* cmd = mCommands.NextCommand(); - encoders.Finish(); + + LazyClearForPass(passResourceUsages[nextPassNumber]); + commandContext->EndBlit(); + + LazyClearRenderPassAttachments(cmd); MTLRenderPassDescriptor* descriptor = CreateMTLRenderPassDescriptor(cmd); - EncodeRenderPass(commandBuffer, descriptor, &encoders, cmd->width, cmd->height); - } break; + DAWN_TRY(EncodeRenderPass(commandContext, descriptor, cmd->width, cmd->height)); + + nextPassNumber++; + break; + } case Command::CopyBufferToBuffer: { CopyBufferToBufferCmd* copy = mCommands.NextCommand(); - encoders.EnsureBlit(commandBuffer); - [encoders.blit copyFromBuffer:ToBackend(copy->source)->GetMTLBuffer() - sourceOffset:copy->sourceOffset - toBuffer:ToBackend(copy->destination)->GetMTLBuffer() - destinationOffset:copy->destinationOffset - size:copy->size]; - } break; + ToBackend(copy->source)->EnsureDataInitialized(commandContext); + ToBackend(copy->destination) + ->EnsureDataInitializedAsDestination(commandContext, + copy->destinationOffset, copy->size); + + [commandContext->EnsureBlit() + copyFromBuffer:ToBackend(copy->source)->GetMTLBuffer() + sourceOffset:copy->sourceOffset + toBuffer:ToBackend(copy->destination)->GetMTLBuffer() + destinationOffset:copy->destinationOffset + size:copy->size]; + break; + } case Command::CopyBufferToTexture: { CopyBufferToTextureCmd* copy = mCommands.NextCommand(); @@ -365,109 +601,41 @@ void ApplyBindGroup(uint32_t index, Buffer* buffer = ToBackend(src.buffer.Get()); Texture* texture = ToBackend(dst.texture.Get()); - MTLOrigin origin; - origin.x = dst.origin.x; - origin.y = dst.origin.y; - origin.z = dst.origin.z; - - MTLSize size; - size.width = copySize.width; - size.height = copySize.height; - size.depth = copySize.depth; - - // When uploading textures from an unpacked buffer, Metal validation layer - // doesn't compute the correct range when checking if the buffer is big enough - // to contain the data for the whole copy. Instead of looking at the position - // of the last texel in the buffer, it computes the volume of the 3D box with - // rowPitch * imageHeight * copySize.depth. For example considering the pixel - // buffer below where in memory, each row data (D) of the texture is followed - // by some padding data (P): - // |DDDDDDD|PP| - // |DDDDDDD|PP| - // |DDDDDDD|PP| - // |DDDDDDD|PP| - // |DDDDDDA|PP| - // The last pixel read will be A, but the driver will think it is the whole - // last padding row, causing it to generate an error when the pixel buffer is - // just big enough. - - // We work around this limitation by detecting when Metal would complain and - // copy the last image and row separately using tight sourceBytesPerRow or - // sourceBytesPerImage. - uint32_t bytesPerImage = src.rowPitch * src.imageHeight; - - // Check whether buffer size is big enough. - bool needWorkaround = - (buffer->GetSize() - src.offset < bytesPerImage * size.depth); - - encoders.EnsureBlit(commandBuffer); - - if (!needWorkaround) { - [encoders.blit copyFromBuffer:buffer->GetMTLBuffer() - sourceOffset:src.offset - sourceBytesPerRow:src.rowPitch - sourceBytesPerImage:(src.rowPitch * src.imageHeight) - sourceSize:size - toTexture:texture->GetMTLTexture() - destinationSlice:dst.slice - destinationLevel:dst.level - destinationOrigin:origin]; - break; - } - - uint64_t offset = src.offset; - - // Doing all the copy except the last image. - if (size.depth > 1) { - [encoders.blit - copyFromBuffer:buffer->GetMTLBuffer() - sourceOffset:offset - sourceBytesPerRow:src.rowPitch - sourceBytesPerImage:(src.rowPitch * src.imageHeight) - sourceSize:MTLSizeMake(size.width, size.height, size.depth - 1) - toTexture:texture->GetMTLTexture() - destinationSlice:dst.slice - destinationLevel:dst.level - destinationOrigin:origin]; - - // Update offset to copy to the last image. - offset += (copySize.depth - 1) * bytesPerImage; - } - - // Doing all the copy in last image except the last row. - if (size.height > 1) { - [encoders.blit copyFromBuffer:buffer->GetMTLBuffer() - sourceOffset:offset - sourceBytesPerRow:src.rowPitch - sourceBytesPerImage:(src.rowPitch * (src.imageHeight - 1)) - sourceSize:MTLSizeMake(size.width, size.height - 1, 1) - toTexture:texture->GetMTLTexture() - destinationSlice:dst.slice - destinationLevel:dst.level - destinationOrigin:MTLOriginMake(origin.x, origin.y, - origin.z + size.depth - 1)]; - - // Update offset to copy to the last row. - offset += (copySize.height - 1) * src.rowPitch; + buffer->EnsureDataInitialized(commandContext); + EnsureDestinationTextureInitialized(texture, copy->destination, copy->copySize); + + TextureBufferCopySplit splitCopies = ComputeTextureBufferCopySplit( + texture, dst.mipLevel, dst.origin, copySize, buffer->GetSize(), src.offset, + src.bytesPerRow, src.rowsPerImage); + + for (uint32_t i = 0; i < splitCopies.count; ++i) { + const TextureBufferCopySplit::CopyInfo& copyInfo = splitCopies.copies[i]; + + const uint32_t copyBaseLayer = copyInfo.textureOrigin.z; + const uint32_t copyLayerCount = copyInfo.copyExtent.depth; + const MTLOrigin textureOrigin = + MTLOriginMake(copyInfo.textureOrigin.x, copyInfo.textureOrigin.y, 0); + const MTLSize copyExtent = + MTLSizeMake(copyInfo.copyExtent.width, copyInfo.copyExtent.height, 1); + + uint64_t bufferOffset = copyInfo.bufferOffset; + for (uint32_t copyLayer = copyBaseLayer; + copyLayer < copyBaseLayer + copyLayerCount; ++copyLayer) { + [commandContext->EnsureBlit() copyFromBuffer:buffer->GetMTLBuffer() + sourceOffset:bufferOffset + sourceBytesPerRow:copyInfo.bytesPerRow + sourceBytesPerImage:copyInfo.bytesPerImage + sourceSize:copyExtent + toTexture:texture->GetMTLTexture() + destinationSlice:copyLayer + destinationLevel:dst.mipLevel + destinationOrigin:textureOrigin]; + bufferOffset += copyInfo.bytesPerImage; + } } - // Doing the last row copy with the exact number of bytes in last row. - // Like copy to a 1D texture to workaround the issue. - uint32_t lastRowDataSize = - copySize.width * TextureFormatPixelSize(texture->GetFormat()); - - [encoders.blit - copyFromBuffer:buffer->GetMTLBuffer() - sourceOffset:offset - sourceBytesPerRow:lastRowDataSize - sourceBytesPerImage:lastRowDataSize - sourceSize:MTLSizeMake(size.width, 1, 1) - toTexture:texture->GetMTLTexture() - destinationSlice:dst.slice - destinationLevel:dst.level - destinationOrigin:MTLOriginMake(origin.x, origin.y + size.height - 1, - origin.z + size.depth - 1)]; - } break; + break; + } case Command::CopyTextureToBuffer: { CopyTextureToBufferCmd* copy = mCommands.NextCommand(); @@ -477,111 +645,43 @@ void ApplyBindGroup(uint32_t index, Texture* texture = ToBackend(src.texture.Get()); Buffer* buffer = ToBackend(dst.buffer.Get()); - MTLOrigin origin; - origin.x = src.origin.x; - origin.y = src.origin.y; - origin.z = src.origin.z; - - MTLSize size; - size.width = copySize.width; - size.height = copySize.height; - size.depth = copySize.depth; - - // When Copy textures to an unpacked buffer, Metal validation layer doesn't - // compute the correct range when checking if the buffer is big enough to - // contain the data for the whole copy. Instead of looking at the position - // of the last texel in the buffer, it computes the volume of the 3D box with - // rowPitch * imageHeight * copySize.depth. - // For example considering the texture below where in memory, each row - // data (D) of the texture is followed by some padding data (P): - // |DDDDDDD|PP| - // |DDDDDDD|PP| - // |DDDDDDD|PP| - // |DDDDDDD|PP| - // |DDDDDDA|PP| - // The last valid pixel read will be A, but the driver will think it needs the - // whole last padding row, causing it to generate an error when the buffer is - // just big enough. - - // We work around this limitation by detecting when Metal would complain and - // copy the last image and row separately using tight destinationBytesPerRow or - // destinationBytesPerImage. - uint32_t bytesPerImage = dst.rowPitch * dst.imageHeight; - - // Check whether buffer size is big enough. - bool needWorkaround = - (buffer->GetSize() - dst.offset < bytesPerImage * size.depth); - - encoders.EnsureBlit(commandBuffer); - - if (!needWorkaround) { - [encoders.blit copyFromTexture:texture->GetMTLTexture() - sourceSlice:src.slice - sourceLevel:src.level - sourceOrigin:origin - sourceSize:size - toBuffer:buffer->GetMTLBuffer() - destinationOffset:dst.offset - destinationBytesPerRow:dst.rowPitch - destinationBytesPerImage:(dst.rowPitch * dst.imageHeight)]; - break; - } - - uint64_t offset = dst.offset; - - // Doing all the copy except the last image. - if (size.depth > 1) { - size.depth = copySize.depth - 1; - - [encoders.blit copyFromTexture:texture->GetMTLTexture() - sourceSlice:src.slice - sourceLevel:src.level - sourceOrigin:origin - sourceSize:MTLSizeMake(size.width, size.height, - size.depth - 1) - toBuffer:buffer->GetMTLBuffer() - destinationOffset:offset - destinationBytesPerRow:dst.rowPitch - destinationBytesPerImage:dst.rowPitch * dst.imageHeight]; - - // Update offset to copy from the last image. - offset += (copySize.depth - 1) * bytesPerImage; - } - - // Doing all the copy in last image except the last row. - if (size.height > 1) { - [encoders.blit copyFromTexture:texture->GetMTLTexture() - sourceSlice:src.slice - sourceLevel:src.level - sourceOrigin:MTLOriginMake(origin.x, origin.y, - origin.z + size.depth - 1) - sourceSize:MTLSizeMake(size.width, size.height - 1, 1) - toBuffer:buffer->GetMTLBuffer() - destinationOffset:offset - destinationBytesPerRow:dst.rowPitch - destinationBytesPerImage:dst.rowPitch * (dst.imageHeight - 1)]; - - // Update offset to copy from the last row. - offset += (copySize.height - 1) * dst.rowPitch; + buffer->EnsureDataInitializedAsDestination(commandContext, copy); + + texture->EnsureSubresourceContentInitialized( + GetSubresourcesAffectedByCopy(src, copySize)); + + TextureBufferCopySplit splitCopies = ComputeTextureBufferCopySplit( + texture, src.mipLevel, src.origin, copySize, buffer->GetSize(), dst.offset, + dst.bytesPerRow, dst.rowsPerImage); + + for (uint32_t i = 0; i < splitCopies.count; ++i) { + const TextureBufferCopySplit::CopyInfo& copyInfo = splitCopies.copies[i]; + + const uint32_t copyBaseLayer = copyInfo.textureOrigin.z; + const uint32_t copyLayerCount = copyInfo.copyExtent.depth; + const MTLOrigin textureOrigin = + MTLOriginMake(copyInfo.textureOrigin.x, copyInfo.textureOrigin.y, 0); + const MTLSize copyExtent = + MTLSizeMake(copyInfo.copyExtent.width, copyInfo.copyExtent.height, 1); + + uint64_t bufferOffset = copyInfo.bufferOffset; + for (uint32_t copyLayer = copyBaseLayer; + copyLayer < copyBaseLayer + copyLayerCount; ++copyLayer) { + [commandContext->EnsureBlit() copyFromTexture:texture->GetMTLTexture() + sourceSlice:copyLayer + sourceLevel:src.mipLevel + sourceOrigin:textureOrigin + sourceSize:copyExtent + toBuffer:buffer->GetMTLBuffer() + destinationOffset:bufferOffset + destinationBytesPerRow:copyInfo.bytesPerRow + destinationBytesPerImage:copyInfo.bytesPerImage]; + bufferOffset += copyInfo.bytesPerImage; + } } - // Doing the last row copy with the exact number of bytes in last row. - // Like copy from a 1D texture to workaround the issue. - uint32_t lastRowDataSize = - copySize.width * TextureFormatPixelSize(texture->GetFormat()); - - [encoders.blit - copyFromTexture:texture->GetMTLTexture() - sourceSlice:src.slice - sourceLevel:src.level - sourceOrigin:MTLOriginMake(origin.x, origin.y + size.height - 1, - origin.z + size.depth - 1) - sourceSize:MTLSizeMake(size.width, 1, 1) - toBuffer:buffer->GetMTLBuffer() - destinationOffset:offset - destinationBytesPerRow:lastRowDataSize - destinationBytesPerImage:lastRowDataSize]; - } break; + break; + } case Command::CopyTextureToTexture: { CopyTextureToTextureCmd* copy = @@ -589,93 +689,154 @@ void ApplyBindGroup(uint32_t index, Texture* srcTexture = ToBackend(copy->source.texture.Get()); Texture* dstTexture = ToBackend(copy->destination.texture.Get()); - MTLOrigin srcOrigin; - srcOrigin.x = copy->source.origin.x; - srcOrigin.y = copy->source.origin.y; - srcOrigin.z = copy->source.origin.z; - - MTLOrigin dstOrigin; - dstOrigin.x = copy->destination.origin.x; - dstOrigin.y = copy->destination.origin.y; - dstOrigin.z = copy->destination.origin.z; - - MTLSize size; - size.width = copy->copySize.width; - size.height = copy->copySize.height; - size.depth = copy->copySize.depth; - - encoders.EnsureBlit(commandBuffer); - - [encoders.blit copyFromTexture:srcTexture->GetMTLTexture() - sourceSlice:copy->source.slice - sourceLevel:copy->source.level - sourceOrigin:srcOrigin - sourceSize:size - toTexture:dstTexture->GetMTLTexture() - destinationSlice:copy->destination.slice - destinationLevel:copy->destination.level - destinationOrigin:dstOrigin]; - } break; - - default: { UNREACHABLE(); } break; + srcTexture->EnsureSubresourceContentInitialized( + GetSubresourcesAffectedByCopy(copy->source, copy->copySize)); + EnsureDestinationTextureInitialized(dstTexture, copy->destination, + copy->copySize); + + // TODO(jiawei.shao@intel.com): support copies with 1D and 3D textures. + ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D && + dstTexture->GetDimension() == wgpu::TextureDimension::e2D); + const MTLSize sizeOneLayer = + MTLSizeMake(copy->copySize.width, copy->copySize.height, 1); + const MTLOrigin sourceOriginNoLayer = + MTLOriginMake(copy->source.origin.x, copy->source.origin.y, 0); + const MTLOrigin destinationOriginNoLayer = + MTLOriginMake(copy->destination.origin.x, copy->destination.origin.y, 0); + + for (uint32_t slice = 0; slice < copy->copySize.depth; ++slice) { + [commandContext->EnsureBlit() + copyFromTexture:srcTexture->GetMTLTexture() + sourceSlice:copy->source.origin.z + slice + sourceLevel:copy->source.mipLevel + sourceOrigin:sourceOriginNoLayer + sourceSize:sizeOneLayer + toTexture:dstTexture->GetMTLTexture() + destinationSlice:copy->destination.origin.z + slice + destinationLevel:copy->destination.mipLevel + destinationOrigin:destinationOriginNoLayer]; + } + break; + } + + case Command::ResolveQuerySet: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } + + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } + + default: { + UNREACHABLE(); + break; + } } } - encoders.Finish(); + commandContext->EndBlit(); + return {}; } - void CommandBuffer::EncodeComputePass(id commandBuffer) { + MaybeError CommandBuffer::EncodeComputePass(CommandRecordingContext* commandContext) { ComputePipeline* lastPipeline = nullptr; + StorageBufferLengthTracker storageBufferLengths = {}; + BindGroupTracker bindGroups(&storageBufferLengths); - // Will be autoreleased - id encoder = [commandBuffer computeCommandEncoder]; + id encoder = commandContext->BeginCompute(); Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::EndComputePass: { mCommands.NextCommand(); - [encoder endEncoding]; - return; - } break; + commandContext->EndCompute(); + return {}; + } case Command::Dispatch: { DispatchCmd* dispatch = mCommands.NextCommand(); + + bindGroups.Apply(encoder); + storageBufferLengths.Apply(encoder, lastPipeline); + [encoder dispatchThreadgroups:MTLSizeMake(dispatch->x, dispatch->y, dispatch->z) threadsPerThreadgroup:lastPipeline->GetLocalWorkGroupSize()]; - } break; + break; + } case Command::DispatchIndirect: { DispatchIndirectCmd* dispatch = mCommands.NextCommand(); + bindGroups.Apply(encoder); + storageBufferLengths.Apply(encoder, lastPipeline); + Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get()); id indirectBuffer = buffer->GetMTLBuffer(); [encoder dispatchThreadgroupsWithIndirectBuffer:indirectBuffer indirectBufferOffset:dispatch->indirectOffset threadsPerThreadgroup:lastPipeline ->GetLocalWorkGroupSize()]; - } break; + break; + } case Command::SetComputePipeline: { SetComputePipelineCmd* cmd = mCommands.NextCommand(); lastPipeline = ToBackend(cmd->pipeline).Get(); + bindGroups.OnSetPipeline(lastPipeline); + lastPipeline->Encode(encoder); - } break; + break; + } case Command::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); - uint64_t* dynamicOffsets = nullptr; + uint32_t* dynamicOffsets = nullptr; if (cmd->dynamicOffsetCount > 0) { - dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); } - ApplyBindGroup(cmd->index, ToBackend(cmd->group.Get()), cmd->dynamicOffsetCount, - dynamicOffsets, ToBackend(lastPipeline->GetLayout()), nil, - encoder); - } break; + bindGroups.OnSetBindGroup(cmd->index, ToBackend(cmd->group.Get()), + cmd->dynamicOffsetCount, dynamicOffsets); + break; + } + + case Command::InsertDebugMarker: { + InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); + char* label = mCommands.NextData(cmd->length + 1); + NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label]; + + [encoder insertDebugSignpost:mtlLabel]; + [mtlLabel release]; + break; + } - default: { UNREACHABLE(); } break; + case Command::PopDebugGroup: { + mCommands.NextCommand(); + + [encoder popDebugGroup]; + break; + } + + case Command::PushDebugGroup: { + PushDebugGroupCmd* cmd = mCommands.NextCommand(); + char* label = mCommands.NextData(cmd->length + 1); + NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label]; + + [encoder pushDebugGroup:mtlLabel]; + [mtlLabel release]; + break; + } + + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } + + default: { + UNREACHABLE(); + break; + } } } @@ -683,12 +844,11 @@ void ApplyBindGroup(uint32_t index, UNREACHABLE(); } - void CommandBuffer::EncodeRenderPass(id commandBuffer, - MTLRenderPassDescriptor* mtlRenderPass, - GlobalEncoders* globalEncoders, - uint32_t width, - uint32_t height) { - ASSERT(mtlRenderPass && globalEncoders); + MaybeError CommandBuffer::EncodeRenderPass(CommandRecordingContext* commandContext, + MTLRenderPassDescriptor* mtlRenderPass, + uint32_t width, + uint32_t height) { + ASSERT(mtlRenderPass); Device* device = ToBackend(GetDevice()); @@ -731,19 +891,20 @@ void ApplyBindGroup(uint32_t index, // If we need to use a temporary resolve texture we need to copy the result of MSAA // resolve back to the true resolve targets. if (useTemporaryResolveTexture) { - EncodeRenderPass(commandBuffer, mtlRenderPass, globalEncoders, width, height); + DAWN_TRY(EncodeRenderPass(commandContext, mtlRenderPass, width, height)); for (uint32_t i = 0; i < kMaxColorAttachments; ++i) { if (trueResolveTextures[i] == nil) { continue; } ASSERT(temporaryResolveTextures[i] != nil); - CopyIntoTrueResolveTarget(commandBuffer, trueResolveTextures[i], + CopyIntoTrueResolveTarget(commandContext, trueResolveTextures[i], trueResolveLevels[i], trueResolveSlices[i], - temporaryResolveTextures[i], width, height, - globalEncoders); + temporaryResolveTextures[i], width, height); + [temporaryResolveTextures[i] release]; + temporaryResolveTextures[i] = nil; } - return; + return {}; } } @@ -755,7 +916,7 @@ void ApplyBindGroup(uint32_t index, std::array, kMaxColorAttachments> resolveTextures = {}; for (uint32_t i = 0; i < kMaxColorAttachments; ++i) { if (mtlRenderPass.colorAttachments[i].storeAction == - MTLStoreActionStoreAndMultisampleResolve) { + kMTLStoreActionStoreAndMultisampleResolve) { hasStoreAndMSAAResolve = true; resolveTextures[i] = mtlRenderPass.colorAttachments[i].resolveTexture; @@ -766,80 +927,114 @@ void ApplyBindGroup(uint32_t index, // If we found a store + MSAA resolve we need to resolve in a different render pass. if (hasStoreAndMSAAResolve) { - EncodeRenderPass(commandBuffer, mtlRenderPass, globalEncoders, width, height); - ResolveInAnotherRenderPass(commandBuffer, mtlRenderPass, resolveTextures); - return; + DAWN_TRY(EncodeRenderPass(commandContext, mtlRenderPass, width, height)); + ResolveInAnotherRenderPass(commandContext, mtlRenderPass, resolveTextures); + return {}; } } - EncodeRenderPassInternal(commandBuffer, mtlRenderPass, width, height); + DAWN_TRY(EncodeRenderPassInternal(commandContext, mtlRenderPass, width, height)); + return {}; } - void CommandBuffer::EncodeRenderPassInternal(id commandBuffer, - MTLRenderPassDescriptor* mtlRenderPass, - uint32_t width, - uint32_t height) { + MaybeError CommandBuffer::EncodeRenderPassInternal(CommandRecordingContext* commandContext, + MTLRenderPassDescriptor* mtlRenderPass, + uint32_t width, + uint32_t height) { RenderPipeline* lastPipeline = nullptr; id indexBuffer = nil; uint32_t indexBufferBaseOffset = 0; + VertexBufferTracker vertexBuffers; + StorageBufferLengthTracker storageBufferLengths = {}; + BindGroupTracker bindGroups(&storageBufferLengths); - // This will be autoreleased - id encoder = - [commandBuffer renderCommandEncoderWithDescriptor:mtlRenderPass]; + id encoder = commandContext->BeginRender(mtlRenderPass); - Command type; - while (mCommands.NextCommandId(&type)) { + auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) { switch (type) { - case Command::EndRenderPass: { - mCommands.NextCommand(); - [encoder endEncoding]; - return; - } break; - case Command::Draw: { - DrawCmd* draw = mCommands.NextCommand(); + DrawCmd* draw = iter->NextCommand(); + + vertexBuffers.Apply(encoder, lastPipeline); + bindGroups.Apply(encoder); + storageBufferLengths.Apply(encoder, lastPipeline); // The instance count must be non-zero, otherwise no-op if (draw->instanceCount != 0) { - [encoder drawPrimitives:lastPipeline->GetMTLPrimitiveTopology() - vertexStart:draw->firstVertex - vertexCount:draw->vertexCount - instanceCount:draw->instanceCount - baseInstance:draw->firstInstance]; + // MTLFeatureSet_iOS_GPUFamily3_v1 does not support baseInstance + if (draw->firstInstance == 0) { + [encoder drawPrimitives:lastPipeline->GetMTLPrimitiveTopology() + vertexStart:draw->firstVertex + vertexCount:draw->vertexCount + instanceCount:draw->instanceCount]; + } else { + [encoder drawPrimitives:lastPipeline->GetMTLPrimitiveTopology() + vertexStart:draw->firstVertex + vertexCount:draw->vertexCount + instanceCount:draw->instanceCount + baseInstance:draw->firstInstance]; + } } - } break; + break; + } case Command::DrawIndexed: { - DrawIndexedCmd* draw = mCommands.NextCommand(); + DrawIndexedCmd* draw = iter->NextCommand(); size_t formatSize = - IndexFormatSize(lastPipeline->GetVertexInputDescriptor()->indexFormat); + IndexFormatSize(lastPipeline->GetVertexStateDescriptor()->indexFormat); + + vertexBuffers.Apply(encoder, lastPipeline); + bindGroups.Apply(encoder); + storageBufferLengths.Apply(encoder, lastPipeline); // The index and instance count must be non-zero, otherwise no-op if (draw->indexCount != 0 && draw->instanceCount != 0) { - [encoder drawIndexedPrimitives:lastPipeline->GetMTLPrimitiveTopology() - indexCount:draw->indexCount - indexType:lastPipeline->GetMTLIndexType() - indexBuffer:indexBuffer - indexBufferOffset:indexBufferBaseOffset + - draw->firstIndex * formatSize - instanceCount:draw->instanceCount - baseVertex:draw->baseVertex - baseInstance:draw->firstInstance]; + // MTLFeatureSet_iOS_GPUFamily3_v1 does not support baseInstance and + // baseVertex. + if (draw->baseVertex == 0 && draw->firstInstance == 0) { + [encoder drawIndexedPrimitives:lastPipeline->GetMTLPrimitiveTopology() + indexCount:draw->indexCount + indexType:lastPipeline->GetMTLIndexType() + indexBuffer:indexBuffer + indexBufferOffset:indexBufferBaseOffset + + draw->firstIndex * formatSize + instanceCount:draw->instanceCount]; + } else { + [encoder drawIndexedPrimitives:lastPipeline->GetMTLPrimitiveTopology() + indexCount:draw->indexCount + indexType:lastPipeline->GetMTLIndexType() + indexBuffer:indexBuffer + indexBufferOffset:indexBufferBaseOffset + + draw->firstIndex * formatSize + instanceCount:draw->instanceCount + baseVertex:draw->baseVertex + baseInstance:draw->firstInstance]; + } } - } break; + break; + } case Command::DrawIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); + + vertexBuffers.Apply(encoder, lastPipeline); + bindGroups.Apply(encoder); + storageBufferLengths.Apply(encoder, lastPipeline); Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); id indirectBuffer = buffer->GetMTLBuffer(); [encoder drawPrimitives:lastPipeline->GetMTLPrimitiveTopology() indirectBuffer:indirectBuffer indirectBufferOffset:draw->indirectOffset]; - } break; + break; + } case Command::DrawIndexedIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); + + vertexBuffers.Apply(encoder, lastPipeline); + bindGroups.Apply(encoder); + storageBufferLengths.Apply(encoder, lastPipeline); Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); id indirectBuffer = buffer->GetMTLBuffer(); @@ -849,46 +1044,114 @@ void ApplyBindGroup(uint32_t index, indexBufferOffset:indexBufferBaseOffset indirectBuffer:indirectBuffer indirectBufferOffset:draw->indirectOffset]; - } break; + break; + } case Command::InsertDebugMarker: { - InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); - auto label = mCommands.NextData(cmd->length + 1); + InsertDebugMarkerCmd* cmd = iter->NextCommand(); + char* label = iter->NextData(cmd->length + 1); NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label]; [encoder insertDebugSignpost:mtlLabel]; [mtlLabel release]; - } break; + break; + } case Command::PopDebugGroup: { - mCommands.NextCommand(); + iter->NextCommand(); [encoder popDebugGroup]; - } break; + break; + } case Command::PushDebugGroup: { - PushDebugGroupCmd* cmd = mCommands.NextCommand(); - auto label = mCommands.NextData(cmd->length + 1); + PushDebugGroupCmd* cmd = iter->NextCommand(); + char* label = iter->NextData(cmd->length + 1); NSString* mtlLabel = [[NSString alloc] initWithUTF8String:label]; [encoder pushDebugGroup:mtlLabel]; [mtlLabel release]; - } break; + break; + } case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mCommands.NextCommand(); - lastPipeline = ToBackend(cmd->pipeline).Get(); + SetRenderPipelineCmd* cmd = iter->NextCommand(); + RenderPipeline* newPipeline = ToBackend(cmd->pipeline).Get(); - [encoder setDepthStencilState:lastPipeline->GetMTLDepthStencilState()]; - [encoder setFrontFacingWinding:lastPipeline->GetMTLFrontFace()]; - [encoder setCullMode:lastPipeline->GetMTLCullMode()]; - lastPipeline->Encode(encoder); - } break; + vertexBuffers.OnSetPipeline(lastPipeline, newPipeline); + bindGroups.OnSetPipeline(newPipeline); + + [encoder setDepthStencilState:newPipeline->GetMTLDepthStencilState()]; + [encoder setFrontFacingWinding:newPipeline->GetMTLFrontFace()]; + [encoder setCullMode:newPipeline->GetMTLCullMode()]; + newPipeline->Encode(encoder); + + lastPipeline = newPipeline; + break; + } + + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = iter->NextCommand(); + uint32_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); + } + + bindGroups.OnSetBindGroup(cmd->index, ToBackend(cmd->group.Get()), + cmd->dynamicOffsetCount, dynamicOffsets); + break; + } + + case Command::SetIndexBuffer: { + SetIndexBufferCmd* cmd = iter->NextCommand(); + auto b = ToBackend(cmd->buffer.Get()); + indexBuffer = b->GetMTLBuffer(); + indexBufferBaseOffset = cmd->offset; + break; + } + + case Command::SetVertexBuffer: { + SetVertexBufferCmd* cmd = iter->NextCommand(); + + vertexBuffers.OnSetVertexBuffer(cmd->slot, ToBackend(cmd->buffer.Get()), + cmd->offset); + break; + } + + default: + UNREACHABLE(); + break; + } + }; + + Command type; + while (mCommands.NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + mCommands.NextCommand(); + commandContext->EndRender(); + return {}; + } case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); [encoder setStencilReferenceValue:cmd->reference]; - } break; + break; + } + + case Command::SetViewport: { + SetViewportCmd* cmd = mCommands.NextCommand(); + MTLViewport viewport; + viewport.originX = cmd->x; + viewport.originY = cmd->y; + viewport.width = cmd->width; + viewport.height = cmd->height; + viewport.znear = cmd->minDepth; + viewport.zfar = cmd->maxDepth; + + [encoder setViewport:viewport]; + break; + } case Command::SetScissorRect: { SetScissorRectCmd* cmd = mCommands.NextCommand(); @@ -908,7 +1171,8 @@ void ApplyBindGroup(uint32_t index, } [encoder setScissorRect:rect]; - } break; + break; + } case Command::SetBlendColor: { SetBlendColorCmd* cmd = mCommands.NextCommand(); @@ -916,50 +1180,31 @@ void ApplyBindGroup(uint32_t index, green:cmd->color.g blue:cmd->color.b alpha:cmd->color.a]; - } break; - - case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mCommands.NextCommand(); - uint64_t* dynamicOffsets = nullptr; - if (cmd->dynamicOffsetCount > 0) { - dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); - } - - ApplyBindGroup(cmd->index, ToBackend(cmd->group.Get()), cmd->dynamicOffsetCount, - dynamicOffsets, ToBackend(lastPipeline->GetLayout()), encoder, - nil); - } break; - - case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mCommands.NextCommand(); - auto b = ToBackend(cmd->buffer.Get()); - indexBuffer = b->GetMTLBuffer(); - indexBufferBaseOffset = cmd->offset; - } break; - - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mCommands.NextCommand(); - auto buffers = mCommands.NextData>(cmd->count); - auto offsets = mCommands.NextData(cmd->count); + break; + } - std::array, kMaxVertexBuffers> mtlBuffers; - std::array mtlOffsets; + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = mCommands.NextCommand(); + auto bundles = mCommands.NextData>(cmd->count); - // Perhaps an "array of vertex buffers(+offsets?)" should be - // a Dawn API primitive to avoid reconstructing this array? for (uint32_t i = 0; i < cmd->count; ++i) { - Buffer* buffer = ToBackend(buffers[i].Get()); - mtlBuffers[i] = buffer->GetMTLBuffer(); - mtlOffsets[i] = offsets[i]; + CommandIterator* iter = bundles[i]->GetCommands(); + iter->Reset(); + while (iter->NextCommandId(&type)) { + EncodeRenderBundleCommand(iter, type); + } } + break; + } - [encoder setVertexBuffers:mtlBuffers.data() - offsets:mtlOffsets.data() - withRange:NSMakeRange(kMaxBindingsPerGroup + cmd->startSlot, - cmd->count)]; - } break; + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } - default: { UNREACHABLE(); } break; + default: { + EncodeRenderBundleCommand(&mCommands, type); + break; + } } } diff --git a/third_party/dawn/src/dawn_native/metal/CommandRecordingContext.h b/third_party/dawn/src/dawn_native/metal/CommandRecordingContext.h new file mode 100644 index 00000000000..531681b4bac --- /dev/null +++ b/third_party/dawn/src/dawn_native/metal/CommandRecordingContext.h @@ -0,0 +1,59 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef DAWNNATIVE_METAL_COMMANDRECORDINGCONTEXT_H_ +#define DAWNNATIVE_METAL_COMMANDRECORDINGCONTEXT_H_ + +#import + +namespace dawn_native { namespace metal { + + // This class wraps a MTLCommandBuffer and tracks which Metal encoder is open. + // Only one encoder may be open at a time. + class CommandRecordingContext { + public: + CommandRecordingContext(); + CommandRecordingContext(id commands); + + CommandRecordingContext(const CommandRecordingContext& rhs) = delete; + CommandRecordingContext& operator=(const CommandRecordingContext& rhs) = delete; + + CommandRecordingContext(CommandRecordingContext&& rhs); + CommandRecordingContext& operator=(CommandRecordingContext&& rhs); + + ~CommandRecordingContext(); + + id GetCommands(); + + id AcquireCommands(); + + id EnsureBlit(); + void EndBlit(); + + id BeginCompute(); + void EndCompute(); + + id BeginRender(MTLRenderPassDescriptor* descriptor); + void EndRender(); + + private: + id mCommands = nil; + id mBlit = nil; + id mCompute = nil; + id mRender = nil; + bool mInEncoder = false; + }; + +}} // namespace dawn_native::metal + +#endif // DAWNNATIVE_METAL_COMMANDRECORDINGCONTEXT_H_ diff --git a/third_party/dawn/src/dawn_native/metal/CommandRecordingContext.mm b/third_party/dawn/src/dawn_native/metal/CommandRecordingContext.mm new file mode 100644 index 00000000000..971691a4b24 --- /dev/null +++ b/third_party/dawn/src/dawn_native/metal/CommandRecordingContext.mm @@ -0,0 +1,125 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/metal/CommandRecordingContext.h" + +#include "common/Assert.h" + +namespace dawn_native { namespace metal { + + CommandRecordingContext::CommandRecordingContext() = default; + + CommandRecordingContext::CommandRecordingContext(id commands) + : mCommands(commands) { + } + + CommandRecordingContext::CommandRecordingContext(CommandRecordingContext&& rhs) + : mCommands(rhs.AcquireCommands()) { + } + + CommandRecordingContext& CommandRecordingContext::operator=(CommandRecordingContext&& rhs) { + mCommands = rhs.AcquireCommands(); + return *this; + } + + CommandRecordingContext::~CommandRecordingContext() { + // Commands must be acquired. + ASSERT(mCommands == nil); + } + + id CommandRecordingContext::GetCommands() { + return mCommands; + } + + id CommandRecordingContext::AcquireCommands() { + if (mCommands == nil) { + return nil; + } + + // A blit encoder can be left open from WriteBuffer, make sure we close it. + EndBlit(); + + ASSERT(!mInEncoder); + id commands = mCommands; + mCommands = nil; + return commands; + } + + id CommandRecordingContext::EnsureBlit() { + ASSERT(mCommands != nil); + + if (mBlit == nil) { + ASSERT(!mInEncoder); + mInEncoder = true; + // The autorelease pool may drain before the encoder is ended. Retain so it stays alive. + mBlit = [[mCommands blitCommandEncoder] retain]; + } + return mBlit; + } + + void CommandRecordingContext::EndBlit() { + ASSERT(mCommands != nil); + + if (mBlit != nil) { + [mBlit endEncoding]; + [mBlit release]; + mBlit = nil; + mInEncoder = false; + } + } + + id CommandRecordingContext::BeginCompute() { + ASSERT(mCommands != nil); + ASSERT(mCompute == nil); + ASSERT(!mInEncoder); + + mInEncoder = true; + // The autorelease pool may drain before the encoder is ended. Retain so it stays alive. + mCompute = [[mCommands computeCommandEncoder] retain]; + return mCompute; + } + + void CommandRecordingContext::EndCompute() { + ASSERT(mCommands != nil); + ASSERT(mCompute != nil); + + [mCompute endEncoding]; + [mCompute release]; + mCompute = nil; + mInEncoder = false; + } + + id CommandRecordingContext::BeginRender( + MTLRenderPassDescriptor* descriptor) { + ASSERT(mCommands != nil); + ASSERT(mRender == nil); + ASSERT(!mInEncoder); + + mInEncoder = true; + // The autorelease pool may drain before the encoder is ended. Retain so it stays alive. + mRender = [[mCommands renderCommandEncoderWithDescriptor:descriptor] retain]; + return mRender; + } + + void CommandRecordingContext::EndRender() { + ASSERT(mCommands != nil); + ASSERT(mRender != nil); + + [mRender endEncoding]; + [mRender release]; + mRender = nil; + mInEncoder = false; + } + +}} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/ComputePipelineMTL.h b/third_party/dawn/src/dawn_native/metal/ComputePipelineMTL.h index 6f3aca9a5c7..7c2d34b8186 100644 --- a/third_party/dawn/src/dawn_native/metal/ComputePipelineMTL.h +++ b/third_party/dawn/src/dawn_native/metal/ComputePipelineMTL.h @@ -23,17 +23,23 @@ namespace dawn_native { namespace metal { class Device; - class ComputePipeline : public ComputePipelineBase { + class ComputePipeline final : public ComputePipelineBase { public: - ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor); - ~ComputePipeline(); + static ResultOrError Create(Device* device, + const ComputePipelineDescriptor* descriptor); void Encode(id encoder); MTLSize GetLocalWorkGroupSize() const; + bool RequiresStorageBufferLength() const; private: + ~ComputePipeline() override; + using ComputePipelineBase::ComputePipelineBase; + MaybeError Initialize(const ComputePipelineDescriptor* descriptor); + id mMtlComputePipelineState = nil; MTLSize mLocalWorkgroupSize; + bool mRequiresStorageBufferLength; }; }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/ComputePipelineMTL.mm b/third_party/dawn/src/dawn_native/metal/ComputePipelineMTL.mm index fc76ee195c0..aca771d244f 100644 --- a/third_party/dawn/src/dawn_native/metal/ComputePipelineMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/ComputePipelineMTL.mm @@ -19,26 +19,36 @@ namespace dawn_native { namespace metal { - ComputePipeline::ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor) - : ComputePipelineBase(device, descriptor) { + // static + ResultOrError ComputePipeline::Create( + Device* device, + const ComputePipelineDescriptor* descriptor) { + Ref pipeline = AcquireRef(new ComputePipeline(device, descriptor)); + DAWN_TRY(pipeline->Initialize(descriptor)); + return pipeline.Detach(); + } + + MaybeError ComputePipeline::Initialize(const ComputePipelineDescriptor* descriptor) { auto mtlDevice = ToBackend(GetDevice())->GetMTLDevice(); - const ShaderModule* computeModule = ToBackend(descriptor->computeStage->module); - const char* computeEntryPoint = descriptor->computeStage->entryPoint; - ShaderModule::MetalFunctionData computeData = computeModule->GetFunction( - computeEntryPoint, dawn::ShaderStage::Compute, ToBackend(GetLayout())); + ShaderModule* computeModule = ToBackend(descriptor->computeStage.module); + const char* computeEntryPoint = descriptor->computeStage.entryPoint; + ShaderModule::MetalFunctionData computeData; + DAWN_TRY(computeModule->GetFunction(computeEntryPoint, SingleShaderStage::Compute, + ToBackend(GetLayout()), &computeData)); NSError* error = nil; mMtlComputePipelineState = [mtlDevice newComputePipelineStateWithFunction:computeData.function error:&error]; if (error != nil) { NSLog(@" error => %@", error); - GetDevice()->HandleError("Error creating pipeline state"); - return; + return DAWN_INTERNAL_ERROR("Error creating pipeline state"); } // Copy over the local workgroup size as it is passed to dispatch explicitly in Metal mLocalWorkgroupSize = computeData.localWorkgroupSize; + mRequiresStorageBufferLength = computeData.needsStorageBufferLength; + return {}; } ComputePipeline::~ComputePipeline() { @@ -53,4 +63,8 @@ return mLocalWorkgroupSize; } + bool ComputePipeline::RequiresStorageBufferLength() const { + return mRequiresStorageBufferLength; + } + }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/DeviceMTL.h b/third_party/dawn/src/dawn_native/metal/DeviceMTL.h index 841f18b01a9..87a697bce5d 100644 --- a/third_party/dawn/src/dawn_native/metal/DeviceMTL.h +++ b/third_party/dawn/src/dawn_native/metal/DeviceMTL.h @@ -18,11 +18,14 @@ #include "dawn_native/dawn_platform.h" #include "common/Serial.h" +#include "dawn_native/Commands.h" #include "dawn_native/Device.h" +#include "dawn_native/metal/CommandRecordingContext.h" #include "dawn_native/metal/Forward.h" +#import #import -#import +#import #include #include @@ -30,28 +33,27 @@ namespace dawn_native { namespace metal { - class MapRequestTracker; - class Device : public DeviceBase { public: - Device(AdapterBase* adapter, id mtlDevice, const DeviceDescriptor* descriptor); - ~Device(); + static ResultOrError Create(AdapterBase* adapter, + id mtlDevice, + const DeviceDescriptor* descriptor); + ~Device() override; - CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) override; + MaybeError Initialize(); - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; - void TickImpl() override; + CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) override; + + MaybeError TickImpl() override; id GetMTLDevice(); + id GetMTLQueue(); - id GetPendingCommandBuffer(); - Serial GetPendingCommandSerial() const override; + CommandRecordingContext* GetPendingCommandContext(); void SubmitPendingCommandBuffer(); - MapRequestTracker* GetMapTracker() const; - - TextureBase* CreateTextureWrappingIOSurface(const TextureDescriptor* descriptor, + TextureBase* CreateTextureWrappingIOSurface(const ExternalImageDescriptor* descriptor, IOSurfaceRef ioSurface, uint32_t plane); void WaitForCommandsToBeScheduled(); @@ -62,18 +64,26 @@ namespace dawn_native { namespace metal { BufferBase* destination, uint64_t destinationOffset, uint64_t size) override; + MaybeError CopyFromStagingToTexture(StagingBufferBase* source, + const TextureDataLayout& dataLayout, + TextureCopy* dst, + const Extent3D copySize); private: + Device(AdapterBase* adapter, id mtlDevice, const DeviceDescriptor* descriptor); + ResultOrError CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override; ResultOrError CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) override; - ResultOrError CreateBufferImpl(const BufferDescriptor* descriptor) override; + ResultOrError> CreateBufferImpl( + const BufferDescriptor* descriptor) override; ResultOrError CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) override; ResultOrError CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) override; - ResultOrError CreateQueueImpl() override; + ResultOrError CreateQuerySetImpl( + const QuerySetDescriptor* descriptor) override; ResultOrError CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) override; ResultOrError CreateSamplerImpl(const SamplerDescriptor* descriptor) override; @@ -81,19 +91,25 @@ namespace dawn_native { namespace metal { const ShaderModuleDescriptor* descriptor) override; ResultOrError CreateSwapChainImpl( const SwapChainDescriptor* descriptor) override; - ResultOrError CreateTextureImpl(const TextureDescriptor* descriptor) override; + ResultOrError CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) override; + ResultOrError> CreateTextureImpl( + const TextureDescriptor* descriptor) override; ResultOrError CreateTextureViewImpl( TextureBase* texture, const TextureViewDescriptor* descriptor) override; void InitTogglesFromDriver(); + void ShutDownImpl() override; + MaybeError WaitForIdleForDestruction() override; + Serial CheckAndUpdateCompletedSerials() override; id mMtlDevice = nil; id mCommandQueue = nil; - std::unique_ptr mMapTracker; - Serial mLastSubmittedSerial = 0; - id mPendingCommands = nil; + CommandRecordingContext mCommandContext; // The completed serial is updated in a Metal completion handler that can be fired on a // different thread, so it needs to be atomic. diff --git a/third_party/dawn/src/dawn_native/metal/DeviceMTL.mm b/third_party/dawn/src/dawn_native/metal/DeviceMTL.mm index d0b58eeb73c..d5dbbc5ea58 100644 --- a/third_party/dawn/src/dawn_native/metal/DeviceMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/DeviceMTL.mm @@ -15,9 +15,11 @@ #include "dawn_native/metal/DeviceMTL.h" #include "dawn_native/BackendConnection.h" -#include "dawn_native/BindGroup.h" #include "dawn_native/BindGroupLayout.h" -#include "dawn_native/DynamicUploader.h" +#include "dawn_native/Commands.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/metal/BindGroupLayoutMTL.h" +#include "dawn_native/metal/BindGroupMTL.h" #include "dawn_native/metal/BufferMTL.h" #include "dawn_native/metal/CommandBufferMTL.h" #include "dawn_native/metal/ComputePipelineMTL.h" @@ -29,56 +31,70 @@ #include "dawn_native/metal/StagingBufferMTL.h" #include "dawn_native/metal/SwapChainMTL.h" #include "dawn_native/metal/TextureMTL.h" +#include "dawn_native/metal/UtilsMetal.h" +#include "dawn_platform/DawnPlatform.h" +#include "dawn_platform/tracing/TraceEvent.h" #include namespace dawn_native { namespace metal { + // static + ResultOrError Device::Create(AdapterBase* adapter, + id mtlDevice, + const DeviceDescriptor* descriptor) { + Ref device = AcquireRef(new Device(adapter, mtlDevice, descriptor)); + DAWN_TRY(device->Initialize()); + return device.Detach(); + } + Device::Device(AdapterBase* adapter, id mtlDevice, const DeviceDescriptor* descriptor) - : DeviceBase(adapter, descriptor), - mMtlDevice([mtlDevice retain]), - mMapTracker(new MapRequestTracker(this)), - mCompletedSerial(0) { + : DeviceBase(adapter, descriptor), mMtlDevice([mtlDevice retain]), mCompletedSerial(0) { [mMtlDevice retain]; - mCommandQueue = [mMtlDevice newCommandQueue]; - - InitTogglesFromDriver(); - if (descriptor != nil) { - ApplyToggleOverrides(descriptor); - } } Device::~Device() { - // Wait for all commands to be finished so we can free resources SubmitPendingCommandBuffer - // may not increment the pendingCommandSerial if there are no pending commands, so we can't - // store the pendingSerial before SubmitPendingCommandBuffer then wait for it to be passed. - // Instead we submit and wait for the serial before the next pendingCommandSerial. - SubmitPendingCommandBuffer(); - while (GetCompletedCommandSerial() != mLastSubmittedSerial) { - usleep(100); - } - Tick(); - - [mPendingCommands release]; - mPendingCommands = nil; - - mMapTracker = nullptr; - mDynamicUploader = nullptr; + ShutDownBase(); + } - [mCommandQueue release]; - mCommandQueue = nil; + MaybeError Device::Initialize() { + InitTogglesFromDriver(); + mCommandQueue = [mMtlDevice newCommandQueue]; - [mMtlDevice release]; - mMtlDevice = nil; + return DeviceBase::Initialize(new Queue(this)); } void Device::InitTogglesFromDriver() { - // TODO(jiawei.shao@intel.com): check iOS feature sets - bool emulateStoreAndMSAAResolve = - ![mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2]; - SetToggle(Toggle::EmulateStoreAndMSAAResolve, emulateStoreAndMSAAResolve); + { + bool haveStoreAndMSAAResolve = false; +#if defined(DAWN_PLATFORM_MACOS) + haveStoreAndMSAAResolve = + [mMtlDevice supportsFeatureSet:MTLFeatureSet_macOS_GPUFamily1_v2]; +#elif defined(DAWN_PLATFORM_IOS) + haveStoreAndMSAAResolve = + [mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v2]; +#endif + // On tvOS, we would need MTLFeatureSet_tvOS_GPUFamily2_v1. + SetToggle(Toggle::EmulateStoreAndMSAAResolve, !haveStoreAndMSAAResolve); + + bool haveSamplerCompare = true; +#if defined(DAWN_PLATFORM_IOS) + haveSamplerCompare = [mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]; +#endif + // TODO(crbug.com/dawn/342): Investigate emulation -- possibly expensive. + SetToggle(Toggle::MetalDisableSamplerCompare, !haveSamplerCompare); + + bool haveBaseVertexBaseInstance = true; +#if defined(DAWN_PLATFORM_IOS) + haveBaseVertexBaseInstance = + [mMtlDevice supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]; +#endif + // TODO(crbug.com/dawn/343): Investigate emulation. + SetToggle(Toggle::DisableBaseVertex, !haveBaseVertexBaseInstance); + SetToggle(Toggle::DisableBaseInstance, !haveBaseVertexBaseInstance); + } // TODO(jiawei.shao@intel.com): tighten this workaround when the driver bug is fixed. SetToggle(Toggle::AlwaysResolveIntoZeroLevelAndLayer, true); @@ -86,46 +102,53 @@ ResultOrError Device::CreateBindGroupImpl( const BindGroupDescriptor* descriptor) { - return new BindGroup(this, descriptor); + return BindGroup::Create(this, descriptor); } ResultOrError Device::CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) { return new BindGroupLayout(this, descriptor); } - ResultOrError Device::CreateBufferImpl(const BufferDescriptor* descriptor) { - return new Buffer(this, descriptor); + ResultOrError> Device::CreateBufferImpl(const BufferDescriptor* descriptor) { + return Buffer::Create(this, descriptor); } - CommandBufferBase* Device::CreateCommandBuffer(CommandEncoderBase* encoder) { - return new CommandBuffer(this, encoder); + CommandBufferBase* Device::CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) { + return new CommandBuffer(encoder, descriptor); } ResultOrError Device::CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) { - return new ComputePipeline(this, descriptor); + return ComputePipeline::Create(this, descriptor); } ResultOrError Device::CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) { return new PipelineLayout(this, descriptor); } - ResultOrError Device::CreateQueueImpl() { - return new Queue(this); + ResultOrError Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation"); } ResultOrError Device::CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) { - return new RenderPipeline(this, descriptor); + return RenderPipeline::Create(this, descriptor); } ResultOrError Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) { - return new Sampler(this, descriptor); + return Sampler::Create(this, descriptor); } ResultOrError Device::CreateShaderModuleImpl( const ShaderModuleDescriptor* descriptor) { - return new ShaderModule(this, descriptor); + return ShaderModule::Create(this, descriptor); } ResultOrError Device::CreateSwapChainImpl( const SwapChainDescriptor* descriptor) { - return new SwapChain(this, descriptor); + return new OldSwapChain(this, descriptor); } - ResultOrError Device::CreateTextureImpl(const TextureDescriptor* descriptor) { - return new Texture(this, descriptor); + ResultOrError Device::CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) { + return new SwapChain(this, surface, previousSwapChain, descriptor); + } + ResultOrError> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { + return AcquireRef(new Texture(this, descriptor)); } ResultOrError Device::CreateTextureViewImpl( TextureBase* texture, @@ -133,101 +156,92 @@ return new TextureView(texture, descriptor); } - Serial Device::GetCompletedCommandSerial() const { + Serial Device::CheckAndUpdateCompletedSerials() { + if (GetCompletedCommandSerial() > mCompletedSerial) { + // sometimes we increase the serials, in which case the completed serial in + // the device base will surpass the completed serial we have in the metal backend, so we + // must update ours when we see that the completed serial from device base has + // increased. + mCompletedSerial = GetCompletedCommandSerial(); + } static_assert(std::is_same::value, ""); return mCompletedSerial.load(); } - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; - } - - void Device::TickImpl() { - Serial completedSerial = GetCompletedCommandSerial(); - - mDynamicUploader->Tick(completedSerial); - mMapTracker->Tick(completedSerial); - - if (mPendingCommands != nil) { + MaybeError Device::TickImpl() { + if (mCommandContext.GetCommands() != nil) { SubmitPendingCommandBuffer(); - } else if (completedSerial == mLastSubmittedSerial) { - // If there's no GPU work in flight we still need to artificially increment the serial - // so that CPU operations waiting on GPU completion can know they don't have to wait. - mCompletedSerial++; - mLastSubmittedSerial++; } + + return {}; } id Device::GetMTLDevice() { return mMtlDevice; } - id Device::GetPendingCommandBuffer() { - if (mPendingCommands == nil) { - mPendingCommands = [mCommandQueue commandBuffer]; - [mPendingCommands retain]; + id Device::GetMTLQueue() { + return mCommandQueue; + } + + CommandRecordingContext* Device::GetPendingCommandContext() { + if (mCommandContext.GetCommands() == nil) { + TRACE_EVENT0(GetPlatform(), General, "[MTLCommandQueue commandBuffer]"); + // The MTLCommandBuffer will be autoreleased by default. + // The autorelease pool may drain before the command buffer is submitted. Retain so it + // stays alive. + mCommandContext = CommandRecordingContext([[mCommandQueue commandBuffer] retain]); } - return mPendingCommands; + return &mCommandContext; } void Device::SubmitPendingCommandBuffer() { - if (mPendingCommands == nil) { + if (mCommandContext.GetCommands() == nil) { return; } - mLastSubmittedSerial++; + IncrementLastSubmittedCommandSerial(); + + // Acquire the pending command buffer, which is retained. It must be released later. + id pendingCommands = mCommandContext.AcquireCommands(); // Replace mLastSubmittedCommands with the mutex held so we avoid races between the // schedule handler and this code. { std::lock_guard lock(mLastSubmittedCommandsMutex); - [mLastSubmittedCommands release]; - mLastSubmittedCommands = mPendingCommands; + mLastSubmittedCommands = pendingCommands; } - // Ok, ObjC blocks are weird. My understanding is that local variables are captured by - // value so this-> works as expected. However it is unclear how members are captured, (are - // they captured using this-> or by value?). To be safe we copy members to local variables - // to ensure they are captured "by value". - - // Free mLastSubmittedCommands as soon as it is scheduled so that it doesn't hold - // references to its resources. Make a local copy of pendingCommands first so it is - // captured "by-value" by the block. - id pendingCommands = mPendingCommands; - - [mPendingCommands addScheduledHandler:^(id) { + [pendingCommands addScheduledHandler:^(id) { // This is DRF because we hold the mutex for mLastSubmittedCommands and pendingCommands // is a local value (and not the member itself). std::lock_guard lock(mLastSubmittedCommandsMutex); if (this->mLastSubmittedCommands == pendingCommands) { - [this->mLastSubmittedCommands release]; this->mLastSubmittedCommands = nil; } }]; // Update the completed serial once the completed handler is fired. Make a local copy of // mLastSubmittedSerial so it is captured by value. - Serial pendingSerial = mLastSubmittedSerial; - [mPendingCommands addCompletedHandler:^(id) { + Serial pendingSerial = GetLastSubmittedCommandSerial(); + // this ObjC block runs on a different thread + [pendingCommands addCompletedHandler:^(id) { + TRACE_EVENT_ASYNC_END0(GetPlatform(), GPUWork, "DeviceMTL::SubmitPendingCommandBuffer", + pendingSerial); ASSERT(pendingSerial > mCompletedSerial.load()); this->mCompletedSerial = pendingSerial; }]; - [mPendingCommands commit]; - mPendingCommands = nil; - } - - MapRequestTracker* Device::GetMapTracker() const { - return mMapTracker.get(); + TRACE_EVENT_ASYNC_BEGIN0(GetPlatform(), GPUWork, "DeviceMTL::SubmitPendingCommandBuffer", + pendingSerial); + [pendingCommands commit]; + [pendingCommands release]; } ResultOrError> Device::CreateStagingBuffer(size_t size) { std::unique_ptr stagingBuffer = std::make_unique(size, this); + DAWN_TRY(stagingBuffer->Initialize()); return std::move(stagingBuffer); } @@ -236,27 +250,91 @@ BufferBase* destination, uint64_t destinationOffset, uint64_t size) { + // Metal validation layers forbid 0-sized copies, assert it is skipped prior to calling + // this function. + ASSERT(size != 0); + + ToBackend(destination) + ->EnsureDataInitializedAsDestination(GetPendingCommandContext(), destinationOffset, + size); + id uploadBuffer = ToBackend(source)->GetBufferHandle(); id buffer = ToBackend(destination)->GetMTLBuffer(); - id commandBuffer = GetPendingCommandBuffer(); - id encoder = [commandBuffer blitCommandEncoder]; - [encoder copyFromBuffer:uploadBuffer - sourceOffset:sourceOffset - toBuffer:buffer - destinationOffset:destinationOffset - size:size]; - [encoder endEncoding]; + [GetPendingCommandContext()->EnsureBlit() copyFromBuffer:uploadBuffer + sourceOffset:sourceOffset + toBuffer:buffer + destinationOffset:destinationOffset + size:size]; + return {}; + } + + MaybeError Device::CopyFromStagingToTexture(StagingBufferBase* source, + const TextureDataLayout& dataLayout, + TextureCopy* dst, + const Extent3D copySize) { + Texture* texture = ToBackend(dst->texture.Get()); + + // This function assumes data is perfectly aligned. Otherwise, it might be necessary + // to split copying to several stages: see ComputeTextureBufferCopySplit. + uint32_t blockSize = dst->texture->GetFormat().blockByteSize; + uint32_t blockWidth = dst->texture->GetFormat().blockWidth; + uint32_t blockHeight = dst->texture->GetFormat().blockHeight; + ASSERT(dataLayout.rowsPerImage == (copySize.height)); + ASSERT(dataLayout.bytesPerRow == (copySize.width) / blockWidth * blockSize); + + // TODO(tommek@google.com): Add tests for this in TextureZeroInitTests. + EnsureDestinationTextureInitialized(texture, *dst, copySize); + + // Metal validation layer requires that if the texture's pixel format is a compressed + // format, the sourceSize must be a multiple of the pixel format's block size or be + // clamped to the edge of the texture if the block extends outside the bounds of a + // texture. + const Extent3D clampedSize = + texture->ClampToMipLevelVirtualSize(dst->mipLevel, dst->origin, copySize); + const uint32_t copyBaseLayer = dst->origin.z; + const uint32_t copyLayerCount = copySize.depth; + const uint64_t bytesPerImage = + dataLayout.rowsPerImage * dataLayout.bytesPerRow / blockHeight; + + uint64_t bufferOffset = dataLayout.offset; + for (uint32_t copyLayer = copyBaseLayer; copyLayer < copyBaseLayer + copyLayerCount; + ++copyLayer) { + [GetPendingCommandContext()->EnsureBlit() + copyFromBuffer:ToBackend(source)->GetBufferHandle() + sourceOffset:bufferOffset + sourceBytesPerRow:dataLayout.bytesPerRow + sourceBytesPerImage:bytesPerImage + sourceSize:MTLSizeMake(clampedSize.width, clampedSize.height, 1) + toTexture:texture->GetMTLTexture() + destinationSlice:copyLayer + destinationLevel:dst->mipLevel + destinationOrigin:MTLOriginMake(dst->origin.x, dst->origin.y, 0)]; + + bufferOffset += bytesPerImage; + } return {}; } - TextureBase* Device::CreateTextureWrappingIOSurface(const TextureDescriptor* descriptor, + TextureBase* Device::CreateTextureWrappingIOSurface(const ExternalImageDescriptor* descriptor, IOSurfaceRef ioSurface, uint32_t plane) { - if (ConsumedError(ValidateTextureDescriptor(this, descriptor))) { + const TextureDescriptor* textureDescriptor = + reinterpret_cast(descriptor->cTextureDescriptor); + + // TODO(dawn:22): Remove once migration from GPUTextureDescriptor.arrayLayerCount to + // GPUTextureDescriptor.size.depth is done. + TextureDescriptor fixedDescriptor; + if (ConsumedError(FixTextureDescriptor(this, textureDescriptor), &fixedDescriptor)) { + return nullptr; + } + textureDescriptor = &fixedDescriptor; + + if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) { return nullptr; } - if (ConsumedError(ValidateIOSurfaceCanBeWrapped(this, descriptor, ioSurface, plane))) { + if (ConsumedError( + ValidateIOSurfaceCanBeWrapped(this, textureDescriptor, ioSurface, plane))) { return nullptr; } @@ -268,4 +346,29 @@ [mLastSubmittedCommands waitUntilScheduled]; } + MaybeError Device::WaitForIdleForDestruction() { + [mCommandContext.AcquireCommands() release]; + CheckPassedSerials(); + + // Wait for all commands to be finished so we can free resources + while (GetCompletedCommandSerial() != GetLastSubmittedCommandSerial()) { + usleep(100); + CheckPassedSerials(); + } + + return {}; + } + + void Device::ShutDownImpl() { + ASSERT(GetState() == State::Disconnected); + + [mCommandContext.AcquireCommands() release]; + + [mCommandQueue release]; + mCommandQueue = nil; + + [mMtlDevice release]; + mMtlDevice = nil; + } + }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/Forward.h b/third_party/dawn/src/dawn_native/metal/Forward.h index 4e889cddec5..9481348f520 100644 --- a/third_party/dawn/src/dawn_native/metal/Forward.h +++ b/third_party/dawn/src/dawn_native/metal/Forward.h @@ -17,22 +17,18 @@ #include "dawn_native/ToBackend.h" -namespace { - class BindGroupBase; - class BindGroup; -} // namespace - namespace dawn_native { namespace metal { class Adapter; - using BindGroup = BindGroupBase; - using BindGroupLayout = BindGroupLayoutBase; + class BindGroup; + class BindGroupLayout; class Buffer; class CommandBuffer; class ComputePipeline; class Device; class Framebuffer; class PipelineLayout; + class QuerySet; class Queue; class RenderPipeline; class Sampler; @@ -51,6 +47,7 @@ namespace dawn_native { namespace metal { using ComputePipelineType = ComputePipeline; using DeviceType = Device; using PipelineLayoutType = PipelineLayout; + using QuerySetType = QuerySet; using QueueType = Queue; using RenderPipelineType = RenderPipeline; using SamplerType = Sampler; diff --git a/third_party/dawn/src/dawn_native/metal/MetalBackend.mm b/third_party/dawn/src/dawn_native/metal/MetalBackend.mm index e5c88673fff..24c44810e91 100644 --- a/third_party/dawn/src/dawn_native/metal/MetalBackend.mm +++ b/third_party/dawn/src/dawn_native/metal/MetalBackend.mm @@ -22,23 +22,24 @@ namespace dawn_native { namespace metal { - id GetMetalDevice(DawnDevice cDevice) { + id GetMetalDevice(WGPUDevice cDevice) { Device* device = reinterpret_cast(cDevice); return device->GetMTLDevice(); } - DawnTexture WrapIOSurface(DawnDevice cDevice, - const DawnTextureDescriptor* cDescriptor, - IOSurfaceRef ioSurface, - uint32_t plane) { + ExternalImageDescriptorIOSurface::ExternalImageDescriptorIOSurface() + : ExternalImageDescriptor(ExternalImageDescriptorType::IOSurface) { + } + + WGPUTexture WrapIOSurface(WGPUDevice cDevice, + const ExternalImageDescriptorIOSurface* cDescriptor) { Device* device = reinterpret_cast(cDevice); - const TextureDescriptor* descriptor = - reinterpret_cast(cDescriptor); - TextureBase* texture = device->CreateTextureWrappingIOSurface(descriptor, ioSurface, plane); - return reinterpret_cast(texture); + TextureBase* texture = device->CreateTextureWrappingIOSurface( + cDescriptor, cDescriptor->ioSurface, cDescriptor->plane); + return reinterpret_cast(texture); } - void WaitForCommandsToBeScheduled(DawnDevice cDevice) { + void WaitForCommandsToBeScheduled(WGPUDevice cDevice) { Device* device = reinterpret_cast(cDevice); device->WaitForCommandsToBeScheduled(); } diff --git a/third_party/dawn/src/dawn_native/metal/PipelineLayoutMTL.h b/third_party/dawn/src/dawn_native/metal/PipelineLayoutMTL.h index 149f375fa3a..b492e3b5827 100644 --- a/third_party/dawn/src/dawn_native/metal/PipelineLayoutMTL.h +++ b/third_party/dawn/src/dawn_native/metal/PipelineLayoutMTL.h @@ -15,6 +15,8 @@ #ifndef DAWNNATIVE_METAL_PIPELINELAYOUTMTL_H_ #define DAWNNATIVE_METAL_PIPELINELAYOUTMTL_H_ +#include "common/ityp_stack_vec.h" +#include "dawn_native/BindingInfo.h" #include "dawn_native/PipelineLayout.h" #include "dawn_native/PerStage.h" @@ -29,16 +31,30 @@ namespace dawn_native { namespace metal { class Device; - class PipelineLayout : public PipelineLayoutBase { + // The number of Metal buffers usable by applications in general + static constexpr size_t kMetalBufferTableSize = 31; + // The Metal buffer slot that Dawn reserves for its own use to pass more data to shaders + static constexpr size_t kBufferLengthBufferSlot = kMetalBufferTableSize - 1; + // The number of Metal buffers Dawn can use in a generic way (i.e. that aren't reserved) + static constexpr size_t kGenericMetalBufferSlots = kMetalBufferTableSize - 1; + + class PipelineLayout final : public PipelineLayoutBase { public: PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor); using BindingIndexInfo = - std::array, kMaxBindGroups>; - const BindingIndexInfo& GetBindingIndexInfo(dawn::ShaderStage stage) const; + ityp::array, + kMaxBindGroups>; + const BindingIndexInfo& GetBindingIndexInfo(SingleShaderStage stage) const; + + // The number of Metal vertex stage buffers used for the whole pipeline layout. + uint32_t GetBufferBindingCount(SingleShaderStage stage); private: + ~PipelineLayout() override = default; PerStage mIndexInfo; + PerStage mBufferBindingCount; }; }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/PipelineLayoutMTL.mm b/third_party/dawn/src/dawn_native/metal/PipelineLayoutMTL.mm index 640fecaa0d1..fa5d9262edf 100644 --- a/third_party/dawn/src/dawn_native/metal/PipelineLayoutMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/PipelineLayoutMTL.mm @@ -24,46 +24,57 @@ : PipelineLayoutBase(device, descriptor) { // Each stage has its own numbering namespace in CompilerMSL. for (auto stage : IterateStages(kAllStages)) { - // Buffer number 0 is reserved for push constants - uint32_t bufferIndex = 1; + uint32_t bufferIndex = 0; uint32_t samplerIndex = 0; uint32_t textureIndex = 0; - for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) { - const auto& groupInfo = GetBindGroupLayout(group)->GetBindingInfo(); - for (size_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) { - if (!(groupInfo.visibilities[binding] & StageBit(stage))) { - continue; - } - if (!groupInfo.mask[binding]) { + for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) { + mIndexInfo[stage][group].resize(GetBindGroupLayout(group)->GetBindingCount()); + + for (BindingIndex bindingIndex{0}; + bindingIndex < GetBindGroupLayout(group)->GetBindingCount(); ++bindingIndex) { + const BindingInfo& bindingInfo = + GetBindGroupLayout(group)->GetBindingInfo(bindingIndex); + if (!(bindingInfo.visibility & StageBit(stage))) { continue; } - switch (groupInfo.types[binding]) { - case dawn::BindingType::UniformBuffer: - case dawn::BindingType::StorageBuffer: - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: - mIndexInfo[stage][group][binding] = bufferIndex; + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + mIndexInfo[stage][group][bindingIndex] = bufferIndex; bufferIndex++; break; - case dawn::BindingType::Sampler: - mIndexInfo[stage][group][binding] = samplerIndex; + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + mIndexInfo[stage][group][bindingIndex] = samplerIndex; samplerIndex++; break; - case dawn::BindingType::SampledTexture: - mIndexInfo[stage][group][binding] = textureIndex; + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + mIndexInfo[stage][group][bindingIndex] = textureIndex; textureIndex++; break; + case wgpu::BindingType::StorageTexture: + UNREACHABLE(); + break; } } } + + mBufferBindingCount[stage] = bufferIndex; } } const PipelineLayout::BindingIndexInfo& PipelineLayout::GetBindingIndexInfo( - dawn::ShaderStage stage) const { + SingleShaderStage stage) const { return mIndexInfo[stage]; } + uint32_t PipelineLayout::GetBufferBindingCount(SingleShaderStage stage) { + return mBufferBindingCount[stage]; + } + }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/QueueMTL.h b/third_party/dawn/src/dawn_native/metal/QueueMTL.h index 79bafb2339f..bda47eb8533 100644 --- a/third_party/dawn/src/dawn_native/metal/QueueMTL.h +++ b/third_party/dawn/src/dawn_native/metal/QueueMTL.h @@ -22,12 +22,17 @@ namespace dawn_native { namespace metal { class CommandBuffer; class Device; - class Queue : public QueueBase { + class Queue final : public QueueBase { public: Queue(Device* device); private: - void SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; + MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; + MaybeError WriteTextureImpl(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) override; }; }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/QueueMTL.mm b/third_party/dawn/src/dawn_native/metal/QueueMTL.mm index ffd82924ed4..c0245b1b63e 100644 --- a/third_party/dawn/src/dawn_native/metal/QueueMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/QueueMTL.mm @@ -14,24 +14,116 @@ #include "dawn_native/metal/QueueMTL.h" +#include "common/Math.h" +#include "dawn_native/Buffer.h" +#include "dawn_native/CommandValidation.h" +#include "dawn_native/Commands.h" +#include "dawn_native/DynamicUploader.h" #include "dawn_native/metal/CommandBufferMTL.h" #include "dawn_native/metal/DeviceMTL.h" +#include "dawn_platform/DawnPlatform.h" +#include "dawn_platform/tracing/TraceEvent.h" namespace dawn_native { namespace metal { + namespace { + ResultOrError UploadTextureDataAligningBytesPerRow( + DeviceBase* device, + const void* data, + size_t dataSize, + uint32_t alignedBytesPerRow, + uint32_t alignedRowsPerImage, + const TextureDataLayout* dataLayout, + const Format& textureFormat, + const Extent3D* writeSize) { + uint32_t newDataSize = ComputeRequiredBytesInCopy( + textureFormat, *writeSize, alignedBytesPerRow, alignedRowsPerImage); + + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( + newDataSize, device->GetPendingCommandSerial())); + ASSERT(uploadHandle.mappedBuffer != nullptr); + + // TODO(tommek@google.com): Add an optimization to do a single memcpy if the data + // is already correctly packed. + uint8_t* dstPointer = static_cast(uploadHandle.mappedBuffer); + const uint8_t* srcPointer = static_cast(data); + srcPointer += dataLayout->offset; + + uint32_t alignedRowsPerImageInBlock = alignedRowsPerImage / textureFormat.blockHeight; + uint32_t dataRowsPerImageInBlock = dataLayout->rowsPerImage / textureFormat.blockHeight; + if (dataRowsPerImageInBlock == 0) { + dataRowsPerImageInBlock = writeSize->height / textureFormat.blockHeight; + } + + ASSERT(dataRowsPerImageInBlock >= alignedRowsPerImageInBlock); + uint64_t imageAdditionalStride = + dataLayout->bytesPerRow * (dataRowsPerImageInBlock - alignedRowsPerImageInBlock); + for (uint32_t d = 0; d < writeSize->depth; ++d) { + for (uint32_t h = 0; h < alignedRowsPerImageInBlock; ++h) { + memcpy(dstPointer, srcPointer, alignedBytesPerRow); + dstPointer += alignedBytesPerRow; + srcPointer += dataLayout->bytesPerRow; + } + srcPointer += imageAdditionalStride; + } + + return uploadHandle; + } + } Queue::Queue(Device* device) : QueueBase(device) { } - void Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { + MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { Device* device = ToBackend(GetDevice()); device->Tick(); - id commandBuffer = device->GetPendingCommandBuffer(); + CommandRecordingContext* commandContext = device->GetPendingCommandContext(); + TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferMTL::FillCommands"); for (uint32_t i = 0; i < commandCount; ++i) { - ToBackend(commands[i])->FillCommands(commandBuffer); + DAWN_TRY(ToBackend(commands[i])->FillCommands(commandContext)); } + TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferMTL::FillCommands"); device->SubmitPendingCommandBuffer(); + return {}; + } + + // We don't write from the CPU to the texture directly which can be done in Metal using the + // replaceRegion function, because the function requires a non-private storage mode and Dawn + // sets the private storage mode by default for all textures except IOSurfaces on macOS. + MaybeError Queue::WriteTextureImpl(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) { + uint32_t blockSize = destination->texture->GetFormat().blockByteSize; + uint32_t blockWidth = destination->texture->GetFormat().blockWidth; + // We are only copying the part of the data that will appear in the texture. + // Note that validating texture copy range ensures that writeSize->width and + // writeSize->height are multiples of blockWidth and blockHeight respectively. + uint32_t alignedBytesPerRow = (writeSize->width) / blockWidth * blockSize; + uint32_t alignedRowsPerImage = writeSize->height; + + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, + UploadTextureDataAligningBytesPerRow( + GetDevice(), data, dataSize, alignedBytesPerRow, alignedRowsPerImage, + dataLayout, destination->texture->GetFormat(), writeSize)); + + TextureDataLayout passDataLayout = *dataLayout; + passDataLayout.offset = uploadHandle.startOffset; + passDataLayout.bytesPerRow = alignedBytesPerRow; + passDataLayout.rowsPerImage = alignedRowsPerImage; + + TextureCopy textureCopy; + textureCopy.texture = destination->texture; + textureCopy.mipLevel = destination->mipLevel; + textureCopy.origin = destination->origin; + + return ToBackend(GetDevice()) + ->CopyFromStagingToTexture(uploadHandle.stagingBuffer, passDataLayout, &textureCopy, + *writeSize); } }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/RenderPipelineMTL.h b/third_party/dawn/src/dawn_native/metal/RenderPipelineMTL.h index a027ffc8062..e27e1c4e771 100644 --- a/third_party/dawn/src/dawn_native/metal/RenderPipelineMTL.h +++ b/third_party/dawn/src/dawn_native/metal/RenderPipelineMTL.h @@ -23,10 +23,10 @@ namespace dawn_native { namespace metal { class Device; - class RenderPipeline : public RenderPipelineBase { + class RenderPipeline final : public RenderPipelineBase { public: - RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor); - ~RenderPipeline(); + static ResultOrError Create(Device* device, + const RenderPipelineDescriptor* descriptor); MTLIndexType GetMTLIndexType() const; MTLPrimitiveType GetMTLPrimitiveTopology() const; @@ -37,7 +37,17 @@ namespace dawn_native { namespace metal { id GetMTLDepthStencilState(); + // For each Dawn vertex buffer, give the index in which it will be positioned in the Metal + // vertex buffer table. + uint32_t GetMtlVertexBufferIndex(uint32_t dawnIndex) const; + + wgpu::ShaderStage GetStagesRequiringStorageBufferLength() const; + private: + ~RenderPipeline() override; + using RenderPipelineBase::RenderPipelineBase; + MaybeError Initialize(const RenderPipelineDescriptor* descriptor); + MTLVertexDescriptor* MakeVertexDesc(); MTLIndexType mMtlIndexType; @@ -46,6 +56,9 @@ namespace dawn_native { namespace metal { MTLCullMode mMtlCullMode; id mMtlRenderPipelineState = nil; id mMtlDepthStencilState = nil; + std::array mMtlVertexBufferIndices; + + wgpu::ShaderStage mStagesRequiringStorageBufferLength = wgpu::ShaderStage::None; }; }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/RenderPipelineMTL.mm b/third_party/dawn/src/dawn_native/metal/RenderPipelineMTL.mm index 7d7fa53f346..9deba399bda 100644 --- a/third_party/dawn/src/dawn_native/metal/RenderPipelineMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/RenderPipelineMTL.mm @@ -23,178 +23,183 @@ namespace dawn_native { namespace metal { namespace { - MTLVertexFormat VertexFormatType(dawn::VertexFormat format) { + MTLVertexFormat VertexFormatType(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar2: + case wgpu::VertexFormat::UChar2: return MTLVertexFormatUChar2; - case dawn::VertexFormat::UChar4: + case wgpu::VertexFormat::UChar4: return MTLVertexFormatUChar4; - case dawn::VertexFormat::Char2: + case wgpu::VertexFormat::Char2: return MTLVertexFormatChar2; - case dawn::VertexFormat::Char4: + case wgpu::VertexFormat::Char4: return MTLVertexFormatChar4; - case dawn::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar2Norm: return MTLVertexFormatUChar2Normalized; - case dawn::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::UChar4Norm: return MTLVertexFormatUChar4Normalized; - case dawn::VertexFormat::Char2Norm: + case wgpu::VertexFormat::Char2Norm: return MTLVertexFormatChar2Normalized; - case dawn::VertexFormat::Char4Norm: + case wgpu::VertexFormat::Char4Norm: return MTLVertexFormatChar4Normalized; - case dawn::VertexFormat::UShort2: + case wgpu::VertexFormat::UShort2: return MTLVertexFormatUShort2; - case dawn::VertexFormat::UShort4: + case wgpu::VertexFormat::UShort4: return MTLVertexFormatUShort4; - case dawn::VertexFormat::Short2: + case wgpu::VertexFormat::Short2: return MTLVertexFormatShort2; - case dawn::VertexFormat::Short4: + case wgpu::VertexFormat::Short4: return MTLVertexFormatShort4; - case dawn::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort2Norm: return MTLVertexFormatUShort2Normalized; - case dawn::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::UShort4Norm: return MTLVertexFormatUShort4Normalized; - case dawn::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Short2Norm: return MTLVertexFormatShort2Normalized; - case dawn::VertexFormat::Short4Norm: + case wgpu::VertexFormat::Short4Norm: return MTLVertexFormatShort4Normalized; - case dawn::VertexFormat::Half2: + case wgpu::VertexFormat::Half2: return MTLVertexFormatHalf2; - case dawn::VertexFormat::Half4: + case wgpu::VertexFormat::Half4: return MTLVertexFormatHalf4; - case dawn::VertexFormat::Float: + case wgpu::VertexFormat::Float: return MTLVertexFormatFloat; - case dawn::VertexFormat::Float2: + case wgpu::VertexFormat::Float2: return MTLVertexFormatFloat2; - case dawn::VertexFormat::Float3: + case wgpu::VertexFormat::Float3: return MTLVertexFormatFloat3; - case dawn::VertexFormat::Float4: + case wgpu::VertexFormat::Float4: return MTLVertexFormatFloat4; - case dawn::VertexFormat::UInt: + case wgpu::VertexFormat::UInt: return MTLVertexFormatUInt; - case dawn::VertexFormat::UInt2: + case wgpu::VertexFormat::UInt2: return MTLVertexFormatUInt2; - case dawn::VertexFormat::UInt3: + case wgpu::VertexFormat::UInt3: return MTLVertexFormatUInt3; - case dawn::VertexFormat::UInt4: + case wgpu::VertexFormat::UInt4: return MTLVertexFormatUInt4; - case dawn::VertexFormat::Int: + case wgpu::VertexFormat::Int: return MTLVertexFormatInt; - case dawn::VertexFormat::Int2: + case wgpu::VertexFormat::Int2: return MTLVertexFormatInt2; - case dawn::VertexFormat::Int3: + case wgpu::VertexFormat::Int3: return MTLVertexFormatInt3; - case dawn::VertexFormat::Int4: + case wgpu::VertexFormat::Int4: return MTLVertexFormatInt4; } } - MTLVertexStepFunction InputStepModeFunction(dawn::InputStepMode mode) { + MTLVertexStepFunction InputStepModeFunction(wgpu::InputStepMode mode) { switch (mode) { - case dawn::InputStepMode::Vertex: + case wgpu::InputStepMode::Vertex: return MTLVertexStepFunctionPerVertex; - case dawn::InputStepMode::Instance: + case wgpu::InputStepMode::Instance: return MTLVertexStepFunctionPerInstance; } } - MTLPrimitiveType MTLPrimitiveTopology(dawn::PrimitiveTopology primitiveTopology) { + MTLPrimitiveType MTLPrimitiveTopology(wgpu::PrimitiveTopology primitiveTopology) { switch (primitiveTopology) { - case dawn::PrimitiveTopology::PointList: + case wgpu::PrimitiveTopology::PointList: return MTLPrimitiveTypePoint; - case dawn::PrimitiveTopology::LineList: + case wgpu::PrimitiveTopology::LineList: return MTLPrimitiveTypeLine; - case dawn::PrimitiveTopology::LineStrip: + case wgpu::PrimitiveTopology::LineStrip: return MTLPrimitiveTypeLineStrip; - case dawn::PrimitiveTopology::TriangleList: + case wgpu::PrimitiveTopology::TriangleList: return MTLPrimitiveTypeTriangle; - case dawn::PrimitiveTopology::TriangleStrip: + case wgpu::PrimitiveTopology::TriangleStrip: return MTLPrimitiveTypeTriangleStrip; } } MTLPrimitiveTopologyClass MTLInputPrimitiveTopology( - dawn::PrimitiveTopology primitiveTopology) { + wgpu::PrimitiveTopology primitiveTopology) { switch (primitiveTopology) { - case dawn::PrimitiveTopology::PointList: + case wgpu::PrimitiveTopology::PointList: return MTLPrimitiveTopologyClassPoint; - case dawn::PrimitiveTopology::LineList: - case dawn::PrimitiveTopology::LineStrip: + case wgpu::PrimitiveTopology::LineList: + case wgpu::PrimitiveTopology::LineStrip: return MTLPrimitiveTopologyClassLine; - case dawn::PrimitiveTopology::TriangleList: - case dawn::PrimitiveTopology::TriangleStrip: + case wgpu::PrimitiveTopology::TriangleList: + case wgpu::PrimitiveTopology::TriangleStrip: return MTLPrimitiveTopologyClassTriangle; } } - MTLIndexType MTLIndexFormat(dawn::IndexFormat format) { + MTLIndexType MTLIndexFormat(wgpu::IndexFormat format) { switch (format) { - case dawn::IndexFormat::Uint16: + case wgpu::IndexFormat::Uint16: return MTLIndexTypeUInt16; - case dawn::IndexFormat::Uint32: + case wgpu::IndexFormat::Uint32: return MTLIndexTypeUInt32; } } - MTLBlendFactor MetalBlendFactor(dawn::BlendFactor factor, bool alpha) { + MTLBlendFactor MetalBlendFactor(wgpu::BlendFactor factor, bool alpha) { switch (factor) { - case dawn::BlendFactor::Zero: + case wgpu::BlendFactor::Zero: return MTLBlendFactorZero; - case dawn::BlendFactor::One: + case wgpu::BlendFactor::One: return MTLBlendFactorOne; - case dawn::BlendFactor::SrcColor: + case wgpu::BlendFactor::SrcColor: return MTLBlendFactorSourceColor; - case dawn::BlendFactor::OneMinusSrcColor: + case wgpu::BlendFactor::OneMinusSrcColor: return MTLBlendFactorOneMinusSourceColor; - case dawn::BlendFactor::SrcAlpha: + case wgpu::BlendFactor::SrcAlpha: return MTLBlendFactorSourceAlpha; - case dawn::BlendFactor::OneMinusSrcAlpha: + case wgpu::BlendFactor::OneMinusSrcAlpha: return MTLBlendFactorOneMinusSourceAlpha; - case dawn::BlendFactor::DstColor: + case wgpu::BlendFactor::DstColor: return MTLBlendFactorDestinationColor; - case dawn::BlendFactor::OneMinusDstColor: + case wgpu::BlendFactor::OneMinusDstColor: return MTLBlendFactorOneMinusDestinationColor; - case dawn::BlendFactor::DstAlpha: + case wgpu::BlendFactor::DstAlpha: return MTLBlendFactorDestinationAlpha; - case dawn::BlendFactor::OneMinusDstAlpha: + case wgpu::BlendFactor::OneMinusDstAlpha: return MTLBlendFactorOneMinusDestinationAlpha; - case dawn::BlendFactor::SrcAlphaSaturated: + case wgpu::BlendFactor::SrcAlphaSaturated: return MTLBlendFactorSourceAlphaSaturated; - case dawn::BlendFactor::BlendColor: + case wgpu::BlendFactor::BlendColor: return alpha ? MTLBlendFactorBlendAlpha : MTLBlendFactorBlendColor; - case dawn::BlendFactor::OneMinusBlendColor: + case wgpu::BlendFactor::OneMinusBlendColor: return alpha ? MTLBlendFactorOneMinusBlendAlpha : MTLBlendFactorOneMinusBlendColor; } } - MTLBlendOperation MetalBlendOperation(dawn::BlendOperation operation) { + MTLBlendOperation MetalBlendOperation(wgpu::BlendOperation operation) { switch (operation) { - case dawn::BlendOperation::Add: + case wgpu::BlendOperation::Add: return MTLBlendOperationAdd; - case dawn::BlendOperation::Subtract: + case wgpu::BlendOperation::Subtract: return MTLBlendOperationSubtract; - case dawn::BlendOperation::ReverseSubtract: + case wgpu::BlendOperation::ReverseSubtract: return MTLBlendOperationReverseSubtract; - case dawn::BlendOperation::Min: + case wgpu::BlendOperation::Min: return MTLBlendOperationMin; - case dawn::BlendOperation::Max: + case wgpu::BlendOperation::Max: return MTLBlendOperationMax; } } - MTLColorWriteMask MetalColorWriteMask(dawn::ColorWriteMask writeMask) { + MTLColorWriteMask MetalColorWriteMask(wgpu::ColorWriteMask writeMask, + bool isDeclaredInFragmentShader) { + if (!isDeclaredInFragmentShader) { + return MTLColorWriteMaskNone; + } + MTLColorWriteMask mask = MTLColorWriteMaskNone; - if (writeMask & dawn::ColorWriteMask::Red) { + if (writeMask & wgpu::ColorWriteMask::Red) { mask |= MTLColorWriteMaskRed; } - if (writeMask & dawn::ColorWriteMask::Green) { + if (writeMask & wgpu::ColorWriteMask::Green) { mask |= MTLColorWriteMaskGreen; } - if (writeMask & dawn::ColorWriteMask::Blue) { + if (writeMask & wgpu::ColorWriteMask::Blue) { mask |= MTLColorWriteMaskBlue; } - if (writeMask & dawn::ColorWriteMask::Alpha) { + if (writeMask & wgpu::ColorWriteMask::Alpha) { mask |= MTLColorWriteMaskAlpha; } @@ -202,7 +207,8 @@ MTLColorWriteMask MetalColorWriteMask(dawn::ColorWriteMask writeMask) { } void ComputeBlendDesc(MTLRenderPipelineColorAttachmentDescriptor* attachment, - const ColorStateDescriptor* descriptor) { + const ColorStateDescriptor* descriptor, + bool isDeclaredInFragmentShader) { attachment.blendingEnabled = BlendEnabled(descriptor); attachment.sourceRGBBlendFactor = MetalBlendFactor(descriptor->colorBlend.srcFactor, false); @@ -214,26 +220,27 @@ void ComputeBlendDesc(MTLRenderPipelineColorAttachmentDescriptor* attachment, attachment.destinationAlphaBlendFactor = MetalBlendFactor(descriptor->alphaBlend.dstFactor, true); attachment.alphaBlendOperation = MetalBlendOperation(descriptor->alphaBlend.operation); - attachment.writeMask = MetalColorWriteMask(descriptor->writeMask); + attachment.writeMask = + MetalColorWriteMask(descriptor->writeMask, isDeclaredInFragmentShader); } - MTLStencilOperation MetalStencilOperation(dawn::StencilOperation stencilOperation) { + MTLStencilOperation MetalStencilOperation(wgpu::StencilOperation stencilOperation) { switch (stencilOperation) { - case dawn::StencilOperation::Keep: + case wgpu::StencilOperation::Keep: return MTLStencilOperationKeep; - case dawn::StencilOperation::Zero: + case wgpu::StencilOperation::Zero: return MTLStencilOperationZero; - case dawn::StencilOperation::Replace: + case wgpu::StencilOperation::Replace: return MTLStencilOperationReplace; - case dawn::StencilOperation::Invert: + case wgpu::StencilOperation::Invert: return MTLStencilOperationInvert; - case dawn::StencilOperation::IncrementClamp: + case wgpu::StencilOperation::IncrementClamp: return MTLStencilOperationIncrementClamp; - case dawn::StencilOperation::DecrementClamp: + case wgpu::StencilOperation::DecrementClamp: return MTLStencilOperationDecrementClamp; - case dawn::StencilOperation::IncrementWrap: + case wgpu::StencilOperation::IncrementWrap: return MTLStencilOperationIncrementWrap; - case dawn::StencilOperation::DecrementWrap: + case wgpu::StencilOperation::DecrementWrap: return MTLStencilOperationDecrementWrap; } } @@ -282,63 +289,90 @@ MTLStencilOperation MetalStencilOperation(dawn::StencilOperation stencilOperatio return mtlDepthStencilDescriptor; } - MTLWinding MTLFrontFace(dawn::FrontFace face) { - // Note that these are inverted because we flip the Y coordinate in the vertex shader + MTLWinding MTLFrontFace(wgpu::FrontFace face) { switch (face) { - case dawn::FrontFace::CW: - return MTLWindingCounterClockwise; - case dawn::FrontFace::CCW: + case wgpu::FrontFace::CW: return MTLWindingClockwise; + case wgpu::FrontFace::CCW: + return MTLWindingCounterClockwise; } } - MTLCullMode ToMTLCullMode(dawn::CullMode mode) { + MTLCullMode ToMTLCullMode(wgpu::CullMode mode) { switch (mode) { - case dawn::CullMode::None: + case wgpu::CullMode::None: return MTLCullModeNone; - case dawn::CullMode::Front: + case wgpu::CullMode::Front: return MTLCullModeFront; - case dawn::CullMode::Back: + case wgpu::CullMode::Back: return MTLCullModeBack; } } } // anonymous namespace - RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor) - : RenderPipelineBase(device, descriptor), - mMtlIndexType(MTLIndexFormat(GetVertexInputDescriptor()->indexFormat)), - mMtlPrimitiveTopology(MTLPrimitiveTopology(GetPrimitiveTopology())), - mMtlFrontFace(MTLFrontFace(GetFrontFace())), - mMtlCullMode(ToMTLCullMode(GetCullMode())) { - auto mtlDevice = device->GetMTLDevice(); + // static + ResultOrError RenderPipeline::Create( + Device* device, + const RenderPipelineDescriptor* descriptor) { + Ref pipeline = AcquireRef(new RenderPipeline(device, descriptor)); + DAWN_TRY(pipeline->Initialize(descriptor)); + return pipeline.Detach(); + } + + MaybeError RenderPipeline::Initialize(const RenderPipelineDescriptor* descriptor) { + mMtlIndexType = MTLIndexFormat(GetVertexStateDescriptor()->indexFormat); + mMtlPrimitiveTopology = MTLPrimitiveTopology(GetPrimitiveTopology()); + mMtlFrontFace = MTLFrontFace(GetFrontFace()); + mMtlCullMode = ToMTLCullMode(GetCullMode()); + auto mtlDevice = ToBackend(GetDevice())->GetMTLDevice(); MTLRenderPipelineDescriptor* descriptorMTL = [MTLRenderPipelineDescriptor new]; - const ShaderModule* vertexModule = ToBackend(descriptor->vertexStage->module); - const char* vertexEntryPoint = descriptor->vertexStage->entryPoint; - ShaderModule::MetalFunctionData vertexData = vertexModule->GetFunction( - vertexEntryPoint, dawn::ShaderStage::Vertex, ToBackend(GetLayout())); + ShaderModule* vertexModule = ToBackend(descriptor->vertexStage.module); + const char* vertexEntryPoint = descriptor->vertexStage.entryPoint; + ShaderModule::MetalFunctionData vertexData; + DAWN_TRY(vertexModule->GetFunction(vertexEntryPoint, SingleShaderStage::Vertex, + ToBackend(GetLayout()), &vertexData)); + descriptorMTL.vertexFunction = vertexData.function; + if (vertexData.needsStorageBufferLength) { + mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Vertex; + } - const ShaderModule* fragmentModule = ToBackend(descriptor->fragmentStage->module); + ShaderModule* fragmentModule = ToBackend(descriptor->fragmentStage->module); const char* fragmentEntryPoint = descriptor->fragmentStage->entryPoint; - ShaderModule::MetalFunctionData fragmentData = fragmentModule->GetFunction( - fragmentEntryPoint, dawn::ShaderStage::Fragment, ToBackend(GetLayout())); + ShaderModule::MetalFunctionData fragmentData; + DAWN_TRY(fragmentModule->GetFunction(fragmentEntryPoint, SingleShaderStage::Fragment, + ToBackend(GetLayout()), &fragmentData)); + descriptorMTL.fragmentFunction = fragmentData.function; + if (fragmentData.needsStorageBufferLength) { + mStagesRequiringStorageBufferLength |= wgpu::ShaderStage::Fragment; + } if (HasDepthStencilAttachment()) { - // TODO(kainino@chromium.org): Handle depth-only and stencil-only formats. - dawn::TextureFormat depthStencilFormat = GetDepthStencilFormat(); - descriptorMTL.depthAttachmentPixelFormat = MetalPixelFormat(depthStencilFormat); - descriptorMTL.stencilAttachmentPixelFormat = MetalPixelFormat(depthStencilFormat); + wgpu::TextureFormat depthStencilFormat = GetDepthStencilFormat(); + const Format& internalFormat = GetDevice()->GetValidInternalFormat(depthStencilFormat); + MTLPixelFormat metalFormat = MetalPixelFormat(depthStencilFormat); + + if (internalFormat.HasDepth()) { + descriptorMTL.depthAttachmentPixelFormat = metalFormat; + } + if (internalFormat.HasStencil()) { + descriptorMTL.stencilAttachmentPixelFormat = metalFormat; + } } + const ShaderModuleBase::FragmentOutputBaseTypes& fragmentOutputBaseTypes = + descriptor->fragmentStage->module->GetFragmentOutputBaseTypes(); for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) { descriptorMTL.colorAttachments[i].pixelFormat = MetalPixelFormat(GetColorAttachmentFormat(i)); const ColorStateDescriptor* descriptor = GetColorStateDescriptor(i); - ComputeBlendDesc(descriptorMTL.colorAttachments[i], descriptor); + bool isDeclaredInFragmentShader = fragmentOutputBaseTypes[i] != Format::Other; + ComputeBlendDesc(descriptorMTL.colorAttachments[i], descriptor, + isDeclaredInFragmentShader); } descriptorMTL.inputPrimitiveTopology = MTLInputPrimitiveTopology(GetPrimitiveTopology()); @@ -349,8 +383,6 @@ MTLCullMode ToMTLCullMode(dawn::CullMode mode) { descriptorMTL.sampleCount = GetSampleCount(); - // TODO(kainino@chromium.org): push constants, textures, samplers - { NSError* error = nil; mMtlRenderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:descriptorMTL @@ -358,8 +390,7 @@ MTLCullMode ToMTLCullMode(dawn::CullMode mode) { [descriptorMTL release]; if (error != nil) { NSLog(@" error => %@", error); - device->HandleError("Error creating rendering pipeline state"); - return; + return DAWN_INTERNAL_ERROR("Error creating rendering pipeline state"); } } @@ -370,6 +401,8 @@ MTLCullMode ToMTLCullMode(dawn::CullMode mode) { MakeDepthStencilDesc(GetDepthStencilStateDescriptor()); mMtlDepthStencilState = [mtlDevice newDepthStencilStateWithDescriptor:depthStencilDesc]; [depthStencilDesc release]; + + return {}; } RenderPipeline::~RenderPipeline() { @@ -401,52 +434,69 @@ MTLCullMode ToMTLCullMode(dawn::CullMode mode) { return mMtlDepthStencilState; } + uint32_t RenderPipeline::GetMtlVertexBufferIndex(uint32_t dawnIndex) const { + ASSERT(dawnIndex < kMaxVertexBuffers); + return mMtlVertexBufferIndices[dawnIndex]; + } + + wgpu::ShaderStage RenderPipeline::GetStagesRequiringStorageBufferLength() const { + return mStagesRequiringStorageBufferLength; + } + MTLVertexDescriptor* RenderPipeline::MakeVertexDesc() { MTLVertexDescriptor* mtlVertexDescriptor = [MTLVertexDescriptor new]; - for (uint32_t i : IterateBitSet(GetAttributesSetMask())) { - const VertexAttributeInfo& info = GetAttribute(i); + // Vertex buffers are packed after all the buffers for the bind groups. + uint32_t mtlVertexBufferIndex = + ToBackend(GetLayout())->GetBufferBindingCount(SingleShaderStage::Vertex); - auto attribDesc = [MTLVertexAttributeDescriptor new]; - attribDesc.format = VertexFormatType(info.format); - attribDesc.offset = info.offset; - attribDesc.bufferIndex = kMaxBindingsPerGroup + info.inputSlot; - mtlVertexDescriptor.attributes[i] = attribDesc; - [attribDesc release]; - } - - for (uint32_t vbInputSlot : IterateBitSet(GetInputsSetMask())) { - const VertexBufferInfo& info = GetInput(vbInputSlot); + for (uint32_t dawnVertexBufferSlot : IterateBitSet(GetVertexBufferSlotsUsed())) { + const VertexBufferInfo& info = GetVertexBuffer(dawnVertexBufferSlot); - auto layoutDesc = [MTLVertexBufferLayoutDescriptor new]; - if (info.stride == 0) { + MTLVertexBufferLayoutDescriptor* layoutDesc = [MTLVertexBufferLayoutDescriptor new]; + if (info.arrayStride == 0) { // For MTLVertexStepFunctionConstant, the stepRate must be 0, - // but the stride must NOT be 0, so we made up it with + // but the arrayStride must NOT be 0, so we made up it with // max(attrib.offset + sizeof(attrib) for each attrib) - size_t max_stride = 0; - for (uint32_t attribIndex : IterateBitSet(GetAttributesSetMask())) { + size_t maxArrayStride = 0; + for (uint32_t attribIndex : IterateBitSet(GetAttributeLocationsUsed())) { const VertexAttributeInfo& attrib = GetAttribute(attribIndex); // Only use the attributes that use the current input - if (attrib.inputSlot != vbInputSlot) { + if (attrib.vertexBufferSlot != dawnVertexBufferSlot) { continue; } - max_stride = std::max(max_stride, - VertexFormatSize(attrib.format) + size_t(attrib.offset)); + maxArrayStride = std::max( + maxArrayStride, VertexFormatSize(attrib.format) + size_t(attrib.offset)); } layoutDesc.stepFunction = MTLVertexStepFunctionConstant; layoutDesc.stepRate = 0; // Metal requires the stride must be a multiple of 4 bytes, align it with next // multiple of 4 if it's not. - layoutDesc.stride = Align(max_stride, 4); + layoutDesc.stride = Align(maxArrayStride, 4); } else { layoutDesc.stepFunction = InputStepModeFunction(info.stepMode); layoutDesc.stepRate = 1; - layoutDesc.stride = info.stride; + layoutDesc.stride = info.arrayStride; } - // TODO(cwallez@chromium.org): make the offset depend on the pipeline layout - mtlVertexDescriptor.layouts[kMaxBindingsPerGroup + vbInputSlot] = layoutDesc; + + mtlVertexDescriptor.layouts[mtlVertexBufferIndex] = layoutDesc; [layoutDesc release]; + + mMtlVertexBufferIndices[dawnVertexBufferSlot] = mtlVertexBufferIndex; + mtlVertexBufferIndex++; } + + for (uint32_t i : IterateBitSet(GetAttributeLocationsUsed())) { + const VertexAttributeInfo& info = GetAttribute(i); + + auto attribDesc = [MTLVertexAttributeDescriptor new]; + attribDesc.format = VertexFormatType(info.format); + attribDesc.offset = info.offset; + attribDesc.bufferIndex = mMtlVertexBufferIndices[info.vertexBufferSlot]; + mtlVertexDescriptor.attributes[i] = attribDesc; + [attribDesc release]; + } + return mtlVertexDescriptor; } diff --git a/third_party/dawn/src/dawn_native/metal/SamplerMTL.h b/third_party/dawn/src/dawn_native/metal/SamplerMTL.h index 776c9356295..31c4ce56411 100644 --- a/third_party/dawn/src/dawn_native/metal/SamplerMTL.h +++ b/third_party/dawn/src/dawn_native/metal/SamplerMTL.h @@ -23,14 +23,16 @@ namespace dawn_native { namespace metal { class Device; - class Sampler : public SamplerBase { + class Sampler final : public SamplerBase { public: - Sampler(Device* device, const SamplerDescriptor* descriptor); - ~Sampler(); + static ResultOrError Create(Device* device, const SamplerDescriptor* descriptor); id GetMTLSamplerState(); private: + Sampler(Device* device, const SamplerDescriptor* descriptor); + ~Sampler() override; + id mMtlSamplerState = nil; }; diff --git a/third_party/dawn/src/dawn_native/metal/SamplerMTL.mm b/third_party/dawn/src/dawn_native/metal/SamplerMTL.mm index eff9c35c45e..eabc73527ac 100644 --- a/third_party/dawn/src/dawn_native/metal/SamplerMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/SamplerMTL.mm @@ -20,36 +20,46 @@ namespace dawn_native { namespace metal { namespace { - MTLSamplerMinMagFilter FilterModeToMinMagFilter(dawn::FilterMode mode) { + MTLSamplerMinMagFilter FilterModeToMinMagFilter(wgpu::FilterMode mode) { switch (mode) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: return MTLSamplerMinMagFilterNearest; - case dawn::FilterMode::Linear: + case wgpu::FilterMode::Linear: return MTLSamplerMinMagFilterLinear; } } - MTLSamplerMipFilter FilterModeToMipFilter(dawn::FilterMode mode) { + MTLSamplerMipFilter FilterModeToMipFilter(wgpu::FilterMode mode) { switch (mode) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: return MTLSamplerMipFilterNearest; - case dawn::FilterMode::Linear: + case wgpu::FilterMode::Linear: return MTLSamplerMipFilterLinear; } } - MTLSamplerAddressMode AddressMode(dawn::AddressMode mode) { + MTLSamplerAddressMode AddressMode(wgpu::AddressMode mode) { switch (mode) { - case dawn::AddressMode::Repeat: + case wgpu::AddressMode::Repeat: return MTLSamplerAddressModeRepeat; - case dawn::AddressMode::MirroredRepeat: + case wgpu::AddressMode::MirrorRepeat: return MTLSamplerAddressModeMirrorRepeat; - case dawn::AddressMode::ClampToEdge: + case wgpu::AddressMode::ClampToEdge: return MTLSamplerAddressModeClampToEdge; } } } + // static + ResultOrError Sampler::Create(Device* device, const SamplerDescriptor* descriptor) { + if (descriptor->compare != wgpu::CompareFunction::Undefined && + device->IsToggleEnabled(Toggle::MetalDisableSamplerCompare)) { + return DAWN_VALIDATION_ERROR("Sampler compare function not supported."); + } + + return new Sampler(device, descriptor); + } + Sampler::Sampler(Device* device, const SamplerDescriptor* descriptor) : SamplerBase(device, descriptor) { MTLSamplerDescriptor* mtlDesc = [MTLSamplerDescriptor new]; @@ -64,7 +74,14 @@ MTLSamplerAddressMode AddressMode(dawn::AddressMode mode) { mtlDesc.lodMinClamp = descriptor->lodMinClamp; mtlDesc.lodMaxClamp = descriptor->lodMaxClamp; - mtlDesc.compareFunction = ToMetalCompareFunction(descriptor->compareFunction); + + if (descriptor->compare != wgpu::CompareFunction::Undefined) { + // Sampler compare is unsupported before A9, which we validate in + // Sampler::Create. + mtlDesc.compareFunction = ToMetalCompareFunction(descriptor->compare); + // The value is default-initialized in the else-case, and we don't set it or the + // Metal debug device errors. + } mMtlSamplerState = [device->GetMTLDevice() newSamplerStateWithDescriptor:mtlDesc]; diff --git a/third_party/dawn/src/dawn_native/metal/ShaderModuleMTL.h b/third_party/dawn/src/dawn_native/metal/ShaderModuleMTL.h index 1e3a7262d90..d4d41abc687 100644 --- a/third_party/dawn/src/dawn_native/metal/ShaderModuleMTL.h +++ b/third_party/dawn/src/dawn_native/metal/ShaderModuleMTL.h @@ -19,6 +19,8 @@ #import +#include "dawn_native/Error.h" + namespace spirv_cross { class CompilerMSL; } @@ -28,26 +30,30 @@ namespace dawn_native { namespace metal { class Device; class PipelineLayout; - class ShaderModule : public ShaderModuleBase { + class ShaderModule final : public ShaderModuleBase { public: - ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor); + static ResultOrError Create(Device* device, + const ShaderModuleDescriptor* descriptor); struct MetalFunctionData { - id function; + id function = nil; MTLSize localWorkgroupSize; + bool needsStorageBufferLength; ~MetalFunctionData() { [function release]; } }; - MetalFunctionData GetFunction(const char* functionName, - dawn::ShaderStage functionStage, - const PipelineLayout* layout) const; + MaybeError GetFunction(const char* functionName, + SingleShaderStage functionStage, + const PipelineLayout* layout, + MetalFunctionData* out); private: - // Calling compile on CompilerMSL somehow changes internal state that makes subsequent - // compiles return invalid MSL. We keep the spirv around and recreate the compiler everytime - // we need to use it. - std::vector mSpirv; + ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor); + ~ShaderModule() override = default; + MaybeError Initialize(); + + shaderc_spvc::CompileOptions GetMSLCompileOptions(); }; }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/ShaderModuleMTL.mm b/third_party/dawn/src/dawn_native/metal/ShaderModuleMTL.mm index 28cff586a82..5983e9ea82d 100644 --- a/third_party/dawn/src/dawn_native/metal/ShaderModuleMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/ShaderModuleMTL.mm @@ -18,7 +18,7 @@ #include "dawn_native/metal/DeviceMTL.h" #include "dawn_native/metal/PipelineLayoutMTL.h" -#include +#include #include @@ -26,94 +26,193 @@ namespace { - spv::ExecutionModel SpirvExecutionModelForStage(dawn::ShaderStage stage) { + spv::ExecutionModel SpirvExecutionModelForStage(SingleShaderStage stage) { switch (stage) { - case dawn::ShaderStage::Vertex: + case SingleShaderStage::Vertex: return spv::ExecutionModelVertex; - case dawn::ShaderStage::Fragment: + case SingleShaderStage::Fragment: return spv::ExecutionModelFragment; - case dawn::ShaderStage::Compute: + case SingleShaderStage::Compute: return spv::ExecutionModelGLCompute; default: UNREACHABLE(); } } + + shaderc_spvc_execution_model ToSpvcExecutionModel(SingleShaderStage stage) { + switch (stage) { + case SingleShaderStage::Vertex: + return shaderc_spvc_execution_model_vertex; + case SingleShaderStage::Fragment: + return shaderc_spvc_execution_model_fragment; + case SingleShaderStage::Compute: + return shaderc_spvc_execution_model_glcompute; + default: + UNREACHABLE(); + return shaderc_spvc_execution_model_invalid; + } + } + } // namespace + + // static + ResultOrError ShaderModule::Create(Device* device, + const ShaderModuleDescriptor* descriptor) { + Ref module = AcquireRef(new ShaderModule(device, descriptor)); + DAWN_TRY(module->Initialize()); + return module.Detach(); } ShaderModule::ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor) : ShaderModuleBase(device, descriptor) { - mSpirv.assign(descriptor->code, descriptor->code + descriptor->codeSize); - spirv_cross::CompilerMSL compiler(mSpirv); - ExtractSpirvInfo(compiler); } - ShaderModule::MetalFunctionData ShaderModule::GetFunction(const char* functionName, - dawn::ShaderStage functionStage, - const PipelineLayout* layout) const { - spirv_cross::CompilerMSL compiler(mSpirv); + MaybeError ShaderModule::Initialize() { + DAWN_TRY(InitializeBase()); + const std::vector& spirv = GetSpirv(); + + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc::CompileOptions options = GetMSLCompileOptions(); + + DAWN_TRY( + CheckSpvcSuccess(mSpvcContext.InitializeForMsl(spirv.data(), spirv.size(), options), + "Unable to initialize instance of spvc")); + + spirv_cross::CompilerMSL* compiler; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetCompiler(reinterpret_cast(&compiler)), + "Unable to get cross compiler")); + DAWN_TRY(ExtractSpirvInfo(*compiler)); + } else { + spirv_cross::CompilerMSL compiler(spirv); + DAWN_TRY(ExtractSpirvInfo(compiler)); + } + return {}; + } + + MaybeError ShaderModule::GetFunction(const char* functionName, + SingleShaderStage functionStage, + const PipelineLayout* layout, + ShaderModule::MetalFunctionData* out) { + ASSERT(!IsError()); + ASSERT(out); + const std::vector& spirv = GetSpirv(); + + std::unique_ptr compilerImpl; + spirv_cross::CompilerMSL* compiler; + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + // Initializing the compiler is needed every call, because this method uses reflection + // to mutate the compiler's IR. + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.InitializeForMsl(spirv.data(), spirv.size(), GetMSLCompileOptions()), + "Unable to initialize instance of spvc")); + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetCompiler(reinterpret_cast(&compiler)), + "Unable to get cross compiler")); + } else { + // If these options are changed, the values in DawnSPIRVCrossMSLFastFuzzer.cpp need to + // be updated. + spirv_cross::CompilerMSL::Options options_msl; - // If these options are changed, the values in DawnSPIRVCrossMSLFastFuzzer.cpp need to be - // updated. - spirv_cross::CompilerGLSL::Options options_glsl; - options_glsl.vertex.flip_vert_y = true; - compiler.spirv_cross::CompilerGLSL::set_common_options(options_glsl); + // Disable PointSize builtin for https://bugs.chromium.org/p/dawn/issues/detail?id=146 + // Because Metal will reject PointSize builtin if the shader is compiled into a render + // pipeline that uses a non-point topology. + // TODO (hao.x.li@intel.com): Remove this once WebGPU requires there is no + // gl_PointSize builtin (https://github.com/gpuweb/gpuweb/issues/332). + options_msl.enable_point_size_builtin = false; + + // Always use vertex buffer 30 (the last one in the vertex buffer table) to contain + // the shader storage buffer lengths. + options_msl.buffer_size_buffer_index = kBufferLengthBufferSlot; + + compilerImpl = std::make_unique(spirv); + compiler = compilerImpl.get(); + compiler->set_msl_options(options_msl); + } // By default SPIRV-Cross will give MSL resources indices in increasing order. // To make the MSL indices match the indices chosen in the PipelineLayout, we build // a table of MSLResourceBinding to give to SPIRV-Cross. - // Reserve index 0 for buffers for the push constants buffer. - for (auto stage : IterateStages(kAllStages)) { - spirv_cross::MSLResourceBinding binding; - binding.stage = SpirvExecutionModelForStage(stage); - binding.desc_set = spirv_cross::kPushConstDescSet; - binding.binding = spirv_cross::kPushConstBinding; - binding.msl_buffer = 0; + // Create one resource binding entry per stage per binding. + for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { + const BindGroupLayoutBase::BindingMap& bindingMap = + layout->GetBindGroupLayout(group)->GetBindingMap(); - compiler.add_msl_resource_binding(binding); - } + for (const auto& it : bindingMap) { + BindingNumber bindingNumber = it.first; + BindingIndex bindingIndex = it.second; - // Create one resource binding entry per stage per binding. - for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { - const auto& bgInfo = layout->GetBindGroupLayout(group)->GetBindingInfo(); - for (uint32_t binding : IterateBitSet(bgInfo.mask)) { - for (auto stage : IterateStages(bgInfo.visibilities[binding])) { - uint32_t index = layout->GetBindingIndexInfo(stage)[group][binding]; - - spirv_cross::MSLResourceBinding mslBinding; - mslBinding.stage = SpirvExecutionModelForStage(stage); - mslBinding.desc_set = group; - mslBinding.binding = binding; - mslBinding.msl_buffer = mslBinding.msl_texture = mslBinding.msl_sampler = index; - - compiler.add_msl_resource_binding(mslBinding); + const BindingInfo& bindingInfo = + layout->GetBindGroupLayout(group)->GetBindingInfo(bindingIndex); + + for (auto stage : IterateStages(bindingInfo.visibility)) { + uint32_t shaderIndex = layout->GetBindingIndexInfo(stage)[group][bindingIndex]; + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc_msl_resource_binding mslBinding; + mslBinding.stage = ToSpvcExecutionModel(stage); + mslBinding.desc_set = static_cast(group); + mslBinding.binding = static_cast(bindingNumber); + mslBinding.msl_buffer = mslBinding.msl_texture = mslBinding.msl_sampler = + shaderIndex; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.AddMSLResourceBinding(mslBinding), + "Unable to add MSL Resource Binding")); + } else { + spirv_cross::MSLResourceBinding mslBinding; + mslBinding.stage = SpirvExecutionModelForStage(stage); + mslBinding.desc_set = static_cast(group); + mslBinding.binding = static_cast(bindingNumber); + mslBinding.msl_buffer = mslBinding.msl_texture = mslBinding.msl_sampler = + shaderIndex; + + compiler->add_msl_resource_binding(mslBinding); + } } } } - MetalFunctionData result; - { - spv::ExecutionModel executionModel = SpirvExecutionModelForStage(functionStage); - auto size = compiler.get_entry_point(functionName, executionModel).workgroup_size; - result.localWorkgroupSize = MTLSizeMake(size.x, size.y, size.z); + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc_execution_model executionModel = ToSpvcExecutionModel(functionStage); + shaderc_spvc_workgroup_size size; + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.GetWorkgroupSize(functionName, executionModel, &size), + "Unable to get workgroup size for shader")); + out->localWorkgroupSize = MTLSizeMake(size.x, size.y, size.z); + } else { + spv::ExecutionModel executionModel = SpirvExecutionModelForStage(functionStage); + auto size = compiler->get_entry_point(functionName, executionModel).workgroup_size; + out->localWorkgroupSize = MTLSizeMake(size.x, size.y, size.z); + } } { // SPIRV-Cross also supports re-ordering attributes but it seems to do the correct thing // by default. - std::string msl = compiler.compile(); - NSString* mslSource = [NSString stringWithFormat:@"%s", msl.c_str()]; - + NSString* mslSource; + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc::CompilationResult result; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.CompileShader(&result), + "Unable to compile MSL shader")); + std::string result_str; + DAWN_TRY(CheckSpvcSuccess(result.GetStringOutput(&result_str), + "Unable to get MSL shader text")); + mslSource = [[NSString alloc] initWithUTF8String:result_str.c_str()]; + } else { + std::string msl = compiler->compile(); + mslSource = [[NSString alloc] initWithUTF8String:msl.c_str()]; + } auto mtlDevice = ToBackend(GetDevice())->GetMTLDevice(); NSError* error = nil; id library = [mtlDevice newLibraryWithSource:mslSource options:nil error:&error]; if (error != nil) { - // TODO(cwallez@chromium.org): forward errors to caller + // TODO(cwallez@chromium.org): Switch that NSLog to use dawn::InfoLog or even be + // folded in the DAWN_VALIDATION_ERROR NSLog(@"MTLDevice newLibraryWithSource => %@", error); + if (error.code != MTLLibraryErrorCompileWarning) { + return DAWN_VALIDATION_ERROR("Unable to create library object"); + } } + // TODO(kainino@chromium.org): make this somehow more robust; it needs to behave like // clean_func_name: // https://github.com/KhronosGroup/SPIRV-Cross/blob/4e915e8c483e319d0dd7a1fa22318bef28f8cca3/spirv_msl.cpp#L1213 @@ -121,12 +220,39 @@ functionName = "main0"; } - NSString* name = [NSString stringWithFormat:@"%s", functionName]; - result.function = [library newFunctionWithName:name]; + NSString* name = [[NSString alloc] initWithUTF8String:functionName]; + out->function = [library newFunctionWithName:name]; [library release]; } - return result; + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + DAWN_TRY( + CheckSpvcSuccess(mSpvcContext.NeedsBufferSizeBuffer(&out->needsStorageBufferLength), + "Unable to determine if shader needs buffer size buffer")); + } else { + out->needsStorageBufferLength = compiler->needs_buffer_size_buffer(); + } + + return {}; + } + + shaderc_spvc::CompileOptions ShaderModule::GetMSLCompileOptions() { + // If these options are changed, the values in DawnSPIRVCrossGLSLFastFuzzer.cpp need to + // be updated. + shaderc_spvc::CompileOptions options = GetCompileOptions(); + + // Disable PointSize builtin for https://bugs.chromium.org/p/dawn/issues/detail?id=146 + // Because Metal will reject PointSize builtin if the shader is compiled into a render + // pipeline that uses a non-point topology. + // TODO (hao.x.li@intel.com): Remove this once WebGPU requires there is no + // gl_PointSize builtin (https://github.com/gpuweb/gpuweb/issues/332). + options.SetMSLEnablePointSizeBuiltIn(false); + + // Always use vertex buffer 30 (the last one in the vertex buffer table) to contain + // the shader storage buffer lengths. + options.SetMSLBufferSizeBufferIndex(kBufferLengthBufferSlot); + + return options; } }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/StagingBufferMTL.h b/third_party/dawn/src/dawn_native/metal/StagingBufferMTL.h index e2d1ecc100a..7e9815b491b 100644 --- a/third_party/dawn/src/dawn_native/metal/StagingBufferMTL.h +++ b/third_party/dawn/src/dawn_native/metal/StagingBufferMTL.h @@ -26,7 +26,7 @@ namespace dawn_native { namespace metal { class StagingBuffer : public StagingBufferBase { public: StagingBuffer(size_t size, Device* device); - ~StagingBuffer(); + ~StagingBuffer() override; id GetBufferHandle() const; diff --git a/third_party/dawn/src/dawn_native/metal/StagingBufferMTL.mm b/third_party/dawn/src/dawn_native/metal/StagingBufferMTL.mm index f03a7691c5e..390f00cfcfa 100644 --- a/third_party/dawn/src/dawn_native/metal/StagingBufferMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/StagingBufferMTL.mm @@ -25,7 +25,16 @@ const size_t bufferSize = GetSize(); mBuffer = [mDevice->GetMTLDevice() newBufferWithLength:bufferSize options:MTLResourceStorageModeShared]; + + if (mBuffer == nil) { + return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer."); + } + mMappedPointer = [mBuffer contents]; + if (mMappedPointer == nullptr) { + return DAWN_INTERNAL_ERROR("Unable to map staging buffer."); + } + return {}; } @@ -38,4 +47,4 @@ return mBuffer; } -}} // namespace dawn_native::metal \ No newline at end of file +}} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/SwapChainMTL.h b/third_party/dawn/src/dawn_native/metal/SwapChainMTL.h index 063add6d7a6..19abc7facdf 100644 --- a/third_party/dawn/src/dawn_native/metal/SwapChainMTL.h +++ b/third_party/dawn/src/dawn_native/metal/SwapChainMTL.h @@ -17,18 +17,42 @@ #include "dawn_native/SwapChain.h" +@class CAMetalLayer; +@protocol CAMetalDrawable; + namespace dawn_native { namespace metal { class Device; + class Texture; - class SwapChain : public SwapChainBase { + class OldSwapChain final : public OldSwapChainBase { public: - SwapChain(Device* device, const SwapChainDescriptor* descriptor); - ~SwapChain(); + OldSwapChain(Device* device, const SwapChainDescriptor* descriptor); protected: + ~OldSwapChain() override; TextureBase* GetNextTextureImpl(const TextureDescriptor* descriptor) override; - void OnBeforePresent(TextureBase* texture) override; + MaybeError OnBeforePresent(TextureViewBase* view) override; + }; + + class SwapChain final : public NewSwapChainBase { + public: + SwapChain(Device* device, + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor); + + private: + ~SwapChain() override; + + CAMetalLayer* mLayer = nullptr; + + id mCurrentDrawable = nil; + Ref mTexture; + + MaybeError PresentImpl() override; + ResultOrError GetCurrentTextureViewImpl() override; + void DetachFromSurfaceImpl() override; }; }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/SwapChainMTL.mm b/third_party/dawn/src/dawn_native/metal/SwapChainMTL.mm index 094e35ac79e..f581da98354 100644 --- a/third_party/dawn/src/dawn_native/metal/SwapChainMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/SwapChainMTL.mm @@ -14,30 +14,36 @@ #include "dawn_native/metal/SwapChainMTL.h" +#include "dawn_native/Surface.h" #include "dawn_native/metal/DeviceMTL.h" #include "dawn_native/metal/TextureMTL.h" #include +#import + namespace dawn_native { namespace metal { - SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor) - : SwapChainBase(device, descriptor) { + // OldSwapChain + + OldSwapChain::OldSwapChain(Device* device, const SwapChainDescriptor* descriptor) + : OldSwapChainBase(device, descriptor) { const auto& im = GetImplementation(); DawnWSIContextMetal wsiContext = {}; wsiContext.device = ToBackend(GetDevice())->GetMTLDevice(); + wsiContext.queue = ToBackend(GetDevice())->GetMTLQueue(); im.Init(im.userData, &wsiContext); } - SwapChain::~SwapChain() { + OldSwapChain::~OldSwapChain() { } - TextureBase* SwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor) { + TextureBase* OldSwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor) { const auto& im = GetImplementation(); DawnSwapChainNextTexture next = {}; DawnSwapChainError error = im.GetNextTexture(im.userData, &next); if (error) { - GetDevice()->HandleError(error); + GetDevice()->HandleError(InternalErrorType::Internal, error); return nullptr; } @@ -45,7 +51,88 @@ return new Texture(ToBackend(GetDevice()), descriptor, nativeTexture); } - void SwapChain::OnBeforePresent(TextureBase*) { + MaybeError OldSwapChain::OnBeforePresent(TextureViewBase*) { + return {}; + } + + // SwapChain + + SwapChain::SwapChain(Device* device, + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) + : NewSwapChainBase(device, surface, descriptor) { + ASSERT(surface->GetType() == Surface::Type::MetalLayer); + + if (previousSwapChain != nullptr) { + // TODO(cwallez@chromium.org): figure out what should happen when surfaces are used by + // multiple backends one after the other. It probably needs to block until the backend + // and GPU are completely finished with the previous swapchain. + ASSERT(previousSwapChain->GetBackendType() == wgpu::BackendType::Metal); + previousSwapChain->DetachFromSurface(); + } + + mLayer = static_cast(surface->GetMetalLayer()); + ASSERT(mLayer != nullptr); + + CGSize size = {}; + size.width = GetWidth(); + size.height = GetHeight(); + [mLayer setDrawableSize:size]; + + [mLayer setFramebufferOnly:(GetUsage() == wgpu::TextureUsage::OutputAttachment)]; + [mLayer setDevice:ToBackend(GetDevice())->GetMTLDevice()]; + [mLayer setPixelFormat:MetalPixelFormat(GetFormat())]; + +#if defined(DAWN_PLATFORM_MACOS) + if (@available(macos 10.13, *)) { + [mLayer setDisplaySyncEnabled:(GetPresentMode() != wgpu::PresentMode::Immediate)]; + } +#endif // defined(DAWN_PLATFORM_MACOS) + + // There is no way to control Fifo vs. Mailbox in Metal. + } + + SwapChain::~SwapChain() { + DetachFromSurface(); + } + + MaybeError SwapChain::PresentImpl() { + ASSERT(mCurrentDrawable != nil); + [mCurrentDrawable present]; + + mTexture->Destroy(); + mTexture = nullptr; + + [mCurrentDrawable release]; + mCurrentDrawable = nil; + + return {}; + } + + ResultOrError SwapChain::GetCurrentTextureViewImpl() { + ASSERT(mCurrentDrawable == nil); + mCurrentDrawable = [mLayer nextDrawable]; + [mCurrentDrawable retain]; + + TextureDescriptor textureDesc = GetSwapChainBaseTextureDescriptor(this); + + // mTexture will add a reference to mCurrentDrawable.texture to keep it alive. + mTexture = + AcquireRef(new Texture(ToBackend(GetDevice()), &textureDesc, mCurrentDrawable.texture)); + return mTexture->CreateView(nullptr); + } + + void SwapChain::DetachFromSurfaceImpl() { + ASSERT((mTexture.Get() == nullptr) == (mCurrentDrawable == nil)); + + if (mTexture.Get() != nullptr) { + mTexture->Destroy(); + mTexture = nullptr; + + [mCurrentDrawable release]; + mCurrentDrawable = nil; + } } }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/TextureMTL.h b/third_party/dawn/src/dawn_native/metal/TextureMTL.h index d4feb5bfc47..7ace3963a0a 100644 --- a/third_party/dawn/src/dawn_native/metal/TextureMTL.h +++ b/third_party/dawn/src/dawn_native/metal/TextureMTL.h @@ -17,44 +17,52 @@ #include "dawn_native/Texture.h" +#include #import +#include "dawn_native/DawnNative.h" namespace dawn_native { namespace metal { class Device; - MTLPixelFormat MetalPixelFormat(dawn::TextureFormat format); + MTLPixelFormat MetalPixelFormat(wgpu::TextureFormat format); MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase* device, const TextureDescriptor* descriptor, IOSurfaceRef ioSurface, uint32_t plane); - class Texture : public TextureBase { + class Texture final : public TextureBase { public: Texture(Device* device, const TextureDescriptor* descriptor); Texture(Device* device, const TextureDescriptor* descriptor, id mtlTexture); Texture(Device* device, - const TextureDescriptor* descriptor, + const ExternalImageDescriptor* descriptor, IOSurfaceRef ioSurface, uint32_t plane); - ~Texture(); id GetMTLTexture(); + void EnsureSubresourceContentInitialized(const SubresourceRange& range); + private: + ~Texture() override; + void DestroyImpl() override; + MaybeError ClearTexture(const SubresourceRange& range, TextureBase::ClearValue clearValue); + id mMtlTexture = nil; }; - class TextureView : public TextureViewBase { + class TextureView final : public TextureViewBase { public: TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor); - ~TextureView(); id GetMTLTexture(); private: + ~TextureView() override; + id mMtlTextureView = nil; }; diff --git a/third_party/dawn/src/dawn_native/metal/TextureMTL.mm b/third_party/dawn/src/dawn_native/metal/TextureMTL.mm index 141ba20e173..454a14b2739 100644 --- a/third_party/dawn/src/dawn_native/metal/TextureMTL.mm +++ b/third_party/dawn/src/dawn_native/metal/TextureMTL.mm @@ -14,31 +14,36 @@ #include "dawn_native/metal/TextureMTL.h" +#include "common/Constants.h" +#include "common/Math.h" +#include "common/Platform.h" +#include "dawn_native/DynamicUploader.h" #include "dawn_native/metal/DeviceMTL.h" +#include "dawn_native/metal/StagingBufferMTL.h" -#include +#include namespace dawn_native { namespace metal { namespace { - bool UsageNeedsTextureView(dawn::TextureUsageBit usage) { - constexpr dawn::TextureUsageBit kUsageNeedsTextureView = - dawn::TextureUsageBit::Storage | dawn::TextureUsageBit::Sampled; + bool UsageNeedsTextureView(wgpu::TextureUsage usage) { + constexpr wgpu::TextureUsage kUsageNeedsTextureView = + wgpu::TextureUsage::Storage | wgpu::TextureUsage::Sampled; return usage & kUsageNeedsTextureView; } - MTLTextureUsage MetalTextureUsage(dawn::TextureUsageBit usage) { + MTLTextureUsage MetalTextureUsage(wgpu::TextureUsage usage) { MTLTextureUsage result = MTLTextureUsageUnknown; // This is 0 - if (usage & (dawn::TextureUsageBit::Storage)) { + if (usage & (wgpu::TextureUsage::Storage)) { result |= MTLTextureUsageShaderWrite | MTLTextureUsageShaderRead; } - if (usage & (dawn::TextureUsageBit::Sampled)) { + if (usage & (wgpu::TextureUsage::Sampled)) { result |= MTLTextureUsageShaderRead; } - if (usage & (dawn::TextureUsageBit::OutputAttachment)) { + if (usage & (wgpu::TextureUsage::OutputAttachment)) { result |= MTLTextureUsageRenderTarget; } @@ -49,30 +54,16 @@ MTLTextureUsage MetalTextureUsage(dawn::TextureUsageBit usage) { return result; } - MTLTextureType MetalTextureType(dawn::TextureDimension dimension, - unsigned int arrayLayers, - unsigned int sampleCount) { - switch (dimension) { - case dawn::TextureDimension::e2D: - if (sampleCount > 1) { - ASSERT(arrayLayers == 1); - return MTLTextureType2DMultisample; - } else { - return (arrayLayers > 1) ? MTLTextureType2DArray : MTLTextureType2D; - } - } - } - - MTLTextureType MetalTextureViewType(dawn::TextureViewDimension dimension, + MTLTextureType MetalTextureViewType(wgpu::TextureViewDimension dimension, unsigned int sampleCount) { switch (dimension) { - case dawn::TextureViewDimension::e2D: + case wgpu::TextureViewDimension::e2D: return (sampleCount > 1) ? MTLTextureType2DMultisample : MTLTextureType2D; - case dawn::TextureViewDimension::e2DArray: + case wgpu::TextureViewDimension::e2DArray: return MTLTextureType2DArray; - case dawn::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::Cube: return MTLTextureTypeCube; - case dawn::TextureViewDimension::CubeArray: + case wgpu::TextureViewDimension::CubeArray: return MTLTextureTypeCubeArray; default: UNREACHABLE(); @@ -82,7 +73,7 @@ MTLTextureType MetalTextureViewType(dawn::TextureViewDimension dimension, bool RequiresCreatingNewTextureView(const TextureBase* texture, const TextureViewDescriptor* textureViewDescriptor) { - if (texture->GetFormat() != textureViewDescriptor->format) { + if (texture->GetFormat().format != textureViewDescriptor->format) { return true; } @@ -95,8 +86,8 @@ bool RequiresCreatingNewTextureView(const TextureBase* texture, } switch (textureViewDescriptor->dimension) { - case dawn::TextureViewDimension::Cube: - case dawn::TextureViewDimension::CubeArray: + case wgpu::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::CubeArray: return true; default: break; @@ -105,41 +96,147 @@ bool RequiresCreatingNewTextureView(const TextureBase* texture, return false; } - ResultOrError GetFormatEquivalentToIOSurfaceFormat(uint32_t format) { + ResultOrError GetFormatEquivalentToIOSurfaceFormat(uint32_t format) { switch (format) { - case 'BGRA': - return dawn::TextureFormat::B8G8R8A8Unorm; - case '2C08': - return dawn::TextureFormat::R8G8Unorm; - case 'L008': - return dawn::TextureFormat::R8Unorm; + case kCVPixelFormatType_32RGBA: + return wgpu::TextureFormat::RGBA8Unorm; + case kCVPixelFormatType_32BGRA: + return wgpu::TextureFormat::BGRA8Unorm; + case kCVPixelFormatType_TwoComponent8: + return wgpu::TextureFormat::RG8Unorm; + case kCVPixelFormatType_OneComponent8: + return wgpu::TextureFormat::R8Unorm; default: return DAWN_VALIDATION_ERROR("Unsupported IOSurface format"); } } + +#if defined(DAWN_PLATFORM_MACOS) + MTLStorageMode kIOSurfaceStorageMode = MTLStorageModeManaged; +#elif defined(DAWN_PLATFORM_IOS) + MTLStorageMode kIOSurfaceStorageMode = MTLStorageModePrivate; +#else +# error "Unsupported Apple platform." +#endif } - MTLPixelFormat MetalPixelFormat(dawn::TextureFormat format) { + MTLPixelFormat MetalPixelFormat(wgpu::TextureFormat format) { switch (format) { - case dawn::TextureFormat::R8G8B8A8Unorm: - return MTLPixelFormatRGBA8Unorm; - case dawn::TextureFormat::R8G8Unorm: - return MTLPixelFormatRG8Unorm; - case dawn::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Unorm: return MTLPixelFormatR8Unorm; - case dawn::TextureFormat::R8G8B8A8Uint: - return MTLPixelFormatRGBA8Uint; - case dawn::TextureFormat::R8G8Uint: - return MTLPixelFormatRG8Uint; - case dawn::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Snorm: + return MTLPixelFormatR8Snorm; + case wgpu::TextureFormat::R8Uint: return MTLPixelFormatR8Uint; - case dawn::TextureFormat::B8G8R8A8Unorm: + case wgpu::TextureFormat::R8Sint: + return MTLPixelFormatR8Sint; + + case wgpu::TextureFormat::R16Uint: + return MTLPixelFormatR16Uint; + case wgpu::TextureFormat::R16Sint: + return MTLPixelFormatR16Sint; + case wgpu::TextureFormat::R16Float: + return MTLPixelFormatR16Float; + case wgpu::TextureFormat::RG8Unorm: + return MTLPixelFormatRG8Unorm; + case wgpu::TextureFormat::RG8Snorm: + return MTLPixelFormatRG8Snorm; + case wgpu::TextureFormat::RG8Uint: + return MTLPixelFormatRG8Uint; + case wgpu::TextureFormat::RG8Sint: + return MTLPixelFormatRG8Sint; + + case wgpu::TextureFormat::R32Uint: + return MTLPixelFormatR32Uint; + case wgpu::TextureFormat::R32Sint: + return MTLPixelFormatR32Sint; + case wgpu::TextureFormat::R32Float: + return MTLPixelFormatR32Float; + case wgpu::TextureFormat::RG16Uint: + return MTLPixelFormatRG16Uint; + case wgpu::TextureFormat::RG16Sint: + return MTLPixelFormatRG16Sint; + case wgpu::TextureFormat::RG16Float: + return MTLPixelFormatRG16Float; + case wgpu::TextureFormat::RGBA8Unorm: + return MTLPixelFormatRGBA8Unorm; + case wgpu::TextureFormat::RGBA8UnormSrgb: + return MTLPixelFormatRGBA8Unorm_sRGB; + case wgpu::TextureFormat::RGBA8Snorm: + return MTLPixelFormatRGBA8Snorm; + case wgpu::TextureFormat::RGBA8Uint: + return MTLPixelFormatRGBA8Uint; + case wgpu::TextureFormat::RGBA8Sint: + return MTLPixelFormatRGBA8Sint; + case wgpu::TextureFormat::BGRA8Unorm: return MTLPixelFormatBGRA8Unorm; - case dawn::TextureFormat::D32FloatS8Uint: + case wgpu::TextureFormat::BGRA8UnormSrgb: + return MTLPixelFormatBGRA8Unorm_sRGB; + case wgpu::TextureFormat::RGB10A2Unorm: + return MTLPixelFormatRGB10A2Unorm; + case wgpu::TextureFormat::RG11B10Float: + return MTLPixelFormatRG11B10Float; + + case wgpu::TextureFormat::RG32Uint: + return MTLPixelFormatRG32Uint; + case wgpu::TextureFormat::RG32Sint: + return MTLPixelFormatRG32Sint; + case wgpu::TextureFormat::RG32Float: + return MTLPixelFormatRG32Float; + case wgpu::TextureFormat::RGBA16Uint: + return MTLPixelFormatRGBA16Uint; + case wgpu::TextureFormat::RGBA16Sint: + return MTLPixelFormatRGBA16Sint; + case wgpu::TextureFormat::RGBA16Float: + return MTLPixelFormatRGBA16Float; + + case wgpu::TextureFormat::RGBA32Uint: + return MTLPixelFormatRGBA32Uint; + case wgpu::TextureFormat::RGBA32Sint: + return MTLPixelFormatRGBA32Sint; + case wgpu::TextureFormat::RGBA32Float: + return MTLPixelFormatRGBA32Float; + + case wgpu::TextureFormat::Depth32Float: + return MTLPixelFormatDepth32Float; + case wgpu::TextureFormat::Depth24Plus: + return MTLPixelFormatDepth32Float; + case wgpu::TextureFormat::Depth24PlusStencil8: return MTLPixelFormatDepth32Float_Stencil8; + +#if defined(DAWN_PLATFORM_MACOS) + case wgpu::TextureFormat::BC1RGBAUnorm: + return MTLPixelFormatBC1_RGBA; + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + return MTLPixelFormatBC1_RGBA_sRGB; + case wgpu::TextureFormat::BC2RGBAUnorm: + return MTLPixelFormatBC2_RGBA; + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + return MTLPixelFormatBC2_RGBA_sRGB; + case wgpu::TextureFormat::BC3RGBAUnorm: + return MTLPixelFormatBC3_RGBA; + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + return MTLPixelFormatBC3_RGBA_sRGB; + case wgpu::TextureFormat::BC4RSnorm: + return MTLPixelFormatBC4_RSnorm; + case wgpu::TextureFormat::BC4RUnorm: + return MTLPixelFormatBC4_RUnorm; + case wgpu::TextureFormat::BC5RGSnorm: + return MTLPixelFormatBC5_RGSnorm; + case wgpu::TextureFormat::BC5RGUnorm: + return MTLPixelFormatBC5_RGUnorm; + case wgpu::TextureFormat::BC6HRGBSfloat: + return MTLPixelFormatBC6H_RGBFloat; + case wgpu::TextureFormat::BC6HRGBUfloat: + return MTLPixelFormatBC6H_RGBUfloat; + case wgpu::TextureFormat::BC7RGBAUnorm: + return MTLPixelFormatBC7_RGBAUnorm; + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return MTLPixelFormatBC7_RGBAUnorm_sRGB; +#endif + default: UNREACHABLE(); - return MTLPixelFormatRGBA8Unorm; } } @@ -154,7 +251,7 @@ MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*, return DAWN_VALIDATION_ERROR("IOSurface plane doesn't exist"); } - if (descriptor->dimension != dawn::TextureDimension::e2D) { + if (descriptor->dimension != wgpu::TextureDimension::e2D) { return DAWN_VALIDATION_ERROR("IOSurface texture must be 2D"); } @@ -162,7 +259,7 @@ MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*, return DAWN_VALIDATION_ERROR("IOSurface mip level count must be 1"); } - if (descriptor->arrayLayerCount != 1) { + if (descriptor->size.depth != 1) { return DAWN_VALIDATION_ERROR("IOSurface array layer count must be 1"); } @@ -176,7 +273,7 @@ MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*, return DAWN_VALIDATION_ERROR("IOSurface size doesn't match descriptor"); } - dawn::TextureFormat ioSurfaceFormat; + wgpu::TextureFormat ioSurfaceFormat; DAWN_TRY_ASSIGN(ioSurfaceFormat, GetFormatEquivalentToIOSurfaceFormat(IOSurfaceGetPixelFormat(ioSurface))); if (descriptor->format != ioSurfaceFormat) { @@ -188,20 +285,38 @@ MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*, MTLTextureDescriptor* CreateMetalTextureDescriptor(const TextureDescriptor* descriptor) { MTLTextureDescriptor* mtlDesc = [MTLTextureDescriptor new]; - mtlDesc.textureType = MetalTextureType(descriptor->dimension, descriptor->arrayLayerCount, - descriptor->sampleCount); - mtlDesc.usage = MetalTextureUsage(descriptor->usage); - mtlDesc.pixelFormat = MetalPixelFormat(descriptor->format); mtlDesc.width = descriptor->size.width; mtlDesc.height = descriptor->size.height; - mtlDesc.depth = descriptor->size.depth; - + mtlDesc.sampleCount = descriptor->sampleCount; + mtlDesc.usage = MetalTextureUsage(descriptor->usage); + mtlDesc.pixelFormat = MetalPixelFormat(descriptor->format); mtlDesc.mipmapLevelCount = descriptor->mipLevelCount; - mtlDesc.arrayLength = descriptor->arrayLayerCount; mtlDesc.storageMode = MTLStorageModePrivate; - mtlDesc.sampleCount = descriptor->sampleCount; + // Choose the correct MTLTextureType and paper over differences in how the array layer count + // is specified. + mtlDesc.depth = descriptor->size.depth; + mtlDesc.arrayLength = 1; + switch (descriptor->dimension) { + case wgpu::TextureDimension::e2D: + if (mtlDesc.depth > 1) { + ASSERT(mtlDesc.sampleCount == 1); + mtlDesc.textureType = MTLTextureType2DArray; + mtlDesc.arrayLength = mtlDesc.depth; + mtlDesc.depth = 1; + } else { + if (mtlDesc.sampleCount > 1) { + mtlDesc.textureType = MTLTextureType2DMultisample; + } else { + mtlDesc.textureType = MTLTextureType2D; + } + } + break; + + default: + UNREACHABLE(); + } return mtlDesc; } @@ -211,6 +326,11 @@ MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*, MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor); mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc]; [mtlDesc release]; + + if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { + device->ConsumedError( + ClearTexture(GetAllSubresources(), TextureBase::ClearValue::NonZero)); + } } Texture::Texture(Device* device, const TextureDescriptor* descriptor, id mtlTexture) @@ -219,16 +339,21 @@ MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*, } Texture::Texture(Device* device, - const TextureDescriptor* descriptor, + const ExternalImageDescriptor* descriptor, IOSurfaceRef ioSurface, uint32_t plane) - : TextureBase(device, descriptor, TextureState::OwnedInternal) { - MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor(descriptor); - mtlDesc.storageMode = MTLStorageModeManaged; + : TextureBase(device, + reinterpret_cast(descriptor->cTextureDescriptor), + TextureState::OwnedInternal) { + MTLTextureDescriptor* mtlDesc = CreateMetalTextureDescriptor( + reinterpret_cast(descriptor->cTextureDescriptor)); + mtlDesc.storageMode = kIOSurfaceStorageMode; mMtlTexture = [device->GetMTLDevice() newTextureWithDescriptor:mtlDesc iosurface:ioSurface plane:plane]; [mtlDesc release]; + + SetIsSubresourceContentInitialized(descriptor->isCleared, {0, 1, 0, 1}); } Texture::~Texture() { @@ -236,14 +361,206 @@ MaybeError ValidateIOSurfaceCanBeWrapped(const DeviceBase*, } void Texture::DestroyImpl() { - [mMtlTexture release]; - mMtlTexture = nil; + if (GetTextureState() == TextureState::OwnedInternal) { + [mMtlTexture release]; + mMtlTexture = nil; + } } id Texture::GetMTLTexture() { return mMtlTexture; } + MaybeError Texture::ClearTexture(const SubresourceRange& range, + TextureBase::ClearValue clearValue) { + Device* device = ToBackend(GetDevice()); + + CommandRecordingContext* commandContext = device->GetPendingCommandContext(); + + const uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1; + const double dClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.0 : 1.0; + + if ((GetUsage() & wgpu::TextureUsage::OutputAttachment) != 0) { + ASSERT(GetFormat().isRenderable); + + // End the blit encoder if it is open. + commandContext->EndBlit(); + + if (GetFormat().HasDepthOrStencil()) { + // Create a render pass to clear each subresource. + for (uint32_t level = range.baseMipLevel; + level < range.baseMipLevel + range.levelCount; ++level) { + for (uint32_t arrayLayer = range.baseArrayLayer; + arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, arrayLayer))) { + // Skip lazy clears if already initialized. + continue; + } + + MTLRenderPassDescriptor* descriptor = + [MTLRenderPassDescriptor renderPassDescriptor]; + + if (GetFormat().HasDepth()) { + descriptor.depthAttachment.texture = GetMTLTexture(); + descriptor.depthAttachment.loadAction = MTLLoadActionClear; + descriptor.depthAttachment.storeAction = MTLStoreActionStore; + descriptor.depthAttachment.clearDepth = dClearColor; + } + if (GetFormat().HasStencil()) { + descriptor.stencilAttachment.texture = GetMTLTexture(); + descriptor.stencilAttachment.loadAction = MTLLoadActionClear; + descriptor.stencilAttachment.storeAction = MTLStoreActionStore; + descriptor.stencilAttachment.clearStencil = + static_cast(clearColor); + } + + commandContext->BeginRender(descriptor); + commandContext->EndRender(); + } + } + } else { + ASSERT(GetFormat().IsColor()); + for (uint32_t level = range.baseMipLevel; + level < range.baseMipLevel + range.levelCount; ++level) { + // Create multiple render passes with each subresource as a color attachment to + // clear them all. Only do this for array layers to ensure all attachments have + // the same size. + MTLRenderPassDescriptor* descriptor = nil; + uint32_t attachment = 0; + + for (uint32_t arrayLayer = range.baseArrayLayer; + arrayLayer < range.baseArrayLayer + range.layerCount; arrayLayer++) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, arrayLayer))) { + // Skip lazy clears if already initialized. + continue; + } + + if (descriptor == nil) { + descriptor = [MTLRenderPassDescriptor renderPassDescriptor]; + } + + descriptor.colorAttachments[attachment].texture = GetMTLTexture(); + descriptor.colorAttachments[attachment].loadAction = MTLLoadActionClear; + descriptor.colorAttachments[attachment].storeAction = MTLStoreActionStore; + descriptor.colorAttachments[attachment].clearColor = + MTLClearColorMake(dClearColor, dClearColor, dClearColor, dClearColor); + descriptor.colorAttachments[attachment].level = level; + descriptor.colorAttachments[attachment].slice = arrayLayer; + + attachment++; + + if (attachment == kMaxColorAttachments) { + attachment = 0; + commandContext->BeginRender(descriptor); + commandContext->EndRender(); + descriptor = nil; + } + } + + if (descriptor != nil) { + commandContext->BeginRender(descriptor); + commandContext->EndRender(); + } + } + } + } else { + // Compute the buffer size big enough to fill the largest mip. + Extent3D largestMipSize = GetMipLevelVirtualSize(range.baseMipLevel); + + // Metal validation layers: sourceBytesPerRow must be at least 64. + uint32_t largestMipBytesPerRow = std::max( + (largestMipSize.width / GetFormat().blockWidth) * GetFormat().blockByteSize, 64u); + + // Metal validation layers: sourceBytesPerImage must be at least 512. + uint64_t largestMipBytesPerImage = + std::max(static_cast(largestMipBytesPerRow) * + (largestMipSize.height / GetFormat().blockHeight), + 512llu); + + // TODO(enga): Multiply by largestMipSize.depth and do a larger 3D copy to clear a whole + // range of subresources when tracking that is improved. + uint64_t bufferSize = largestMipBytesPerImage * 1; + + if (bufferSize > std::numeric_limits::max()) { + return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer."); + } + + DynamicUploader* uploader = device->GetDynamicUploader(); + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, + uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); + memset(uploadHandle.mappedBuffer, clearColor, bufferSize); + + id encoder = commandContext->EnsureBlit(); + id uploadBuffer = ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle(); + + // Encode a buffer to texture copy to clear each subresource. + for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; + ++level) { + Extent3D virtualSize = GetMipLevelVirtualSize(level); + + for (uint32_t arrayLayer = range.baseArrayLayer; + arrayLayer < range.baseArrayLayer + range.layerCount; ++arrayLayer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, arrayLayer))) { + // Skip lazy clears if already initialized. + continue; + } + + // If the texture’s pixel format is a combined depth/stencil format, then + // options must be set to either blit the depth attachment portion or blit the + // stencil attachment portion. + std::array blitOptions = { + MTLBlitOptionNone, MTLBlitOptionDepthFromDepthStencil, + MTLBlitOptionStencilFromDepthStencil}; + + auto blitOptionStart = blitOptions.begin(); + auto blitOptionEnd = blitOptionStart + 1; + if (GetFormat().format == wgpu::TextureFormat::Depth24PlusStencil8) { + blitOptionStart = blitOptions.begin() + 1; + blitOptionEnd = blitOptionStart + 2; + } + + for (auto it = blitOptionStart; it != blitOptionEnd; ++it) { + [encoder copyFromBuffer:uploadBuffer + sourceOffset:uploadHandle.startOffset + sourceBytesPerRow:largestMipBytesPerRow + sourceBytesPerImage:largestMipBytesPerImage + sourceSize:MTLSizeMake(virtualSize.width, virtualSize.height, + 1) + toTexture:GetMTLTexture() + destinationSlice:arrayLayer + destinationLevel:level + destinationOrigin:MTLOriginMake(0, 0, 0) + options:(*it)]; + } + } + } + } + + if (clearValue == TextureBase::ClearValue::Zero) { + SetIsSubresourceContentInitialized(true, range); + device->IncrementLazyClearCountForTesting(); + } + return {}; + } + + void Texture::EnsureSubresourceContentInitialized(const SubresourceRange& range) { + if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { + return; + } + if (!IsSubresourceContentInitialized(range)) { + // If subresource has not been initialized, clear it to black as it could + // contain dirty bits from recycled memory + GetDevice()->ConsumedError(ClearTexture(range, TextureBase::ClearValue::Zero)); + } + } + TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) : TextureViewBase(texture, descriptor) { id mtlTexture = ToBackend(texture)->GetMTLTexture(); diff --git a/third_party/dawn/src/dawn_native/metal/UtilsMetal.h b/third_party/dawn/src/dawn_native/metal/UtilsMetal.h index 574036d6438..fe0e2283d29 100644 --- a/third_party/dawn/src/dawn_native/metal/UtilsMetal.h +++ b/third_party/dawn/src/dawn_native/metal/UtilsMetal.h @@ -16,12 +16,42 @@ #define DAWNNATIVE_METAL_UTILSMETAL_H_ #include "dawn_native/dawn_platform.h" +#include "dawn_native/metal/DeviceMTL.h" +#include "dawn_native/metal/TextureMTL.h" #import namespace dawn_native { namespace metal { - MTLCompareFunction ToMetalCompareFunction(dawn::CompareFunction compareFunction); + MTLCompareFunction ToMetalCompareFunction(wgpu::CompareFunction compareFunction); + + struct TextureBufferCopySplit { + static constexpr uint32_t kMaxTextureBufferCopyRegions = 3; + + struct CopyInfo { + NSUInteger bufferOffset; + NSUInteger bytesPerRow; + NSUInteger bytesPerImage; + Origin3D textureOrigin; + Extent3D copyExtent; + }; + + uint32_t count = 0; + std::array copies; + }; + + TextureBufferCopySplit ComputeTextureBufferCopySplit(const Texture* texture, + uint32_t mipLevel, + Origin3D origin, + Extent3D copyExtent, + uint64_t bufferSize, + uint64_t bufferOffset, + uint32_t bytesPerRow, + uint32_t rowsPerImage); + + void EnsureDestinationTextureInitialized(Texture* texture, + const TextureCopy& dst, + const Extent3D& size); }} // namespace dawn_native::metal diff --git a/third_party/dawn/src/dawn_native/metal/UtilsMetal.mm b/third_party/dawn/src/dawn_native/metal/UtilsMetal.mm index 86210379521..13b4668818e 100644 --- a/third_party/dawn/src/dawn_native/metal/UtilsMetal.mm +++ b/third_party/dawn/src/dawn_native/metal/UtilsMetal.mm @@ -13,27 +13,154 @@ // limitations under the License. #include "dawn_native/metal/UtilsMetal.h" +#include "dawn_native/CommandBuffer.h" + +#include "common/Assert.h" namespace dawn_native { namespace metal { - MTLCompareFunction ToMetalCompareFunction(dawn::CompareFunction compareFunction) { + MTLCompareFunction ToMetalCompareFunction(wgpu::CompareFunction compareFunction) { switch (compareFunction) { - case dawn::CompareFunction::Never: + case wgpu::CompareFunction::Never: return MTLCompareFunctionNever; - case dawn::CompareFunction::Less: + case wgpu::CompareFunction::Less: return MTLCompareFunctionLess; - case dawn::CompareFunction::LessEqual: + case wgpu::CompareFunction::LessEqual: return MTLCompareFunctionLessEqual; - case dawn::CompareFunction::Greater: + case wgpu::CompareFunction::Greater: return MTLCompareFunctionGreater; - case dawn::CompareFunction::GreaterEqual: + case wgpu::CompareFunction::GreaterEqual: return MTLCompareFunctionGreaterEqual; - case dawn::CompareFunction::NotEqual: + case wgpu::CompareFunction::NotEqual: return MTLCompareFunctionNotEqual; - case dawn::CompareFunction::Equal: + case wgpu::CompareFunction::Equal: return MTLCompareFunctionEqual; - case dawn::CompareFunction::Always: + case wgpu::CompareFunction::Always: return MTLCompareFunctionAlways; + default: + UNREACHABLE(); + } + } + + TextureBufferCopySplit ComputeTextureBufferCopySplit(const Texture* texture, + uint32_t mipLevel, + Origin3D origin, + Extent3D copyExtent, + uint64_t bufferSize, + uint64_t bufferOffset, + uint32_t bytesPerRow, + uint32_t rowsPerImage) { + TextureBufferCopySplit copy; + const Format textureFormat = texture->GetFormat(); + + // When copying textures from/to an unpacked buffer, the Metal validation layer doesn't + // compute the correct range when checking if the buffer is big enough to contain the + // data for the whole copy. Instead of looking at the position of the last texel in the + // buffer, it computes the volume of the 3D box with bytesPerRow * (rowsPerImage / + // format.blockHeight) * copySize.depth. For example considering the pixel buffer below + // where in memory, each row data (D) of the texture is followed by some padding data + // (P): + // |DDDDDDD|PP| + // |DDDDDDD|PP| + // |DDDDDDD|PP| + // |DDDDDDD|PP| + // |DDDDDDA|PP| + // The last pixel read will be A, but the driver will think it is the whole last padding + // row, causing it to generate an error when the pixel buffer is just big enough. + + // We work around this limitation by detecting when Metal would complain and copy the + // last image and row separately using tight sourceBytesPerRow or sourceBytesPerImage. + uint32_t dataRowsPerImage = rowsPerImage / textureFormat.blockHeight; + uint32_t bytesPerImage = bytesPerRow * dataRowsPerImage; + + // Metal validation layer requires that if the texture's pixel format is a compressed + // format, the sourceSize must be a multiple of the pixel format's block size or be + // clamped to the edge of the texture if the block extends outside the bounds of a + // texture. + const Extent3D clampedCopyExtent = + texture->ClampToMipLevelVirtualSize(mipLevel, origin, copyExtent); + + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + + // Check whether buffer size is big enough. + bool needWorkaround = bufferSize - bufferOffset < bytesPerImage * copyExtent.depth; + if (!needWorkaround) { + copy.count = 1; + copy.copies[0].bufferOffset = bufferOffset; + copy.copies[0].bytesPerRow = bytesPerRow; + copy.copies[0].bytesPerImage = bytesPerImage; + copy.copies[0].textureOrigin = origin; + copy.copies[0].copyExtent = {clampedCopyExtent.width, clampedCopyExtent.height, + copyExtent.depth}; + return copy; + } + + uint64_t currentOffset = bufferOffset; + + // Doing all the copy except the last image. + if (copyExtent.depth > 1) { + copy.copies[copy.count].bufferOffset = currentOffset; + copy.copies[copy.count].bytesPerRow = bytesPerRow; + copy.copies[copy.count].bytesPerImage = bytesPerImage; + copy.copies[copy.count].textureOrigin = origin; + copy.copies[copy.count].copyExtent = {clampedCopyExtent.width, clampedCopyExtent.height, + copyExtent.depth - 1}; + + ++copy.count; + + // Update offset to copy to the last image. + currentOffset += (copyExtent.depth - 1) * bytesPerImage; + } + + // Doing all the copy in last image except the last row. + uint32_t copyBlockRowCount = copyExtent.height / textureFormat.blockHeight; + if (copyBlockRowCount > 1) { + copy.copies[copy.count].bufferOffset = currentOffset; + copy.copies[copy.count].bytesPerRow = bytesPerRow; + copy.copies[copy.count].bytesPerImage = bytesPerRow * (copyBlockRowCount - 1); + copy.copies[copy.count].textureOrigin = {origin.x, origin.y, + origin.z + copyExtent.depth - 1}; + + ASSERT(copyExtent.height - textureFormat.blockHeight < + texture->GetMipLevelVirtualSize(mipLevel).height); + copy.copies[copy.count].copyExtent = {clampedCopyExtent.width, + copyExtent.height - textureFormat.blockHeight, 1}; + + ++copy.count; + + // Update offset to copy to the last row. + currentOffset += (copyBlockRowCount - 1) * bytesPerRow; + } + + // Doing the last row copy with the exact number of bytes in last row. + // Workaround this issue in a way just like the copy to a 1D texture. + uint32_t lastRowDataSize = + (copyExtent.width / textureFormat.blockWidth) * textureFormat.blockByteSize; + uint32_t lastRowCopyExtentHeight = + textureFormat.blockHeight + clampedCopyExtent.height - copyExtent.height; + ASSERT(lastRowCopyExtentHeight <= textureFormat.blockHeight); + + copy.copies[copy.count].bufferOffset = currentOffset; + copy.copies[copy.count].bytesPerRow = lastRowDataSize; + copy.copies[copy.count].bytesPerImage = lastRowDataSize; + copy.copies[copy.count].textureOrigin = { + origin.x, origin.y + copyExtent.height - textureFormat.blockHeight, + origin.z + copyExtent.depth - 1}; + copy.copies[copy.count].copyExtent = {clampedCopyExtent.width, lastRowCopyExtentHeight, 1}; + ++copy.count; + + return copy; + } + + void EnsureDestinationTextureInitialized(Texture* texture, + const TextureCopy& dst, + const Extent3D& size) { + ASSERT(texture == dst.texture.Get()); + SubresourceRange range = GetSubresourcesAffectedByCopy(dst, size); + if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), size, dst.mipLevel)) { + texture->SetIsSubresourceContentInitialized(true, range); + } else { + texture->EnsureSubresourceContentInitialized(range); } } diff --git a/third_party/dawn/src/dawn_native/null/DeviceNull.cpp b/third_party/dawn/src/dawn_native/null/DeviceNull.cpp index 41203dd9415..9b04aeee802 100644 --- a/third_party/dawn/src/dawn_native/null/DeviceNull.cpp +++ b/third_party/dawn/src/dawn_native/null/DeviceNull.cpp @@ -16,31 +16,38 @@ #include "dawn_native/BackendConnection.h" #include "dawn_native/Commands.h" -#include "dawn_native/DynamicUploader.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/Instance.h" +#include "dawn_native/Surface.h" -#include +#include namespace dawn_native { namespace null { // Implementation of pre-Device objects: the null adapter, null backend connection and Connect() - class Adapter : public AdapterBase { - public: - Adapter(InstanceBase* instance) : AdapterBase(instance, BackendType::Null) { - mPCIInfo.name = "Null backend"; - mDeviceType = DeviceType::CPU; - } - virtual ~Adapter() = default; + Adapter::Adapter(InstanceBase* instance) : AdapterBase(instance, wgpu::BackendType::Null) { + mPCIInfo.name = "Null backend"; + mAdapterType = wgpu::AdapterType::CPU; - private: - ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override { - return {new Device(this, descriptor)}; - } - }; + // Enable all extensions by default for the convenience of tests. + mSupportedExtensions.extensionsBitSet.flip(); + } + + Adapter::~Adapter() = default; + + // Used for the tests that intend to use an adapter without all extensions enabled. + void Adapter::SetSupportedExtensions(const std::vector& requiredExtensions) { + mSupportedExtensions = GetInstance()->ExtensionNamesToExtensionsSet(requiredExtensions); + } + + ResultOrError Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) { + return Device::Create(this, descriptor); + } class Backend : public BackendConnection { public: - Backend(InstanceBase* instance) : BackendConnection(instance, BackendType::Null) { + Backend(InstanceBase* instance) : BackendConnection(instance, wgpu::BackendType::Null) { } std::vector> DiscoverDefaultAdapters() override { @@ -62,7 +69,7 @@ namespace dawn_native { namespace null { } StagingBufferBase* staging; - Buffer* destination; + Ref destination; uint64_t sourceOffset; uint64_t destinationOffset; uint64_t size; @@ -70,19 +77,19 @@ namespace dawn_native { namespace null { // Device - Device::Device(Adapter* adapter, const DeviceDescriptor* descriptor) - : DeviceBase(adapter, descriptor) { - // Apply toggle overrides if necessary for test - if (descriptor != nullptr) { - ApplyToggleOverrides(descriptor); - } + // static + ResultOrError Device::Create(Adapter* adapter, const DeviceDescriptor* descriptor) { + Ref device = AcquireRef(new Device(adapter, descriptor)); + DAWN_TRY(device->Initialize()); + return device.Detach(); } Device::~Device() { - mDynamicUploader = nullptr; + ShutDownBase(); + } - mPendingOperations.clear(); - ASSERT(mMemoryUsage == 0); + MaybeError Device::Initialize() { + return DeviceBase::Initialize(new Queue(this)); } ResultOrError Device::CreateBindGroupImpl( @@ -93,12 +100,13 @@ namespace dawn_native { namespace null { const BindGroupLayoutDescriptor* descriptor) { return new BindGroupLayout(this, descriptor); } - ResultOrError Device::CreateBufferImpl(const BufferDescriptor* descriptor) { + ResultOrError> Device::CreateBufferImpl(const BufferDescriptor* descriptor) { DAWN_TRY(IncrementMemoryUsage(descriptor->size)); - return new Buffer(this, descriptor); + return AcquireRef(new Buffer(this, descriptor)); } - CommandBufferBase* Device::CreateCommandBuffer(CommandEncoderBase* encoder) { - return new CommandBuffer(this, encoder); + CommandBufferBase* Device::CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) { + return new CommandBuffer(encoder, descriptor); } ResultOrError Device::CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) { @@ -108,8 +116,8 @@ namespace dawn_native { namespace null { const PipelineLayoutDescriptor* descriptor) { return new PipelineLayout(this, descriptor); } - ResultOrError Device::CreateQueueImpl() { - return new Queue(this); + ResultOrError Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) { + return new QuerySet(this, descriptor); } ResultOrError Device::CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) { @@ -120,19 +128,42 @@ namespace dawn_native { namespace null { } ResultOrError Device::CreateShaderModuleImpl( const ShaderModuleDescriptor* descriptor) { - auto module = new ShaderModule(this, descriptor); - - spirv_cross::Compiler compiler(descriptor->code, descriptor->codeSize); - module->ExtractSpirvInfo(compiler); - - return module; + Ref module = AcquireRef(new ShaderModule(this, descriptor)); + + if (IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc::CompileOptions options; + options.SetValidate(IsValidationEnabled()); + shaderc_spvc::Context* context = module->GetContext(); + shaderc_spvc_status status = context->InitializeForGlsl( + module->GetSpirv().data(), module->GetSpirv().size(), options); + if (status != shaderc_spvc_status_success) { + return DAWN_VALIDATION_ERROR("Unable to initialize instance of spvc"); + } + + spirv_cross::Compiler* compiler; + status = context->GetCompiler(reinterpret_cast(&compiler)); + if (status != shaderc_spvc_status_success) { + return DAWN_VALIDATION_ERROR("Unable to get cross compiler"); + } + DAWN_TRY(module->ExtractSpirvInfo(*compiler)); + } else { + spirv_cross::Compiler compiler(module->GetSpirv()); + DAWN_TRY(module->ExtractSpirvInfo(compiler)); + } + return module.Detach(); } ResultOrError Device::CreateSwapChainImpl( const SwapChainDescriptor* descriptor) { - return new SwapChain(this, descriptor); + return new OldSwapChain(this, descriptor); } - ResultOrError Device::CreateTextureImpl(const TextureDescriptor* descriptor) { - return new Texture(this, descriptor, TextureBase::TextureState::OwnedInternal); + ResultOrError Device::CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) { + return new SwapChain(this, surface, previousSwapChain, descriptor); + } + ResultOrError> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { + return AcquireRef(new Texture(this, descriptor, TextureBase::TextureState::OwnedInternal)); } ResultOrError Device::CreateTextureViewImpl( TextureBase* texture, @@ -143,54 +174,66 @@ namespace dawn_native { namespace null { ResultOrError> Device::CreateStagingBuffer(size_t size) { std::unique_ptr stagingBuffer = std::make_unique(size, this); + DAWN_TRY(stagingBuffer->Initialize()); return std::move(stagingBuffer); } + void Device::ShutDownImpl() { + ASSERT(GetState() == State::Disconnected); + + // Clear pending operations before checking mMemoryUsage because some operations keep a + // reference to Buffers. + mPendingOperations.clear(); + ASSERT(mMemoryUsage == 0); + } + + MaybeError Device::WaitForIdleForDestruction() { + mPendingOperations.clear(); + return {}; + } + MaybeError Device::CopyFromStagingToBuffer(StagingBufferBase* source, uint64_t sourceOffset, BufferBase* destination, uint64_t destinationOffset, uint64_t size) { + if (IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + destination->SetIsDataInitialized(); + } + auto operation = std::make_unique(); operation->staging = source; - operation->destination = reinterpret_cast(destination); + operation->destination = ToBackend(destination); operation->sourceOffset = sourceOffset; operation->destinationOffset = destinationOffset; operation->size = size; - ToBackend(GetDevice())->AddPendingOperation(std::move(operation)); + AddPendingOperation(std::move(operation)); return {}; } - MaybeError Device::IncrementMemoryUsage(size_t bytes) { - static_assert(kMaxMemoryUsage <= std::numeric_limits::max() / 2, ""); + MaybeError Device::IncrementMemoryUsage(uint64_t bytes) { + static_assert(kMaxMemoryUsage <= std::numeric_limits::max(), ""); if (bytes > kMaxMemoryUsage || mMemoryUsage + bytes > kMaxMemoryUsage) { - return DAWN_CONTEXT_LOST_ERROR("Out of memory."); + return DAWN_OUT_OF_MEMORY_ERROR("Out of memory."); } mMemoryUsage += bytes; return {}; } - void Device::DecrementMemoryUsage(size_t bytes) { + void Device::DecrementMemoryUsage(uint64_t bytes) { ASSERT(mMemoryUsage >= bytes); mMemoryUsage -= bytes; } - Serial Device::GetCompletedCommandSerial() const { - return mCompletedSerial; - } - - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; + MaybeError Device::TickImpl() { + SubmitPendingOperations(); + return {}; } - void Device::TickImpl() { - SubmitPendingOperations(); + Serial Device::CheckAndUpdateCompletedSerials() { + return GetLastSubmittedCommandSerial(); } void Device::AddPendingOperation(std::unique_ptr operation) { @@ -202,22 +245,30 @@ namespace dawn_native { namespace null { } mPendingOperations.clear(); - mCompletedSerial = mLastSubmittedSerial; - mLastSubmittedSerial++; + CheckPassedSerials(); + IncrementLastSubmittedCommandSerial(); } - // Buffer + // BindGroupDataHolder - struct BufferMapReadOperation : PendingOperation { - virtual void Execute() { - buffer->MapReadOperationCompleted(serial, ptr, isWrite); - } + BindGroupDataHolder::BindGroupDataHolder(size_t size) + : mBindingDataAllocation(malloc(size)) // malloc is guaranteed to return a + // pointer aligned enough for the allocation + { + } - Ref buffer; - void* ptr; - uint32_t serial; - bool isWrite; - }; + BindGroupDataHolder::~BindGroupDataHolder() { + free(mBindingDataAllocation); + } + + // BindGroup + + BindGroup::BindGroup(DeviceBase* device, const BindGroupDescriptor* descriptor) + : BindGroupDataHolder(descriptor->layout->GetBindingDataSize()), + BindGroupBase(device, descriptor, mBindingDataAllocation) { + } + + // Buffer Buffer::Buffer(Device* device, const BufferDescriptor* descriptor) : BufferBase(device, descriptor) { @@ -229,25 +280,16 @@ namespace dawn_native { namespace null { ToBackend(GetDevice())->DecrementMemoryUsage(GetSize()); } - bool Buffer::IsMapWritable() const { + bool Buffer::IsMappableAtCreation() const { // Only return true for mappable buffers so we can test cases that need / don't need a // staging buffer. - return (GetUsage() & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) != 0; + return (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0; } - MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { - *mappedPointer = mBackingData.get(); + MaybeError Buffer::MapAtCreationImpl() { return {}; } - void Buffer::MapReadOperationCompleted(uint32_t serial, void* ptr, bool isWrite) { - if (isWrite) { - CallMapWriteCallback(serial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, ptr, GetSize()); - } else { - CallMapReadCallback(serial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, ptr, GetSize()); - } - } - void Buffer::CopyFromStaging(StagingBufferBase* staging, uint64_t sourceOffset, uint64_t destinationOffset, @@ -256,31 +298,26 @@ namespace dawn_native { namespace null { memcpy(mBackingData.get() + destinationOffset, ptr + sourceOffset, size); } - MaybeError Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const void* data) { - ASSERT(start + count <= GetSize()); + void Buffer::DoWriteBuffer(uint64_t bufferOffset, const void* data, size_t size) { + ASSERT(bufferOffset + size <= GetSize()); ASSERT(mBackingData); - memcpy(mBackingData.get() + start, data, count); - return {}; + memcpy(mBackingData.get() + bufferOffset, data, size); } - void Buffer::MapReadAsyncImpl(uint32_t serial) { - MapAsyncImplCommon(serial, false); + MaybeError Buffer::MapReadAsyncImpl() { + return {}; } - void Buffer::MapWriteAsyncImpl(uint32_t serial) { - MapAsyncImplCommon(serial, true); + MaybeError Buffer::MapWriteAsyncImpl() { + return {}; } - void Buffer::MapAsyncImplCommon(uint32_t serial, bool isWrite) { - ASSERT(mBackingData); - - auto operation = std::make_unique(); - operation->buffer = this; - operation->ptr = mBackingData.get(); - operation->serial = serial; - operation->isWrite = isWrite; + MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) { + return {}; + } - ToBackend(GetDevice())->AddPendingOperation(std::move(operation)); + void* Buffer::GetMappedPointerImpl() { + return mBackingData.get(); } void Buffer::UnmapImpl() { @@ -291,14 +328,27 @@ namespace dawn_native { namespace null { // CommandBuffer - CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder) - : CommandBufferBase(device, encoder), mCommands(encoder->AcquireCommands()) { + CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) + : CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) { } CommandBuffer::~CommandBuffer() { FreeCommands(&mCommands); } + // QuerySet + + QuerySet::QuerySet(Device* device, const QuerySetDescriptor* descriptor) + : QuerySetBase(device, descriptor) { + } + + QuerySet::~QuerySet() { + DestroyInternal(); + } + + void QuerySet::DestroyImpl() { + } + // Queue Queue::Queue(Device* device) : QueueBase(device) { @@ -307,26 +357,76 @@ namespace dawn_native { namespace null { Queue::~Queue() { } - void Queue::SubmitImpl(uint32_t, CommandBufferBase* const*) { + MaybeError Queue::SubmitImpl(uint32_t, CommandBufferBase* const*) { ToBackend(GetDevice())->SubmitPendingOperations(); + return {}; + } + + MaybeError Queue::WriteBufferImpl(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size) { + ToBackend(buffer)->DoWriteBuffer(bufferOffset, data, size); + return {}; } // SwapChain - SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor) - : SwapChainBase(device, descriptor) { + SwapChain::SwapChain(Device* device, + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) + : NewSwapChainBase(device, surface, descriptor) { + if (previousSwapChain != nullptr) { + // TODO(cwallez@chromium.org): figure out what should happen when surfaces are used by + // multiple backends one after the other. It probably needs to block until the backend + // and GPU are completely finished with the previous swapchain. + ASSERT(previousSwapChain->GetBackendType() == wgpu::BackendType::Null); + previousSwapChain->DetachFromSurface(); + } + } + + SwapChain::~SwapChain() { + DetachFromSurface(); + } + + MaybeError SwapChain::PresentImpl() { + mTexture->Destroy(); + mTexture = nullptr; + return {}; + } + + ResultOrError SwapChain::GetCurrentTextureViewImpl() { + TextureDescriptor textureDesc = GetSwapChainBaseTextureDescriptor(this); + mTexture = AcquireRef( + new Texture(GetDevice(), &textureDesc, TextureBase::TextureState::OwnedInternal)); + return mTexture->CreateView(nullptr); + } + + void SwapChain::DetachFromSurfaceImpl() { + if (mTexture.Get() != nullptr) { + mTexture->Destroy(); + mTexture = nullptr; + } + } + + // OldSwapChain + + OldSwapChain::OldSwapChain(Device* device, const SwapChainDescriptor* descriptor) + : OldSwapChainBase(device, descriptor) { const auto& im = GetImplementation(); im.Init(im.userData, nullptr); } - SwapChain::~SwapChain() { + OldSwapChain::~OldSwapChain() { } - TextureBase* SwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor) { + TextureBase* OldSwapChain::GetNextTextureImpl(const TextureDescriptor* descriptor) { return GetDevice()->CreateTexture(descriptor); } - void SwapChain::OnBeforePresent(TextureBase*) { + MaybeError OldSwapChain::OnBeforePresent(TextureViewBase*) { + return {}; } // NativeSwapChainImpl @@ -334,8 +434,8 @@ namespace dawn_native { namespace null { void NativeSwapChainImpl::Init(WSIContext* context) { } - DawnSwapChainError NativeSwapChainImpl::Configure(DawnTextureFormat format, - DawnTextureUsageBit, + DawnSwapChainError NativeSwapChainImpl::Configure(WGPUTextureFormat format, + WGPUTextureUsage, uint32_t width, uint32_t height) { return DAWN_SWAP_CHAIN_NO_ERROR; @@ -349,8 +449,8 @@ namespace dawn_native { namespace null { return DAWN_SWAP_CHAIN_NO_ERROR; } - dawn::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const { - return dawn::TextureFormat::R8G8B8A8Unorm; + wgpu::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const { + return wgpu::TextureFormat::RGBA8Unorm; } // StagingBuffer diff --git a/third_party/dawn/src/dawn_native/null/DeviceNull.h b/third_party/dawn/src/dawn_native/null/DeviceNull.h index 5ebc64cc43d..9f39cba2057 100644 --- a/third_party/dawn/src/dawn_native/null/DeviceNull.h +++ b/third_party/dawn/src/dawn_native/null/DeviceNull.h @@ -15,6 +15,7 @@ #ifndef DAWNNATIVE_NULL_DEVICENULL_H_ #define DAWNNATIVE_NULL_DEVICENULL_H_ +#include "dawn_native/Adapter.h" #include "dawn_native/BindGroup.h" #include "dawn_native/BindGroupLayout.h" #include "dawn_native/Buffer.h" @@ -23,9 +24,10 @@ #include "dawn_native/ComputePipeline.h" #include "dawn_native/Device.h" #include "dawn_native/PipelineLayout.h" +#include "dawn_native/QuerySet.h" #include "dawn_native/Queue.h" #include "dawn_native/RenderPipeline.h" -#include "dawn_native/RingBuffer.h" +#include "dawn_native/RingBufferAllocator.h" #include "dawn_native/Sampler.h" #include "dawn_native/ShaderModule.h" #include "dawn_native/StagingBuffer.h" @@ -37,13 +39,14 @@ namespace dawn_native { namespace null { class Adapter; - using BindGroup = BindGroupBase; + class BindGroup; using BindGroupLayout = BindGroupLayoutBase; class Buffer; class CommandBuffer; using ComputePipeline = ComputePipelineBase; class Device; using PipelineLayout = PipelineLayoutBase; + class QuerySet; class Queue; using RenderPipeline = RenderPipelineBase; using Sampler = SamplerBase; @@ -61,6 +64,7 @@ namespace dawn_native { namespace null { using ComputePipelineType = ComputePipeline; using DeviceType = Device; using PipelineLayoutType = PipelineLayout; + using QuerySetType = QuerySet; using QueueType = Queue; using RenderPipelineType = RenderPipeline; using SamplerType = Sampler; @@ -82,15 +86,15 @@ namespace dawn_native { namespace null { class Device : public DeviceBase { public: - Device(Adapter* adapter, const DeviceDescriptor* descriptor); - ~Device(); + static ResultOrError Create(Adapter* adapter, const DeviceDescriptor* descriptor); + ~Device() override; - CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) override; + MaybeError Initialize(); - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; - Serial GetPendingCommandSerial() const override; - void TickImpl() override; + CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) override; + + MaybeError TickImpl() override; void AddPendingOperation(std::unique_ptr operation); void SubmitPendingOperations(); @@ -102,20 +106,24 @@ namespace dawn_native { namespace null { uint64_t destinationOffset, uint64_t size) override; - MaybeError IncrementMemoryUsage(size_t bytes); - void DecrementMemoryUsage(size_t bytes); + MaybeError IncrementMemoryUsage(uint64_t bytes); + void DecrementMemoryUsage(uint64_t bytes); private: + using DeviceBase::DeviceBase; + ResultOrError CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override; ResultOrError CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) override; - ResultOrError CreateBufferImpl(const BufferDescriptor* descriptor) override; + ResultOrError> CreateBufferImpl( + const BufferDescriptor* descriptor) override; ResultOrError CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) override; ResultOrError CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) override; - ResultOrError CreateQueueImpl() override; + ResultOrError CreateQuerySetImpl( + const QuerySetDescriptor* descriptor) override; ResultOrError CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) override; ResultOrError CreateSamplerImpl(const SamplerDescriptor* descriptor) override; @@ -123,84 +131,158 @@ namespace dawn_native { namespace null { const ShaderModuleDescriptor* descriptor) override; ResultOrError CreateSwapChainImpl( const SwapChainDescriptor* descriptor) override; - ResultOrError CreateTextureImpl(const TextureDescriptor* descriptor) override; + ResultOrError CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) override; + ResultOrError> CreateTextureImpl( + const TextureDescriptor* descriptor) override; ResultOrError CreateTextureViewImpl( TextureBase* texture, const TextureViewDescriptor* descriptor) override; - Serial mCompletedSerial = 0; - Serial mLastSubmittedSerial = 0; + Serial CheckAndUpdateCompletedSerials() override; + + void ShutDownImpl() override; + MaybeError WaitForIdleForDestruction() override; + std::vector> mPendingOperations; - static constexpr size_t kMaxMemoryUsage = 256 * 1024 * 1024; + static constexpr uint64_t kMaxMemoryUsage = 256 * 1024 * 1024; size_t mMemoryUsage = 0; }; - class Buffer : public BufferBase { + class Adapter : public AdapterBase { + public: + Adapter(InstanceBase* instance); + ~Adapter() override; + + // Used for the tests that intend to use an adapter without all extensions enabled. + void SetSupportedExtensions(const std::vector& requiredExtensions); + + private: + ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override; + }; + + // Helper class so |BindGroup| can allocate memory for its binding data, + // before calling the BindGroupBase base class constructor. + class BindGroupDataHolder { + protected: + explicit BindGroupDataHolder(size_t size); + ~BindGroupDataHolder(); + + void* mBindingDataAllocation; + }; + + // We don't have the complexity of placement-allocation of bind group data in + // the Null backend. This class, keeps the binding data in a separate allocation for simplicity. + class BindGroup final : private BindGroupDataHolder, public BindGroupBase { + public: + BindGroup(DeviceBase* device, const BindGroupDescriptor* descriptor); + + private: + ~BindGroup() override = default; + }; + + class Buffer final : public BufferBase { public: Buffer(Device* device, const BufferDescriptor* descriptor); - ~Buffer(); - void MapReadOperationCompleted(uint32_t serial, void* ptr, bool isWrite); void CopyFromStaging(StagingBufferBase* staging, uint64_t sourceOffset, uint64_t destinationOffset, uint64_t size); + void DoWriteBuffer(uint64_t bufferOffset, const void* data, size_t size); + private: + ~Buffer() override; + // Dawn API - MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const void* data) override; - void MapReadAsyncImpl(uint32_t serial) override; - void MapWriteAsyncImpl(uint32_t serial) override; + MaybeError MapReadAsyncImpl() override; + MaybeError MapWriteAsyncImpl() override; + MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override; void UnmapImpl() override; void DestroyImpl() override; - bool IsMapWritable() const override; - MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; - void MapAsyncImplCommon(uint32_t serial, bool isWrite); + bool IsMappableAtCreation() const override; + MaybeError MapAtCreationImpl() override; + void* GetMappedPointerImpl() override; std::unique_ptr mBackingData; }; - class CommandBuffer : public CommandBufferBase { + class CommandBuffer final : public CommandBufferBase { public: - CommandBuffer(Device* device, CommandEncoderBase* encoder); - ~CommandBuffer(); + CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor); private: + ~CommandBuffer() override; + CommandIterator mCommands; }; - class Queue : public QueueBase { + class QuerySet final : public QuerySetBase { + public: + QuerySet(Device* device, const QuerySetDescriptor* descriptor); + + private: + ~QuerySet() override; + + void DestroyImpl() override; + }; + + class Queue final : public QueueBase { public: Queue(Device* device); - ~Queue(); private: - void SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; + ~Queue() override; + MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; + MaybeError WriteBufferImpl(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size) override; + }; + + class SwapChain final : public NewSwapChainBase { + public: + SwapChain(Device* device, + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor); + + private: + ~SwapChain() override; + + Ref mTexture; + + MaybeError PresentImpl() override; + ResultOrError GetCurrentTextureViewImpl() override; + void DetachFromSurfaceImpl() override; }; - class SwapChain : public SwapChainBase { + class OldSwapChain final : public OldSwapChainBase { public: - SwapChain(Device* device, const SwapChainDescriptor* descriptor); - ~SwapChain(); + OldSwapChain(Device* device, const SwapChainDescriptor* descriptor); protected: + ~OldSwapChain() override; TextureBase* GetNextTextureImpl(const TextureDescriptor* descriptor) override; - void OnBeforePresent(TextureBase*) override; + MaybeError OnBeforePresent(TextureViewBase*) override; }; class NativeSwapChainImpl { public: using WSIContext = struct {}; void Init(WSIContext* context); - DawnSwapChainError Configure(DawnTextureFormat format, - DawnTextureUsageBit, + DawnSwapChainError Configure(WGPUTextureFormat format, + WGPUTextureUsage, uint32_t width, uint32_t height); DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture); DawnSwapChainError Present(); - dawn::TextureFormat GetPreferredFormat() const; + wgpu::TextureFormat GetPreferredFormat() const; }; class StagingBuffer : public StagingBufferBase { diff --git a/third_party/dawn/src/dawn_native/null/NullBackend.cpp b/third_party/dawn/src/dawn_native/null/NullBackend.cpp index ef1bbe49388..a48dcdccc4e 100644 --- a/third_party/dawn/src/dawn_native/null/NullBackend.cpp +++ b/third_party/dawn/src/dawn_native/null/NullBackend.cpp @@ -25,7 +25,7 @@ namespace dawn_native { namespace null { DawnSwapChainImplementation CreateNativeSwapChainImpl() { DawnSwapChainImplementation impl; impl = CreateSwapChainImplementation(new NativeSwapChainImpl()); - impl.textureUsage = DAWN_TEXTURE_USAGE_BIT_PRESENT; + impl.textureUsage = WGPUTextureUsage_Present; return impl; } diff --git a/third_party/dawn/src/dawn_native/opengl/BackendGL.cpp b/third_party/dawn/src/dawn_native/opengl/BackendGL.cpp index 1e5c6716935..2d3685980ec 100644 --- a/third_party/dawn/src/dawn_native/opengl/BackendGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/BackendGL.cpp @@ -14,30 +14,164 @@ #include "dawn_native/opengl/BackendGL.h" +#include "common/GPUInfo.h" +#include "common/Log.h" +#include "dawn_native/Instance.h" #include "dawn_native/OpenGLBackend.h" #include "dawn_native/opengl/DeviceGL.h" +#include + namespace dawn_native { namespace opengl { + namespace { + + struct Vendor { + const char* vendorName; + uint32_t vendorId; + }; + + const Vendor kVendors[] = {{"ATI", gpu_info::kVendorID_AMD}, + {"ARM", gpu_info::kVendorID_ARM}, + {"Imagination", gpu_info::kVendorID_ImgTec}, + {"Intel", gpu_info::kVendorID_Intel}, + {"NVIDIA", gpu_info::kVendorID_Nvidia}, + {"Qualcomm", gpu_info::kVendorID_Qualcomm}}; + + uint32_t GetVendorIdFromVendors(const char* vendor) { + uint32_t vendorId = 0; + for (const auto& it : kVendors) { + // Matching vendor name with vendor string + if (strstr(vendor, it.vendorName) != nullptr) { + vendorId = it.vendorId; + break; + } + } + return vendorId; + } + + void KHRONOS_APIENTRY OnGLDebugMessage(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* userParam) { + const char* sourceText; + switch (source) { + case GL_DEBUG_SOURCE_API: + sourceText = "OpenGL"; + break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + sourceText = "Window System"; + break; + case GL_DEBUG_SOURCE_SHADER_COMPILER: + sourceText = "Shader Compiler"; + break; + case GL_DEBUG_SOURCE_THIRD_PARTY: + sourceText = "Third Party"; + break; + case GL_DEBUG_SOURCE_APPLICATION: + sourceText = "Application"; + break; + case GL_DEBUG_SOURCE_OTHER: + sourceText = "Other"; + break; + default: + sourceText = "UNKNOWN"; + break; + } + + const char* severityText; + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH: + severityText = "High"; + break; + case GL_DEBUG_SEVERITY_MEDIUM: + severityText = "Medium"; + break; + case GL_DEBUG_SEVERITY_LOW: + severityText = "Low"; + break; + case GL_DEBUG_SEVERITY_NOTIFICATION: + severityText = "Notification"; + break; + default: + severityText = "UNKNOWN"; + break; + } + + if (type == GL_DEBUG_TYPE_ERROR) { + dawn::WarningLog() << "OpenGL error:" + << "\n Source: " << sourceText // + << "\n ID: " << id // + << "\n Severity: " << severityText // + << "\n Message: " << message; + + // Abort on an error when in Debug mode. + UNREACHABLE(); + } + } + + } // anonymous namespace + // The OpenGL backend's Adapter. class Adapter : public AdapterBase { public: - Adapter(InstanceBase* instance) : AdapterBase(instance, BackendType::OpenGL) { + Adapter(InstanceBase* instance) : AdapterBase(instance, wgpu::BackendType::OpenGL) { } MaybeError Initialize(const AdapterDiscoveryOptions* options) { // Use getProc to populate the dispatch table DAWN_TRY(mFunctions.Initialize(options->getProc)); + // Use the debug output functionality to get notified about GL errors + // TODO(cwallez@chromium.org): add support for the KHR_debug and ARB_debug_output + // extensions + bool hasDebugOutput = mFunctions.IsAtLeastGL(4, 3) || mFunctions.IsAtLeastGLES(3, 2); + + if (GetInstance()->IsBackendValidationEnabled() && hasDebugOutput) { + mFunctions.Enable(GL_DEBUG_OUTPUT); + mFunctions.Enable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + + // Any GL error; dangerous undefined behavior; any shader compiler and linker errors + mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, + 0, nullptr, GL_TRUE); + + // Severe performance warnings; GLSL or other shader compiler and linker warnings; + // use of currently deprecated behavior + mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, + 0, nullptr, GL_TRUE); + + // Performance warnings from redundant state changes; trivial undefined behavior + // This is disabled because we do an incredible amount of redundant state changes. + mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, + nullptr, GL_FALSE); + + // Any message which is not an error or performance concern + mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, + GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, + GL_FALSE); + mFunctions.DebugMessageCallback(&OnGLDebugMessage, nullptr); + } + // Set state that never changes between devices. mFunctions.Enable(GL_DEPTH_TEST); mFunctions.Enable(GL_SCISSOR_TEST); mFunctions.Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX); mFunctions.Enable(GL_MULTISAMPLE); + mFunctions.Enable(GL_FRAMEBUFFER_SRGB); + mFunctions.Enable(GL_SAMPLE_MASK); mPCIInfo.name = reinterpret_cast(mFunctions.GetString(GL_RENDERER)); + // Workaroud to find vendor id from vendor name + const char* vendor = reinterpret_cast(mFunctions.GetString(GL_VENDOR)); + mPCIInfo.vendorId = GetVendorIdFromVendors(vendor); + + InitializeSupportedExtensions(); + return {}; } @@ -49,13 +183,51 @@ namespace dawn_native { namespace opengl { ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override { // There is no limit on the number of devices created from this adapter because they can // all share the same backing OpenGL context. - return {new Device(this, descriptor, mFunctions)}; + return Device::Create(this, descriptor, mFunctions); + } + + void InitializeSupportedExtensions() { + // TextureCompressionBC + { + // BC1, BC2 and BC3 are not supported in OpenGL or OpenGL ES core features. + bool supportsS3TC = + mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_s3tc"); + + // COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT and + // COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT requires both GL_EXT_texture_sRGB and + // GL_EXT_texture_compression_s3tc on desktop OpenGL drivers. + // (https://www.khronos.org/registry/OpenGL/extensions/EXT/EXT_texture_sRGB.txt) + bool supportsTextureSRGB = mFunctions.IsGLExtensionSupported("GL_EXT_texture_sRGB"); + + // GL_EXT_texture_compression_s3tc_srgb is an extension in OpenGL ES. + bool supportsS3TCSRGB = + mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_s3tc_srgb"); + + // BC4 and BC5 + bool supportsRGTC = + mFunctions.IsAtLeastGL(3, 0) || + mFunctions.IsGLExtensionSupported("GL_ARB_texture_compression_rgtc") || + mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_rgtc"); + + // BC6 and BC7 + bool supportsBPTC = + mFunctions.IsAtLeastGL(4, 2) || + mFunctions.IsGLExtensionSupported("GL_ARB_texture_compression_bptc") || + mFunctions.IsGLExtensionSupported("GL_EXT_texture_compression_bptc"); + + if (supportsS3TC && (supportsTextureSRGB || supportsS3TCSRGB) && supportsRGTC && + supportsBPTC) { + mSupportedExtensions.EnableExtension( + dawn_native::Extension::TextureCompressionBC); + } + } } }; // Implementation of the OpenGL backend's BackendConnection - Backend::Backend(InstanceBase* instance) : BackendConnection(instance, BackendType::OpenGL) { + Backend::Backend(InstanceBase* instance) + : BackendConnection(instance, wgpu::BackendType::OpenGL) { } std::vector> Backend::DiscoverDefaultAdapters() { @@ -71,7 +243,7 @@ namespace dawn_native { namespace opengl { return DAWN_VALIDATION_ERROR("The OpenGL backend can only create a single adapter"); } - ASSERT(optionsBase->backendType == BackendType::OpenGL); + ASSERT(optionsBase->backendType == WGPUBackendType_OpenGL); const AdapterDiscoveryOptions* options = static_cast(optionsBase); diff --git a/third_party/dawn/src/dawn_native/opengl/BindGroupGL.cpp b/third_party/dawn/src/dawn_native/opengl/BindGroupGL.cpp new file mode 100644 index 00000000000..d96baea233d --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/BindGroupGL.cpp @@ -0,0 +1,78 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/opengl/BindGroupGL.h" + +#include "dawn_native/Texture.h" +#include "dawn_native/opengl/BindGroupLayoutGL.h" +#include "dawn_native/opengl/DeviceGL.h" + +namespace dawn_native { namespace opengl { + + MaybeError ValidateGLBindGroupDescriptor(const BindGroupDescriptor* descriptor) { + const BindGroupLayoutBase::BindingMap& bindingMap = descriptor->layout->GetBindingMap(); + for (uint32_t i = 0; i < descriptor->entryCount; ++i) { + const BindGroupEntry& entry = descriptor->entries[i]; + + const auto& it = bindingMap.find(BindingNumber(entry.binding)); + BindingIndex bindingIndex = it->second; + ASSERT(bindingIndex < descriptor->layout->GetBindingCount()); + + const BindingInfo& bindingInfo = descriptor->layout->GetBindingInfo(bindingIndex); + switch (bindingInfo.type) { + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + ASSERT(entry.textureView != nullptr); + const uint32_t textureViewLayerCount = entry.textureView->GetLayerCount(); + if (textureViewLayerCount != 1 && + textureViewLayerCount != + entry.textureView->GetTexture()->GetArrayLayers()) { + return DAWN_VALIDATION_ERROR( + "Currently the OpenGL backend only supports either binding a layer or " + "the entire texture as storage texture."); + } + } break; + + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::SampledTexture: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + break; + + case wgpu::BindingType::StorageTexture: + default: + UNREACHABLE(); + break; + } + } + + return {}; + } + + BindGroup::BindGroup(Device* device, const BindGroupDescriptor* descriptor) + : BindGroupBase(this, device, descriptor) { + } + + BindGroup::~BindGroup() { + ToBackend(GetLayout())->DeallocateBindGroup(this); + } + + // static + BindGroup* BindGroup::Create(Device* device, const BindGroupDescriptor* descriptor) { + return ToBackend(descriptor->layout)->AllocateBindGroup(device, descriptor); + } + +}} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/BindGroupGL.h b/third_party/dawn/src/dawn_native/opengl/BindGroupGL.h new file mode 100644 index 00000000000..f9f11514100 --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/BindGroupGL.h @@ -0,0 +1,40 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_OPENGL_BINDGROUPGL_H_ +#define DAWNNATIVE_OPENGL_BINDGROUPGL_H_ + +#include "common/PlacementAllocated.h" +#include "dawn_native/BindGroup.h" + +namespace dawn_native { namespace opengl { + + class BindGroupLayout; + class Device; + + MaybeError ValidateGLBindGroupDescriptor(const BindGroupDescriptor* descriptor); + + class BindGroup final : public BindGroupBase, public PlacementAllocated { + public: + BindGroup(Device* device, const BindGroupDescriptor* descriptor); + + static BindGroup* Create(Device* device, const BindGroupDescriptor* descriptor); + + private: + ~BindGroup() override; + }; + +}} // namespace dawn_native::opengl + +#endif // DAWNNATIVE_OPENGL_BINDGROUPGL_H_ diff --git a/third_party/dawn/src/dawn_native/opengl/BindGroupLayoutGL.cpp b/third_party/dawn/src/dawn_native/opengl/BindGroupLayoutGL.cpp new file mode 100644 index 00000000000..7c098c8700b --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/BindGroupLayoutGL.cpp @@ -0,0 +1,36 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/opengl/BindGroupLayoutGL.h" + +#include "dawn_native/opengl/BindGroupGL.h" + +namespace dawn_native { namespace opengl { + + BindGroupLayout::BindGroupLayout(DeviceBase* device, + const BindGroupLayoutDescriptor* descriptor) + : BindGroupLayoutBase(device, descriptor), + mBindGroupAllocator(MakeFrontendBindGroupAllocator(4096)) { + } + + BindGroup* BindGroupLayout::AllocateBindGroup(Device* device, + const BindGroupDescriptor* descriptor) { + return mBindGroupAllocator.Allocate(device, descriptor); + } + + void BindGroupLayout::DeallocateBindGroup(BindGroup* bindGroup) { + mBindGroupAllocator.Deallocate(bindGroup); + } + +}} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/BindGroupLayoutGL.h b/third_party/dawn/src/dawn_native/opengl/BindGroupLayoutGL.h new file mode 100644 index 00000000000..aeba62f54c3 --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/BindGroupLayoutGL.h @@ -0,0 +1,40 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_OPENGL_BINDGROUPLAYOUTGL_H_ +#define DAWNNATIVE_OPENGL_BINDGROUPLAYOUTGL_H_ + +#include "common/SlabAllocator.h" +#include "dawn_native/BindGroupLayout.h" + +namespace dawn_native { namespace opengl { + + class BindGroup; + class Device; + + class BindGroupLayout final : public BindGroupLayoutBase { + public: + BindGroupLayout(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor); + + BindGroup* AllocateBindGroup(Device* device, const BindGroupDescriptor* descriptor); + void DeallocateBindGroup(BindGroup* bindGroup); + + private: + ~BindGroupLayout() override = default; + SlabAllocator mBindGroupAllocator; + }; + +}} // namespace dawn_native::opengl + +#endif // DAWNNATIVE_OPENGL_BINDGROUPLAYOUTGL_H_ diff --git a/third_party/dawn/src/dawn_native/opengl/BufferGL.cpp b/third_party/dawn/src/dawn_native/opengl/BufferGL.cpp index 2d6856fcf95..6289c6edb05 100644 --- a/third_party/dawn/src/dawn_native/opengl/BufferGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/BufferGL.cpp @@ -14,6 +14,7 @@ #include "dawn_native/opengl/BufferGL.h" +#include "dawn_native/CommandBuffer.h" #include "dawn_native/opengl/DeviceGL.h" namespace dawn_native { namespace opengl { @@ -22,9 +23,19 @@ namespace dawn_native { namespace opengl { Buffer::Buffer(Device* device, const BufferDescriptor* descriptor) : BufferBase(device, descriptor) { + // TODO(cwallez@chromium.org): Have a global "zero" buffer instead of creating a new 4-byte + // buffer? + uint64_t size = GetAppliedSize(); + device->gl.GenBuffers(1, &mBuffer); device->gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer); - device->gl.BufferData(GL_ARRAY_BUFFER, GetSize(), nullptr, GL_STATIC_DRAW); + + if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { + std::vector clearValues(size, 1u); + device->gl.BufferData(GL_ARRAY_BUFFER, size, clearValues.data(), GL_STATIC_DRAW); + } else { + device->gl.BufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STATIC_DRAW); + } } Buffer::~Buffer() { @@ -35,47 +46,137 @@ namespace dawn_native { namespace opengl { return mBuffer; } - bool Buffer::IsMapWritable() const { + uint64_t Buffer::GetAppliedSize() const { + // TODO(cwallez@chromium.org): Have a global "zero" buffer instead of creating a new 4-byte + // buffer? + return std::max(GetSize(), uint64_t(4u)); + } + + void Buffer::EnsureDataInitialized() { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; + } + + InitializeToZero(); + } + + void Buffer::EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; + } + + if (IsFullBufferRange(offset, size)) { + SetIsDataInitialized(); + } else { + InitializeToZero(); + } + } + + void Buffer::EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; + } + + if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) { + SetIsDataInitialized(); + } else { + InitializeToZero(); + } + } + + void Buffer::InitializeToZero() { + ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)); + ASSERT(!IsDataInitialized()); + + const uint64_t size = GetAppliedSize(); + Device* device = ToBackend(GetDevice()); + + const std::vector clearValues(size, 0u); + device->gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer); + device->gl.BufferSubData(GL_ARRAY_BUFFER, 0, size, clearValues.data()); + device->IncrementLazyClearCountForTesting(); + + SetIsDataInitialized(); + } + + bool Buffer::IsMappableAtCreation() const { // TODO(enga): All buffers in GL can be mapped. Investigate if mapping them will cause the // driver to migrate it to shared memory. return true; } - MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { - const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; + MaybeError Buffer::MapAtCreationImpl() { + EnsureDataInitialized(); + const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer); - void* data = gl.MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - *mappedPointer = reinterpret_cast(data); + mMappedData = gl.MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); return {}; } - MaybeError Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const void* data) { + MaybeError Buffer::MapReadAsyncImpl() { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; + // TODO(cwallez@chromium.org): this does GPU->CPU synchronization, we could require a high + // version of OpenGL that would let us map the buffer unsynchronized. gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer); - gl.BufferSubData(GL_ARRAY_BUFFER, start, count, data); + mMappedData = gl.MapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); return {}; } - void Buffer::MapReadAsyncImpl(uint32_t serial) { + MaybeError Buffer::MapWriteAsyncImpl() { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; // TODO(cwallez@chromium.org): this does GPU->CPU synchronization, we could require a high // version of OpenGL that would let us map the buffer unsynchronized. gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer); - void* data = gl.MapBuffer(GL_ARRAY_BUFFER, GL_READ_ONLY); - CallMapReadCallback(serial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data, GetSize()); + mMappedData = gl.MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + return {}; } - void Buffer::MapWriteAsyncImpl(uint32_t serial) { + MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; + // It is an error to map an empty range in OpenGL. We always have at least a 4-byte buffer + // so we extend the range to be 4 bytes. + if (size == 0) { + if (offset != 0) { + offset -= 4; + } + size = 4; + } + + EnsureDataInitialized(); + // TODO(cwallez@chromium.org): this does GPU->CPU synchronization, we could require a high // version of OpenGL that would let us map the buffer unsynchronized. gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer); - void* data = gl.MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - CallMapWriteCallback(serial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data, GetSize()); + void* mappedData = nullptr; + if (mode & wgpu::MapMode::Read) { + mappedData = gl.MapBufferRange(GL_ARRAY_BUFFER, offset, size, GL_MAP_READ_BIT); + } else { + ASSERT(mode & wgpu::MapMode::Write); + mappedData = gl.MapBufferRange(GL_ARRAY_BUFFER, offset, size, GL_MAP_WRITE_BIT); + } + + // The frontend asks that the pointer returned by GetMappedPointerImpl is from the start of + // the resource but OpenGL gives us the pointer at offset. Remove the offset. + mMappedData = static_cast(mappedData) - offset; + return {}; + } + + void* Buffer::GetMappedPointerImpl() { + // The mapping offset has already been removed. + return mMappedData; } void Buffer::UnmapImpl() { @@ -83,6 +184,7 @@ namespace dawn_native { namespace opengl { gl.BindBuffer(GL_ARRAY_BUFFER, mBuffer); gl.UnmapBuffer(GL_ARRAY_BUFFER); + mMappedData = nullptr; } void Buffer::DestroyImpl() { diff --git a/third_party/dawn/src/dawn_native/opengl/BufferGL.h b/third_party/dawn/src/dawn_native/opengl/BufferGL.h index f1dd0a56ed1..8fcc7fcc346 100644 --- a/third_party/dawn/src/dawn_native/opengl/BufferGL.h +++ b/third_party/dawn/src/dawn_native/opengl/BufferGL.h @@ -17,31 +17,40 @@ #include "dawn_native/Buffer.h" -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { class Device; - class Buffer : public BufferBase { + class Buffer final : public BufferBase { public: Buffer(Device* device, const BufferDescriptor* descriptor); - ~Buffer(); GLuint GetHandle() const; + void EnsureDataInitialized(); + void EnsureDataInitializedAsDestination(uint64_t offset, uint64_t size); + void EnsureDataInitializedAsDestination(const CopyTextureToBufferCmd* copy); + private: + ~Buffer() override; // Dawn API - MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const void* data) override; - void MapReadAsyncImpl(uint32_t serial) override; - void MapWriteAsyncImpl(uint32_t serial) override; + MaybeError MapReadAsyncImpl() override; + MaybeError MapWriteAsyncImpl() override; + MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override; void UnmapImpl() override; void DestroyImpl() override; - bool IsMapWritable() const override; - MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; + bool IsMappableAtCreation() const override; + MaybeError MapAtCreationImpl() override; + void* GetMappedPointerImpl() override; + uint64_t GetAppliedSize() const; + + void InitializeToZero(); GLuint mBuffer = 0; + void* mMappedData = nullptr; }; }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/CommandBufferGL.cpp b/third_party/dawn/src/dawn_native/opengl/CommandBufferGL.cpp index 13673d78fbd..51b9e7c1ec7 100644 --- a/third_party/dawn/src/dawn_native/opengl/CommandBufferGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/CommandBufferGL.cpp @@ -15,8 +15,10 @@ #include "dawn_native/opengl/CommandBufferGL.h" #include "dawn_native/BindGroup.h" +#include "dawn_native/BindGroupTracker.h" #include "dawn_native/CommandEncoder.h" #include "dawn_native/Commands.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/opengl/BufferGL.h" #include "dawn_native/opengl/ComputePipelineGL.h" #include "dawn_native/opengl/DeviceGL.h" @@ -26,6 +28,7 @@ #include "dawn_native/opengl/RenderPipelineGL.h" #include "dawn_native/opengl/SamplerGL.h" #include "dawn_native/opengl/TextureGL.h" +#include "dawn_native/opengl/UtilsGL.h" #include @@ -33,134 +36,119 @@ namespace dawn_native { namespace opengl { namespace { - GLenum IndexFormatType(dawn::IndexFormat format) { + GLenum IndexFormatType(wgpu::IndexFormat format) { switch (format) { - case dawn::IndexFormat::Uint16: + case wgpu::IndexFormat::Uint16: return GL_UNSIGNED_SHORT; - case dawn::IndexFormat::Uint32: + case wgpu::IndexFormat::Uint32: return GL_UNSIGNED_INT; default: UNREACHABLE(); } } - GLenum VertexFormatType(dawn::VertexFormat format) { + GLenum VertexFormatType(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar2: - case dawn::VertexFormat::UChar4: - case dawn::VertexFormat::UChar2Norm: - case dawn::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::UChar2: + case wgpu::VertexFormat::UChar4: + case wgpu::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar4Norm: return GL_UNSIGNED_BYTE; - case dawn::VertexFormat::Char2: - case dawn::VertexFormat::Char4: - case dawn::VertexFormat::Char2Norm: - case dawn::VertexFormat::Char4Norm: + case wgpu::VertexFormat::Char2: + case wgpu::VertexFormat::Char4: + case wgpu::VertexFormat::Char2Norm: + case wgpu::VertexFormat::Char4Norm: return GL_BYTE; - case dawn::VertexFormat::UShort2: - case dawn::VertexFormat::UShort4: - case dawn::VertexFormat::UShort2Norm: - case dawn::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::UShort2: + case wgpu::VertexFormat::UShort4: + case wgpu::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort4Norm: return GL_UNSIGNED_SHORT; - case dawn::VertexFormat::Short2: - case dawn::VertexFormat::Short4: - case dawn::VertexFormat::Short2Norm: - case dawn::VertexFormat::Short4Norm: + case wgpu::VertexFormat::Short2: + case wgpu::VertexFormat::Short4: + case wgpu::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Short4Norm: return GL_SHORT; - case dawn::VertexFormat::Half2: - case dawn::VertexFormat::Half4: + case wgpu::VertexFormat::Half2: + case wgpu::VertexFormat::Half4: return GL_HALF_FLOAT; - case dawn::VertexFormat::Float: - case dawn::VertexFormat::Float2: - case dawn::VertexFormat::Float3: - case dawn::VertexFormat::Float4: + case wgpu::VertexFormat::Float: + case wgpu::VertexFormat::Float2: + case wgpu::VertexFormat::Float3: + case wgpu::VertexFormat::Float4: return GL_FLOAT; - case dawn::VertexFormat::UInt: - case dawn::VertexFormat::UInt2: - case dawn::VertexFormat::UInt3: - case dawn::VertexFormat::UInt4: + case wgpu::VertexFormat::UInt: + case wgpu::VertexFormat::UInt2: + case wgpu::VertexFormat::UInt3: + case wgpu::VertexFormat::UInt4: return GL_UNSIGNED_INT; - case dawn::VertexFormat::Int: - case dawn::VertexFormat::Int2: - case dawn::VertexFormat::Int3: - case dawn::VertexFormat::Int4: + case wgpu::VertexFormat::Int: + case wgpu::VertexFormat::Int2: + case wgpu::VertexFormat::Int3: + case wgpu::VertexFormat::Int4: return GL_INT; default: UNREACHABLE(); } } - GLboolean VertexFormatIsNormalized(dawn::VertexFormat format) { + GLboolean VertexFormatIsNormalized(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar2Norm: - case dawn::VertexFormat::UChar4Norm: - case dawn::VertexFormat::Char2Norm: - case dawn::VertexFormat::Char4Norm: - case dawn::VertexFormat::UShort2Norm: - case dawn::VertexFormat::UShort4Norm: - case dawn::VertexFormat::Short2Norm: - case dawn::VertexFormat::Short4Norm: + case wgpu::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::Char2Norm: + case wgpu::VertexFormat::Char4Norm: + case wgpu::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Short4Norm: return GL_TRUE; default: return GL_FALSE; } } - bool VertexFormatIsInt(dawn::VertexFormat format) { + bool VertexFormatIsInt(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar2: - case dawn::VertexFormat::UChar4: - case dawn::VertexFormat::Char2: - case dawn::VertexFormat::Char4: - case dawn::VertexFormat::UShort2: - case dawn::VertexFormat::UShort4: - case dawn::VertexFormat::Short2: - case dawn::VertexFormat::Short4: - case dawn::VertexFormat::UInt: - case dawn::VertexFormat::UInt2: - case dawn::VertexFormat::UInt3: - case dawn::VertexFormat::UInt4: - case dawn::VertexFormat::Int: - case dawn::VertexFormat::Int2: - case dawn::VertexFormat::Int3: - case dawn::VertexFormat::Int4: + case wgpu::VertexFormat::UChar2: + case wgpu::VertexFormat::UChar4: + case wgpu::VertexFormat::Char2: + case wgpu::VertexFormat::Char4: + case wgpu::VertexFormat::UShort2: + case wgpu::VertexFormat::UShort4: + case wgpu::VertexFormat::Short2: + case wgpu::VertexFormat::Short4: + case wgpu::VertexFormat::UInt: + case wgpu::VertexFormat::UInt2: + case wgpu::VertexFormat::UInt3: + case wgpu::VertexFormat::UInt4: + case wgpu::VertexFormat::Int: + case wgpu::VertexFormat::Int2: + case wgpu::VertexFormat::Int3: + case wgpu::VertexFormat::Int4: return true; default: return false; } } - GLint GetStencilMaskFromStencilFormat(dawn::TextureFormat depthStencilFormat) { - switch (depthStencilFormat) { - case dawn::TextureFormat::D32FloatS8Uint: - return 0xFF; - default: - UNREACHABLE(); - } - } - // Vertex buffers and index buffers are implemented as part of an OpenGL VAO that - // corresponds to an VertexInput. On the contrary in Dawn they are part of the global state. - // This means that we have to re-apply these buffers on an VertexInput change. - class InputBufferTracker { + // corresponds to a VertexState. On the contrary in Dawn they are part of the global state. + // This means that we have to re-apply these buffers on a VertexState change. + class VertexStateBufferBindingTracker { public: void OnSetIndexBuffer(BufferBase* buffer) { mIndexBufferDirty = true; mIndexBuffer = ToBackend(buffer); } - void OnSetVertexBuffers(uint32_t startSlot, - uint32_t count, - Ref* buffers, - uint64_t* offsets) { - for (uint32_t i = 0; i < count; ++i) { - uint32_t slot = startSlot + i; - mVertexBuffers[slot] = ToBackend(buffers[i].Get()); - mVertexBufferOffsets[slot] = offsets[i]; - } + void OnSetVertexBuffer(uint32_t slot, BufferBase* buffer, uint64_t offset) { + mVertexBuffers[slot] = ToBackend(buffer); + mVertexBufferOffsets[slot] = offset; // Use 64 bit masks and make sure there are no shift UB static_assert(kMaxVertexBuffers <= 8 * sizeof(unsigned long long) - 1, ""); - mDirtyVertexBuffers |= ((1ull << count) - 1ull) << startSlot; + mDirtyVertexBuffers |= 1ull << slot; } void OnSetPipeline(RenderPipelineBase* pipeline) { @@ -169,7 +157,7 @@ namespace dawn_native { namespace opengl { } mIndexBufferDirty = true; - mDirtyVertexBuffers |= pipeline->GetInputsSetMask(); + mDirtyVertexBuffers |= pipeline->GetVertexBufferSlotsUsed(); mLastPipeline = pipeline; } @@ -180,30 +168,32 @@ namespace dawn_native { namespace opengl { mIndexBufferDirty = false; } - for (uint32_t slot : - IterateBitSet(mDirtyVertexBuffers & mLastPipeline->GetInputsSetMask())) { + for (uint32_t slot : IterateBitSet(mDirtyVertexBuffers & + mLastPipeline->GetVertexBufferSlotsUsed())) { for (uint32_t location : - IterateBitSet(mLastPipeline->GetAttributesUsingInput(slot))) { - auto attribute = mLastPipeline->GetAttribute(location); + IterateBitSet(mLastPipeline->GetAttributesUsingVertexBuffer(slot))) { + const VertexAttributeInfo& attribute = + mLastPipeline->GetAttribute(location); GLuint buffer = mVertexBuffers[slot]->GetHandle(); uint64_t offset = mVertexBufferOffsets[slot]; - auto input = mLastPipeline->GetInput(slot); - auto components = VertexFormatNumComponents(attribute.format); - auto formatType = VertexFormatType(attribute.format); + const VertexBufferInfo& vertexBuffer = mLastPipeline->GetVertexBuffer(slot); + uint32_t components = VertexFormatNumComponents(attribute.format); + GLenum formatType = VertexFormatType(attribute.format); GLboolean normalized = VertexFormatIsNormalized(attribute.format); gl.BindBuffer(GL_ARRAY_BUFFER, buffer); if (VertexFormatIsInt(attribute.format)) { - gl.VertexAttribIPointer(location, components, formatType, input.stride, - reinterpret_cast(static_cast( - offset + attribute.offset))); - } else { - gl.VertexAttribPointer( - location, components, formatType, normalized, input.stride, + gl.VertexAttribIPointer( + location, components, formatType, vertexBuffer.arrayStride, reinterpret_cast( static_cast(offset + attribute.offset))); + } else { + gl.VertexAttribPointer(location, components, formatType, normalized, + vertexBuffer.arrayStride, + reinterpret_cast(static_cast( + offset + attribute.offset))); } } } @@ -222,66 +212,156 @@ namespace dawn_native { namespace opengl { RenderPipelineBase* mLastPipeline = nullptr; }; - // Handles SetBindGroup commands with the specifics of translating to OpenGL texture and - // buffer units - void ApplyBindGroup(const OpenGLFunctions& gl, - uint32_t index, - BindGroupBase* group, - PipelineLayout* pipelineLayout, - PipelineGL* pipeline) { - const auto& indices = pipelineLayout->GetBindingIndexInfo()[index]; - const auto& layout = group->GetLayout()->GetBindingInfo(); - - for (uint32_t bindingIndex : IterateBitSet(layout.mask)) { - switch (layout.types[bindingIndex]) { - case dawn::BindingType::UniformBuffer: { - BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); - GLuint buffer = ToBackend(binding.buffer)->GetHandle(); - GLuint uboIndex = indices[bindingIndex]; - - gl.BindBufferRange(GL_UNIFORM_BUFFER, uboIndex, buffer, binding.offset, - binding.size); - } break; - - case dawn::BindingType::Sampler: { - GLuint sampler = - ToBackend(group->GetBindingAsSampler(bindingIndex))->GetHandle(); - GLuint samplerIndex = indices[bindingIndex]; - - for (auto unit : pipeline->GetTextureUnitsForSampler(samplerIndex)) { - gl.BindSampler(unit, sampler); + class BindGroupTracker : public BindGroupTrackerBase { + public: + void OnSetPipeline(RenderPipeline* pipeline) { + BindGroupTrackerBase::OnSetPipeline(pipeline); + mPipeline = pipeline; + } + + void OnSetPipeline(ComputePipeline* pipeline) { + BindGroupTrackerBase::OnSetPipeline(pipeline); + mPipeline = pipeline; + } + + void Apply(const OpenGLFunctions& gl) { + for (BindGroupIndex index : + IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) { + ApplyBindGroup(gl, index, mBindGroups[index], mDynamicOffsetCounts[index], + mDynamicOffsets[index].data()); + } + DidApply(); + } + + private: + void ApplyBindGroup(const OpenGLFunctions& gl, + BindGroupIndex index, + BindGroupBase* group, + uint32_t dynamicOffsetCount, + uint64_t* dynamicOffsets) { + const auto& indices = ToBackend(mPipelineLayout)->GetBindingIndexInfo()[index]; + uint32_t currentDynamicOffsetIndex = 0; + + for (BindingIndex bindingIndex{0}; + bindingIndex < group->GetLayout()->GetBindingCount(); ++bindingIndex) { + const BindingInfo& bindingInfo = + group->GetLayout()->GetBindingInfo(bindingIndex); + + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: { + BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); + GLuint buffer = ToBackend(binding.buffer)->GetHandle(); + GLuint uboIndex = indices[bindingIndex]; + GLuint offset = binding.offset; + + if (bindingInfo.hasDynamicOffset) { + offset += dynamicOffsets[currentDynamicOffsetIndex]; + ++currentDynamicOffsetIndex; + } + + gl.BindBufferRange(GL_UNIFORM_BUFFER, uboIndex, buffer, offset, + binding.size); + break; } - } break; - case dawn::BindingType::SampledTexture: { - TextureView* view = ToBackend(group->GetBindingAsTextureView(bindingIndex)); - GLuint handle = view->GetHandle(); - GLenum target = view->GetGLTarget(); - GLuint viewIndex = indices[bindingIndex]; + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: { + BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); + GLuint buffer = ToBackend(binding.buffer)->GetHandle(); + GLuint ssboIndex = indices[bindingIndex]; + GLuint offset = binding.offset; + + if (bindingInfo.hasDynamicOffset) { + offset += dynamicOffsets[currentDynamicOffsetIndex]; + ++currentDynamicOffsetIndex; + } - for (auto unit : pipeline->GetTextureUnitsForTextureView(viewIndex)) { - gl.ActiveTexture(GL_TEXTURE0 + unit); - gl.BindTexture(target, handle); + gl.BindBufferRange(GL_SHADER_STORAGE_BUFFER, ssboIndex, buffer, offset, + binding.size); + break; } - } break; - case dawn::BindingType::StorageBuffer: { - BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); - GLuint buffer = ToBackend(binding.buffer)->GetHandle(); - GLuint ssboIndex = indices[bindingIndex]; + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: { + Sampler* sampler = ToBackend(group->GetBindingAsSampler(bindingIndex)); + GLuint samplerIndex = indices[bindingIndex]; + + for (PipelineGL::SamplerUnit unit : + mPipeline->GetTextureUnitsForSampler(samplerIndex)) { + // Only use filtering for certain texture units, because int and + // uint texture are only complete without filtering + if (unit.shouldUseFiltering) { + gl.BindSampler(unit.unit, sampler->GetFilteringHandle()); + } else { + gl.BindSampler(unit.unit, sampler->GetNonFilteringHandle()); + } + } + break; + } - gl.BindBufferRange(GL_SHADER_STORAGE_BUFFER, ssboIndex, buffer, - binding.offset, binding.size); - } break; + case wgpu::BindingType::SampledTexture: { + TextureView* view = + ToBackend(group->GetBindingAsTextureView(bindingIndex)); + GLuint handle = view->GetHandle(); + GLenum target = view->GetGLTarget(); + GLuint viewIndex = indices[bindingIndex]; - // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: - UNREACHABLE(); - break; + for (auto unit : mPipeline->GetTextureUnitsForTextureView(viewIndex)) { + gl.ActiveTexture(GL_TEXTURE0 + unit); + gl.BindTexture(target, handle); + } + break; + } + + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + TextureView* view = + ToBackend(group->GetBindingAsTextureView(bindingIndex)); + Texture* texture = ToBackend(view->GetTexture()); + GLuint handle = texture->GetHandle(); + GLuint imageIndex = indices[bindingIndex]; + + GLenum access; + switch (bindingInfo.type) { + case wgpu::BindingType::ReadonlyStorageTexture: + access = GL_READ_ONLY; + break; + case wgpu::BindingType::WriteonlyStorageTexture: + access = GL_WRITE_ONLY; + break; + default: + UNREACHABLE(); + break; + } + + // OpenGL ES only supports either binding a layer or the entire texture + // in glBindImageTexture(). + GLboolean isLayered; + if (view->GetLayerCount() == 1) { + isLayered = GL_FALSE; + } else if (texture->GetArrayLayers() == view->GetLayerCount()) { + isLayered = GL_TRUE; + } else { + UNREACHABLE(); + } + + gl.BindImageTexture(imageIndex, handle, view->GetBaseMipLevel(), + isLayered, view->GetBaseArrayLayer(), access, + texture->GetGLFormat().internalFormat); + break; + } + + case wgpu::BindingType::StorageTexture: + UNREACHABLE(); + break; + + // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. + } } } - } + + PipelineGL* mPipeline = nullptr; + }; void ResolveMultisampledRenderTargets(const OpenGLFunctions& gl, const BeginRenderPassCmd* renderPass) { @@ -290,7 +370,8 @@ namespace dawn_native { namespace opengl { GLuint readFbo = 0; GLuint writeFbo = 0; - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { + for (uint32_t i : + IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { if (renderPass->colorAttachments[i].resolveTarget.Get() != nullptr) { if (readFbo == 0) { ASSERT(writeFbo == 0); @@ -336,10 +417,32 @@ namespace dawn_native { namespace opengl { gl.DeleteFramebuffers(1, &readFbo); gl.DeleteFramebuffers(1, &writeFbo); } + + // OpenGL SPEC requires the source/destination region must be a region that is contained + // within srcImage/dstImage. Here the size of the image refers to the virtual size, while + // Dawn validates texture copy extent with the physical size, so we need to re-calculate the + // texture copy extent to ensure it should fit in the virtual size of the subresource. + Extent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy, + const Extent3D& copySize) { + Extent3D validTextureCopyExtent = copySize; + const TextureBase* texture = textureCopy.texture.Get(); + Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(textureCopy.mipLevel); + if (textureCopy.origin.x + copySize.width > virtualSizeAtLevel.width) { + ASSERT(texture->GetFormat().isCompressed); + validTextureCopyExtent.width = virtualSizeAtLevel.width - textureCopy.origin.x; + } + if (textureCopy.origin.y + copySize.height > virtualSizeAtLevel.height) { + ASSERT(texture->GetFormat().isCompressed); + validTextureCopyExtent.height = virtualSizeAtLevel.height - textureCopy.origin.y; + } + + return validTextureCopyExtent; + } + } // namespace - CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder) - : CommandBufferBase(device, encoder), mCommands(encoder->AcquireCommands()) { + CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) + : CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) { } CommandBuffer::~CommandBuffer() { @@ -349,22 +452,51 @@ namespace dawn_native { namespace opengl { void CommandBuffer::Execute() { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; + auto TransitionForPass = [](const PassResourceUsage& usages) { + for (size_t i = 0; i < usages.textures.size(); i++) { + Texture* texture = ToBackend(usages.textures[i]); + // Clear textures that are not output attachments. Output attachments will be + // cleared in BeginRenderPass by setting the loadop to clear when the + // texture subresource has not been initialized before the render pass. + if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) { + texture->EnsureSubresourceContentInitialized(texture->GetAllSubresources()); + } + } + }; + + const std::vector& passResourceUsages = GetResourceUsages().perPass; + uint32_t nextPassNumber = 0; + Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::BeginComputePass: { mCommands.NextCommand(); + TransitionForPass(passResourceUsages[nextPassNumber]); ExecuteComputePass(); - } break; + + nextPassNumber++; + break; + } case Command::BeginRenderPass: { auto* cmd = mCommands.NextCommand(); + TransitionForPass(passResourceUsages[nextPassNumber]); + + LazyClearRenderPassAttachments(cmd); ExecuteRenderPass(cmd); - } break; + + nextPassNumber++; + break; + } case Command::CopyBufferToBuffer: { CopyBufferToBufferCmd* copy = mCommands.NextCommand(); + ToBackend(copy->source)->EnsureDataInitialized(); + ToBackend(copy->destination) + ->EnsureDataInitializedAsDestination(copy->destinationOffset, copy->size); + gl.BindBuffer(GL_PIXEL_PACK_BUFFER, ToBackend(copy->source)->GetHandle()); gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, ToBackend(copy->destination)->GetHandle()); @@ -373,7 +505,8 @@ namespace dawn_native { namespace opengl { gl.BindBuffer(GL_PIXEL_PACK_BUFFER, 0); gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - } break; + break; + } case Command::CopyBufferToTexture: { CopyBufferToTextureCmd* copy = mCommands.NextCommand(); @@ -383,38 +516,83 @@ namespace dawn_native { namespace opengl { Buffer* buffer = ToBackend(src.buffer.Get()); Texture* texture = ToBackend(dst.texture.Get()); GLenum target = texture->GetGLTarget(); - auto format = texture->GetGLFormat(); + const GLFormat& format = texture->GetGLFormat(); + + buffer->EnsureDataInitialized(); + + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + SubresourceRange subresources = {dst.mipLevel, 1, dst.origin.z, + copy->copySize.depth}; + if (IsCompleteSubresourceCopiedTo(texture, copySize, dst.mipLevel)) { + texture->SetIsSubresourceContentInitialized(true, subresources); + } else { + texture->EnsureSubresourceContentInitialized(subresources); + } gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer->GetHandle()); gl.ActiveTexture(GL_TEXTURE0); gl.BindTexture(target, texture->GetHandle()); - gl.PixelStorei(GL_UNPACK_ROW_LENGTH, - src.rowPitch / TextureFormatPixelSize(texture->GetFormat())); - gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, src.imageHeight); - switch (texture->GetDimension()) { - case dawn::TextureDimension::e2D: - if (texture->GetArrayLayers() > 1) { - gl.TexSubImage3D( - target, dst.level, dst.origin.x, dst.origin.y, dst.slice, - copySize.width, copySize.height, 1, format.format, format.type, - reinterpret_cast(static_cast(src.offset))); - } else { - gl.TexSubImage2D( - target, dst.level, dst.origin.x, dst.origin.y, copySize.width, - copySize.height, format.format, format.type, - reinterpret_cast(static_cast(src.offset))); - } - break; - - default: - UNREACHABLE(); + const Format& formatInfo = texture->GetFormat(); + gl.PixelStorei( + GL_UNPACK_ROW_LENGTH, + src.bytesPerRow / formatInfo.blockByteSize * formatInfo.blockWidth); + gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, src.rowsPerImage); + + if (formatInfo.isCompressed) { + gl.PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_SIZE, formatInfo.blockByteSize); + gl.PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_WIDTH, formatInfo.blockWidth); + gl.PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_HEIGHT, formatInfo.blockHeight); + gl.PixelStorei(GL_UNPACK_COMPRESSED_BLOCK_DEPTH, 1); + + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + uint64_t copyDataSize = (copySize.width / formatInfo.blockWidth) * + (copySize.height / formatInfo.blockHeight) * + formatInfo.blockByteSize * copySize.depth; + Extent3D copyExtent = ComputeTextureCopyExtent(dst, copySize); + + if (texture->GetArrayLayers() > 1) { + gl.CompressedTexSubImage3D( + target, dst.mipLevel, dst.origin.x, dst.origin.y, dst.origin.z, + copyExtent.width, copyExtent.height, copyExtent.depth, + format.internalFormat, copyDataSize, + reinterpret_cast(static_cast(src.offset))); + } else { + gl.CompressedTexSubImage2D( + target, dst.mipLevel, dst.origin.x, dst.origin.y, copyExtent.width, + copyExtent.height, format.internalFormat, copyDataSize, + reinterpret_cast(static_cast(src.offset))); + } + } else { + switch (texture->GetDimension()) { + case wgpu::TextureDimension::e2D: + if (texture->GetArrayLayers() > 1) { + gl.TexSubImage3D(target, dst.mipLevel, dst.origin.x, + dst.origin.y, dst.origin.z, copySize.width, + copySize.height, copySize.depth, format.format, + format.type, + reinterpret_cast( + static_cast(src.offset))); + } else { + gl.TexSubImage2D(target, dst.mipLevel, dst.origin.x, + dst.origin.y, copySize.width, copySize.height, + format.format, format.type, + reinterpret_cast( + static_cast(src.offset))); + } + break; + + default: + UNREACHABLE(); + } } + gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0); gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - } break; + break; + } case Command::CopyTextureToBuffer: { CopyTextureToBufferCmd* copy = mCommands.NextCommand(); @@ -423,9 +601,22 @@ namespace dawn_native { namespace opengl { auto& copySize = copy->copySize; Texture* texture = ToBackend(src.texture.Get()); Buffer* buffer = ToBackend(dst.buffer.Get()); - auto format = texture->GetGLFormat(); + const Format& format = texture->GetFormat(); + const GLFormat& glFormat = texture->GetGLFormat(); GLenum target = texture->GetGLTarget(); + // TODO(jiawei.shao@intel.com): support texture-to-buffer copy with compressed + // texture formats. + if (format.isCompressed) { + UNREACHABLE(); + } + + buffer->EnsureDataInitializedAsDestination(copy); + + ASSERT(texture->GetDimension() == wgpu::TextureDimension::e2D); + SubresourceRange subresources = {src.mipLevel, 1, src.origin.z, + copy->copySize.depth}; + texture->EnsureSubresourceContentInitialized(subresources); // The only way to move data from a texture to a buffer in GL is via // glReadPixels with a pack buffer. Create a temporary FBO for the copy. gl.BindTexture(target, texture->GetHandle()); @@ -433,55 +624,118 @@ namespace dawn_native { namespace opengl { GLuint readFBO = 0; gl.GenFramebuffers(1, &readFBO); gl.BindFramebuffer(GL_READ_FRAMEBUFFER, readFBO); + + GLenum glAttachment = 0; + switch (format.aspect) { + case Format::Aspect::Color: + glAttachment = GL_COLOR_ATTACHMENT0; + break; + case Format::Aspect::Depth: + glAttachment = GL_DEPTH_ATTACHMENT; + break; + case Format::Aspect::Stencil: + glAttachment = GL_STENCIL_ATTACHMENT; + break; + case Format::Aspect::DepthStencil: + glAttachment = GL_DEPTH_STENCIL_ATTACHMENT; + break; + default: + UNREACHABLE(); + break; + } + + gl.BindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle()); + gl.PixelStorei(GL_PACK_ROW_LENGTH, dst.bytesPerRow / format.blockByteSize); + gl.PixelStorei(GL_PACK_IMAGE_HEIGHT, dst.rowsPerImage); + + uint8_t* offset = + reinterpret_cast(static_cast(dst.offset)); switch (texture->GetDimension()) { - case dawn::TextureDimension::e2D: - if (texture->GetArrayLayers() > 1) { - gl.FramebufferTextureLayer( - GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture->GetHandle(), - src.level, src.slice); - } else { - gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_TEXTURE_2D, texture->GetHandle(), - src.level); + case wgpu::TextureDimension::e2D: { + if (texture->GetArrayLayers() == 1) { + gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER, glAttachment, target, + texture->GetHandle(), src.mipLevel); + gl.ReadPixels(src.origin.x, src.origin.y, copySize.width, + copySize.height, glFormat.format, glFormat.type, + offset); + break; } + + const uint64_t bytesPerImage = dst.bytesPerRow * dst.rowsPerImage; + for (uint32_t layer = 0; layer < copySize.depth; ++layer) { + gl.FramebufferTextureLayer(GL_READ_FRAMEBUFFER, glAttachment, + texture->GetHandle(), src.mipLevel, + src.origin.z + layer); + gl.ReadPixels(src.origin.x, src.origin.y, copySize.width, + copySize.height, glFormat.format, glFormat.type, + offset); + + offset += bytesPerImage; + } + break; + } default: UNREACHABLE(); } - gl.BindBuffer(GL_PIXEL_PACK_BUFFER, buffer->GetHandle()); - gl.PixelStorei(GL_PACK_ROW_LENGTH, - dst.rowPitch / TextureFormatPixelSize(texture->GetFormat())); - gl.PixelStorei(GL_PACK_IMAGE_HEIGHT, dst.imageHeight); - ASSERT(copySize.depth == 1 && src.origin.z == 0); - void* offset = reinterpret_cast(static_cast(dst.offset)); - gl.ReadPixels(src.origin.x, src.origin.y, copySize.width, copySize.height, - format.format, format.type, offset); gl.PixelStorei(GL_PACK_ROW_LENGTH, 0); gl.PixelStorei(GL_PACK_IMAGE_HEIGHT, 0); gl.BindBuffer(GL_PIXEL_PACK_BUFFER, 0); gl.DeleteFramebuffers(1, &readFBO); - } break; + break; + } case Command::CopyTextureToTexture: { CopyTextureToTextureCmd* copy = mCommands.NextCommand(); auto& src = copy->source; auto& dst = copy->destination; - auto& copySize = copy->copySize; + + // TODO(jiawei.shao@intel.com): add workaround for the case that imageExtentSrc + // is not equal to imageExtentDst. For example when copySize fits in the virtual + // size of the source image but does not fit in the one of the destination + // image. + Extent3D copySize = ComputeTextureCopyExtent(dst, copy->copySize); Texture* srcTexture = ToBackend(src.texture.Get()); Texture* dstTexture = ToBackend(dst.texture.Get()); - + SubresourceRange srcRange = {src.mipLevel, 1, src.origin.z, + copy->copySize.depth}; + SubresourceRange dstRange = {dst.mipLevel, 1, dst.origin.z, + copy->copySize.depth}; + + srcTexture->EnsureSubresourceContentInitialized(srcRange); + if (IsCompleteSubresourceCopiedTo(dstTexture, copySize, dst.mipLevel)) { + dstTexture->SetIsSubresourceContentInitialized(true, dstRange); + } else { + dstTexture->EnsureSubresourceContentInitialized(dstRange); + } gl.CopyImageSubData(srcTexture->GetHandle(), srcTexture->GetGLTarget(), - src.level, src.origin.x, src.origin.y, src.slice, + src.mipLevel, src.origin.x, src.origin.y, src.origin.z, dstTexture->GetHandle(), dstTexture->GetGLTarget(), - dst.level, dst.origin.x, dst.origin.y, dst.slice, - copySize.width, copySize.height, 1); - } break; + dst.mipLevel, dst.origin.x, dst.origin.y, dst.origin.z, + copySize.width, copySize.height, copy->copySize.depth); + break; + } + + case Command::ResolveQuerySet: { + // TODO(hao.x.li@intel.com): Resolve non-precise occlusion query. + SkipCommand(&mCommands, type); + break; + } + + case Command::WriteTimestamp: { + // WriteTimestamp is not supported on OpenGL + UNREACHABLE(); + break; + } - default: { UNREACHABLE(); } break; + default: { + UNREACHABLE(); + break; + } } } } @@ -489,6 +743,7 @@ namespace dawn_native { namespace opengl { void CommandBuffer::ExecuteComputePass() { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; ComputePipeline* lastPipeline = nullptr; + BindGroupTracker bindGroupTracker = {}; Command type; while (mCommands.NextCommandId(&type)) { @@ -496,17 +751,21 @@ namespace dawn_native { namespace opengl { case Command::EndComputePass: { mCommands.NextCommand(); return; - } break; + } case Command::Dispatch: { DispatchCmd* dispatch = mCommands.NextCommand(); + bindGroupTracker.Apply(gl); + gl.DispatchCompute(dispatch->x, dispatch->y, dispatch->z); // TODO(cwallez@chromium.org): add barriers to the API gl.MemoryBarrier(GL_ALL_BARRIER_BITS); - } break; + break; + } case Command::DispatchIndirect: { DispatchIndirectCmd* dispatch = mCommands.NextCommand(); + bindGroupTracker.Apply(gl); uint64_t indirectBufferOffset = dispatch->indirectOffset; Buffer* indirectBuffer = ToBackend(dispatch->indirectBuffer.Get()); @@ -515,21 +774,48 @@ namespace dawn_native { namespace opengl { gl.DispatchComputeIndirect(static_cast(indirectBufferOffset)); // TODO(cwallez@chromium.org): add barriers to the API gl.MemoryBarrier(GL_ALL_BARRIER_BITS); - } break; + break; + } case Command::SetComputePipeline: { SetComputePipelineCmd* cmd = mCommands.NextCommand(); lastPipeline = ToBackend(cmd->pipeline).Get(); lastPipeline->ApplyNow(); - } break; + + bindGroupTracker.OnSetPipeline(lastPipeline); + break; + } case Command::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); - ApplyBindGroup(gl, cmd->index, cmd->group.Get(), - ToBackend(lastPipeline->GetLayout()), lastPipeline); - } break; + uint32_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + } + bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(), + cmd->dynamicOffsetCount, dynamicOffsets); + break; + } - default: { UNREACHABLE(); } break; + case Command::InsertDebugMarker: + case Command::PopDebugGroup: + case Command::PushDebugGroup: { + // Due to lack of linux driver support for GL_EXT_debug_marker + // extension these functions are skipped. + SkipCommand(&mCommands, type); + break; + } + + case Command::WriteTimestamp: { + // WriteTimestamp is not supported on OpenGL + UNREACHABLE(); + break; + } + + default: { + UNREACHABLE(); + break; + } } } @@ -561,7 +847,8 @@ namespace dawn_native { namespace opengl { // Construct GL framebuffer unsigned int attachmentCount = 0; - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { + for (uint32_t i : + IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { TextureViewBase* textureView = renderPass->colorAttachments[i].view.Get(); GLuint texture = ToBackend(textureView->GetTexture())->GetHandle(); @@ -577,89 +864,100 @@ namespace dawn_native { namespace opengl { } drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i; attachmentCount = i + 1; - - // TODO(kainino@chromium.org): the color clears (later in - // this function) may be undefined for non-normalized integer formats. - dawn::TextureFormat format = textureView->GetTexture()->GetFormat(); - ASSERT(format == dawn::TextureFormat::R8G8B8A8Unorm || - format == dawn::TextureFormat::R8G8Unorm || - format == dawn::TextureFormat::R8Unorm || - format == dawn::TextureFormat::B8G8R8A8Unorm); } gl.DrawBuffers(attachmentCount, drawBuffers.data()); - if (renderPass->hasDepthStencilAttachment) { + if (renderPass->attachmentState->HasDepthStencilAttachment()) { TextureViewBase* textureView = renderPass->depthStencilAttachment.view.Get(); GLuint texture = ToBackend(textureView->GetTexture())->GetHandle(); - dawn::TextureFormat format = textureView->GetTexture()->GetFormat(); + const Format& format = textureView->GetTexture()->GetFormat(); // Attach depth/stencil buffer. GLenum glAttachment = 0; // TODO(kainino@chromium.org): it may be valid to just always use // GL_DEPTH_STENCIL_ATTACHMENT here. - if (TextureFormatHasDepth(format)) { - if (TextureFormatHasStencil(format)) { - glAttachment = GL_DEPTH_STENCIL_ATTACHMENT; - } else { + switch (format.aspect) { + case Format::Aspect::Depth: glAttachment = GL_DEPTH_ATTACHMENT; - } - } else { - glAttachment = GL_STENCIL_ATTACHMENT; + break; + case Format::Aspect::Stencil: + glAttachment = GL_STENCIL_ATTACHMENT; + break; + case Format::Aspect::DepthStencil: + glAttachment = GL_DEPTH_STENCIL_ATTACHMENT; + break; + default: + UNREACHABLE(); + break; } - GLenum target = ToBackend(textureView->GetTexture())->GetGLTarget(); - gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, glAttachment, target, texture, 0); - - // TODO(kainino@chromium.org): the depth/stencil clears (later in - // this function) may be undefined for other texture formats. - ASSERT(format == dawn::TextureFormat::D32FloatS8Uint); + if (textureView->GetTexture()->GetArrayLayers() == 1) { + GLenum target = ToBackend(textureView->GetTexture())->GetGLTarget(); + gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, glAttachment, target, texture, + textureView->GetBaseMipLevel()); + } else { + gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, glAttachment, texture, + textureView->GetBaseMipLevel(), + textureView->GetBaseArrayLayer()); + } } } + ASSERT(gl.CheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE); + // Set defaults for dynamic state before executing clears and commands. PersistentPipelineState persistentPipelineState; persistentPipelineState.SetDefaultState(gl); gl.BlendColor(0, 0, 0, 0); gl.Viewport(0, 0, renderPass->width, renderPass->height); + gl.DepthRangef(0.0, 1.0); gl.Scissor(0, 0, renderPass->width, renderPass->height); // Clear framebuffer attachments as needed { - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { - const auto& attachmentInfo = renderPass->colorAttachments[i]; + for (uint32_t i : + IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { + auto* attachmentInfo = &renderPass->colorAttachments[i]; // Load op - color - if (attachmentInfo.loadOp == dawn::LoadOp::Clear) { + // TODO(cwallez@chromium.org): Choose the clear function depending on the + // componentType: things work for now because the clear color is always a float, but + // when that's fixed will lose precision on integer formats when converting to + // float. + if (attachmentInfo->loadOp == wgpu::LoadOp::Clear) { gl.ColorMaski(i, true, true, true, true); - gl.ClearBufferfv(GL_COLOR, i, &attachmentInfo.clearColor.r); + gl.ClearBufferfv(GL_COLOR, i, &attachmentInfo->clearColor.r); + } + + if (attachmentInfo->storeOp == wgpu::StoreOp::Clear) { + // TODO(natlee@microsoft.com): call glDiscard to do optimization } } - if (renderPass->hasDepthStencilAttachment) { - const auto& attachmentInfo = renderPass->depthStencilAttachment; - dawn::TextureFormat attachmentFormat = - attachmentInfo.view->GetTexture()->GetFormat(); + if (renderPass->attachmentState->HasDepthStencilAttachment()) { + auto* attachmentInfo = &renderPass->depthStencilAttachment; + const Format& attachmentFormat = attachmentInfo->view->GetTexture()->GetFormat(); // Load op - depth/stencil - bool doDepthClear = TextureFormatHasDepth(attachmentFormat) && - (attachmentInfo.depthLoadOp == dawn::LoadOp::Clear); - bool doStencilClear = TextureFormatHasStencil(attachmentFormat) && - (attachmentInfo.stencilLoadOp == dawn::LoadOp::Clear); + bool doDepthClear = attachmentFormat.HasDepth() && + (attachmentInfo->depthLoadOp == wgpu::LoadOp::Clear); + bool doStencilClear = attachmentFormat.HasStencil() && + (attachmentInfo->stencilLoadOp == wgpu::LoadOp::Clear); if (doDepthClear) { gl.DepthMask(GL_TRUE); } if (doStencilClear) { - gl.StencilMask(GetStencilMaskFromStencilFormat(attachmentFormat)); + gl.StencilMask(GetStencilMaskFromStencilFormat(attachmentFormat.format)); } if (doDepthClear && doStencilClear) { - gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, attachmentInfo.clearDepth, - attachmentInfo.clearStencil); + gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, attachmentInfo->clearDepth, + attachmentInfo->clearStencil); } else if (doDepthClear) { - gl.ClearBufferfv(GL_DEPTH, 0, &attachmentInfo.clearDepth); + gl.ClearBufferfv(GL_DEPTH, 0, &attachmentInfo->clearDepth); } else if (doStencilClear) { - const GLint clearStencil = attachmentInfo.clearStencil; + const GLint clearStencil = attachmentInfo->clearStencil; gl.ClearBufferiv(GL_STENCIL, 0, &clearStencil); } } @@ -668,25 +966,15 @@ namespace dawn_native { namespace opengl { RenderPipeline* lastPipeline = nullptr; uint64_t indexBufferBaseOffset = 0; - InputBufferTracker inputBuffers; + VertexStateBufferBindingTracker vertexStateBufferBindingTracker; + BindGroupTracker bindGroupTracker = {}; - Command type; - while (mCommands.NextCommandId(&type)) { + auto DoRenderBundleCommand = [&](CommandIterator* iter, Command type) { switch (type) { - case Command::EndRenderPass: { - mCommands.NextCommand(); - - if (renderPass->sampleCount > 1) { - ResolveMultisampledRenderTargets(gl, renderPass); - } - - gl.DeleteFramebuffers(1, &fbo); - return; - } break; - case Command::Draw: { - DrawCmd* draw = mCommands.NextCommand(); - inputBuffers.Apply(gl); + DrawCmd* draw = iter->NextCommand(); + vertexStateBufferBindingTracker.Apply(gl); + bindGroupTracker.Apply(gl); if (draw->firstInstance > 0) { gl.DrawArraysInstancedBaseInstance( @@ -698,14 +986,16 @@ namespace dawn_native { namespace opengl { draw->firstVertex, draw->vertexCount, draw->instanceCount); } - } break; + break; + } case Command::DrawIndexed: { - DrawIndexedCmd* draw = mCommands.NextCommand(); - inputBuffers.Apply(gl); + DrawIndexedCmd* draw = iter->NextCommand(); + vertexStateBufferBindingTracker.Apply(gl); + bindGroupTracker.Apply(gl); - dawn::IndexFormat indexFormat = - lastPipeline->GetVertexInputDescriptor()->indexFormat; + wgpu::IndexFormat indexFormat = + lastPipeline->GetVertexStateDescriptor()->indexFormat; size_t formatSize = IndexFormatSize(indexFormat); GLenum formatType = IndexFormatType(indexFormat); @@ -716,18 +1006,31 @@ namespace dawn_native { namespace opengl { indexBufferBaseOffset), draw->instanceCount, draw->baseVertex, draw->firstInstance); } else { - // This branch is only needed on OpenGL < 4.2 - gl.DrawElementsInstancedBaseVertex( - lastPipeline->GetGLPrimitiveTopology(), draw->indexCount, formatType, - reinterpret_cast(draw->firstIndex * formatSize + - indexBufferBaseOffset), - draw->instanceCount, draw->baseVertex); + // This branch is only needed on OpenGL < 4.2; ES < 3.2 + if (draw->baseVertex != 0) { + gl.DrawElementsInstancedBaseVertex( + lastPipeline->GetGLPrimitiveTopology(), draw->indexCount, + formatType, + reinterpret_cast(draw->firstIndex * formatSize + + indexBufferBaseOffset), + draw->instanceCount, draw->baseVertex); + } else { + // This branch is only needed on OpenGL < 3.2; ES < 3.2 + gl.DrawElementsInstanced( + lastPipeline->GetGLPrimitiveTopology(), draw->indexCount, + formatType, + reinterpret_cast(draw->firstIndex * formatSize + + indexBufferBaseOffset), + draw->instanceCount); + } } - } break; + break; + } case Command::DrawIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); - inputBuffers.Apply(gl); + DrawIndirectCmd* draw = iter->NextCommand(); + vertexStateBufferBindingTracker.Apply(gl); + bindGroupTracker.Apply(gl); uint64_t indirectBufferOffset = draw->indirectOffset; Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get()); @@ -736,14 +1039,16 @@ namespace dawn_native { namespace opengl { gl.DrawArraysIndirect( lastPipeline->GetGLPrimitiveTopology(), reinterpret_cast(static_cast(indirectBufferOffset))); - } break; + break; + } case Command::DrawIndexedIndirect: { - DrawIndexedIndirectCmd* draw = mCommands.NextCommand(); - inputBuffers.Apply(gl); + DrawIndexedIndirectCmd* draw = iter->NextCommand(); + vertexStateBufferBindingTracker.Apply(gl); + bindGroupTracker.Apply(gl); - dawn::IndexFormat indexFormat = - lastPipeline->GetVertexInputDescriptor()->indexFormat; + wgpu::IndexFormat indexFormat = + lastPipeline->GetVertexStateDescriptor()->indexFormat; GLenum formatType = IndexFormatType(indexFormat); uint64_t indirectBufferOffset = draw->indirectOffset; @@ -753,59 +1058,121 @@ namespace dawn_native { namespace opengl { gl.DrawElementsIndirect( lastPipeline->GetGLPrimitiveTopology(), formatType, reinterpret_cast(static_cast(indirectBufferOffset))); - } break; + break; + } case Command::InsertDebugMarker: case Command::PopDebugGroup: case Command::PushDebugGroup: { // Due to lack of linux driver support for GL_EXT_debug_marker // extension these functions are skipped. - SkipCommand(&mCommands, type); - } break; + SkipCommand(iter, type); + break; + } case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mCommands.NextCommand(); + SetRenderPipelineCmd* cmd = iter->NextCommand(); lastPipeline = ToBackend(cmd->pipeline).Get(); lastPipeline->ApplyNow(persistentPipelineState); - inputBuffers.OnSetPipeline(lastPipeline); - } break; + vertexStateBufferBindingTracker.OnSetPipeline(lastPipeline); + bindGroupTracker.OnSetPipeline(lastPipeline); + break; + } + + case Command::SetBindGroup: { + SetBindGroupCmd* cmd = iter->NextCommand(); + uint32_t* dynamicOffsets = nullptr; + if (cmd->dynamicOffsetCount > 0) { + dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); + } + bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(), + cmd->dynamicOffsetCount, dynamicOffsets); + break; + } + + case Command::SetIndexBuffer: { + SetIndexBufferCmd* cmd = iter->NextCommand(); + indexBufferBaseOffset = cmd->offset; + vertexStateBufferBindingTracker.OnSetIndexBuffer(cmd->buffer.Get()); + break; + } + + case Command::SetVertexBuffer: { + SetVertexBufferCmd* cmd = iter->NextCommand(); + vertexStateBufferBindingTracker.OnSetVertexBuffer(cmd->slot, cmd->buffer.Get(), + cmd->offset); + break; + } + + default: + UNREACHABLE(); + break; + } + }; + + Command type; + while (mCommands.NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + mCommands.NextCommand(); + + if (renderPass->attachmentState->GetSampleCount() > 1) { + ResolveMultisampledRenderTargets(gl, renderPass); + } + gl.DeleteFramebuffers(1, &fbo); + return; + } case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); persistentPipelineState.SetStencilReference(gl, cmd->reference); - } break; + break; + } + + case Command::SetViewport: { + SetViewportCmd* cmd = mCommands.NextCommand(); + gl.ViewportIndexedf(0, cmd->x, cmd->y, cmd->width, cmd->height); + gl.DepthRangef(cmd->minDepth, cmd->maxDepth); + break; + } case Command::SetScissorRect: { SetScissorRectCmd* cmd = mCommands.NextCommand(); gl.Scissor(cmd->x, cmd->y, cmd->width, cmd->height); - } break; + break; + } case Command::SetBlendColor: { SetBlendColorCmd* cmd = mCommands.NextCommand(); gl.BlendColor(cmd->color.r, cmd->color.g, cmd->color.b, cmd->color.a); - } break; + break; + } - case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mCommands.NextCommand(); - ApplyBindGroup(gl, cmd->index, cmd->group.Get(), - ToBackend(lastPipeline->GetLayout()), lastPipeline); - } break; + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = mCommands.NextCommand(); + auto bundles = mCommands.NextData>(cmd->count); - case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mCommands.NextCommand(); - indexBufferBaseOffset = cmd->offset; - inputBuffers.OnSetIndexBuffer(cmd->buffer.Get()); - } break; + for (uint32_t i = 0; i < cmd->count; ++i) { + CommandIterator* iter = bundles[i]->GetCommands(); + iter->Reset(); + while (iter->NextCommandId(&type)) { + DoRenderBundleCommand(iter, type); + } + } + break; + } - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mCommands.NextCommand(); - auto buffers = mCommands.NextData>(cmd->count); - auto offsets = mCommands.NextData(cmd->count); - inputBuffers.OnSetVertexBuffers(cmd->startSlot, cmd->count, buffers, offsets); - } break; + case Command::WriteTimestamp: { + // WriteTimestamp is not supported on OpenGL + UNREACHABLE(); + break; + } - default: { UNREACHABLE(); } break; + default: { + DoRenderBundleCommand(&mCommands, type); + break; + } } } diff --git a/third_party/dawn/src/dawn_native/opengl/CommandBufferGL.h b/third_party/dawn/src/dawn_native/opengl/CommandBufferGL.h index 983f0b519d4..482f10d09d4 100644 --- a/third_party/dawn/src/dawn_native/opengl/CommandBufferGL.h +++ b/third_party/dawn/src/dawn_native/opengl/CommandBufferGL.h @@ -26,14 +26,14 @@ namespace dawn_native { namespace opengl { class Device; - class CommandBuffer : public CommandBufferBase { + class CommandBuffer final : public CommandBufferBase { public: - CommandBuffer(Device* device, CommandEncoderBase* encoder); - ~CommandBuffer(); + CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor); void Execute(); private: + ~CommandBuffer() override; void ExecuteComputePass(); void ExecuteRenderPass(BeginRenderPassCmd* renderPass); diff --git a/third_party/dawn/src/dawn_native/opengl/ComputePipelineGL.cpp b/third_party/dawn/src/dawn_native/opengl/ComputePipelineGL.cpp index bb4e8f5c77c..ab25bb1e23d 100644 --- a/third_party/dawn/src/dawn_native/opengl/ComputePipelineGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/ComputePipelineGL.cpp @@ -21,7 +21,7 @@ namespace dawn_native { namespace opengl { ComputePipeline::ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor) : ComputePipelineBase(device, descriptor) { PerStage modules(nullptr); - modules[dawn::ShaderStage::Compute] = ToBackend(descriptor->computeStage->module); + modules[SingleShaderStage::Compute] = ToBackend(descriptor->computeStage.module); PipelineGL::Initialize(device->gl, ToBackend(descriptor->layout), modules); } diff --git a/third_party/dawn/src/dawn_native/opengl/ComputePipelineGL.h b/third_party/dawn/src/dawn_native/opengl/ComputePipelineGL.h index 12856cade24..95f6db91312 100644 --- a/third_party/dawn/src/dawn_native/opengl/ComputePipelineGL.h +++ b/third_party/dawn/src/dawn_native/opengl/ComputePipelineGL.h @@ -19,17 +19,20 @@ #include "dawn_native/opengl/PipelineGL.h" -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { class Device; - class ComputePipeline : public ComputePipelineBase, public PipelineGL { + class ComputePipeline final : public ComputePipelineBase, public PipelineGL { public: ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor); void ApplyNow(); + + private: + ~ComputePipeline() override = default; }; }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/DeviceGL.cpp b/third_party/dawn/src/dawn_native/opengl/DeviceGL.cpp index 33d44a7c7ef..2f06d85da9b 100644 --- a/third_party/dawn/src/dawn_native/opengl/DeviceGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/DeviceGL.cpp @@ -15,13 +15,16 @@ #include "dawn_native/opengl/DeviceGL.h" #include "dawn_native/BackendConnection.h" -#include "dawn_native/BindGroup.h" #include "dawn_native/BindGroupLayout.h" -#include "dawn_native/DynamicUploader.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/StagingBuffer.h" +#include "dawn_native/opengl/BindGroupGL.h" +#include "dawn_native/opengl/BindGroupLayoutGL.h" #include "dawn_native/opengl/BufferGL.h" #include "dawn_native/opengl/CommandBufferGL.h" #include "dawn_native/opengl/ComputePipelineGL.h" #include "dawn_native/opengl/PipelineLayoutGL.h" +#include "dawn_native/opengl/QuerySetGL.h" #include "dawn_native/opengl/QueueGL.h" #include "dawn_native/opengl/RenderPipelineGL.h" #include "dawn_native/opengl/SamplerGL.h" @@ -31,42 +34,80 @@ namespace dawn_native { namespace opengl { + // static + ResultOrError Device::Create(AdapterBase* adapter, + const DeviceDescriptor* descriptor, + const OpenGLFunctions& functions) { + Ref device = AcquireRef(new Device(adapter, descriptor, functions)); + DAWN_TRY(device->Initialize()); + return device.Detach(); + } + Device::Device(AdapterBase* adapter, const DeviceDescriptor* descriptor, const OpenGLFunctions& functions) : DeviceBase(adapter, descriptor), gl(functions) { - if (descriptor != nullptr) { - ApplyToggleOverrides(descriptor); - } } Device::~Device() { - CheckPassedFences(); - ASSERT(mFencesInFlight.empty()); + ShutDownBase(); + } + + MaybeError Device::Initialize() { + InitTogglesFromDriver(); + mFormatTable = BuildGLFormatTable(); + + return DeviceBase::Initialize(new Queue(this)); + } + + void Device::InitTogglesFromDriver() { + bool supportsBaseVertex = gl.IsAtLeastGLES(3, 2) || gl.IsAtLeastGL(3, 2); + + bool supportsBaseInstance = gl.IsAtLeastGLES(3, 2) || gl.IsAtLeastGL(4, 2); + + // TODO(crbug.com/dawn/343): We can support the extension variants, but need to load the EXT + // procs without the extension suffix. + // We'll also need emulation of shader builtins gl_BaseVertex and gl_BaseInstance. - // Some operations might have been started since the last submit and waiting - // on a serial that doesn't have a corresponding fence enqueued. Force all - // operations to look as if they were completed (because they were). - mCompletedSerial = mLastSubmittedSerial + 1; + // supportsBaseVertex |= + // (gl.IsAtLeastGLES(2, 0) && + // (gl.IsGLExtensionSupported("OES_draw_elements_base_vertex") || + // gl.IsGLExtensionSupported("EXT_draw_elements_base_vertex"))) || + // (gl.IsAtLeastGL(3, 1) && gl.IsGLExtensionSupported("ARB_draw_elements_base_vertex")); + + // supportsBaseInstance |= + // (gl.IsAtLeastGLES(3, 1) && gl.IsGLExtensionSupported("EXT_base_instance")) || + // (gl.IsAtLeastGL(3, 1) && gl.IsGLExtensionSupported("ARB_base_instance")); + + // TODO(crbug.com/dawn/343): Investigate emulation. + SetToggle(Toggle::DisableBaseVertex, !supportsBaseVertex); + SetToggle(Toggle::DisableBaseInstance, !supportsBaseInstance); + } - mDynamicUploader = nullptr; + const GLFormat& Device::GetGLFormat(const Format& format) { + ASSERT(format.isSupported); + ASSERT(format.GetIndex() < mFormatTable.size()); - Tick(); + const GLFormat& result = mFormatTable[format.GetIndex()]; + ASSERT(result.isSupportedOnBackend); + return result; } ResultOrError Device::CreateBindGroupImpl( const BindGroupDescriptor* descriptor) { - return new BindGroup(this, descriptor); + DAWN_TRY(ValidateGLBindGroupDescriptor(descriptor)); + return BindGroup::Create(this, descriptor); } ResultOrError Device::CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) { return new BindGroupLayout(this, descriptor); } - ResultOrError Device::CreateBufferImpl(const BufferDescriptor* descriptor) { - return new Buffer(this, descriptor); + ResultOrError> Device::CreateBufferImpl(const BufferDescriptor* descriptor) { + return AcquireRef(new Buffer(this, descriptor)); } - CommandBufferBase* Device::CreateCommandBuffer(CommandEncoderBase* encoder) { - return new CommandBuffer(this, encoder); + CommandBufferBase* Device::CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) { + return new CommandBuffer(encoder, descriptor); } ResultOrError Device::CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) { @@ -76,8 +117,8 @@ namespace dawn_native { namespace opengl { const PipelineLayoutDescriptor* descriptor) { return new PipelineLayout(this, descriptor); } - ResultOrError Device::CreateQueueImpl() { - return new Queue(this); + ResultOrError Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) { + return new QuerySet(this, descriptor); } ResultOrError Device::CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) { @@ -88,14 +129,20 @@ namespace dawn_native { namespace opengl { } ResultOrError Device::CreateShaderModuleImpl( const ShaderModuleDescriptor* descriptor) { - return new ShaderModule(this, descriptor); + return ShaderModule::Create(this, descriptor); } ResultOrError Device::CreateSwapChainImpl( const SwapChainDescriptor* descriptor) { return new SwapChain(this, descriptor); } - ResultOrError Device::CreateTextureImpl(const TextureDescriptor* descriptor) { - return new Texture(this, descriptor); + ResultOrError Device::CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) { + return DAWN_VALIDATION_ERROR("New swapchains not implemented."); + } + ResultOrError> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { + return AcquireRef(new Texture(this, descriptor)); } ResultOrError Device::CreateTextureViewImpl( TextureBase* texture, @@ -105,45 +152,36 @@ namespace dawn_native { namespace opengl { void Device::SubmitFenceSync() { GLsync sync = gl.FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); - mLastSubmittedSerial++; - mFencesInFlight.emplace(sync, mLastSubmittedSerial); - } - - Serial Device::GetCompletedCommandSerial() const { - return mCompletedSerial; - } - - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; + IncrementLastSubmittedCommandSerial(); + mFencesInFlight.emplace(sync, GetLastSubmittedCommandSerial()); } - void Device::TickImpl() { - CheckPassedFences(); + MaybeError Device::TickImpl() { + return {}; } - void Device::CheckPassedFences() { + Serial Device::CheckAndUpdateCompletedSerials() { + Serial fenceSerial = 0; while (!mFencesInFlight.empty()) { GLsync sync = mFencesInFlight.front().first; - Serial fenceSerial = mFencesInFlight.front().second; + Serial tentativeSerial = mFencesInFlight.front().second; // Fence are added in order, so we can stop searching as soon // as we see one that's not ready. GLenum result = gl.ClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0); if (result == GL_TIMEOUT_EXPIRED) { - continue; + return fenceSerial; } + // Update fenceSerial since fence is ready. + fenceSerial = tentativeSerial; gl.DeleteSync(sync); mFencesInFlight.pop(); - ASSERT(fenceSerial > mCompletedSerial); - mCompletedSerial = fenceSerial; + ASSERT(fenceSerial > GetCompletedCommandSerial()); } + return fenceSerial; } ResultOrError> Device::CreateStagingBuffer(size_t size) { @@ -158,4 +196,16 @@ namespace dawn_native { namespace opengl { return DAWN_UNIMPLEMENTED_ERROR("Device unable to copy from staging buffer."); } + void Device::ShutDownImpl() { + ASSERT(GetState() == State::Disconnected); + } + + MaybeError Device::WaitForIdleForDestruction() { + gl.Finish(); + CheckPassedSerials(); + ASSERT(mFencesInFlight.empty()); + + return {}; + } + }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/DeviceGL.h b/third_party/dawn/src/dawn_native/opengl/DeviceGL.h index bc1938d7637..a297081d83c 100644 --- a/third_party/dawn/src/dawn_native/opengl/DeviceGL.h +++ b/third_party/dawn/src/dawn_native/opengl/DeviceGL.h @@ -19,7 +19,9 @@ #include "common/Platform.h" #include "dawn_native/Device.h" +#include "dawn_native/QuerySet.h" #include "dawn_native/opengl/Forward.h" +#include "dawn_native/opengl/GLFormat.h" #include "dawn_native/opengl/OpenGLFunctions.h" #include @@ -33,23 +35,25 @@ namespace dawn_native { namespace opengl { class Device : public DeviceBase { public: - Device(AdapterBase* adapter, - const DeviceDescriptor* descriptor, - const OpenGLFunctions& functions); - ~Device(); + static ResultOrError Create(AdapterBase* adapter, + const DeviceDescriptor* descriptor, + const OpenGLFunctions& functions); + ~Device() override; + + MaybeError Initialize(); // Contains all the OpenGL entry points, glDoFoo is called via device->gl.DoFoo. const OpenGLFunctions gl; + const GLFormat& GetGLFormat(const Format& format); + void SubmitFenceSync(); // Dawn API - CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) override; + CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) override; - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; - Serial GetPendingCommandSerial() const override; - void TickImpl() override; + MaybeError TickImpl() override; ResultOrError> CreateStagingBuffer(size_t size) override; MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, @@ -59,16 +63,22 @@ namespace dawn_native { namespace opengl { uint64_t size) override; private: + Device(AdapterBase* adapter, + const DeviceDescriptor* descriptor, + const OpenGLFunctions& functions); + ResultOrError CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override; ResultOrError CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) override; - ResultOrError CreateBufferImpl(const BufferDescriptor* descriptor) override; + ResultOrError> CreateBufferImpl( + const BufferDescriptor* descriptor) override; ResultOrError CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) override; ResultOrError CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) override; - ResultOrError CreateQueueImpl() override; + ResultOrError CreateQuerySetImpl( + const QuerySetDescriptor* descriptor) override; ResultOrError CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) override; ResultOrError CreateSamplerImpl(const SamplerDescriptor* descriptor) override; @@ -76,16 +86,24 @@ namespace dawn_native { namespace opengl { const ShaderModuleDescriptor* descriptor) override; ResultOrError CreateSwapChainImpl( const SwapChainDescriptor* descriptor) override; - ResultOrError CreateTextureImpl(const TextureDescriptor* descriptor) override; + ResultOrError CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) override; + ResultOrError> CreateTextureImpl( + const TextureDescriptor* descriptor) override; ResultOrError CreateTextureViewImpl( TextureBase* texture, const TextureViewDescriptor* descriptor) override; - void CheckPassedFences(); + void InitTogglesFromDriver(); + Serial CheckAndUpdateCompletedSerials() override; + void ShutDownImpl() override; + MaybeError WaitForIdleForDestruction() override; - Serial mCompletedSerial = 0; - Serial mLastSubmittedSerial = 0; std::queue> mFencesInFlight; + + GLFormatTable mFormatTable; }; }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/Forward.h b/third_party/dawn/src/dawn_native/opengl/Forward.h index 6542ff90299..82d07661ae8 100644 --- a/third_party/dawn/src/dawn_native/opengl/Forward.h +++ b/third_party/dawn/src/dawn_native/opengl/Forward.h @@ -17,23 +17,18 @@ #include "dawn_native/ToBackend.h" -namespace { - class BindGroupBase; - class BindGroup; - class RenderPassDescriptor; -} // namespace - namespace dawn_native { namespace opengl { class Adapter; - using BindGroup = BindGroupBase; - using BindGroupLayout = BindGroupLayoutBase; + class BindGroup; + class BindGroupLayout; class Buffer; class CommandBuffer; class ComputePipeline; class Device; class PersistentPipelineState; class PipelineLayout; + class QuerySet; class Queue; class RenderPipeline; class Sampler; @@ -51,6 +46,7 @@ namespace dawn_native { namespace opengl { using ComputePipelineType = ComputePipeline; using DeviceType = Device; using PipelineLayoutType = PipelineLayout; + using QuerySetType = QuerySet; using QueueType = Queue; using RenderPipelineType = RenderPipeline; using SamplerType = Sampler; diff --git a/third_party/dawn/src/dawn_native/opengl/GLFormat.cpp b/third_party/dawn/src/dawn_native/opengl/GLFormat.cpp new file mode 100644 index 00000000000..9e1a20c09bb --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/GLFormat.cpp @@ -0,0 +1,118 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/opengl/GLFormat.h" + +namespace dawn_native { namespace opengl { + + GLFormatTable BuildGLFormatTable() { + GLFormatTable table; + + using Type = GLFormat::ComponentType; + + auto AddFormat = [&table](wgpu::TextureFormat dawnFormat, GLenum internalFormat, + GLenum format, GLenum type, Type componentType) { + size_t index = ComputeFormatIndex(dawnFormat); + ASSERT(index < table.size()); + + table[index].internalFormat = internalFormat; + table[index].format = format; + table[index].type = type; + table[index].componentType = componentType; + table[index].isSupportedOnBackend = true; + }; + + // It's dangerous to go alone, take this: + // + // [ANGLE's formatutils.cpp] + // [ANGLE's formatutilsgl.cpp] + // + // The format tables in these files are extremely complete and the best reference on GL + // format support, enums, etc. + + // clang-format off + + // 1 byte color formats + AddFormat(wgpu::TextureFormat::R8Unorm, GL_R8, GL_RED, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::R8Snorm, GL_R8_SNORM, GL_RED, GL_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::R8Uint, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, Type::Uint); + AddFormat(wgpu::TextureFormat::R8Sint, GL_R8I, GL_RED_INTEGER, GL_BYTE, Type::Int); + + // 2 bytes color formats + AddFormat(wgpu::TextureFormat::R16Uint, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, Type::Uint); + AddFormat(wgpu::TextureFormat::R16Sint, GL_R16I, GL_RED_INTEGER, GL_SHORT, Type::Int); + AddFormat(wgpu::TextureFormat::R16Float, GL_R16F, GL_RED, GL_HALF_FLOAT, Type::Float); + AddFormat(wgpu::TextureFormat::RG8Unorm, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::RG8Snorm, GL_RG8_SNORM, GL_RG, GL_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::RG8Uint, GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE, Type::Uint); + AddFormat(wgpu::TextureFormat::RG8Sint, GL_RG8I, GL_RG_INTEGER, GL_BYTE, Type::Int); + + // 4 bytes color formats + AddFormat(wgpu::TextureFormat::R32Uint, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, Type::Uint); + AddFormat(wgpu::TextureFormat::R32Sint, GL_R32I, GL_RED_INTEGER, GL_INT, Type::Int); + AddFormat(wgpu::TextureFormat::R32Float, GL_R32F, GL_RED, GL_FLOAT, Type::Float); + AddFormat(wgpu::TextureFormat::RG16Uint, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, Type::Uint); + AddFormat(wgpu::TextureFormat::RG16Sint, GL_RG16I, GL_RG_INTEGER, GL_SHORT, Type::Int); + AddFormat(wgpu::TextureFormat::RG16Float, GL_RG16F, GL_RG, GL_HALF_FLOAT, Type::Float); + AddFormat(wgpu::TextureFormat::RGBA8Unorm, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::RGBA8UnormSrgb, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::RGBA8Snorm, GL_RGBA8_SNORM, GL_RGBA, GL_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::RGBA8Uint, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, Type::Uint); + AddFormat(wgpu::TextureFormat::RGBA8Sint, GL_RGBA8I, GL_RGBA_INTEGER, GL_BYTE, Type::Int); + + // This doesn't have an enum for the internal format in OpenGL, so use RGBA8. + AddFormat(wgpu::TextureFormat::BGRA8Unorm, GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::RGB10A2Unorm, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, Type::Float); + AddFormat(wgpu::TextureFormat::RG11B10Float, GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, Type::Float); + + // 8 bytes color formats + AddFormat(wgpu::TextureFormat::RG32Uint, GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, Type::Uint); + AddFormat(wgpu::TextureFormat::RG32Sint, GL_RG32I, GL_RG_INTEGER, GL_INT, Type::Int); + AddFormat(wgpu::TextureFormat::RG32Float, GL_RG32F, GL_RG, GL_FLOAT, Type::Float); + AddFormat(wgpu::TextureFormat::RGBA16Uint, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, Type::Uint); + AddFormat(wgpu::TextureFormat::RGBA16Sint, GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT, Type::Int); + AddFormat(wgpu::TextureFormat::RGBA16Float, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, Type::Float); + + // 16 bytes color formats + AddFormat(wgpu::TextureFormat::RGBA32Uint, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, Type::Uint); + AddFormat(wgpu::TextureFormat::RGBA32Sint, GL_RGBA32I, GL_RGBA_INTEGER, GL_INT, Type::Int); + AddFormat(wgpu::TextureFormat::RGBA32Float, GL_RGBA32F, GL_RGBA, GL_FLOAT, Type::Float); + + // Depth stencil formats + AddFormat(wgpu::TextureFormat::Depth32Float, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, Type::DepthStencil); + AddFormat(wgpu::TextureFormat::Depth24Plus, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT, Type::DepthStencil); + AddFormat(wgpu::TextureFormat::Depth24PlusStencil8, GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, Type::DepthStencil); + + // Block compressed formats + AddFormat(wgpu::TextureFormat::BC1RGBAUnorm, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC1RGBAUnormSrgb, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC2RGBAUnorm, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC2RGBAUnormSrgb, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC3RGBAUnorm, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC3RGBAUnormSrgb, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC4RSnorm, GL_COMPRESSED_SIGNED_RED_RGTC1, GL_RED, GL_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC4RUnorm, GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC5RGSnorm, GL_COMPRESSED_SIGNED_RG_RGTC2, GL_RG, GL_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC5RGUnorm, GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC6HRGBSfloat, GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT, GL_RGB, GL_HALF_FLOAT, Type::Float); + AddFormat(wgpu::TextureFormat::BC6HRGBUfloat, GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT, GL_RGB, GL_HALF_FLOAT, Type::Float); + AddFormat(wgpu::TextureFormat::BC7RGBAUnorm, GL_COMPRESSED_RGBA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + AddFormat(wgpu::TextureFormat::BC7RGBAUnormSrgb, GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_BYTE, Type::Float); + + // clang-format on + + return table; + } + +}} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/GLFormat.h b/third_party/dawn/src/dawn_native/opengl/GLFormat.h new file mode 100644 index 00000000000..255b17cdd5e --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/GLFormat.h @@ -0,0 +1,42 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_OPENGL_GLFORMAT_H_ +#define DAWNNATIVE_OPENGL_GLFORMAT_H_ + +#include "dawn_native/Format.h" +#include "dawn_native/opengl/opengl_platform.h" + +namespace dawn_native { namespace opengl { + + class Device; + + struct GLFormat { + GLenum internalFormat = 0; + GLenum format = 0; + GLenum type = 0; + bool isSupportedOnBackend = false; + + // OpenGL has different functions depending on the format component type, for example + // glClearBufferfv is only valid on formats with the Float ComponentType + enum ComponentType { Float, Int, Uint, DepthStencil }; + ComponentType componentType; + }; + + using GLFormatTable = std::array; + GLFormatTable BuildGLFormatTable(); + +}} // namespace dawn_native::opengl + +#endif // DAWNNATIVE_OPENGL_GLFORMAT_H_ diff --git a/third_party/dawn/src/dawn_native/opengl/NativeSwapChainImplGL.cpp b/third_party/dawn/src/dawn_native/opengl/NativeSwapChainImplGL.cpp new file mode 100644 index 00000000000..3cfdad4f367 --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/NativeSwapChainImplGL.cpp @@ -0,0 +1,87 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/opengl/NativeSwapChainImplGL.h" + +#include "dawn_native/opengl/DeviceGL.h" + +namespace dawn_native { namespace opengl { + + NativeSwapChainImpl::NativeSwapChainImpl(Device* device, + PresentCallback present, + void* presentUserdata) + : mPresentCallback(present), mPresentUserdata(presentUserdata), mDevice(device) { + } + + NativeSwapChainImpl::~NativeSwapChainImpl() { + const OpenGLFunctions& gl = mDevice->gl; + gl.DeleteTextures(1, &mBackTexture); + gl.DeleteFramebuffers(1, &mBackFBO); + } + + void NativeSwapChainImpl::Init(DawnWSIContextGL* /*context*/) { + const OpenGLFunctions& gl = mDevice->gl; + gl.GenTextures(1, &mBackTexture); + gl.BindTexture(GL_TEXTURE_2D, mBackTexture); + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + + gl.GenFramebuffers(1, &mBackFBO); + gl.BindFramebuffer(GL_READ_FRAMEBUFFER, mBackFBO); + gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + mBackTexture, 0); + } + + DawnSwapChainError NativeSwapChainImpl::Configure(WGPUTextureFormat format, + WGPUTextureUsage usage, + uint32_t width, + uint32_t height) { + if (format != WGPUTextureFormat_RGBA8Unorm) { + return "unsupported format"; + } + ASSERT(width > 0); + ASSERT(height > 0); + mWidth = width; + mHeight = height; + + const OpenGLFunctions& gl = mDevice->gl; + gl.BindTexture(GL_TEXTURE_2D, mBackTexture); + // Reallocate the texture + gl.TexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, + nullptr); + + return DAWN_SWAP_CHAIN_NO_ERROR; + } + + DawnSwapChainError NativeSwapChainImpl::GetNextTexture(DawnSwapChainNextTexture* nextTexture) { + nextTexture->texture.u32 = mBackTexture; + return DAWN_SWAP_CHAIN_NO_ERROR; + } + + DawnSwapChainError NativeSwapChainImpl::Present() { + const OpenGLFunctions& gl = mDevice->gl; + gl.BindFramebuffer(GL_READ_FRAMEBUFFER, mBackFBO); + gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + gl.BlitFramebuffer(0, 0, mWidth, mHeight, 0, mHeight, mWidth, 0, GL_COLOR_BUFFER_BIT, + GL_NEAREST); + + mPresentCallback(mPresentUserdata); + + return DAWN_SWAP_CHAIN_NO_ERROR; + } + + wgpu::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const { + return wgpu::TextureFormat::RGBA8Unorm; + } + +}} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/NativeSwapChainImplGL.h b/third_party/dawn/src/dawn_native/opengl/NativeSwapChainImplGL.h new file mode 100644 index 00000000000..acda00576f8 --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/NativeSwapChainImplGL.h @@ -0,0 +1,58 @@ +// Copyright 2017 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_OPENGL_NATIVESWAPCHAINIMPLGL_H_ +#define DAWNNATIVE_OPENGL_NATIVESWAPCHAINIMPLGL_H_ + +#include "dawn_native/OpenGLBackend.h" + +#include "dawn_native/dawn_platform.h" +#include "dawn_native/opengl/opengl_platform.h" + +namespace dawn_native { namespace opengl { + + class Device; + + class NativeSwapChainImpl { + public: + using WSIContext = DawnWSIContextGL; + + NativeSwapChainImpl(Device* device, PresentCallback present, void* presentUserdata); + ~NativeSwapChainImpl(); + + void Init(DawnWSIContextGL* context); + DawnSwapChainError Configure(WGPUTextureFormat format, + WGPUTextureUsage, + uint32_t width, + uint32_t height); + DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture); + DawnSwapChainError Present(); + + wgpu::TextureFormat GetPreferredFormat() const; + + private: + PresentCallback mPresentCallback; + void* mPresentUserdata; + + uint32_t mWidth = 0; + uint32_t mHeight = 0; + GLuint mBackFBO = 0; + GLuint mBackTexture = 0; + + Device* mDevice = nullptr; + }; + +}} // namespace dawn_native::opengl + +#endif // DAWNNATIVE_OPENGL_NATIVESWAPCHAINIMPLGL_H_ diff --git a/third_party/dawn/src/dawn_native/opengl/OpenGLBackend.cpp b/third_party/dawn/src/dawn_native/opengl/OpenGLBackend.cpp index f91518991e7..34513f5a11a 100644 --- a/third_party/dawn/src/dawn_native/opengl/OpenGLBackend.cpp +++ b/third_party/dawn/src/dawn_native/opengl/OpenGLBackend.cpp @@ -17,12 +17,33 @@ #include "dawn_native/OpenGLBackend.h" +#include "common/SwapChainUtils.h" #include "dawn_native/opengl/DeviceGL.h" +#include "dawn_native/opengl/NativeSwapChainImplGL.h" namespace dawn_native { namespace opengl { AdapterDiscoveryOptions::AdapterDiscoveryOptions() - : AdapterDiscoveryOptionsBase(BackendType::OpenGL) { + : AdapterDiscoveryOptionsBase(WGPUBackendType_OpenGL) { + } + + DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device, + PresentCallback present, + void* presentUserdata) { + Device* backendDevice = reinterpret_cast(device); + + DawnSwapChainImplementation impl; + impl = CreateSwapChainImplementation( + new NativeSwapChainImpl(backendDevice, present, presentUserdata)); + impl.textureUsage = WGPUTextureUsage_Present; + + return impl; + } + + WGPUTextureFormat GetNativeSwapChainPreferredFormat( + const DawnSwapChainImplementation* swapChain) { + NativeSwapChainImpl* impl = reinterpret_cast(swapChain->userData); + return static_cast(impl->GetPreferredFormat()); } }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/OpenGLFunctions.cpp b/third_party/dawn/src/dawn_native/opengl/OpenGLFunctions.cpp index d56aa24116a..c54a0adb083 100644 --- a/third_party/dawn/src/dawn_native/opengl/OpenGLFunctions.cpp +++ b/third_party/dawn/src/dawn_native/opengl/OpenGLFunctions.cpp @@ -15,13 +15,14 @@ #include "dawn_native/opengl/OpenGLFunctions.h" #include +#include namespace dawn_native { namespace opengl { MaybeError OpenGLFunctions::Initialize(GetProcAddress getProc) { PFNGLGETSTRINGPROC getString = reinterpret_cast(getProc("glGetString")); if (getString == nullptr) { - return DAWN_CONTEXT_LOST_ERROR("Couldn't load glGetString"); + return DAWN_INTERNAL_ERROR("Couldn't load glGetString"); } std::string version = reinterpret_cast(getString(GL_VERSION)); @@ -53,7 +54,34 @@ namespace dawn_native { namespace opengl { DAWN_TRY(LoadDesktopGLProcs(getProc, mMajorVersion, mMinorVersion)); } + InitializeSupportedGLExtensions(); + return {}; } + void OpenGLFunctions::InitializeSupportedGLExtensions() { + int32_t numExtensions; + GetIntegerv(GL_NUM_EXTENSIONS, &numExtensions); + + for (int32_t i = 0; i < numExtensions; ++i) { + const char* extensionName = reinterpret_cast(GetStringi(GL_EXTENSIONS, i)); + mSupportedGLExtensionsSet.insert(extensionName); + } + } + + bool OpenGLFunctions::IsGLExtensionSupported(const char* extension) const { + ASSERT(extension != nullptr); + return mSupportedGLExtensionsSet.count(extension) != 0; + } + + bool OpenGLFunctions::IsAtLeastGL(uint32_t majorVersion, uint32_t minorVersion) const { + return mStandard == Standard::Desktop && + std::tie(mMajorVersion, mMinorVersion) >= std::tie(majorVersion, minorVersion); + } + + bool OpenGLFunctions::IsAtLeastGLES(uint32_t majorVersion, uint32_t minorVersion) const { + return mStandard == Standard::ES && + std::tie(mMajorVersion, mMinorVersion) >= std::tie(majorVersion, minorVersion); + } + }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/OpenGLFunctions.h b/third_party/dawn/src/dawn_native/opengl/OpenGLFunctions.h index 6f737ec9b9e..e51430067e8 100644 --- a/third_party/dawn/src/dawn_native/opengl/OpenGLFunctions.h +++ b/third_party/dawn/src/dawn_native/opengl/OpenGLFunctions.h @@ -15,6 +15,8 @@ #ifndef DAWNNATIVE_OPENGL_OPENGLFUNCTIONS_H_ #define DAWNNATIVE_OPENGL_OPENGLFUNCTIONS_H_ +#include + #include "dawn_native/opengl/OpenGLFunctionsBase_autogen.h" namespace dawn_native { namespace opengl { @@ -23,7 +25,14 @@ namespace dawn_native { namespace opengl { public: MaybeError Initialize(GetProcAddress getProc); + bool IsAtLeastGL(uint32_t majorVersion, uint32_t minorVersion) const; + bool IsAtLeastGLES(uint32_t majorVersion, uint32_t minorVersion) const; + + bool IsGLExtensionSupported(const char* extension) const; + private: + void InitializeSupportedGLExtensions(); + uint32_t mMajorVersion; uint32_t mMinorVersion; @@ -32,6 +41,8 @@ namespace dawn_native { namespace opengl { ES, }; Standard mStandard; + + std::unordered_set mSupportedGLExtensionsSet; }; }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/PersistentPipelineStateGL.h b/third_party/dawn/src/dawn_native/opengl/PersistentPipelineStateGL.h index 7169b76132d..c98ec152a4a 100644 --- a/third_party/dawn/src/dawn_native/opengl/PersistentPipelineStateGL.h +++ b/third_party/dawn/src/dawn_native/opengl/PersistentPipelineStateGL.h @@ -16,8 +16,7 @@ #define DAWNNATIVE_OPENGL_PERSISTENTPIPELINESTATEGL_H_ #include "dawn_native/dawn_platform.h" - -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { diff --git a/third_party/dawn/src/dawn_native/opengl/PipelineGL.cpp b/third_party/dawn/src/dawn_native/opengl/PipelineGL.cpp index 697ab823f91..76c24b6b407 100644 --- a/third_party/dawn/src/dawn_native/opengl/PipelineGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/PipelineGL.cpp @@ -15,26 +15,26 @@ #include "dawn_native/opengl/PipelineGL.h" #include "common/BitSetIterator.h" +#include "common/Log.h" #include "dawn_native/BindGroupLayout.h" #include "dawn_native/opengl/Forward.h" #include "dawn_native/opengl/OpenGLFunctions.h" #include "dawn_native/opengl/PipelineLayoutGL.h" #include "dawn_native/opengl/ShaderModuleGL.h" -#include #include namespace dawn_native { namespace opengl { namespace { - GLenum GLShaderType(dawn::ShaderStage stage) { + GLenum GLShaderType(SingleShaderStage stage) { switch (stage) { - case dawn::ShaderStage::Vertex: + case SingleShaderStage::Vertex: return GL_VERTEX_SHADER; - case dawn::ShaderStage::Fragment: + case SingleShaderStage::Fragment: return GL_FRAGMENT_SHADER; - case dawn::ShaderStage::Compute: + case SingleShaderStage::Compute: return GL_COMPUTE_SHADER; default: UNREACHABLE(); @@ -64,9 +64,8 @@ namespace dawn_native { namespace opengl { if (infoLogLength > 1) { std::vector buffer(infoLogLength); gl.GetShaderInfoLog(shader, infoLogLength, nullptr, &buffer[0]); - std::cout << source << std::endl; - std::cout << "Program compilation failed:\n"; - std::cout << buffer.data() << std::endl; + dawn::ErrorLog() << source << "\nProgram compilation failed:\n" + << buffer.data(); } } return shader; @@ -74,14 +73,14 @@ namespace dawn_native { namespace opengl { mProgram = gl.CreateProgram(); - dawn::ShaderStageBit activeStages = dawn::ShaderStageBit::None; - for (dawn::ShaderStage stage : IterateStages(kAllStages)) { + wgpu::ShaderStage activeStages = wgpu::ShaderStage::None; + for (SingleShaderStage stage : IterateStages(kAllStages)) { if (modules[stage] != nullptr) { activeStages |= StageBit(stage); } } - for (dawn::ShaderStage stage : IterateStages(activeStages)) { + for (SingleShaderStage stage : IterateStages(activeStages)) { GLuint shader = CreateShader(gl, GLShaderType(stage), modules[stage]->GetSource()); gl.AttachShader(mProgram, shader); } @@ -97,8 +96,7 @@ namespace dawn_native { namespace opengl { if (infoLogLength > 1) { std::vector buffer(infoLogLength); gl.GetProgramInfoLog(mProgram, infoLogLength, nullptr, &buffer[0]); - std::cout << "Program link failed:\n"; - std::cout << buffer.data() << std::endl; + dawn::ErrorLog() << "Program link failed:\n" << buffer.data(); } } @@ -108,38 +106,56 @@ namespace dawn_native { namespace opengl { // etc. const auto& indices = layout->GetBindingIndexInfo(); - for (uint32_t group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { - const auto& groupInfo = layout->GetBindGroupLayout(group)->GetBindingInfo(); + for (BindGroupIndex group : IterateBitSet(layout->GetBindGroupLayoutsMask())) { + const BindGroupLayoutBase* bgl = layout->GetBindGroupLayout(group); - for (uint32_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) { - if (!groupInfo.mask[binding]) { - continue; - } + for (const auto& it : bgl->GetBindingMap()) { + BindingNumber bindingNumber = it.first; + BindingIndex bindingIndex = it.second; - std::string name = GetBindingName(group, binding); - switch (groupInfo.types[binding]) { - case dawn::BindingType::UniformBuffer: { + std::string name = GetBindingName(group, bindingNumber); + switch (bgl->GetBindingInfo(bindingIndex).type) { + case wgpu::BindingType::UniformBuffer: { GLint location = gl.GetUniformBlockIndex(mProgram, name.c_str()); - gl.UniformBlockBinding(mProgram, location, indices[group][binding]); - } break; + if (location != -1) { + gl.UniformBlockBinding(mProgram, location, + indices[group][bindingIndex]); + } + break; + } - case dawn::BindingType::StorageBuffer: { + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: { GLuint location = gl.GetProgramResourceIndex( mProgram, GL_SHADER_STORAGE_BLOCK, name.c_str()); - gl.ShaderStorageBlockBinding(mProgram, location, indices[group][binding]); - } break; + if (location != GL_INVALID_INDEX) { + gl.ShaderStorageBlockBinding(mProgram, location, + indices[group][bindingIndex]); + } + break; + } - case dawn::BindingType::Sampler: - case dawn::BindingType::SampledTexture: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::SampledTexture: // These binding types are handled in the separate sampler and texture // emulation break; - // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + GLint location = gl.GetUniformLocation(mProgram, name.c_str()); + if (location != -1) { + gl.Uniform1i(location, indices[group][bindingIndex]); + } + break; + } + + case wgpu::BindingType::StorageTexture: UNREACHABLE(); break; + + // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset. } } } @@ -147,7 +163,7 @@ namespace dawn_native { namespace opengl { // Compute links between stages for combined samplers, then bind them to texture units { std::set combinedSamplersSet; - for (dawn::ShaderStage stage : IterateStages(activeStages)) { + for (SingleShaderStage stage : IterateStages(activeStages)) { for (const auto& combined : modules[stage]->GetCombinedSamplerInfo()) { combinedSamplersSet.insert(combined); } @@ -160,28 +176,50 @@ namespace dawn_native { namespace opengl { for (const auto& combined : combinedSamplersSet) { std::string name = combined.GetName(); GLint location = gl.GetUniformLocation(mProgram, name.c_str()); + + if (location == -1) { + continue; + } + gl.Uniform1i(location, textureUnit); - GLuint samplerIndex = - indices[combined.samplerLocation.group][combined.samplerLocation.binding]; - mUnitsForSamplers[samplerIndex].push_back(textureUnit); + bool shouldUseFiltering; + { + const BindGroupLayoutBase* bgl = + layout->GetBindGroupLayout(combined.textureLocation.group); + BindingIndex bindingIndex = + bgl->GetBindingIndex(combined.textureLocation.binding); - GLuint textureIndex = - indices[combined.textureLocation.group][combined.textureLocation.binding]; - mUnitsForTextures[textureIndex].push_back(textureUnit); + GLuint textureIndex = indices[combined.textureLocation.group][bindingIndex]; + mUnitsForTextures[textureIndex].push_back(textureUnit); + + Format::Type componentType = + bgl->GetBindingInfo(bindingIndex).textureComponentType; + shouldUseFiltering = componentType == Format::Type::Float; + } + { + const BindGroupLayoutBase* bgl = + layout->GetBindGroupLayout(combined.samplerLocation.group); + BindingIndex bindingIndex = + bgl->GetBindingIndex(combined.samplerLocation.binding); + + GLuint samplerIndex = indices[combined.samplerLocation.group][bindingIndex]; + mUnitsForSamplers[samplerIndex].push_back({textureUnit, shouldUseFiltering}); + } textureUnit++; } } } - const std::vector& PipelineGL::GetTextureUnitsForSampler(GLuint index) const { + const std::vector& PipelineGL::GetTextureUnitsForSampler( + GLuint index) const { ASSERT(index < mUnitsForSamplers.size()); return mUnitsForSamplers[index]; } const std::vector& PipelineGL::GetTextureUnitsForTextureView(GLuint index) const { - ASSERT(index < mUnitsForSamplers.size()); + ASSERT(index < mUnitsForTextures.size()); return mUnitsForTextures[index]; } diff --git a/third_party/dawn/src/dawn_native/opengl/PipelineGL.h b/third_party/dawn/src/dawn_native/opengl/PipelineGL.h index e5245425374..a79909ab55c 100644 --- a/third_party/dawn/src/dawn_native/opengl/PipelineGL.h +++ b/third_party/dawn/src/dawn_native/opengl/PipelineGL.h @@ -17,7 +17,7 @@ #include "dawn_native/Pipeline.h" -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" #include @@ -36,10 +36,13 @@ namespace dawn_native { namespace opengl { const PipelineLayout* layout, const PerStage& modules); - using BindingLocations = - std::array, kMaxBindGroups>; - - const std::vector& GetTextureUnitsForSampler(GLuint index) const; + // For each unit a sampler is bound to we need to know if we should use filtering or not + // because int and uint texture are only complete without filtering. + struct SamplerUnit { + GLuint unit; + bool shouldUseFiltering; + }; + const std::vector& GetTextureUnitsForSampler(GLuint index) const; const std::vector& GetTextureUnitsForTextureView(GLuint index) const; GLuint GetProgramHandle() const; @@ -47,7 +50,7 @@ namespace dawn_native { namespace opengl { private: GLuint mProgram; - std::vector> mUnitsForSamplers; + std::vector> mUnitsForSamplers; std::vector> mUnitsForTextures; }; diff --git a/third_party/dawn/src/dawn_native/opengl/PipelineLayoutGL.cpp b/third_party/dawn/src/dawn_native/opengl/PipelineLayoutGL.cpp index e646feff93b..8faab78b0d9 100644 --- a/third_party/dawn/src/dawn_native/opengl/PipelineLayoutGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/PipelineLayoutGL.cpp @@ -26,39 +26,46 @@ namespace dawn_native { namespace opengl { GLuint samplerIndex = 0; GLuint sampledTextureIndex = 0; GLuint ssboIndex = 0; + GLuint storageTextureIndex = 0; - for (uint32_t group : IterateBitSet(GetBindGroupLayoutsMask())) { - const auto& groupInfo = GetBindGroupLayout(group)->GetBindingInfo(); + for (BindGroupIndex group : IterateBitSet(GetBindGroupLayoutsMask())) { + const BindGroupLayoutBase* bgl = GetBindGroupLayout(group); + mIndexInfo[group].resize(bgl->GetBindingCount()); - for (size_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) { - if (!groupInfo.mask[binding]) { - continue; - } - - switch (groupInfo.types[binding]) { - case dawn::BindingType::UniformBuffer: - mIndexInfo[group][binding] = uboIndex; + for (BindingIndex bindingIndex{0}; bindingIndex < bgl->GetBindingCount(); + ++bindingIndex) { + switch (bgl->GetBindingInfo(bindingIndex).type) { + case wgpu::BindingType::UniformBuffer: + mIndexInfo[group][bindingIndex] = uboIndex; uboIndex++; break; - case dawn::BindingType::Sampler: - mIndexInfo[group][binding] = samplerIndex; + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + mIndexInfo[group][bindingIndex] = samplerIndex; samplerIndex++; break; - case dawn::BindingType::SampledTexture: - mIndexInfo[group][binding] = sampledTextureIndex; + case wgpu::BindingType::SampledTexture: + mIndexInfo[group][bindingIndex] = sampledTextureIndex; sampledTextureIndex++; break; - case dawn::BindingType::StorageBuffer: - mIndexInfo[group][binding] = ssboIndex; + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + mIndexInfo[group][bindingIndex] = ssboIndex; ssboIndex++; break; - // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + mIndexInfo[group][bindingIndex] = storageTextureIndex; + storageTextureIndex++; + break; + + case wgpu::BindingType::StorageTexture: UNREACHABLE(); break; + + // TODO(shaobo.yan@intel.com): Implement dynamic buffer offset } } } diff --git a/third_party/dawn/src/dawn_native/opengl/PipelineLayoutGL.h b/third_party/dawn/src/dawn_native/opengl/PipelineLayoutGL.h index abf6acf91e6..eeff7182dda 100644 --- a/third_party/dawn/src/dawn_native/opengl/PipelineLayoutGL.h +++ b/third_party/dawn/src/dawn_native/opengl/PipelineLayoutGL.h @@ -17,18 +17,21 @@ #include "dawn_native/PipelineLayout.h" -#include "glad/glad.h" +#include "common/ityp_array.h" +#include "common/ityp_vector.h" +#include "dawn_native/BindingInfo.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { class Device; - class PipelineLayout : public PipelineLayoutBase { + class PipelineLayout final : public PipelineLayoutBase { public: PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor); using BindingIndexInfo = - std::array, kMaxBindGroups>; + ityp::array, kMaxBindGroups>; const BindingIndexInfo& GetBindingIndexInfo() const; GLuint GetTextureUnitsUsed() const; @@ -36,6 +39,7 @@ namespace dawn_native { namespace opengl { size_t GetNumSampledTextures() const; private: + ~PipelineLayout() override = default; BindingIndexInfo mIndexInfo; size_t mNumSamplers; size_t mNumSampledTextures; diff --git a/third_party/dawn/src/dawn_native/opengl/QuerySetGL.cpp b/third_party/dawn/src/dawn_native/opengl/QuerySetGL.cpp new file mode 100644 index 00000000000..6ff5d20603f --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/QuerySetGL.cpp @@ -0,0 +1,32 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/opengl/QuerySetGL.h" + +#include "dawn_native/opengl/DeviceGL.h" + +namespace dawn_native { namespace opengl { + + QuerySet::QuerySet(Device* device, const QuerySetDescriptor* descriptor) + : QuerySetBase(device, descriptor) { + } + + QuerySet::~QuerySet() { + DestroyInternal(); + } + + void QuerySet::DestroyImpl() { + } + +}} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/QuerySetGL.h b/third_party/dawn/src/dawn_native/opengl/QuerySetGL.h new file mode 100644 index 00000000000..2a83bdd0468 --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/QuerySetGL.h @@ -0,0 +1,36 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_OPENGL_QUERYSETGL_H_ +#define DAWNNATIVE_OPENGL_QUERYSETGL_H_ + +#include "dawn_native/QuerySet.h" + +namespace dawn_native { namespace opengl { + + class Device; + + class QuerySet final : public QuerySetBase { + public: + QuerySet(Device* device, const QuerySetDescriptor* descriptor); + + private: + ~QuerySet() override; + + void DestroyImpl() override; + }; + +}} // namespace dawn_native::opengl + +#endif // DAWNNATIVE_OPENGL_QUERYSETGL_H_ diff --git a/third_party/dawn/src/dawn_native/opengl/QueueGL.cpp b/third_party/dawn/src/dawn_native/opengl/QueueGL.cpp index 241854a6fb3..88d1575cf44 100644 --- a/third_party/dawn/src/dawn_native/opengl/QueueGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/QueueGL.cpp @@ -14,22 +14,41 @@ #include "dawn_native/opengl/QueueGL.h" +#include "dawn_native/opengl/BufferGL.h" #include "dawn_native/opengl/CommandBufferGL.h" #include "dawn_native/opengl/DeviceGL.h" +#include "dawn_platform/DawnPlatform.h" +#include "dawn_platform/tracing/TraceEvent.h" namespace dawn_native { namespace opengl { Queue::Queue(Device* device) : QueueBase(device) { } - void Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { + MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { Device* device = ToBackend(GetDevice()); + TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, "CommandBufferGL::Execute"); for (uint32_t i = 0; i < commandCount; ++i) { ToBackend(commands[i])->Execute(); } + TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferGL::Execute"); device->SubmitFenceSync(); + return {}; + } + + MaybeError Queue::WriteBufferImpl(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size) { + const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; + + ToBackend(buffer)->EnsureDataInitializedAsDestination(bufferOffset, size); + + gl.BindBuffer(GL_ARRAY_BUFFER, ToBackend(buffer)->GetHandle()); + gl.BufferSubData(GL_ARRAY_BUFFER, bufferOffset, size, data); + return {}; } }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/QueueGL.h b/third_party/dawn/src/dawn_native/opengl/QueueGL.h index 687d7a4491f..301b1ad7007 100644 --- a/third_party/dawn/src/dawn_native/opengl/QueueGL.h +++ b/third_party/dawn/src/dawn_native/opengl/QueueGL.h @@ -22,12 +22,16 @@ namespace dawn_native { namespace opengl { class CommandBuffer; class Device; - class Queue : public QueueBase { + class Queue final : public QueueBase { public: Queue(Device* device); private: - void SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; + MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; + MaybeError WriteBufferImpl(BufferBase* buffer, + uint64_t bufferOffset, + const void* data, + size_t size) override; }; }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/RenderPipelineGL.cpp b/third_party/dawn/src/dawn_native/opengl/RenderPipelineGL.cpp index 037f1854c44..441df88f8c0 100644 --- a/third_party/dawn/src/dawn_native/opengl/RenderPipelineGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/RenderPipelineGL.cpp @@ -23,67 +23,84 @@ namespace dawn_native { namespace opengl { namespace { - GLenum GLPrimitiveTopology(dawn::PrimitiveTopology primitiveTopology) { + GLenum GLPrimitiveTopology(wgpu::PrimitiveTopology primitiveTopology) { switch (primitiveTopology) { - case dawn::PrimitiveTopology::PointList: + case wgpu::PrimitiveTopology::PointList: return GL_POINTS; - case dawn::PrimitiveTopology::LineList: + case wgpu::PrimitiveTopology::LineList: return GL_LINES; - case dawn::PrimitiveTopology::LineStrip: + case wgpu::PrimitiveTopology::LineStrip: return GL_LINE_STRIP; - case dawn::PrimitiveTopology::TriangleList: + case wgpu::PrimitiveTopology::TriangleList: return GL_TRIANGLES; - case dawn::PrimitiveTopology::TriangleStrip: + case wgpu::PrimitiveTopology::TriangleStrip: return GL_TRIANGLE_STRIP; default: UNREACHABLE(); } } - GLenum GLBlendFactor(dawn::BlendFactor factor, bool alpha) { + void ApplyFrontFaceAndCulling(const OpenGLFunctions& gl, + wgpu::FrontFace face, + wgpu::CullMode mode) { + if (mode == wgpu::CullMode::None) { + gl.Disable(GL_CULL_FACE); + } else { + gl.Enable(GL_CULL_FACE); + // Note that we invert winding direction in OpenGL. Because Y axis is up in OpenGL, + // which is different from WebGPU and other backends (Y axis is down). + GLenum direction = (face == wgpu::FrontFace::CCW) ? GL_CW : GL_CCW; + gl.FrontFace(direction); + + GLenum cullMode = (mode == wgpu::CullMode::Front) ? GL_FRONT : GL_BACK; + gl.CullFace(cullMode); + } + } + + GLenum GLBlendFactor(wgpu::BlendFactor factor, bool alpha) { switch (factor) { - case dawn::BlendFactor::Zero: + case wgpu::BlendFactor::Zero: return GL_ZERO; - case dawn::BlendFactor::One: + case wgpu::BlendFactor::One: return GL_ONE; - case dawn::BlendFactor::SrcColor: + case wgpu::BlendFactor::SrcColor: return GL_SRC_COLOR; - case dawn::BlendFactor::OneMinusSrcColor: + case wgpu::BlendFactor::OneMinusSrcColor: return GL_ONE_MINUS_SRC_COLOR; - case dawn::BlendFactor::SrcAlpha: + case wgpu::BlendFactor::SrcAlpha: return GL_SRC_ALPHA; - case dawn::BlendFactor::OneMinusSrcAlpha: + case wgpu::BlendFactor::OneMinusSrcAlpha: return GL_ONE_MINUS_SRC_ALPHA; - case dawn::BlendFactor::DstColor: + case wgpu::BlendFactor::DstColor: return GL_DST_COLOR; - case dawn::BlendFactor::OneMinusDstColor: + case wgpu::BlendFactor::OneMinusDstColor: return GL_ONE_MINUS_DST_COLOR; - case dawn::BlendFactor::DstAlpha: + case wgpu::BlendFactor::DstAlpha: return GL_DST_ALPHA; - case dawn::BlendFactor::OneMinusDstAlpha: + case wgpu::BlendFactor::OneMinusDstAlpha: return GL_ONE_MINUS_DST_ALPHA; - case dawn::BlendFactor::SrcAlphaSaturated: + case wgpu::BlendFactor::SrcAlphaSaturated: return GL_SRC_ALPHA_SATURATE; - case dawn::BlendFactor::BlendColor: + case wgpu::BlendFactor::BlendColor: return alpha ? GL_CONSTANT_ALPHA : GL_CONSTANT_COLOR; - case dawn::BlendFactor::OneMinusBlendColor: + case wgpu::BlendFactor::OneMinusBlendColor: return alpha ? GL_ONE_MINUS_CONSTANT_ALPHA : GL_ONE_MINUS_CONSTANT_COLOR; default: UNREACHABLE(); } } - GLenum GLBlendMode(dawn::BlendOperation operation) { + GLenum GLBlendMode(wgpu::BlendOperation operation) { switch (operation) { - case dawn::BlendOperation::Add: + case wgpu::BlendOperation::Add: return GL_FUNC_ADD; - case dawn::BlendOperation::Subtract: + case wgpu::BlendOperation::Subtract: return GL_FUNC_SUBTRACT; - case dawn::BlendOperation::ReverseSubtract: + case wgpu::BlendOperation::ReverseSubtract: return GL_FUNC_REVERSE_SUBTRACT; - case dawn::BlendOperation::Min: + case wgpu::BlendOperation::Min: return GL_MIN; - case dawn::BlendOperation::Max: + case wgpu::BlendOperation::Max: return GL_MAX; default: UNREACHABLE(); @@ -105,29 +122,29 @@ namespace dawn_native { namespace opengl { } else { gl.Disablei(GL_BLEND, attachment); } - gl.ColorMaski(attachment, descriptor->writeMask & dawn::ColorWriteMask::Red, - descriptor->writeMask & dawn::ColorWriteMask::Green, - descriptor->writeMask & dawn::ColorWriteMask::Blue, - descriptor->writeMask & dawn::ColorWriteMask::Alpha); + gl.ColorMaski(attachment, descriptor->writeMask & wgpu::ColorWriteMask::Red, + descriptor->writeMask & wgpu::ColorWriteMask::Green, + descriptor->writeMask & wgpu::ColorWriteMask::Blue, + descriptor->writeMask & wgpu::ColorWriteMask::Alpha); } - GLuint OpenGLStencilOperation(dawn::StencilOperation stencilOperation) { + GLuint OpenGLStencilOperation(wgpu::StencilOperation stencilOperation) { switch (stencilOperation) { - case dawn::StencilOperation::Keep: + case wgpu::StencilOperation::Keep: return GL_KEEP; - case dawn::StencilOperation::Zero: + case wgpu::StencilOperation::Zero: return GL_ZERO; - case dawn::StencilOperation::Replace: + case wgpu::StencilOperation::Replace: return GL_REPLACE; - case dawn::StencilOperation::Invert: + case wgpu::StencilOperation::Invert: return GL_INVERT; - case dawn::StencilOperation::IncrementClamp: + case wgpu::StencilOperation::IncrementClamp: return GL_INCR; - case dawn::StencilOperation::DecrementClamp: + case wgpu::StencilOperation::DecrementClamp: return GL_DECR; - case dawn::StencilOperation::IncrementWrap: + case wgpu::StencilOperation::IncrementWrap: return GL_INCR_WRAP; - case dawn::StencilOperation::DecrementWrap: + case wgpu::StencilOperation::DecrementWrap: return GL_DECR_WRAP; default: UNREACHABLE(); @@ -138,7 +155,7 @@ namespace dawn_native { namespace opengl { const DepthStencilStateDescriptor* descriptor, PersistentPipelineState* persistentPipelineState) { // Depth writes only occur if depth is enabled - if (descriptor->depthCompare == dawn::CompareFunction::Always && + if (descriptor->depthCompare == wgpu::CompareFunction::Always && !descriptor->depthWriteEnabled) { gl.Disable(GL_DEPTH_TEST); } else { @@ -181,11 +198,11 @@ namespace dawn_native { namespace opengl { mVertexArrayObject(0), mGlPrimitiveTopology(GLPrimitiveTopology(GetPrimitiveTopology())) { PerStage modules(nullptr); - modules[dawn::ShaderStage::Vertex] = ToBackend(descriptor->vertexStage->module); - modules[dawn::ShaderStage::Fragment] = ToBackend(descriptor->fragmentStage->module); + modules[SingleShaderStage::Vertex] = ToBackend(descriptor->vertexStage.module); + modules[SingleShaderStage::Fragment] = ToBackend(descriptor->fragmentStage->module); PipelineGL::Initialize(device->gl, ToBackend(GetLayout()), modules); - CreateVAOForVertexInput(descriptor->vertexInput); + CreateVAOForVertexState(descriptor->vertexState); } RenderPipeline::~RenderPipeline() { @@ -198,28 +215,28 @@ namespace dawn_native { namespace opengl { return mGlPrimitiveTopology; } - void RenderPipeline::CreateVAOForVertexInput(const VertexInputDescriptor* vertexInput) { + void RenderPipeline::CreateVAOForVertexState(const VertexStateDescriptor* vertexState) { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; gl.GenVertexArrays(1, &mVertexArrayObject); gl.BindVertexArray(mVertexArrayObject); - for (uint32_t location : IterateBitSet(GetAttributesSetMask())) { + for (uint32_t location : IterateBitSet(GetAttributeLocationsUsed())) { const auto& attribute = GetAttribute(location); gl.EnableVertexAttribArray(location); - attributesUsingInput[attribute.inputSlot][location] = true; - auto input = GetInput(attribute.inputSlot); + attributesUsingVertexBuffer[attribute.vertexBufferSlot][location] = true; + const VertexBufferInfo& vertexBuffer = GetVertexBuffer(attribute.vertexBufferSlot); - if (input.stride == 0) { + if (vertexBuffer.arrayStride == 0) { // Emulate a stride of zero (constant vertex attribute) by // setting the attribute instance divisor to a huge number. gl.VertexAttribDivisor(location, 0xffffffff); } else { - switch (input.stepMode) { - case dawn::InputStepMode::Vertex: + switch (vertexBuffer.stepMode) { + case wgpu::InputStepMode::Vertex: break; - case dawn::InputStepMode::Instance: + case wgpu::InputStepMode::Instance: gl.VertexAttribDivisor(location, 1); break; default: @@ -236,8 +253,12 @@ namespace dawn_native { namespace opengl { ASSERT(mVertexArrayObject); gl.BindVertexArray(mVertexArrayObject); + ApplyFrontFaceAndCulling(gl, GetFrontFace(), GetCullMode()); + ApplyDepthStencilState(gl, GetDepthStencilStateDescriptor(), &persistentPipelineState); + gl.SampleMaski(0, GetSampleMask()); + for (uint32_t attachmentSlot : IterateBitSet(GetColorAttachmentsMask())) { ApplyColorState(gl, attachmentSlot, GetColorStateDescriptor(attachmentSlot)); } diff --git a/third_party/dawn/src/dawn_native/opengl/RenderPipelineGL.h b/third_party/dawn/src/dawn_native/opengl/RenderPipelineGL.h index 6dc3c1495df..2f3363a4bd6 100644 --- a/third_party/dawn/src/dawn_native/opengl/RenderPipelineGL.h +++ b/third_party/dawn/src/dawn_native/opengl/RenderPipelineGL.h @@ -18,8 +18,7 @@ #include "dawn_native/RenderPipeline.h" #include "dawn_native/opengl/PipelineGL.h" - -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" #include @@ -28,17 +27,17 @@ namespace dawn_native { namespace opengl { class Device; class PersistentPipelineState; - class RenderPipeline : public RenderPipelineBase, public PipelineGL { + class RenderPipeline final : public RenderPipelineBase, public PipelineGL { public: RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor); - ~RenderPipeline(); GLenum GetGLPrimitiveTopology() const; void ApplyNow(PersistentPipelineState& persistentPipelineState); private: - void CreateVAOForVertexInput(const VertexInputDescriptor* vertexInput); + ~RenderPipeline() override; + void CreateVAOForVertexState(const VertexStateDescriptor* vertexState); // TODO(yunchao.he@intel.com): vao need to be deduplicated between pipelines. GLuint mVertexArrayObject; diff --git a/third_party/dawn/src/dawn_native/opengl/SamplerGL.cpp b/third_party/dawn/src/dawn_native/opengl/SamplerGL.cpp index d9e665db86a..1b5b44cc5cd 100644 --- a/third_party/dawn/src/dawn_native/opengl/SamplerGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/SamplerGL.cpp @@ -21,33 +21,33 @@ namespace dawn_native { namespace opengl { namespace { - GLenum MagFilterMode(dawn::FilterMode filter) { + GLenum MagFilterMode(wgpu::FilterMode filter) { switch (filter) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: return GL_NEAREST; - case dawn::FilterMode::Linear: + case wgpu::FilterMode::Linear: return GL_LINEAR; default: UNREACHABLE(); } } - GLenum MinFilterMode(dawn::FilterMode minFilter, dawn::FilterMode mipMapFilter) { + GLenum MinFilterMode(wgpu::FilterMode minFilter, wgpu::FilterMode mipMapFilter) { switch (minFilter) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: switch (mipMapFilter) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: return GL_NEAREST_MIPMAP_NEAREST; - case dawn::FilterMode::Linear: + case wgpu::FilterMode::Linear: return GL_NEAREST_MIPMAP_LINEAR; default: UNREACHABLE(); } - case dawn::FilterMode::Linear: + case wgpu::FilterMode::Linear: switch (mipMapFilter) { - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: return GL_LINEAR_MIPMAP_NEAREST; - case dawn::FilterMode::Linear: + case wgpu::FilterMode::Linear: return GL_LINEAR_MIPMAP_LINEAR; default: UNREACHABLE(); @@ -57,13 +57,13 @@ namespace dawn_native { namespace opengl { } } - GLenum WrapMode(dawn::AddressMode mode) { + GLenum WrapMode(wgpu::AddressMode mode) { switch (mode) { - case dawn::AddressMode::Repeat: + case wgpu::AddressMode::Repeat: return GL_REPEAT; - case dawn::AddressMode::MirroredRepeat: + case wgpu::AddressMode::MirrorRepeat: return GL_MIRRORED_REPEAT; - case dawn::AddressMode::ClampToEdge: + case wgpu::AddressMode::ClampToEdge: return GL_CLAMP_TO_EDGE; default: UNREACHABLE(); @@ -76,26 +76,53 @@ namespace dawn_native { namespace opengl { : SamplerBase(device, descriptor) { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; - gl.GenSamplers(1, &mHandle); - gl.SamplerParameteri(mHandle, GL_TEXTURE_MAG_FILTER, MagFilterMode(descriptor->magFilter)); - gl.SamplerParameteri(mHandle, GL_TEXTURE_MIN_FILTER, - MinFilterMode(descriptor->minFilter, descriptor->mipmapFilter)); - gl.SamplerParameteri(mHandle, GL_TEXTURE_WRAP_R, WrapMode(descriptor->addressModeW)); - gl.SamplerParameteri(mHandle, GL_TEXTURE_WRAP_S, WrapMode(descriptor->addressModeU)); - gl.SamplerParameteri(mHandle, GL_TEXTURE_WRAP_T, WrapMode(descriptor->addressModeV)); - - gl.SamplerParameterf(mHandle, GL_TEXTURE_MIN_LOD, descriptor->lodMinClamp); - gl.SamplerParameterf(mHandle, GL_TEXTURE_MAX_LOD, descriptor->lodMaxClamp); - - if (ToOpenGLCompareFunction(descriptor->compareFunction) != GL_NEVER) { - gl.SamplerParameteri(mHandle, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); - gl.SamplerParameteri(mHandle, GL_TEXTURE_COMPARE_FUNC, - ToOpenGLCompareFunction(descriptor->compareFunction)); + gl.GenSamplers(1, &mFilteringHandle); + SetupGLSampler(mFilteringHandle, descriptor, false); + + gl.GenSamplers(1, &mNonFilteringHandle); + SetupGLSampler(mNonFilteringHandle, descriptor, true); + } + + Sampler::~Sampler() { + const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; + gl.DeleteSamplers(1, &mFilteringHandle); + gl.DeleteSamplers(1, &mNonFilteringHandle); + } + + void Sampler::SetupGLSampler(GLuint sampler, + const SamplerDescriptor* descriptor, + bool forceNearest) { + const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; + + if (forceNearest) { + gl.SamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl.SamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); + } else { + gl.SamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, + MagFilterMode(descriptor->magFilter)); + gl.SamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, + MinFilterMode(descriptor->minFilter, descriptor->mipmapFilter)); + } + gl.SamplerParameteri(sampler, GL_TEXTURE_WRAP_R, WrapMode(descriptor->addressModeW)); + gl.SamplerParameteri(sampler, GL_TEXTURE_WRAP_S, WrapMode(descriptor->addressModeU)); + gl.SamplerParameteri(sampler, GL_TEXTURE_WRAP_T, WrapMode(descriptor->addressModeV)); + + gl.SamplerParameterf(sampler, GL_TEXTURE_MIN_LOD, descriptor->lodMinClamp); + gl.SamplerParameterf(sampler, GL_TEXTURE_MAX_LOD, descriptor->lodMaxClamp); + + if (descriptor->compare != wgpu::CompareFunction::Undefined) { + gl.SamplerParameteri(sampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + gl.SamplerParameteri(sampler, GL_TEXTURE_COMPARE_FUNC, + ToOpenGLCompareFunction(descriptor->compare)); } } - GLuint Sampler::GetHandle() const { - return mHandle; + GLuint Sampler::GetFilteringHandle() const { + return mFilteringHandle; + } + + GLuint Sampler::GetNonFilteringHandle() const { + return mNonFilteringHandle; } }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/SamplerGL.h b/third_party/dawn/src/dawn_native/opengl/SamplerGL.h index 3e6e84c86a1..06448907475 100644 --- a/third_party/dawn/src/dawn_native/opengl/SamplerGL.h +++ b/third_party/dawn/src/dawn_native/opengl/SamplerGL.h @@ -17,20 +17,29 @@ #include "dawn_native/Sampler.h" -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { class Device; - class Sampler : public SamplerBase { + class Sampler final : public SamplerBase { public: Sampler(Device* device, const SamplerDescriptor* descriptor); - GLuint GetHandle() const; + GLuint GetFilteringHandle() const; + GLuint GetNonFilteringHandle() const; private: - GLuint mHandle; + ~Sampler() override; + + void SetupGLSampler(GLuint sampler, const SamplerDescriptor* descriptor, bool forceNearest); + + GLuint mFilteringHandle; + + // This is a sampler equivalent to mFilteringHandle except that it uses NEAREST filtering + // for everything, which is important to preserve texture completeness for u/int textures. + GLuint mNonFilteringHandle; }; }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/ShaderModuleGL.cpp b/third_party/dawn/src/dawn_native/opengl/ShaderModuleGL.cpp index 0166b3f2d9e..53aa101ad1b 100644 --- a/third_party/dawn/src/dawn_native/opengl/ShaderModuleGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/ShaderModuleGL.cpp @@ -18,15 +18,16 @@ #include "common/Platform.h" #include "dawn_native/opengl/DeviceGL.h" -#include +#include #include namespace dawn_native { namespace opengl { - std::string GetBindingName(uint32_t group, uint32_t binding) { + std::string GetBindingName(BindGroupIndex group, BindingNumber bindingNumber) { std::ostringstream o; - o << "dawn_binding_" << group << "_" << binding; + o << "dawn_binding_" << static_cast(group) << "_" + << static_cast(bindingNumber); return o.str(); } @@ -42,101 +43,189 @@ namespace dawn_native { namespace opengl { std::string CombinedSampler::GetName() const { std::ostringstream o; o << "dawn_combined"; - o << "_" << samplerLocation.group << "_" << samplerLocation.binding; - o << "_with_" << textureLocation.group << "_" << textureLocation.binding; + o << "_" << static_cast(samplerLocation.group) << "_" + << static_cast(samplerLocation.binding); + o << "_with_" << static_cast(textureLocation.group) << "_" + << static_cast(textureLocation.binding); return o.str(); } + // static + ResultOrError ShaderModule::Create(Device* device, + const ShaderModuleDescriptor* descriptor) { + Ref module = AcquireRef(new ShaderModule(device, descriptor)); + DAWN_TRY(module->Initialize()); + return module.Detach(); + } + + const char* ShaderModule::GetSource() const { + return mGlslSource.c_str(); + } + + const ShaderModule::CombinedSamplerInfo& ShaderModule::GetCombinedSamplerInfo() const { + return mCombinedInfo; + } + ShaderModule::ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor) : ShaderModuleBase(device, descriptor) { - spirv_cross::CompilerGLSL compiler(descriptor->code, descriptor->codeSize); - // If these options are changed, the values in DawnSPIRVCrossGLSLFastFuzzer.cpp need to be - // updated. - spirv_cross::CompilerGLSL::Options options; - - // The range of Z-coordinate in the clipping volume of OpenGL is [-w, w], while it is [0, w] - // in D3D12, Metal and Vulkan, so we should normalize it in shaders in all backends. - // See the documentation of spirv_cross::CompilerGLSL::Options::vertex::fixup_clipspace for - // more details. - options.vertex.fixup_clipspace = true; - - // TODO(cwallez@chromium.org): discover the backing context version and use that. + } + + MaybeError ShaderModule::Initialize() { + DAWN_TRY(InitializeBase()); + const std::vector& spirv = GetSpirv(); + + std::unique_ptr compilerImpl; + spirv_cross::CompilerGLSL* compiler; + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + // If these options are changed, the values in DawnSPIRVCrossGLSLFastFuzzer.cpp need to + // be updated. + shaderc_spvc::CompileOptions options = GetCompileOptions(); + + // The range of Z-coordinate in the clipping volume of OpenGL is [-w, w], while it is + // [0, w] in D3D12, Metal and Vulkan, so we should normalize it in shaders in all + // backends. See the documentation of + // spirv_cross::CompilerGLSL::Options::vertex::fixup_clipspace for more details. + options.SetFlipVertY(true); + options.SetFixupClipspace(true); + + // TODO(cwallez@chromium.org): discover the backing context version and use that. #if defined(DAWN_PLATFORM_APPLE) - options.version = 410; + options.SetGLSLLanguageVersion(410); #else - options.version = 440; + options.SetGLSLLanguageVersion(440); #endif - compiler.set_common_options(options); - - // Rename the push constant block to be prefixed with the shader stage type so that uniform - // names don't match between the FS and the VS. - const auto& resources = compiler.get_shader_resources(); - if (resources.push_constant_buffers.size() > 0) { - const char* prefix = nullptr; - switch (compiler.get_execution_model()) { - case spv::ExecutionModelVertex: - prefix = "vs_"; - break; - case spv::ExecutionModelFragment: - prefix = "fs_"; - break; - case spv::ExecutionModelGLCompute: - prefix = "cs_"; - break; - default: - UNREACHABLE(); - } - auto interfaceBlock = resources.push_constant_buffers[0]; - compiler.set_name(interfaceBlock.id, prefix + interfaceBlock.name); + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.InitializeForGlsl(spirv.data(), spirv.size(), options), + "Unable to initialize instance of spvc")); + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetCompiler(reinterpret_cast(&compiler)), + "Unable to get cross compiler")); + } else { + // If these options are changed, the values in DawnSPIRVCrossGLSLFastFuzzer.cpp need to + // be updated. + spirv_cross::CompilerGLSL::Options options; + + // The range of Z-coordinate in the clipping volume of OpenGL is [-w, w], while it is + // [0, w] in D3D12, Metal and Vulkan, so we should normalize it in shaders in all + // backends. See the documentation of + // spirv_cross::CompilerGLSL::Options::vertex::fixup_clipspace for more details. + options.vertex.flip_vert_y = true; + options.vertex.fixup_clipspace = true; + + // TODO(cwallez@chromium.org): discover the backing context version and use that. +#if defined(DAWN_PLATFORM_APPLE) + options.version = 410; +#else + options.version = 440; +#endif + + compilerImpl = std::make_unique(spirv); + compiler = compilerImpl.get(); + compiler->set_common_options(options); } - ExtractSpirvInfo(compiler); + DAWN_TRY(ExtractSpirvInfo(*compiler)); - const auto& bindingInfo = GetBindingInfo(); + const ShaderModuleBase::ModuleBindingInfo& bindingInfo = GetBindingInfo(); // Extract bindings names so that it can be used to get its location in program. // Now translate the separate sampler / textures into combined ones and store their info. // We need to do this before removing the set and binding decorations. - compiler.build_combined_image_samplers(); - - for (const auto& combined : compiler.get_combined_image_samplers()) { - mCombinedInfo.emplace_back(); - - auto& info = mCombinedInfo.back(); - info.samplerLocation.group = - compiler.get_decoration(combined.sampler_id, spv::DecorationDescriptorSet); - info.samplerLocation.binding = - compiler.get_decoration(combined.sampler_id, spv::DecorationBinding); - info.textureLocation.group = - compiler.get_decoration(combined.image_id, spv::DecorationDescriptorSet); - info.textureLocation.binding = - compiler.get_decoration(combined.image_id, spv::DecorationBinding); - compiler.set_name(combined.combined_id, info.GetName()); + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + mSpvcContext.BuildCombinedImageSamplers(); + } else { + compiler->build_combined_image_samplers(); + } + + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + std::vector samplers; + mSpvcContext.GetCombinedImageSamplers(&samplers); + for (auto sampler : samplers) { + mCombinedInfo.emplace_back(); + auto& info = mCombinedInfo.back(); + + uint32_t samplerGroup; + mSpvcContext.GetDecoration(sampler.sampler_id, + shaderc_spvc_decoration_descriptorset, &samplerGroup); + info.samplerLocation.group = BindGroupIndex(samplerGroup); + + uint32_t samplerBinding; + mSpvcContext.GetDecoration(sampler.sampler_id, shaderc_spvc_decoration_binding, + &samplerBinding); + info.samplerLocation.binding = BindingNumber(samplerBinding); + + uint32_t textureGroup; + mSpvcContext.GetDecoration(sampler.image_id, shaderc_spvc_decoration_descriptorset, + &textureGroup); + info.textureLocation.group = BindGroupIndex(textureGroup); + + uint32_t textureBinding; + mSpvcContext.GetDecoration(sampler.image_id, shaderc_spvc_decoration_binding, + &textureBinding); + info.textureLocation.binding = BindingNumber(textureBinding); + + mSpvcContext.SetName(sampler.combined_id, info.GetName()); + } + } else { + for (const auto& combined : compiler->get_combined_image_samplers()) { + mCombinedInfo.emplace_back(); + + auto& info = mCombinedInfo.back(); + info.samplerLocation.group = BindGroupIndex( + compiler->get_decoration(combined.sampler_id, spv::DecorationDescriptorSet)); + info.samplerLocation.binding = BindingNumber( + compiler->get_decoration(combined.sampler_id, spv::DecorationBinding)); + info.textureLocation.group = BindGroupIndex( + compiler->get_decoration(combined.image_id, spv::DecorationDescriptorSet)); + info.textureLocation.binding = BindingNumber( + compiler->get_decoration(combined.image_id, spv::DecorationBinding)); + compiler->set_name(combined.combined_id, info.GetName()); + } } // Change binding names to be "dawn_binding__". // Also unsets the SPIRV "Binding" decoration as it outputs "layout(binding=)" which // isn't supported on OSX's OpenGL. - for (uint32_t group = 0; group < kMaxBindGroups; ++group) { - for (uint32_t binding = 0; binding < kMaxBindingsPerGroup; ++binding) { - const auto& info = bindingInfo[group][binding]; - if (info.used) { - compiler.set_name(info.base_type_id, GetBindingName(group, binding)); - compiler.unset_decoration(info.id, spv::DecorationBinding); - compiler.unset_decoration(info.id, spv::DecorationDescriptorSet); + for (BindGroupIndex group(0); group < kMaxBindGroupsTyped; ++group) { + for (const auto& it : bindingInfo[group]) { + BindingNumber bindingNumber = it.first; + const auto& info = it.second; + + uint32_t resourceId; + switch (info.type) { + // When the resource is a uniform or shader storage block, we should change the + // block name instead of the instance name. + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::UniformBuffer: + resourceId = info.base_type_id; + break; + default: + resourceId = info.id; + break; + } + + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + mSpvcContext.SetName(resourceId, GetBindingName(group, bindingNumber)); + mSpvcContext.UnsetDecoration(info.id, shaderc_spvc_decoration_binding); + mSpvcContext.UnsetDecoration(info.id, shaderc_spvc_decoration_descriptorset); + } else { + compiler->set_name(resourceId, GetBindingName(group, bindingNumber)); + compiler->unset_decoration(info.id, spv::DecorationBinding); + compiler->unset_decoration(info.id, spv::DecorationDescriptorSet); } } } - mGlslSource = compiler.compile(); - } - - const char* ShaderModule::GetSource() const { - return mGlslSource.c_str(); - } - - const ShaderModule::CombinedSamplerInfo& ShaderModule::GetCombinedSamplerInfo() const { - return mCombinedInfo; + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc::CompilationResult result; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.CompileShader(&result), + "Unable to compile GLSL shader using spvc")); + DAWN_TRY(CheckSpvcSuccess(result.GetStringOutput(&mGlslSource), + "Unable to get GLSL shader text")); + } else { + mGlslSource = compiler->compile(); + } + return {}; } }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/ShaderModuleGL.h b/third_party/dawn/src/dawn_native/opengl/ShaderModuleGL.h index 8e485e6afd6..3ddd19a7f18 100644 --- a/third_party/dawn/src/dawn_native/opengl/ShaderModuleGL.h +++ b/third_party/dawn/src/dawn_native/opengl/ShaderModuleGL.h @@ -17,17 +17,17 @@ #include "dawn_native/ShaderModule.h" -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { class Device; - std::string GetBindingName(uint32_t group, uint32_t binding); + std::string GetBindingName(BindGroupIndex group, BindingNumber bindingNumber); struct BindingLocation { - uint32_t group; - uint32_t binding; + BindGroupIndex group; + BindingNumber binding; }; bool operator<(const BindingLocation& a, const BindingLocation& b); @@ -38,9 +38,10 @@ namespace dawn_native { namespace opengl { }; bool operator<(const CombinedSampler& a, const CombinedSampler& b); - class ShaderModule : public ShaderModuleBase { + class ShaderModule final : public ShaderModuleBase { public: - ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor); + static ResultOrError Create(Device* device, + const ShaderModuleDescriptor* descriptor); using CombinedSamplerInfo = std::vector; @@ -48,6 +49,10 @@ namespace dawn_native { namespace opengl { const CombinedSamplerInfo& GetCombinedSamplerInfo() const; private: + ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor); + ~ShaderModule() override = default; + MaybeError Initialize(); + CombinedSamplerInfo mCombinedInfo; std::string mGlslSource; }; diff --git a/third_party/dawn/src/dawn_native/opengl/SwapChainGL.cpp b/third_party/dawn/src/dawn_native/opengl/SwapChainGL.cpp index 2a9fe294d55..8223a2ceb12 100644 --- a/third_party/dawn/src/dawn_native/opengl/SwapChainGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/SwapChainGL.cpp @@ -23,7 +23,7 @@ namespace dawn_native { namespace opengl { SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor) - : SwapChainBase(device, descriptor) { + : OldSwapChainBase(device, descriptor) { const auto& im = GetImplementation(); im.Init(im.userData, nullptr); } @@ -36,7 +36,7 @@ namespace dawn_native { namespace opengl { DawnSwapChainNextTexture next = {}; DawnSwapChainError error = im.GetNextTexture(im.userData, &next); if (error) { - GetDevice()->HandleError(error); + GetDevice()->HandleError(InternalErrorType::Internal, error); return nullptr; } GLuint nativeTexture = next.texture.u32; @@ -44,7 +44,8 @@ namespace dawn_native { namespace opengl { TextureBase::TextureState::OwnedExternal); } - void SwapChain::OnBeforePresent(TextureBase*) { + MaybeError SwapChain::OnBeforePresent(TextureViewBase*) { + return {}; } }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/SwapChainGL.h b/third_party/dawn/src/dawn_native/opengl/SwapChainGL.h index 5e532fa4f4f..0cce92594ac 100644 --- a/third_party/dawn/src/dawn_native/opengl/SwapChainGL.h +++ b/third_party/dawn/src/dawn_native/opengl/SwapChainGL.h @@ -17,20 +17,20 @@ #include "dawn_native/SwapChain.h" -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { class Device; - class SwapChain : public SwapChainBase { + class SwapChain final : public OldSwapChainBase { public: SwapChain(Device* device, const SwapChainDescriptor* descriptor); - ~SwapChain(); protected: + ~SwapChain() override; TextureBase* GetNextTextureImpl(const TextureDescriptor* descriptor) override; - void OnBeforePresent(TextureBase* texture) override; + MaybeError OnBeforePresent(TextureViewBase* view) override; }; }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/TextureGL.cpp b/third_party/dawn/src/dawn_native/opengl/TextureGL.cpp index 1ffcacbc784..0c4ea27bd78 100644 --- a/third_party/dawn/src/dawn_native/opengl/TextureGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/TextureGL.cpp @@ -13,12 +13,13 @@ // limitations under the License. #include "dawn_native/opengl/TextureGL.h" -#include "dawn_native/opengl/DeviceGL.h" #include "common/Assert.h" - -#include -#include +#include "common/Constants.h" +#include "common/Math.h" +#include "dawn_native/opengl/BufferGL.h" +#include "dawn_native/opengl/DeviceGL.h" +#include "dawn_native/opengl/UtilsGL.h" namespace dawn_native { namespace opengl { @@ -26,8 +27,8 @@ namespace dawn_native { namespace opengl { GLenum TargetForTexture(const TextureDescriptor* descriptor) { switch (descriptor->dimension) { - case dawn::TextureDimension::e2D: - if (descriptor->arrayLayerCount > 1) { + case wgpu::TextureDimension::e2D: + if (descriptor->size.depth > 1) { ASSERT(descriptor->sampleCount == 1); return GL_TEXTURE_2D_ARRAY; } else { @@ -44,17 +45,21 @@ namespace dawn_native { namespace opengl { } } - GLenum TargetForTextureViewDimension(dawn::TextureViewDimension dimension, + GLenum TargetForTextureViewDimension(wgpu::TextureViewDimension dimension, + uint32_t arrayLayerCount, uint32_t sampleCount) { switch (dimension) { - case dawn::TextureViewDimension::e2D: + case wgpu::TextureViewDimension::e2D: return (sampleCount > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; - case dawn::TextureViewDimension::e2DArray: + case wgpu::TextureViewDimension::e2DArray: + if (arrayLayerCount == 1) { + return (sampleCount > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D; + } ASSERT(sampleCount == 1); return GL_TEXTURE_2D_ARRAY; - case dawn::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::Cube: return GL_TEXTURE_CUBE_MAP; - case dawn::TextureViewDimension::CubeArray: + case wgpu::TextureViewDimension::CubeArray: return GL_TEXTURE_CUBE_MAP_ARRAY; default: UNREACHABLE(); @@ -62,47 +67,21 @@ namespace dawn_native { namespace opengl { } } - TextureFormatInfo GetGLFormatInfo(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::R8G8B8A8Unorm: - return {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; - case dawn::TextureFormat::R8G8Unorm: - return {GL_RG8, GL_RG, GL_UNSIGNED_BYTE}; - case dawn::TextureFormat::R8Unorm: - return {GL_R8, GL_RED, GL_UNSIGNED_BYTE}; - case dawn::TextureFormat::R8G8B8A8Uint: - return {GL_RGBA8UI, GL_RGBA, GL_UNSIGNED_INT}; - case dawn::TextureFormat::R8G8Uint: - return {GL_RG8UI, GL_RG, GL_UNSIGNED_INT}; - case dawn::TextureFormat::R8Uint: - return {GL_R8UI, GL_RED, GL_UNSIGNED_INT}; - case dawn::TextureFormat::B8G8R8A8Unorm: - // This doesn't have an enum for the internal format in OpenGL, so use RGBA8. - return {GL_RGBA8, GL_BGRA, GL_UNSIGNED_BYTE}; - case dawn::TextureFormat::D32FloatS8Uint: - return {GL_DEPTH32F_STENCIL8, GL_DEPTH_STENCIL, - GL_FLOAT_32_UNSIGNED_INT_24_8_REV}; - default: - UNREACHABLE(); - return {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; - } - } - GLuint GenTexture(const OpenGLFunctions& gl) { GLuint handle = 0; gl.GenTextures(1, &handle); return handle; } - bool UsageNeedsTextureView(dawn::TextureUsageBit usage) { - constexpr dawn::TextureUsageBit kUsageNeedingTextureView = - dawn::TextureUsageBit::Storage | dawn::TextureUsageBit::Sampled; + bool UsageNeedsTextureView(wgpu::TextureUsage usage) { + constexpr wgpu::TextureUsage kUsageNeedingTextureView = + wgpu::TextureUsage::Storage | wgpu::TextureUsage::Sampled; return usage & kUsageNeedingTextureView; } bool RequiresCreatingNewTextureView(const TextureBase* texture, const TextureViewDescriptor* textureViewDescriptor) { - if (texture->GetFormat() != textureViewDescriptor->format) { + if (texture->GetFormat().format != textureViewDescriptor->format) { return true; } @@ -115,8 +94,8 @@ namespace dawn_native { namespace opengl { } switch (textureViewDescriptor->dimension) { - case dawn::TextureViewDimension::Cube: - case dawn::TextureViewDimension::CubeArray: + case wgpu::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::CubeArray: return true; default: break; @@ -133,13 +112,13 @@ namespace dawn_native { namespace opengl { : Texture(device, descriptor, GenTexture(device->gl), TextureState::OwnedInternal) { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; - uint32_t width = GetSize().width; - uint32_t height = GetSize().height; + uint32_t width = GetWidth(); + uint32_t height = GetHeight(); uint32_t levels = GetNumMipLevels(); uint32_t arrayLayers = GetArrayLayers(); uint32_t sampleCount = GetSampleCount(); - auto formatInfo = GetGLFormatInfo(GetFormat()); + const GLFormat& glFormat = GetGLFormat(); gl.BindTexture(mTarget, mHandle); @@ -147,17 +126,17 @@ namespace dawn_native { namespace opengl { // GL_TRUE, so the storage of the texture must be allocated with glTexStorage*D. // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTextureView.xhtml switch (GetDimension()) { - case dawn::TextureDimension::e2D: + case wgpu::TextureDimension::e2D: if (arrayLayers > 1) { ASSERT(!IsMultisampledTexture()); - gl.TexStorage3D(mTarget, levels, formatInfo.internalFormat, width, height, + gl.TexStorage3D(mTarget, levels, glFormat.internalFormat, width, height, arrayLayers); } else { if (IsMultisampledTexture()) { - gl.TexStorage2DMultisample(mTarget, sampleCount, formatInfo.internalFormat, + gl.TexStorage2DMultisample(mTarget, sampleCount, glFormat.internalFormat, width, height, true); } else { - gl.TexStorage2D(mTarget, levels, formatInfo.internalFormat, width, height); + gl.TexStorage2D(mTarget, levels, glFormat.internalFormat, width, height); } } break; @@ -170,15 +149,8 @@ namespace dawn_native { namespace opengl { gl.TexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1); if (GetDevice()->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { - static constexpr uint32_t MAX_TEXEL_SIZE = 16; - ASSERT(TextureFormatPixelSize(GetFormat()) <= MAX_TEXEL_SIZE); - GLubyte clearColor[MAX_TEXEL_SIZE]; - std::fill(clearColor, clearColor + MAX_TEXEL_SIZE, 255); - - // TODO(natlee@microsoft.com): clear all subresources - for (uint32_t i = 0; i < GL_TEXTURE_MAX_LEVEL; i++) { - gl.ClearTexImage(mHandle, i, formatInfo.format, formatInfo.type, clearColor); - } + GetDevice()->ConsumedError( + ClearTexture(GetAllSubresources(), TextureBase::ClearValue::NonZero)); } } @@ -195,8 +167,10 @@ namespace dawn_native { namespace opengl { } void Texture::DestroyImpl() { - ToBackend(GetDevice())->gl.DeleteTextures(1, &mHandle); - mHandle = 0; + if (GetTextureState() == TextureState::OwnedInternal) { + ToBackend(GetDevice())->gl.DeleteTextures(1, &mHandle); + mHandle = 0; + } } GLuint Texture::GetHandle() const { @@ -207,15 +181,213 @@ namespace dawn_native { namespace opengl { return mTarget; } - TextureFormatInfo Texture::GetGLFormat() const { - return GetGLFormatInfo(GetFormat()); + const GLFormat& Texture::GetGLFormat() const { + return ToBackend(GetDevice())->GetGLFormat(GetFormat()); + } + + MaybeError Texture::ClearTexture(const SubresourceRange& range, + TextureBase::ClearValue clearValue) { + // TODO(jiawei.shao@intel.com): initialize the textures with compressed formats. + if (GetFormat().isCompressed) { + return {}; + } + + Device* device = ToBackend(GetDevice()); + const OpenGLFunctions& gl = device->gl; + + uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1; + float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f; + + if (GetFormat().isRenderable) { + if (GetFormat().HasDepthOrStencil()) { + bool doDepthClear = GetFormat().HasDepth(); + bool doStencilClear = GetFormat().HasStencil(); + GLfloat depth = fClearColor; + GLint stencil = clearColor; + if (doDepthClear) { + gl.DepthMask(GL_TRUE); + } + if (doStencilClear) { + gl.StencilMask(GetStencilMaskFromStencilFormat(GetFormat().format)); + } + + auto DoClear = [&]() { + if (doDepthClear && doStencilClear) { + gl.ClearBufferfi(GL_DEPTH_STENCIL, 0, depth, stencil); + } else if (doDepthClear) { + gl.ClearBufferfv(GL_DEPTH, 0, &depth); + } else if (doStencilClear) { + gl.ClearBufferiv(GL_STENCIL, 0, &stencil); + } + }; + + GLuint framebuffer = 0; + gl.GenFramebuffers(1, &framebuffer); + gl.BindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer); + + for (uint32_t level = range.baseMipLevel; + level < range.baseMipLevel + range.levelCount; ++level) { + switch (GetDimension()) { + case wgpu::TextureDimension::e2D: + if (GetArrayLayers() == 1) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, 0))) { + // Skip lazy clears if already initialized. + continue; + } + gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, + GL_DEPTH_STENCIL_ATTACHMENT, GetGLTarget(), + GetHandle(), static_cast(level)); + DoClear(); + } else { + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, layer))) { + // Skip lazy clears if already initialized. + continue; + } + + gl.FramebufferTextureLayer( + GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GetHandle(), static_cast(level), + static_cast(layer)); + DoClear(); + } + } + break; + + default: + UNREACHABLE(); + } + } + + gl.DeleteFramebuffers(1, &framebuffer); + } else { + static constexpr uint32_t MAX_TEXEL_SIZE = 16; + ASSERT(GetFormat().blockByteSize <= MAX_TEXEL_SIZE); + std::array clearColorData; + clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 255; + clearColorData.fill(clearColor); + + const GLFormat& glFormat = GetGLFormat(); + for (uint32_t level = range.baseMipLevel; + level < range.baseMipLevel + range.levelCount; ++level) { + Extent3D mipSize = GetMipLevelPhysicalSize(level); + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, layer))) { + // Skip lazy clears if already initialized. + continue; + } + gl.ClearTexSubImage(mHandle, static_cast(level), 0, 0, + static_cast(layer), mipSize.width, + mipSize.height, 1, glFormat.format, glFormat.type, + clearColorData.data()); + } + } + } + } else { + // TODO(natlee@microsoft.com): test compressed textures are cleared + // create temp buffer with clear color to copy to the texture image + ASSERT(kTextureBytesPerRowAlignment % GetFormat().blockByteSize == 0); + uint32_t bytesPerRow = + Align((GetWidth() / GetFormat().blockWidth) * GetFormat().blockByteSize, + kTextureBytesPerRowAlignment); + + // Make sure that we are not rounding + ASSERT(bytesPerRow % GetFormat().blockByteSize == 0); + ASSERT(GetHeight() % GetFormat().blockHeight == 0); + + dawn_native::BufferDescriptor descriptor = {}; + descriptor.mappedAtCreation = true; + descriptor.usage = wgpu::BufferUsage::CopySrc; + descriptor.size = bytesPerRow * (GetHeight() / GetFormat().blockHeight); + if (descriptor.size > std::numeric_limits::max()) { + return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer."); + } + + // TODO(natlee@microsoft.com): use Dynamic Uplaoder here for temp buffer + Ref srcBuffer = AcquireRef(ToBackend(device->CreateBuffer(&descriptor))); + + // Fill the buffer with clear color + memset(srcBuffer->GetMappedRange(0, descriptor.size), clearColor, descriptor.size); + srcBuffer->Unmap(); + + // Bind buffer and texture, and make the buffer to texture copy + gl.PixelStorei(GL_UNPACK_ROW_LENGTH, + (bytesPerRow / GetFormat().blockByteSize) * GetFormat().blockWidth); + gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); + for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; + ++level) { + gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, srcBuffer->GetHandle()); + gl.ActiveTexture(GL_TEXTURE0); + gl.BindTexture(GetGLTarget(), GetHandle()); + + Extent3D size = GetMipLevelPhysicalSize(level); + switch (GetDimension()) { + case wgpu::TextureDimension::e2D: + if (GetArrayLayers() == 1) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, 0))) { + // Skip lazy clears if already initialized. + continue; + } + gl.TexSubImage2D(GetGLTarget(), static_cast(level), 0, 0, + size.width, size.height, GetGLFormat().format, + GetGLFormat().type, 0); + } else { + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, layer))) { + // Skip lazy clears if already initialized. + continue; + } + gl.TexSubImage3D(GetGLTarget(), static_cast(level), 0, 0, + static_cast(layer), size.width, size.height, + 1, GetGLFormat().format, GetGLFormat().type, 0); + } + } + break; + + default: + UNREACHABLE(); + } + } + gl.PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gl.PixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0); + + gl.BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } + if (clearValue == TextureBase::ClearValue::Zero) { + SetIsSubresourceContentInitialized(true, range); + device->IncrementLazyClearCountForTesting(); + } + return {}; + } + + void Texture::EnsureSubresourceContentInitialized(const SubresourceRange& range) { + if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { + return; + } + if (!IsSubresourceContentInitialized(range)) { + GetDevice()->ConsumedError(ClearTexture(range, TextureBase::ClearValue::Zero)); + } } // TextureView TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) : TextureViewBase(texture, descriptor), mOwnsHandle(false) { - mTarget = TargetForTextureViewDimension(descriptor->dimension, texture->GetSampleCount()); + mTarget = TargetForTextureViewDimension(descriptor->dimension, descriptor->arrayLayerCount, + texture->GetSampleCount()); if (!UsageNeedsTextureView(texture->GetUsage())) { mHandle = 0; @@ -227,11 +399,10 @@ namespace dawn_native { namespace opengl { const OpenGLFunctions& gl = ToBackend(GetDevice())->gl; mHandle = GenTexture(gl); const Texture* textureGL = ToBackend(texture); - TextureFormatInfo textureViewFormat = GetGLFormatInfo(descriptor->format); - gl.TextureView(mHandle, mTarget, textureGL->GetHandle(), - textureViewFormat.internalFormat, descriptor->baseMipLevel, - descriptor->mipLevelCount, descriptor->baseArrayLayer, - descriptor->arrayLayerCount); + const GLFormat& glFormat = ToBackend(GetDevice())->GetGLFormat(GetFormat()); + gl.TextureView(mHandle, mTarget, textureGL->GetHandle(), glFormat.internalFormat, + descriptor->baseMipLevel, descriptor->mipLevelCount, + descriptor->baseArrayLayer, descriptor->arrayLayerCount); mOwnsHandle = true; } } diff --git a/third_party/dawn/src/dawn_native/opengl/TextureGL.h b/third_party/dawn/src/dawn_native/opengl/TextureGL.h index 1dacd179ab3..a01b94348bc 100644 --- a/third_party/dawn/src/dawn_native/opengl/TextureGL.h +++ b/third_party/dawn/src/dawn_native/opengl/TextureGL.h @@ -17,47 +17,47 @@ #include "dawn_native/Texture.h" -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { class Device; + struct GLFormat; - struct TextureFormatInfo { - GLenum internalFormat; - GLenum format; - GLenum type; - }; - - class Texture : public TextureBase { + class Texture final : public TextureBase { public: Texture(Device* device, const TextureDescriptor* descriptor); Texture(Device* device, const TextureDescriptor* descriptor, GLuint handle, TextureState state); - ~Texture(); GLuint GetHandle() const; GLenum GetGLTarget() const; - TextureFormatInfo GetGLFormat() const; + const GLFormat& GetGLFormat() const; + + void EnsureSubresourceContentInitialized(const SubresourceRange& range); private: + ~Texture() override; + void DestroyImpl() override; + MaybeError ClearTexture(const SubresourceRange& range, TextureBase::ClearValue clearValue); GLuint mHandle; GLenum mTarget; }; - class TextureView : public TextureViewBase { + class TextureView final : public TextureViewBase { public: TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor); - ~TextureView(); GLuint GetHandle() const; GLenum GetGLTarget() const; private: + ~TextureView() override; + GLuint mHandle; GLenum mTarget; bool mOwnsHandle; diff --git a/third_party/dawn/src/dawn_native/opengl/UtilsGL.cpp b/third_party/dawn/src/dawn_native/opengl/UtilsGL.cpp index 0419d4be998..3905b269f75 100644 --- a/third_party/dawn/src/dawn_native/opengl/UtilsGL.cpp +++ b/third_party/dawn/src/dawn_native/opengl/UtilsGL.cpp @@ -18,27 +18,35 @@ namespace dawn_native { namespace opengl { - GLuint ToOpenGLCompareFunction(dawn::CompareFunction compareFunction) { + GLuint ToOpenGLCompareFunction(wgpu::CompareFunction compareFunction) { switch (compareFunction) { - case dawn::CompareFunction::Never: + case wgpu::CompareFunction::Never: return GL_NEVER; - case dawn::CompareFunction::Less: + case wgpu::CompareFunction::Less: return GL_LESS; - case dawn::CompareFunction::LessEqual: + case wgpu::CompareFunction::LessEqual: return GL_LEQUAL; - case dawn::CompareFunction::Greater: + case wgpu::CompareFunction::Greater: return GL_GREATER; - case dawn::CompareFunction::GreaterEqual: + case wgpu::CompareFunction::GreaterEqual: return GL_GEQUAL; - case dawn::CompareFunction::NotEqual: + case wgpu::CompareFunction::NotEqual: return GL_NOTEQUAL; - case dawn::CompareFunction::Equal: + case wgpu::CompareFunction::Equal: return GL_EQUAL; - case dawn::CompareFunction::Always: + case wgpu::CompareFunction::Always: return GL_ALWAYS; default: UNREACHABLE(); } } + GLint GetStencilMaskFromStencilFormat(wgpu::TextureFormat depthStencilFormat) { + switch (depthStencilFormat) { + case wgpu::TextureFormat::Depth24PlusStencil8: + return 0xFF; + default: + UNREACHABLE(); + } + } }} // namespace dawn_native::opengl diff --git a/third_party/dawn/src/dawn_native/opengl/UtilsGL.h b/third_party/dawn/src/dawn_native/opengl/UtilsGL.h index f82eb286671..2f87b378132 100644 --- a/third_party/dawn/src/dawn_native/opengl/UtilsGL.h +++ b/third_party/dawn/src/dawn_native/opengl/UtilsGL.h @@ -16,12 +16,12 @@ #define DAWNNATIVE_OPENGL_UTILSGL_H_ #include "dawn_native/dawn_platform.h" -#include "glad/glad.h" +#include "dawn_native/opengl/opengl_platform.h" namespace dawn_native { namespace opengl { - GLuint ToOpenGLCompareFunction(dawn::CompareFunction compareFunction); - + GLuint ToOpenGLCompareFunction(wgpu::CompareFunction compareFunction); + GLint GetStencilMaskFromStencilFormat(wgpu::TextureFormat depthStencilFormat); }} // namespace dawn_native::opengl #endif // DAWNNATIVE_OPENGL_UTILSGL_H_ diff --git a/third_party/dawn/src/dawn_native/opengl/opengl_platform.h b/third_party/dawn/src/dawn_native/opengl/opengl_platform.h new file mode 100644 index 00000000000..783f6e48ca4 --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/opengl_platform.h @@ -0,0 +1,15 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/opengl/opengl_platform_autogen.h" diff --git a/third_party/dawn/src/dawn_native/opengl/supported_extensions.json b/third_party/dawn/src/dawn_native/opengl/supported_extensions.json new file mode 100644 index 00000000000..344fe3df3bc --- /dev/null +++ b/third_party/dawn/src/dawn_native/opengl/supported_extensions.json @@ -0,0 +1,22 @@ +{ + "_comment": [ + "Copyright 2019 The Dawn Authors", + "", + "Licensed under the Apache License, Version 2.0 (the \"License\");", + "you may not use this file except in compliance with the License.", + "You may obtain a copy of the License at", + "", + " http://www.apache.org/licenses/LICENSE-2.0", + "", + "Unless required by applicable law or agreed to in writing, software", + "distributed under the License is distributed on an \"AS IS\" BASIS,", + "WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.", + "See the License for the specific language governing permissions and", + "limitations under the License." + ], + + "supported_extensions": [ + "GL_EXT_texture_compression_s3tc", + "GL_EXT_texture_compression_s3tc_srgb" + ] +} diff --git a/third_party/dawn/src/dawn_native/vulkan/AdapterVk.cpp b/third_party/dawn/src/dawn_native/vulkan/AdapterVk.cpp index 614eedadefe..43ed6acbc2c 100644 --- a/third_party/dawn/src/dawn_native/vulkan/AdapterVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/AdapterVk.cpp @@ -20,7 +20,7 @@ namespace dawn_native { namespace vulkan { Adapter::Adapter(Backend* backend, VkPhysicalDevice physicalDevice) - : AdapterBase(backend->GetInstance(), BackendType::Vulkan), + : AdapterBase(backend->GetInstance(), wgpu::BackendType::Vulkan), mPhysicalDevice(physicalDevice), mBackend(backend) { } @@ -39,6 +39,13 @@ namespace dawn_native { namespace vulkan { MaybeError Adapter::Initialize() { DAWN_TRY_ASSIGN(mDeviceInfo, GatherDeviceInfo(*this)); + if (!mDeviceInfo.HasExt(DeviceExt::Maintenance1)) { + return DAWN_INTERNAL_ERROR( + "Dawn requires Vulkan 1.1 or Vulkan 1.0 with KHR_Maintenance1 in order to support " + "viewport flipY"); + } + + InitializeSupportedExtensions(); mPCIInfo.deviceId = mDeviceInfo.properties.deviceID; mPCIInfo.vendorId = mDeviceInfo.properties.vendorID; @@ -46,26 +53,45 @@ namespace dawn_native { namespace vulkan { switch (mDeviceInfo.properties.deviceType) { case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: - mDeviceType = DeviceType::IntegratedGPU; + mAdapterType = wgpu::AdapterType::IntegratedGPU; break; case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: - mDeviceType = DeviceType::DiscreteGPU; + mAdapterType = wgpu::AdapterType::DiscreteGPU; break; case VK_PHYSICAL_DEVICE_TYPE_CPU: - mDeviceType = DeviceType::CPU; + mAdapterType = wgpu::AdapterType::CPU; break; default: - mDeviceType = DeviceType::Unknown; + mAdapterType = wgpu::AdapterType::Unknown; break; } return {}; } + void Adapter::InitializeSupportedExtensions() { + if (mDeviceInfo.features.textureCompressionBC == VK_TRUE) { + mSupportedExtensions.EnableExtension(Extension::TextureCompressionBC); + } + + if (mDeviceInfo.HasExt(DeviceExt::ShaderFloat16Int8) && + mDeviceInfo.shaderFloat16Int8Features.shaderFloat16 == VK_TRUE && + mDeviceInfo.HasExt(DeviceExt::_16BitStorage) && + mDeviceInfo._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess == VK_TRUE) { + mSupportedExtensions.EnableExtension(Extension::ShaderFloat16); + } + + if (mDeviceInfo.features.pipelineStatisticsQuery == VK_TRUE) { + mSupportedExtensions.EnableExtension(Extension::PipelineStatisticsQuery); + } + + if (mDeviceInfo.properties.limits.timestampComputeAndGraphics == VK_TRUE) { + mSupportedExtensions.EnableExtension(Extension::TimestampQuery); + } + } + ResultOrError Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) { - std::unique_ptr device = std::make_unique(this, descriptor); - DAWN_TRY(device->Initialize()); - return device.release(); + return Device::Create(this, descriptor); } }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/AdapterVk.h b/third_party/dawn/src/dawn_native/vulkan/AdapterVk.h index 2d9ed83ed6f..67f77d9c7a7 100644 --- a/third_party/dawn/src/dawn_native/vulkan/AdapterVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/AdapterVk.h @@ -27,7 +27,7 @@ namespace dawn_native { namespace vulkan { class Adapter : public AdapterBase { public: Adapter(Backend* backend, VkPhysicalDevice physicalDevice); - virtual ~Adapter() = default; + ~Adapter() override = default; const VulkanDeviceInfo& GetDeviceInfo() const; VkPhysicalDevice GetPhysicalDevice() const; @@ -37,6 +37,7 @@ namespace dawn_native { namespace vulkan { private: ResultOrError CreateDeviceImpl(const DeviceDescriptor* descriptor) override; + void InitializeSupportedExtensions(); VkPhysicalDevice mPhysicalDevice; Backend* mBackend; diff --git a/third_party/dawn/src/dawn_native/vulkan/BackendVk.cpp b/third_party/dawn/src/dawn_native/vulkan/BackendVk.cpp index 6ca0a26ca35..613f0a01260 100644 --- a/third_party/dawn/src/dawn_native/vulkan/BackendVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/BackendVk.cpp @@ -14,24 +14,47 @@ #include "dawn_native/vulkan/BackendVk.h" +#include "common/BitSetIterator.h" +#include "common/Log.h" +#include "common/SystemUtils.h" #include "dawn_native/Instance.h" #include "dawn_native/VulkanBackend.h" #include "dawn_native/vulkan/AdapterVk.h" #include "dawn_native/vulkan/VulkanError.h" -#include +// TODO(crbug.com/dawn/283): Link against the Vulkan Loader and remove this. +#if defined(DAWN_ENABLE_SWIFTSHADER) +# if defined(DAWN_PLATFORM_LINUX) || defined(DAWN_PLATFORM_FUSCHIA) +constexpr char kSwiftshaderLibName[] = "libvk_swiftshader.so"; +# elif defined(DAWN_PLATFORM_WINDOWS) +constexpr char kSwiftshaderLibName[] = "vk_swiftshader.dll"; +# elif defined(DAWN_PLATFORM_MACOS) +constexpr char kSwiftshaderLibName[] = "libvk_swiftshader.dylib"; +# else +# error "Unimplemented Swiftshader Vulkan backend platform" +# endif +#endif -#if DAWN_PLATFORM_LINUX -const char kVulkanLibName[] = "libvulkan.so.1"; -#elif DAWN_PLATFORM_WINDOWS -const char kVulkanLibName[] = "vulkan-1.dll"; +#if defined(DAWN_PLATFORM_LINUX) +# if defined(DAWN_PLATFORM_ANDROID) +constexpr char kVulkanLibName[] = "libvulkan.so"; +# else +constexpr char kVulkanLibName[] = "libvulkan.so.1"; +# endif +#elif defined(DAWN_PLATFORM_WINDOWS) +constexpr char kVulkanLibName[] = "vulkan-1.dll"; +#elif defined(DAWN_PLATFORM_MACOS) +constexpr char kVulkanLibName[] = "libvulkan.dylib"; +#elif defined(DAWN_PLATFORM_FUCHSIA) +constexpr char kVulkanLibName[] = "libvulkan.so"; #else # error "Unimplemented Vulkan backend platform" #endif namespace dawn_native { namespace vulkan { - Backend::Backend(InstanceBase* instance) : BackendConnection(instance, BackendType::Vulkan) { + Backend::Backend(InstanceBase* instance) + : BackendConnection(instance, wgpu::BackendType::Vulkan) { } Backend::~Backend() { @@ -55,9 +78,66 @@ namespace dawn_native { namespace vulkan { return mInstance; } - MaybeError Backend::Initialize() { - if (!mVulkanLib.Open(kVulkanLibName)) { - return DAWN_CONTEXT_LOST_ERROR(std::string("Couldn't open ") + kVulkanLibName); + const VulkanGlobalInfo& Backend::GetGlobalInfo() const { + return mGlobalInfo; + } + + MaybeError Backend::LoadVulkan(bool useSwiftshader) { + // First try to load the system Vulkan driver, if that fails, + // try to load with Swiftshader. Note: The system driver could potentially be Swiftshader + // if it was installed. + if (mVulkanLib.Open(kVulkanLibName)) { + return {}; + } + dawn::WarningLog() << std::string("Couldn't open ") + kVulkanLibName; + + // If |useSwiftshader == true|, fallback and try to directly load the Swiftshader + // library. + if (useSwiftshader) { +#if defined(DAWN_ENABLE_SWIFTSHADER) + if (mVulkanLib.Open(kSwiftshaderLibName)) { + return {}; + } + dawn::WarningLog() << std::string("Couldn't open ") + kSwiftshaderLibName; +#else + UNREACHABLE(); +#endif // defined(DAWN_ENABLE_SWIFTSHADER) + } + + return DAWN_INTERNAL_ERROR("Couldn't load Vulkan"); + } + + MaybeError Backend::Initialize(bool useSwiftshader) { + DAWN_TRY(LoadVulkan(useSwiftshader)); + + // These environment variables need only be set while loading procs and gathering device + // info. + ScopedEnvironmentVar vkICDFilenames; + ScopedEnvironmentVar vkLayerPath; + + if (useSwiftshader) { +#if defined(DAWN_SWIFTSHADER_VK_ICD_JSON) + std::string fullSwiftshaderICDPath = + GetExecutableDirectory() + DAWN_SWIFTSHADER_VK_ICD_JSON; + if (!vkICDFilenames.Set("VK_ICD_FILENAMES", fullSwiftshaderICDPath.c_str())) { + return DAWN_INTERNAL_ERROR("Couldn't set VK_ICD_FILENAMES"); + } +#else + dawn::WarningLog() << "Swiftshader enabled but Dawn was not built with " + "DAWN_SWIFTSHADER_VK_ICD_JSON."; +#endif + } + + if (GetInstance()->IsBackendValidationEnabled()) { +#if defined(DAWN_ENABLE_VULKAN_VALIDATION_LAYERS) + std::string vkDataDir = GetExecutableDirectory() + DAWN_VK_DATA_DIR; + if (!vkLayerPath.Set("VK_LAYER_PATH", vkDataDir.c_str())) { + return DAWN_INTERNAL_ERROR("Couldn't set VK_LAYER_PATH"); + } +#else + dawn::WarningLog() << "Backend validation enabled but Dawn was not built with " + "DAWN_ENABLE_VULKAN_VALIDATION_LAYERS."; +#endif } DAWN_TRY(mFunctions.LoadGlobalProcs(mVulkanLib)); @@ -70,7 +150,7 @@ namespace dawn_native { namespace vulkan { DAWN_TRY(mFunctions.LoadInstanceProcs(mInstance, mGlobalInfo)); - if (usedGlobalKnobs.debugReport) { + if (usedGlobalKnobs.HasExt(InstanceExt::DebugReport)) { DAWN_TRY(RegisterDebugReport()); } @@ -97,9 +177,7 @@ namespace dawn_native { namespace vulkan { ResultOrError Backend::CreateInstance() { VulkanGlobalKnobs usedKnobs = {}; - - std::vector layersToRequest; - std::vector extensionsToRequest; + std::vector layerNames; // vktrace works by instering a layer, but we hide it behind a macro due to the vktrace // layer crashes when used without vktrace server started. See this vktrace issue: @@ -108,7 +186,7 @@ namespace dawn_native { namespace vulkan { // by other layers. #if defined(DAWN_USE_VKTRACE) if (mGlobalInfo.vktrace) { - layersToRequest.push_back(kLayerNameLunargVKTrace); + layerNames.push_back(kLayerNameLunargVKTrace); usedKnobs.vktrace = true; } #endif @@ -116,47 +194,34 @@ namespace dawn_native { namespace vulkan { // it unless we are debugging in RenderDoc so we hide it behind a macro. #if defined(DAWN_USE_RENDERDOC) if (mGlobalInfo.renderDocCapture) { - layersToRequest.push_back(kLayerNameRenderDocCapture); + layerNames.push_back(kLayerNameRenderDocCapture); usedKnobs.renderDocCapture = true; } #endif if (GetInstance()->IsBackendValidationEnabled()) { - if (mGlobalInfo.standardValidation) { - layersToRequest.push_back(kLayerNameLunargStandardValidation); - usedKnobs.standardValidation = true; - } - if (mGlobalInfo.debugReport) { - extensionsToRequest.push_back(kExtensionNameExtDebugReport); - usedKnobs.debugReport = true; + if (mGlobalInfo.validation) { + layerNames.push_back(kLayerNameKhronosValidation); + usedKnobs.validation = true; } } - // Always request all extensions used to create VkSurfaceKHR objects so that they are - // always available for embedders looking to create VkSurfaceKHR on our VkInstance. - if (mGlobalInfo.macosSurface) { - extensionsToRequest.push_back(kExtensionNameMvkMacosSurface); - usedKnobs.macosSurface = true; - } - if (mGlobalInfo.surface) { - extensionsToRequest.push_back(kExtensionNameKhrSurface); - usedKnobs.surface = true; - } - if (mGlobalInfo.waylandSurface) { - extensionsToRequest.push_back(kExtensionNameKhrWaylandSurface); - usedKnobs.waylandSurface = true; - } - if (mGlobalInfo.win32Surface) { - extensionsToRequest.push_back(kExtensionNameKhrWin32Surface); - usedKnobs.win32Surface = true; - } - if (mGlobalInfo.xcbSurface) { - extensionsToRequest.push_back(kExtensionNameKhrXcbSurface); - usedKnobs.xcbSurface = true; + // Available and known instance extensions default to being requested, but some special + // cases are removed. + InstanceExtSet extensionsToRequest = mGlobalInfo.extensions; + + if (!GetInstance()->IsBackendValidationEnabled()) { + extensionsToRequest.Set(InstanceExt::DebugReport, false); } - if (mGlobalInfo.xlibSurface) { - extensionsToRequest.push_back(kExtensionNameKhrXlibSurface); - usedKnobs.xlibSurface = true; + usedKnobs.extensions = extensionsToRequest; + + std::vector extensionNames; + for (uint32_t ext : IterateBitSet(extensionsToRequest.extensionBitSet)) { + const InstanceExtInfo& info = GetInstanceExtInfo(static_cast(ext)); + + if (info.versionPromoted > mGlobalInfo.apiVersion) { + extensionNames.push_back(info.name); + } } VkApplicationInfo appInfo; @@ -166,17 +231,27 @@ namespace dawn_native { namespace vulkan { appInfo.applicationVersion = 0; appInfo.pEngineName = nullptr; appInfo.engineVersion = 0; - appInfo.apiVersion = VK_API_VERSION_1_0; + // Vulkan 1.0 implementations were required to return VK_ERROR_INCOMPATIBLE_DRIVER if + // apiVersion was larger than 1.0. Meanwhile, as long as the instance supports at least + // Vulkan 1.1, an application can use different versions of Vulkan with an instance than + // it does with a device or physical device. So we should set apiVersion to Vulkan 1.0 + // if the instance only supports Vulkan 1.0. Otherwise we set apiVersion to Vulkan 1.2, + // treat 1.2 as the highest API version dawn targets. + if (mGlobalInfo.apiVersion == VK_MAKE_VERSION(1, 0, 0)) { + appInfo.apiVersion = mGlobalInfo.apiVersion; + } else { + appInfo.apiVersion = VK_MAKE_VERSION(1, 2, 0); + } VkInstanceCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; createInfo.pApplicationInfo = &appInfo; - createInfo.enabledLayerCount = static_cast(layersToRequest.size()); - createInfo.ppEnabledLayerNames = layersToRequest.data(); - createInfo.enabledExtensionCount = static_cast(extensionsToRequest.size()); - createInfo.ppEnabledExtensionNames = extensionsToRequest.data(); + createInfo.enabledLayerCount = static_cast(layerNames.size()); + createInfo.ppEnabledLayerNames = layerNames.data(); + createInfo.enabledExtensionCount = static_cast(extensionNames.size()); + createInfo.ppEnabledExtensionNames = extensionNames.data(); DAWN_TRY(CheckVkSuccess(mFunctions.CreateInstance(&createInfo, nullptr, &mInstance), "vkCreateInstance")); @@ -193,7 +268,7 @@ namespace dawn_native { namespace vulkan { createInfo.pUserData = this; return CheckVkSuccess(mFunctions.CreateDebugReportCallbackEXT( - mInstance, &createInfo, nullptr, &mDebugReportCallback), + mInstance, &createInfo, nullptr, &*mDebugReportCallback), "vkCreateDebugReportcallback"); } @@ -206,16 +281,16 @@ namespace dawn_native { namespace vulkan { const char* /*pLayerPrefix*/, const char* pMessage, void* /*pUserdata*/) { - std::cout << pMessage << std::endl; + dawn::WarningLog() << pMessage; ASSERT((flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) == 0); return VK_FALSE; } - BackendConnection* Connect(InstanceBase* instance) { + BackendConnection* Connect(InstanceBase* instance, bool useSwiftshader) { Backend* backend = new Backend(instance); - if (instance->ConsumedError(backend->Initialize())) { + if (instance->ConsumedError(backend->Initialize(useSwiftshader))) { delete backend; return nullptr; } diff --git a/third_party/dawn/src/dawn_native/vulkan/BackendVk.h b/third_party/dawn/src/dawn_native/vulkan/BackendVk.h index b3ab47ec9bd..7d22b671f93 100644 --- a/third_party/dawn/src/dawn_native/vulkan/BackendVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/BackendVk.h @@ -30,12 +30,14 @@ namespace dawn_native { namespace vulkan { const VulkanFunctions& GetFunctions() const; VkInstance GetVkInstance() const; + const VulkanGlobalInfo& GetGlobalInfo() const; - MaybeError Initialize(); + MaybeError Initialize(bool useSwiftshader); std::vector> DiscoverDefaultAdapters() override; private: + MaybeError LoadVulkan(bool useSwiftshader); ResultOrError CreateInstance(); MaybeError RegisterDebugReport(); diff --git a/third_party/dawn/src/dawn_native/vulkan/BindGroupLayoutVk.cpp b/third_party/dawn/src/dawn_native/vulkan/BindGroupLayoutVk.cpp index 83f58381b9a..54c3dcfc40f 100644 --- a/third_party/dawn/src/dawn_native/vulkan/BindGroupLayoutVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/BindGroupLayoutVk.cpp @@ -15,22 +15,29 @@ #include "dawn_native/vulkan/BindGroupLayoutVk.h" #include "common/BitSetIterator.h" +#include "common/ityp_vector.h" +#include "dawn_native/vulkan/BindGroupVk.h" +#include "dawn_native/vulkan/DescriptorSetAllocator.h" #include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/VulkanError.h" + +#include namespace dawn_native { namespace vulkan { namespace { - VkShaderStageFlags VulkanShaderStageFlags(dawn::ShaderStageBit stages) { + VkShaderStageFlags VulkanShaderStageFlags(wgpu::ShaderStage stages) { VkShaderStageFlags flags = 0; - if (stages & dawn::ShaderStageBit::Vertex) { + if (stages & wgpu::ShaderStage::Vertex) { flags |= VK_SHADER_STAGE_VERTEX_BIT; } - if (stages & dawn::ShaderStageBit::Fragment) { + if (stages & wgpu::ShaderStage::Fragment) { flags |= VK_SHADER_STAGE_FRAGMENT_BIT; } - if (stages & dawn::ShaderStageBit::Compute) { + if (stages & wgpu::ShaderStage::Compute) { flags |= VK_SHADER_STAGE_COMPUTE_BIT; } @@ -39,63 +46,112 @@ namespace dawn_native { namespace vulkan { } // anonymous namespace - VkDescriptorType VulkanDescriptorType(dawn::BindingType type) { + VkDescriptorType VulkanDescriptorType(wgpu::BindingType type, bool isDynamic) { switch (type) { - case dawn::BindingType::UniformBuffer: + case wgpu::BindingType::UniformBuffer: + if (isDynamic) { + return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + } return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; - case dawn::BindingType::Sampler: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: return VK_DESCRIPTOR_TYPE_SAMPLER; - case dawn::BindingType::SampledTexture: + case wgpu::BindingType::SampledTexture: return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; - case dawn::BindingType::StorageBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + if (isDynamic) { + return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + } return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; - case dawn::BindingType::DynamicUniformBuffer: - return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - case dawn::BindingType::DynamicStorageBuffer: - return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC; + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + case wgpu::BindingType::StorageTexture: default: UNREACHABLE(); } } - BindGroupLayout::BindGroupLayout(Device* device, const BindGroupLayoutDescriptor* descriptor) - : BindGroupLayoutBase(device, descriptor) { - const auto& info = GetBindingInfo(); + // static + ResultOrError BindGroupLayout::Create( + Device* device, + const BindGroupLayoutDescriptor* descriptor) { + Ref bgl = AcquireRef(new BindGroupLayout(device, descriptor)); + DAWN_TRY(bgl->Initialize()); + return bgl.Detach(); + } + MaybeError BindGroupLayout::Initialize() { // Compute the bindings that will be chained in the DescriptorSetLayout create info. We add // one entry per binding set. This might be optimized by computing continuous ranges of // bindings of the same type. - uint32_t numBindings = 0; - std::array bindings; - for (uint32_t bindingIndex : IterateBitSet(info.mask)) { - auto& binding = bindings[numBindings]; - binding.binding = bindingIndex; - binding.descriptorType = VulkanDescriptorType(info.types[bindingIndex]); - binding.descriptorCount = 1; - binding.stageFlags = VulkanShaderStageFlags(info.visibilities[bindingIndex]); - binding.pImmutableSamplers = nullptr; - - numBindings++; + ityp::vector bindings; + bindings.reserve(GetBindingCount()); + + for (const auto& it : GetBindingMap()) { + BindingNumber bindingNumber = it.first; + BindingIndex bindingIndex = it.second; + const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex); + + VkDescriptorSetLayoutBinding vkBinding; + vkBinding.binding = static_cast(bindingNumber); + vkBinding.descriptorType = + VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset); + vkBinding.descriptorCount = 1; + vkBinding.stageFlags = VulkanShaderStageFlags(bindingInfo.visibility); + vkBinding.pImmutableSamplers = nullptr; + + bindings.emplace_back(vkBinding); } VkDescriptorSetLayoutCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; - createInfo.bindingCount = numBindings; + createInfo.bindingCount = static_cast(bindings.size()); createInfo.pBindings = bindings.data(); - if (device->fn.CreateDescriptorSetLayout(device->GetVkDevice(), &createInfo, nullptr, - &mHandle) != VK_SUCCESS) { - ASSERT(false); + Device* device = ToBackend(GetDevice()); + DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorSetLayout( + device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + "CreateDescriptorSetLayout")); + + // Compute the size of descriptor pools used for this layout. + std::map descriptorCountPerType; + + for (BindingIndex bindingIndex{0}; bindingIndex < GetBindingCount(); ++bindingIndex) { + const BindingInfo& bindingInfo = GetBindingInfo(bindingIndex); + VkDescriptorType vulkanType = + VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset); + + // map::operator[] will return 0 if the key doesn't exist. + descriptorCountPerType[vulkanType]++; } + + // TODO(enga): Consider deduping allocators for layouts with the same descriptor type + // counts. + mDescriptorSetAllocator = + std::make_unique(this, std::move(descriptorCountPerType)); + return {}; + } + + BindGroupLayout::BindGroupLayout(DeviceBase* device, + const BindGroupLayoutDescriptor* descriptor) + : BindGroupLayoutBase(device, descriptor), + mBindGroupAllocator(MakeFrontendBindGroupAllocator(4096)) { } BindGroupLayout::~BindGroupLayout() { + Device* device = ToBackend(GetDevice()); + // DescriptorSetLayout aren't used by execution on the GPU and can be deleted at any time, - // so we destroy mHandle immediately instead of using the FencedDeleter + // so we can destroy mHandle immediately instead of using the FencedDeleter. + // (Swiftshader implements this wrong b/154522740). + // In practice, the GPU is done with all descriptor sets because bind group deallocation + // refs the bind group layout so that once the bind group is finished being used, we can + // recycle its descriptor set. if (mHandle != VK_NULL_HANDLE) { - Device* device = ToBackend(GetDevice()); device->fn.DestroyDescriptorSetLayout(device->GetVkDevice(), mHandle, nullptr); mHandle = VK_NULL_HANDLE; } @@ -105,55 +161,23 @@ namespace dawn_native { namespace vulkan { return mHandle; } - BindGroupLayout::PoolSizeSpec BindGroupLayout::ComputePoolSizes(uint32_t* numPoolSizes) const { - uint32_t numSizes = 0; - PoolSizeSpec result{}; - - // Defines an array and indices into it that will contain for each sampler type at which - // position it is in the PoolSizeSpec, or -1 if it isn't present yet. - enum DescriptorType { - UNIFORM_BUFFER, - SAMPLER, - SAMPLED_IMAGE, - STORAGE_BUFFER, - MAX_TYPE, - }; - static_assert(MAX_TYPE == kMaxPoolSizesNeeded, ""); - auto ToDescriptorType = [](dawn::BindingType type) -> DescriptorType { - switch (type) { - case dawn::BindingType::UniformBuffer: - case dawn::BindingType::DynamicUniformBuffer: - return UNIFORM_BUFFER; - case dawn::BindingType::Sampler: - return SAMPLER; - case dawn::BindingType::SampledTexture: - return SAMPLED_IMAGE; - case dawn::BindingType::StorageBuffer: - case dawn::BindingType::DynamicStorageBuffer: - return STORAGE_BUFFER; - default: - UNREACHABLE(); - } - }; - - std::array descriptorTypeIndex; - descriptorTypeIndex.fill(-1); - - const auto& info = GetBindingInfo(); - for (uint32_t bindingIndex : IterateBitSet(info.mask)) { - DescriptorType type = ToDescriptorType(info.types[bindingIndex]); - - if (descriptorTypeIndex[type] == -1) { - descriptorTypeIndex[type] = numSizes; - result[numSizes].type = VulkanDescriptorType(info.types[bindingIndex]); - result[numSizes].descriptorCount = 1; - numSizes++; - } else { - result[descriptorTypeIndex[type]].descriptorCount++; - } - } + ResultOrError BindGroupLayout::AllocateBindGroup( + Device* device, + const BindGroupDescriptor* descriptor) { + DescriptorSetAllocation descriptorSetAllocation; + DAWN_TRY_ASSIGN(descriptorSetAllocation, mDescriptorSetAllocator->Allocate()); + + return mBindGroupAllocator.Allocate(device, descriptor, descriptorSetAllocation); + } + + void BindGroupLayout::DeallocateBindGroup(BindGroup* bindGroup, + DescriptorSetAllocation* descriptorSetAllocation) { + mDescriptorSetAllocator->Deallocate(descriptorSetAllocation); + mBindGroupAllocator.Deallocate(bindGroup); + } - *numPoolSizes = numSizes; - return result; + void BindGroupLayout::FinishDeallocation(Serial completedSerial) { + mDescriptorSetAllocator->FinishDeallocation(completedSerial); } + }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/BindGroupLayoutVk.h b/third_party/dawn/src/dawn_native/vulkan/BindGroupLayoutVk.h index 6a6d8cd58c8..31dd1fad77d 100644 --- a/third_party/dawn/src/dawn_native/vulkan/BindGroupLayoutVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/BindGroupLayoutVk.h @@ -17,27 +17,56 @@ #include "dawn_native/BindGroupLayout.h" +#include "common/Serial.h" +#include "common/SlabAllocator.h" #include "common/vulkan_platform.h" +#include + namespace dawn_native { namespace vulkan { + class BindGroup; + struct DescriptorSetAllocation; + class DescriptorSetAllocator; class Device; - VkDescriptorType VulkanDescriptorType(dawn::BindingType type); + VkDescriptorType VulkanDescriptorType(wgpu::BindingType type, bool isDynamic); - class BindGroupLayout : public BindGroupLayoutBase { + // In Vulkan descriptor pools have to be sized to an exact number of descriptors. This means + // it's hard to have something where we can mix different types of descriptor sets because + // we don't know if their vector of number of descriptors will be similar. + // + // That's why that in addition to containing the VkDescriptorSetLayout to create + // VkDescriptorSets for its bindgroups, the layout also acts as an allocator for the descriptor + // sets. + // + // The allocations is done with one pool per descriptor set, which is inefficient, but at least + // the pools are reused when no longer used. Minimizing the number of descriptor pool allocation + // is important because creating them can incur GPU memory allocation which is usually an + // expensive syscall. + class BindGroupLayout final : public BindGroupLayoutBase { public: - BindGroupLayout(Device* device, const BindGroupLayoutDescriptor* descriptor); - ~BindGroupLayout(); + static ResultOrError Create(Device* device, + const BindGroupLayoutDescriptor* descriptor); + + BindGroupLayout(DeviceBase* device, const BindGroupLayoutDescriptor* descriptor); VkDescriptorSetLayout GetHandle() const; - static constexpr size_t kMaxPoolSizesNeeded = 4; - using PoolSizeSpec = std::array; - PoolSizeSpec ComputePoolSizes(uint32_t* numPoolSizes) const; + ResultOrError AllocateBindGroup(Device* device, + const BindGroupDescriptor* descriptor); + void DeallocateBindGroup(BindGroup* bindGroup, + DescriptorSetAllocation* descriptorSetAllocation); + void FinishDeallocation(Serial completedSerial); private: + ~BindGroupLayout() override; + MaybeError Initialize(); + VkDescriptorSetLayout mHandle = VK_NULL_HANDLE; + + SlabAllocator mBindGroupAllocator; + std::unique_ptr mDescriptorSetAllocator; }; }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/BindGroupVk.cpp b/third_party/dawn/src/dawn_native/vulkan/BindGroupVk.cpp index 30d59698ccc..1124e15d8fb 100644 --- a/third_party/dawn/src/dawn_native/vulkan/BindGroupVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/BindGroupVk.cpp @@ -14,103 +14,99 @@ #include "dawn_native/vulkan/BindGroupVk.h" +#include "common/BitSetIterator.h" +#include "common/ityp_stack_vec.h" #include "dawn_native/vulkan/BindGroupLayoutVk.h" #include "dawn_native/vulkan/BufferVk.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" #include "dawn_native/vulkan/SamplerVk.h" #include "dawn_native/vulkan/TextureVk.h" - -#include "common/BitSetIterator.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { - BindGroup::BindGroup(Device* device, const BindGroupDescriptor* descriptor) - : BindGroupBase(device, descriptor) { - // Create a pool to hold our descriptor set. - // TODO(cwallez@chromium.org): This horribly inefficient, find a way to be better, for - // example by having one pool per bind group layout instead. - uint32_t numPoolSizes = 0; - auto poolSizes = ToBackend(GetLayout())->ComputePoolSizes(&numPoolSizes); - - VkDescriptorPoolCreateInfo createInfo; - createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - createInfo.pNext = nullptr; - createInfo.flags = 0; - createInfo.maxSets = 1; - createInfo.poolSizeCount = numPoolSizes; - createInfo.pPoolSizes = poolSizes.data(); - - if (device->fn.CreateDescriptorPool(device->GetVkDevice(), &createInfo, nullptr, &mPool) != - VK_SUCCESS) { - ASSERT(false); - } - - // Now do the allocation of one descriptor set, this is very suboptimal too. - VkDescriptorSetLayout vkLayout = ToBackend(GetLayout())->GetHandle(); - - VkDescriptorSetAllocateInfo allocateInfo; - allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; - allocateInfo.pNext = nullptr; - allocateInfo.descriptorPool = mPool; - allocateInfo.descriptorSetCount = 1; - allocateInfo.pSetLayouts = &vkLayout; - - if (device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo, &mHandle) != - VK_SUCCESS) { - ASSERT(false); - } + // static + ResultOrError BindGroup::Create(Device* device, + const BindGroupDescriptor* descriptor) { + return ToBackend(descriptor->layout)->AllocateBindGroup(device, descriptor); + } + BindGroup::BindGroup(Device* device, + const BindGroupDescriptor* descriptor, + DescriptorSetAllocation descriptorSetAllocation) + : BindGroupBase(this, device, descriptor), + mDescriptorSetAllocation(descriptorSetAllocation) { // Now do a write of a single descriptor set with all possible chained data allocated on the // stack. + const uint32_t bindingCount = static_cast((GetLayout()->GetBindingCount())); + ityp::stack_vec writes( + bindingCount); + ityp::stack_vec + writeBufferInfo(bindingCount); + ityp::stack_vec + writeImageInfo(bindingCount); + uint32_t numWrites = 0; - std::array writes; - std::array writeBufferInfo; - std::array writeImageInfo; + for (const auto& it : GetLayout()->GetBindingMap()) { + BindingNumber bindingNumber = it.first; + BindingIndex bindingIndex = it.second; + const BindingInfo& bindingInfo = GetLayout()->GetBindingInfo(bindingIndex); - const auto& layoutInfo = GetLayout()->GetBindingInfo(); - for (uint32_t bindingIndex : IterateBitSet(layoutInfo.mask)) { auto& write = writes[numWrites]; write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write.pNext = nullptr; - write.dstSet = mHandle; - write.dstBinding = bindingIndex; + write.dstSet = GetHandle(); + write.dstBinding = static_cast(bindingNumber); write.dstArrayElement = 0; write.descriptorCount = 1; - write.descriptorType = VulkanDescriptorType(layoutInfo.types[bindingIndex]); + write.descriptorType = + VulkanDescriptorType(bindingInfo.type, bindingInfo.hasDynamicOffset); - switch (layoutInfo.types[bindingIndex]) { - case dawn::BindingType::UniformBuffer: - case dawn::BindingType::StorageBuffer: - case dawn::BindingType::DynamicUniformBuffer: - case dawn::BindingType::DynamicStorageBuffer: { + switch (bindingInfo.type) { + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::StorageBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: { BufferBinding binding = GetBindingAsBufferBinding(bindingIndex); writeBufferInfo[numWrites].buffer = ToBackend(binding.buffer)->GetHandle(); writeBufferInfo[numWrites].offset = binding.offset; writeBufferInfo[numWrites].range = binding.size; - write.pBufferInfo = &writeBufferInfo[numWrites]; - } break; + break; + } - case dawn::BindingType::Sampler: { + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: { Sampler* sampler = ToBackend(GetBindingAsSampler(bindingIndex)); writeImageInfo[numWrites].sampler = sampler->GetHandle(); write.pImageInfo = &writeImageInfo[numWrites]; - } break; + break; + } - case dawn::BindingType::SampledTexture: { + case wgpu::BindingType::SampledTexture: { TextureView* view = ToBackend(GetBindingAsTextureView(bindingIndex)); writeImageInfo[numWrites].imageView = view->GetHandle(); - // TODO(cwallez@chromium.org): This isn't true in general: if the image can has + // TODO(cwallez@chromium.org): This isn't true in general: if the image has // two read-only usages one of which is Sampled. Works for now though :) writeImageInfo[numWrites].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; write.pImageInfo = &writeImageInfo[numWrites]; - } break; + break; + } + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + TextureView* view = ToBackend(GetBindingAsTextureView(bindingIndex)); + + writeImageInfo[numWrites].imageView = view->GetHandle(); + writeImageInfo[numWrites].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + write.pImageInfo = &writeImageInfo[numWrites]; + break; + } default: UNREACHABLE(); } @@ -118,23 +114,17 @@ namespace dawn_native { namespace vulkan { numWrites++; } + // TODO(cwallez@chromium.org): Batch these updates device->fn.UpdateDescriptorSets(device->GetVkDevice(), numWrites, writes.data(), 0, nullptr); } BindGroup::~BindGroup() { - // The descriptor set doesn't need to be delete because it's done implicitly when the - // descriptor pool is destroyed. - mHandle = VK_NULL_HANDLE; - - if (mPool != VK_NULL_HANDLE) { - ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mPool); - mPool = VK_NULL_HANDLE; - } + ToBackend(GetLayout())->DeallocateBindGroup(this, &mDescriptorSetAllocation); } VkDescriptorSet BindGroup::GetHandle() const { - return mHandle; + return mDescriptorSetAllocation.set; } }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/BindGroupVk.h b/third_party/dawn/src/dawn_native/vulkan/BindGroupVk.h index 5071796e284..411c1149405 100644 --- a/third_party/dawn/src/dawn_native/vulkan/BindGroupVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/BindGroupVk.h @@ -17,22 +17,32 @@ #include "dawn_native/BindGroup.h" +#include "common/PlacementAllocated.h" #include "common/vulkan_platform.h" +#include "dawn_native/vulkan/BindGroupLayoutVk.h" +#include "dawn_native/vulkan/DescriptorSetAllocation.h" namespace dawn_native { namespace vulkan { class Device; - class BindGroup : public BindGroupBase { + class BindGroup final : public BindGroupBase, public PlacementAllocated { public: - BindGroup(Device* device, const BindGroupDescriptor* descriptor); - ~BindGroup(); + static ResultOrError Create(Device* device, + const BindGroupDescriptor* descriptor); + + BindGroup(Device* device, + const BindGroupDescriptor* descriptor, + DescriptorSetAllocation descriptorSetAllocation); VkDescriptorSet GetHandle() const; private: - VkDescriptorPool mPool = VK_NULL_HANDLE; - VkDescriptorSet mHandle = VK_NULL_HANDLE; + ~BindGroup() override; + + // The descriptor set in this allocation outlives the BindGroup because it is owned by + // the BindGroupLayout which is referenced by the BindGroup. + DescriptorSetAllocation mDescriptorSetAllocation; }; }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/BufferVk.cpp b/third_party/dawn/src/dawn_native/vulkan/BufferVk.cpp index 4ead2a99e40..e011455348b 100644 --- a/third_party/dawn/src/dawn_native/vulkan/BufferVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/BufferVk.cpp @@ -14,8 +14,12 @@ #include "dawn_native/vulkan/BufferVk.h" +#include "dawn_native/CommandBuffer.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/ResourceHeapVk.h" +#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" +#include "dawn_native/vulkan/VulkanError.h" #include @@ -23,86 +27,87 @@ namespace dawn_native { namespace vulkan { namespace { - VkBufferUsageFlags VulkanBufferUsage(dawn::BufferUsageBit usage) { + VkBufferUsageFlags VulkanBufferUsage(wgpu::BufferUsage usage) { VkBufferUsageFlags flags = 0; - if (usage & dawn::BufferUsageBit::TransferSrc) { + if (usage & wgpu::BufferUsage::CopySrc) { flags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; } - if (usage & dawn::BufferUsageBit::TransferDst) { + if (usage & wgpu::BufferUsage::CopyDst) { flags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; } - if (usage & dawn::BufferUsageBit::Index) { + if (usage & wgpu::BufferUsage::Index) { flags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; } - if (usage & dawn::BufferUsageBit::Vertex) { + if (usage & wgpu::BufferUsage::Vertex) { flags |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; } - if (usage & dawn::BufferUsageBit::Uniform) { + if (usage & wgpu::BufferUsage::Uniform) { flags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; } - if (usage & dawn::BufferUsageBit::Storage) { + if (usage & wgpu::BufferUsage::Storage) { flags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; } - if (usage & dawn::BufferUsageBit::Indirect) { + if (usage & wgpu::BufferUsage::Indirect) { flags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; } return flags; } - VkPipelineStageFlags VulkanPipelineStage(dawn::BufferUsageBit usage) { + VkPipelineStageFlags VulkanPipelineStage(wgpu::BufferUsage usage) { VkPipelineStageFlags flags = 0; - if (usage & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) { + if (usage & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) { flags |= VK_PIPELINE_STAGE_HOST_BIT; } - if (usage & (dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst)) { + if (usage & (wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst)) { flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; } - if (usage & (dawn::BufferUsageBit::Index | dawn::BufferUsageBit::Vertex)) { + if (usage & (wgpu::BufferUsage::Index | wgpu::BufferUsage::Vertex)) { flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; } - if (usage & (dawn::BufferUsageBit::Uniform | dawn::BufferUsageBit::Storage)) { + if (usage & (wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage | + kReadOnlyStorageBuffer)) { flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; } - if (usage & dawn::BufferUsageBit::Indirect) { + if (usage & wgpu::BufferUsage::Indirect) { flags |= VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT; } return flags; } - VkAccessFlags VulkanAccessFlags(dawn::BufferUsageBit usage) { + VkAccessFlags VulkanAccessFlags(wgpu::BufferUsage usage) { VkAccessFlags flags = 0; - if (usage & dawn::BufferUsageBit::MapRead) { + if (usage & wgpu::BufferUsage::MapRead) { flags |= VK_ACCESS_HOST_READ_BIT; } - if (usage & dawn::BufferUsageBit::MapWrite) { + if (usage & wgpu::BufferUsage::MapWrite) { flags |= VK_ACCESS_HOST_WRITE_BIT; } - if (usage & dawn::BufferUsageBit::TransferSrc) { + if (usage & wgpu::BufferUsage::CopySrc) { flags |= VK_ACCESS_TRANSFER_READ_BIT; } - if (usage & dawn::BufferUsageBit::TransferDst) { + if (usage & wgpu::BufferUsage::CopyDst) { flags |= VK_ACCESS_TRANSFER_WRITE_BIT; } - if (usage & dawn::BufferUsageBit::Index) { + if (usage & wgpu::BufferUsage::Index) { flags |= VK_ACCESS_INDEX_READ_BIT; } - if (usage & dawn::BufferUsageBit::Vertex) { + if (usage & wgpu::BufferUsage::Vertex) { flags |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; } - if (usage & dawn::BufferUsageBit::Uniform) { + if (usage & wgpu::BufferUsage::Uniform) { flags |= VK_ACCESS_UNIFORM_READ_BIT; } - if (usage & dawn::BufferUsageBit::Storage) { + if (usage & wgpu::BufferUsage::Storage) { flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (usage & dawn::BufferUsageBit::Indirect) { + if (usage & wgpu::BufferUsage::Indirect) { flags |= VK_ACCESS_INDIRECT_COMMAND_READ_BIT; } @@ -111,59 +116,92 @@ namespace dawn_native { namespace vulkan { } // namespace - Buffer::Buffer(Device* device, const BufferDescriptor* descriptor) - : BufferBase(device, descriptor) { + // static + ResultOrError> Buffer::Create(Device* device, const BufferDescriptor* descriptor) { + Ref buffer = AcquireRef(new Buffer(device, descriptor)); + DAWN_TRY(buffer->Initialize()); + return std::move(buffer); + } + + MaybeError Buffer::Initialize() { + // Avoid passing ludicrously large sizes to drivers because it causes issues: drivers add + // some constants to the size passed and align it, but for values close to the maximum + // VkDeviceSize this can cause overflows and makes drivers crash or return bad sizes in the + // VkmemoryRequirements. See https://gitlab.khronos.org/vulkan/vulkan/issues/1904 + // Any size with one of two top bits of VkDeviceSize set is a HUGE allocation and we can + // safely return an OOM error. + if (GetSize() & (uint64_t(3) << uint64_t(62))) { + return DAWN_OUT_OF_MEMORY_ERROR("Buffer size is HUGE and could cause overflows"); + } + VkBufferCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; - createInfo.size = GetSize(); - // Add TransferDst for non-mappable buffer initialization in CreateBufferMapped + // TODO(cwallez@chromium.org): Have a global "zero" buffer that can do everything instead + // of creating a new 4-byte buffer? + createInfo.size = std::max(GetSize(), uint64_t(4u)); + // Add CopyDst for non-mappable buffer initialization in CreateBufferMapped // and robust resource initialization. - createInfo.usage = VulkanBufferUsage(GetUsage() | dawn::BufferUsageBit::TransferDst); + createInfo.usage = VulkanBufferUsage(GetUsage() | wgpu::BufferUsage::CopyDst); createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.queueFamilyIndexCount = 0; createInfo.pQueueFamilyIndices = 0; - if (device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &mHandle) != - VK_SUCCESS) { - ASSERT(false); - } + Device* device = ToBackend(GetDevice()); + DAWN_TRY(CheckVkOOMThenSuccess( + device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + "vkCreateBuffer")); VkMemoryRequirements requirements; device->fn.GetBufferMemoryRequirements(device->GetVkDevice(), mHandle, &requirements); bool requestMappable = - (GetUsage() & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) != 0; - if (!device->GetMemoryAllocator()->Allocate(requirements, requestMappable, - &mMemoryAllocation)) { - ASSERT(false); - } + (GetUsage() & (wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite)) != 0; + DAWN_TRY_ASSIGN(mMemoryAllocation, device->AllocateMemory(requirements, requestMappable)); - if (device->fn.BindBufferMemory(device->GetVkDevice(), mHandle, - mMemoryAllocation.GetMemory(), - mMemoryAllocation.GetMemoryOffset()) != VK_SUCCESS) { - ASSERT(false); + DAWN_TRY(CheckVkSuccess( + device->fn.BindBufferMemory(device->GetVkDevice(), mHandle, + ToBackend(mMemoryAllocation.GetResourceHeap())->GetMemory(), + mMemoryAllocation.GetOffset()), + "vkBindBufferMemory")); + + if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { + ClearBuffer(device->GetPendingRecordingContext(), 0x01010101); } + + return {}; } Buffer::~Buffer() { DestroyInternal(); } - void Buffer::OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data) { - CallMapReadCallback(mapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data, GetSize()); + VkBuffer Buffer::GetHandle() const { + return mHandle; } - void Buffer::OnMapWriteCommandSerialFinished(uint32_t mapSerial, void* data) { - CallMapWriteCallback(mapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data, GetSize()); - } + void Buffer::TransitionUsageNow(CommandRecordingContext* recordingContext, + wgpu::BufferUsage usage) { + std::vector barriers; + VkPipelineStageFlags srcStages = 0; + VkPipelineStageFlags dstStages = 0; - VkBuffer Buffer::GetHandle() const { - return mHandle; + TransitionUsageNow(recordingContext, usage, &barriers, &srcStages, &dstStages); + + if (barriers.size() > 0) { + ASSERT(barriers.size() == 1); + ToBackend(GetDevice()) + ->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0, + nullptr, barriers.size(), barriers.data(), 0, nullptr); + } } - void Buffer::TransitionUsageNow(VkCommandBuffer commands, dawn::BufferUsageBit usage) { + void Buffer::TransitionUsageNow(CommandRecordingContext* recordingContext, + wgpu::BufferUsage usage, + std::vector* bufferBarriers, + VkPipelineStageFlags* srcStages, + VkPipelineStageFlags* dstStages) { bool lastIncludesTarget = (mLastUsage & usage) == usage; bool lastReadOnly = (mLastUsage & kReadOnlyBufferUsages) == mLastUsage; @@ -173,13 +211,13 @@ namespace dawn_native { namespace vulkan { } // Special-case for the initial transition: Vulkan doesn't allow access flags to be 0. - if (mLastUsage == dawn::BufferUsageBit::None) { + if (mLastUsage == wgpu::BufferUsage::None) { mLastUsage = usage; return; } - VkPipelineStageFlags srcStages = VulkanPipelineStage(mLastUsage); - VkPipelineStageFlags dstStages = VulkanPipelineStage(usage); + *srcStages |= VulkanPipelineStage(mLastUsage); + *dstStages |= VulkanPipelineStage(usage); VkBufferMemoryBarrier barrier; barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; @@ -192,55 +230,71 @@ namespace dawn_native { namespace vulkan { barrier.offset = 0; barrier.size = GetSize(); - ToBackend(GetDevice()) - ->fn.CmdPipelineBarrier(commands, srcStages, dstStages, 0, 0, nullptr, 1, &barrier, 0, - nullptr); + bufferBarriers->push_back(barrier); mLastUsage = usage; } - bool Buffer::IsMapWritable() const { + bool Buffer::IsMappableAtCreation() const { // TODO(enga): Handle CPU-visible memory on UMA return mMemoryAllocation.GetMappedPointer() != nullptr; } - MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) { - *mappedPointer = mMemoryAllocation.GetMappedPointer(); + MaybeError Buffer::MapAtCreationImpl() { + CommandRecordingContext* recordingContext = + ToBackend(GetDevice())->GetPendingRecordingContext(); + + // TODO(jiawei.shao@intel.com): initialize mapped buffer in CPU side. + EnsureDataInitialized(recordingContext); + return {}; } - void Buffer::MapReadAsyncImpl(uint32_t serial) { + MaybeError Buffer::MapReadAsyncImpl() { Device* device = ToBackend(GetDevice()); - VkCommandBuffer commands = device->GetPendingCommandBuffer(); - TransitionUsageNow(commands, dawn::BufferUsageBit::MapRead); + CommandRecordingContext* recordingContext = device->GetPendingRecordingContext(); + TransitionUsageNow(recordingContext, wgpu::BufferUsage::MapRead); + return {}; + } - uint8_t* memory = mMemoryAllocation.GetMappedPointer(); - ASSERT(memory != nullptr); + MaybeError Buffer::MapWriteAsyncImpl() { + Device* device = ToBackend(GetDevice()); - MapRequestTracker* tracker = device->GetMapRequestTracker(); - tracker->Track(this, serial, memory, false); + CommandRecordingContext* recordingContext = device->GetPendingRecordingContext(); + TransitionUsageNow(recordingContext, wgpu::BufferUsage::MapWrite); + return {}; } - void Buffer::MapWriteAsyncImpl(uint32_t serial) { + MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) { Device* device = ToBackend(GetDevice()); - VkCommandBuffer commands = device->GetPendingCommandBuffer(); - TransitionUsageNow(commands, dawn::BufferUsageBit::MapWrite); + CommandRecordingContext* recordingContext = device->GetPendingRecordingContext(); - uint8_t* memory = mMemoryAllocation.GetMappedPointer(); - ASSERT(memory != nullptr); + // TODO(jiawei.shao@intel.com): initialize mapped buffer in CPU side. + EnsureDataInitialized(recordingContext); - MapRequestTracker* tracker = device->GetMapRequestTracker(); - tracker->Track(this, serial, memory, true); + if (mode & wgpu::MapMode::Read) { + TransitionUsageNow(recordingContext, wgpu::BufferUsage::MapRead); + } else { + ASSERT(mode & wgpu::MapMode::Write); + TransitionUsageNow(recordingContext, wgpu::BufferUsage::MapWrite); + } + return {}; } void Buffer::UnmapImpl() { // No need to do anything, we keep CPU-visible memory mapped at all time. } + void* Buffer::GetMappedPointerImpl() { + uint8_t* memory = mMemoryAllocation.GetMappedPointer(); + ASSERT(memory != nullptr); + return memory; + } + void Buffer::DestroyImpl() { - ToBackend(GetDevice())->GetMemoryAllocator()->Free(&mMemoryAllocation); + ToBackend(GetDevice())->DeallocateMemory(&mMemoryAllocation); if (mHandle != VK_NULL_HANDLE) { ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); @@ -248,34 +302,68 @@ namespace dawn_native { namespace vulkan { } } - // MapRequestTracker + void Buffer::EnsureDataInitialized(CommandRecordingContext* recordingContext) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; + } - MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) { + InitializeToZero(recordingContext); } - MapRequestTracker::~MapRequestTracker() { - ASSERT(mInflightRequests.Empty()); + void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext, + uint64_t offset, + uint64_t size) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; + } + + if (IsFullBufferRange(offset, size)) { + SetIsDataInitialized(); + } else { + InitializeToZero(recordingContext); + } } - void MapRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite) { - Request request; - request.buffer = buffer; - request.mapSerial = mapSerial; - request.data = data; - request.isWrite = isWrite; + void Buffer::EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext, + const CopyTextureToBufferCmd* copy) { + // TODO(jiawei.shao@intel.com): check Toggle::LazyClearResourceOnFirstUse + // instead when buffer lazy initialization is completely supported. + if (IsDataInitialized() || + !GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)) { + return; + } - mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial()); + if (IsFullBufferOverwrittenInTextureToBufferCopy(copy)) { + SetIsDataInitialized(); + } else { + InitializeToZero(recordingContext); + } } - void MapRequestTracker::Tick(Serial finishedSerial) { - for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) { - if (request.isWrite) { - request.buffer->OnMapWriteCommandSerialFinished(request.mapSerial, request.data); - } else { - request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data); - } - } - mInflightRequests.ClearUpTo(finishedSerial); + void Buffer::InitializeToZero(CommandRecordingContext* recordingContext) { + ASSERT(GetDevice()->IsToggleEnabled(Toggle::LazyClearBufferOnFirstUse)); + ASSERT(!IsDataInitialized()); + + ClearBuffer(recordingContext, 0u); + GetDevice()->IncrementLazyClearCountForTesting(); + SetIsDataInitialized(); } + void Buffer::ClearBuffer(CommandRecordingContext* recordingContext, uint32_t clearValue) { + ASSERT(recordingContext != nullptr); + + TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); + + Device* device = ToBackend(GetDevice()); + // TODO(jiawei.shao@intel.com): find out why VK_WHOLE_SIZE doesn't work on old Windows Intel + // Vulkan drivers. + device->fn.CmdFillBuffer(recordingContext->commandBuffer, mHandle, 0, GetSize(), + clearValue); + } }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/BufferVk.h b/third_party/dawn/src/dawn_native/vulkan/BufferVk.h index 20fb9f08537..14495d19d46 100644 --- a/third_party/dawn/src/dawn_native/vulkan/BufferVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/BufferVk.h @@ -19,61 +19,59 @@ #include "common/SerialQueue.h" #include "common/vulkan_platform.h" -#include "dawn_native/vulkan/MemoryAllocator.h" +#include "dawn_native/ResourceMemoryAllocation.h" namespace dawn_native { namespace vulkan { + struct CommandRecordingContext; class Device; - class Buffer : public BufferBase { + class Buffer final : public BufferBase { public: - Buffer(Device* device, const BufferDescriptor* descriptor); - ~Buffer(); - - void OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data); - void OnMapWriteCommandSerialFinished(uint32_t mapSerial, void* data); + static ResultOrError> Create(Device* device, + const BufferDescriptor* descriptor); VkBuffer GetHandle() const; // Transitions the buffer to be used as `usage`, recording any necessary barrier in // `commands`. // TODO(cwallez@chromium.org): coalesce barriers and do them early when possible. - void TransitionUsageNow(VkCommandBuffer commands, dawn::BufferUsageBit usage); + void TransitionUsageNow(CommandRecordingContext* recordingContext, wgpu::BufferUsage usage); + void TransitionUsageNow(CommandRecordingContext* recordingContext, + wgpu::BufferUsage usage, + std::vector* bufferBarriers, + VkPipelineStageFlags* srcStages, + VkPipelineStageFlags* dstStages); + + void EnsureDataInitialized(CommandRecordingContext* recordingContext); + void EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext, + uint64_t offset, + uint64_t size); + void EnsureDataInitializedAsDestination(CommandRecordingContext* recordingContext, + const CopyTextureToBufferCmd* copy); private: + ~Buffer() override; + using BufferBase::BufferBase; + MaybeError Initialize(); + void InitializeToZero(CommandRecordingContext* recordingContext); + void ClearBuffer(CommandRecordingContext* recordingContext, uint32_t clearValue); + // Dawn API - void MapReadAsyncImpl(uint32_t serial) override; - void MapWriteAsyncImpl(uint32_t serial) override; + MaybeError MapReadAsyncImpl() override; + MaybeError MapWriteAsyncImpl() override; + MaybeError MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) override; void UnmapImpl() override; void DestroyImpl() override; - bool IsMapWritable() const override; - MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override; + bool IsMappableAtCreation() const override; + MaybeError MapAtCreationImpl() override; + void* GetMappedPointerImpl() override; VkBuffer mHandle = VK_NULL_HANDLE; - DeviceMemoryAllocation mMemoryAllocation; - - dawn::BufferUsageBit mLastUsage = dawn::BufferUsageBit::None; - }; + ResourceMemoryAllocation mMemoryAllocation; - class MapRequestTracker { - public: - MapRequestTracker(Device* device); - ~MapRequestTracker(); - - void Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite); - void Tick(Serial finishedSerial); - - private: - Device* mDevice; - - struct Request { - Ref buffer; - uint32_t mapSerial; - void* data; - bool isWrite; - }; - SerialQueue mInflightRequests; + wgpu::BufferUsage mLastUsage = wgpu::BufferUsage::None; }; }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/CommandBufferVk.cpp b/third_party/dawn/src/dawn_native/vulkan/CommandBufferVk.cpp index ddf489357db..ba48a35c214 100644 --- a/third_party/dawn/src/dawn_native/vulkan/CommandBufferVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/CommandBufferVk.cpp @@ -14,10 +14,14 @@ #include "dawn_native/vulkan/CommandBufferVk.h" +#include "dawn_native/BindGroupAndStorageBarrierTracker.h" #include "dawn_native/CommandEncoder.h" +#include "dawn_native/CommandValidation.h" #include "dawn_native/Commands.h" +#include "dawn_native/RenderBundle.h" #include "dawn_native/vulkan/BindGroupVk.h" #include "dawn_native/vulkan/BufferVk.h" +#include "dawn_native/vulkan/CommandRecordingContext.h" #include "dawn_native/vulkan/ComputePipelineVk.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" @@ -25,49 +29,32 @@ #include "dawn_native/vulkan/RenderPassCache.h" #include "dawn_native/vulkan/RenderPipelineVk.h" #include "dawn_native/vulkan/TextureVk.h" +#include "dawn_native/vulkan/UtilsVulkan.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { namespace { - VkIndexType VulkanIndexType(dawn::IndexFormat format) { + VkIndexType VulkanIndexType(wgpu::IndexFormat format) { switch (format) { - case dawn::IndexFormat::Uint16: + case wgpu::IndexFormat::Uint16: return VK_INDEX_TYPE_UINT16; - case dawn::IndexFormat::Uint32: + case wgpu::IndexFormat::Uint32: return VK_INDEX_TYPE_UINT32; default: UNREACHABLE(); } } - VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy, - const TextureCopy& textureCopy, - const Extent3D& copySize) { - const Texture* texture = ToBackend(textureCopy.texture.Get()); - - VkBufferImageCopy region; - - region.bufferOffset = bufferCopy.offset; - // In Vulkan the row length is in texels while it is in bytes for Dawn - region.bufferRowLength = - bufferCopy.rowPitch / TextureFormatPixelSize(texture->GetFormat()); - region.bufferImageHeight = bufferCopy.imageHeight; - - region.imageSubresource.aspectMask = texture->GetVkAspectMask(); - region.imageSubresource.mipLevel = textureCopy.level; - region.imageSubresource.baseArrayLayer = textureCopy.slice; - region.imageSubresource.layerCount = 1; - - region.imageOffset.x = textureCopy.origin.x; - region.imageOffset.y = textureCopy.origin.y; - region.imageOffset.z = textureCopy.origin.z; - - region.imageExtent.width = copySize.width; - region.imageExtent.height = copySize.height; - region.imageExtent.depth = copySize.depth; - - return region; + bool HasSameTextureCopyExtent(const TextureCopy& srcCopy, + const TextureCopy& dstCopy, + const Extent3D& copySize) { + Extent3D imageExtentSrc = ComputeTextureCopyExtent(srcCopy, copySize); + Extent3D imageExtentDst = ComputeTextureCopyExtent(dstCopy, copySize); + return imageExtentSrc.width == imageExtentDst.width && + imageExtentSrc.height == imageExtentDst.height && + imageExtentSrc.depth == imageExtentDst.depth; } VkImageCopy ComputeImageCopyRegion(const TextureCopy& srcCopy, @@ -78,120 +65,157 @@ namespace dawn_native { namespace vulkan { VkImageCopy region; + // TODO(jiawei.shao@intel.com): support 1D and 3D textures + ASSERT(srcTexture->GetDimension() == wgpu::TextureDimension::e2D && + dstTexture->GetDimension() == wgpu::TextureDimension::e2D); region.srcSubresource.aspectMask = srcTexture->GetVkAspectMask(); - region.srcSubresource.mipLevel = srcCopy.level; - region.srcSubresource.baseArrayLayer = srcCopy.slice; - region.srcSubresource.layerCount = 1; + region.srcSubresource.mipLevel = srcCopy.mipLevel; + region.srcSubresource.baseArrayLayer = srcCopy.origin.z; + region.srcSubresource.layerCount = copySize.depth; region.srcOffset.x = srcCopy.origin.x; region.srcOffset.y = srcCopy.origin.y; - region.srcOffset.z = srcCopy.origin.z; + region.srcOffset.z = 0; region.dstSubresource.aspectMask = dstTexture->GetVkAspectMask(); - region.dstSubresource.mipLevel = dstCopy.level; - region.dstSubresource.baseArrayLayer = dstCopy.slice; - region.dstSubresource.layerCount = 1; + region.dstSubresource.mipLevel = dstCopy.mipLevel; + region.dstSubresource.baseArrayLayer = dstCopy.origin.z; + region.dstSubresource.layerCount = copySize.depth; region.dstOffset.x = dstCopy.origin.x; region.dstOffset.y = dstCopy.origin.y; - region.dstOffset.z = dstCopy.origin.z; + region.dstOffset.z = 0; - region.extent.width = copySize.width; - region.extent.height = copySize.height; - region.extent.depth = copySize.depth; + ASSERT(HasSameTextureCopyExtent(srcCopy, dstCopy, copySize)); + Extent3D imageExtent = ComputeTextureCopyExtent(dstCopy, copySize); + region.extent.width = imageExtent.width; + region.extent.height = imageExtent.height; + region.extent.depth = 1; return region; } - class DescriptorSetTracker { - public: - void OnSetBindGroup(uint32_t index, - VkDescriptorSet set, - uint32_t dynamicOffsetCount, - uint64_t* dynamicOffsets) { - mDirtySets.set(index); - mSets[index] = set; - mDynamicOffsetCounts[index] = dynamicOffsetCount; - if (dynamicOffsetCount > 0) { - // Vulkan backend use uint32_t as dynamic offsets type, it is not correct. - // Vulkan should use VkDeviceSize. Dawn vulkan backend has to handle this. - for (uint32_t i = 0; i < dynamicOffsetCount; ++i) { - ASSERT(dynamicOffsets[i] <= std::numeric_limits::max()); - mDynamicOffsets[index][i] = static_cast(dynamicOffsets[i]); - } - } + void ApplyDescriptorSets( + Device* device, + VkCommandBuffer commands, + VkPipelineBindPoint bindPoint, + VkPipelineLayout pipelineLayout, + const BindGroupLayoutMask& bindGroupsToApply, + const ityp::array& bindGroups, + const ityp::array& dynamicOffsetCounts, + const ityp::array, + kMaxBindGroups>& dynamicOffsets) { + for (BindGroupIndex dirtyIndex : IterateBitSet(bindGroupsToApply)) { + VkDescriptorSet set = ToBackend(bindGroups[dirtyIndex])->GetHandle(); + const uint32_t* dynamicOffset = dynamicOffsetCounts[dirtyIndex] > 0 + ? dynamicOffsets[dirtyIndex].data() + : nullptr; + device->fn.CmdBindDescriptorSets(commands, bindPoint, pipelineLayout, + static_cast(dirtyIndex), 1, &*set, + dynamicOffsetCounts[dirtyIndex], dynamicOffset); } + } - void OnPipelineLayoutChange(PipelineLayout* layout) { - if (layout == mCurrentLayout) { - return; - } - - if (mCurrentLayout == nullptr) { - // We're at the beginning of a pass so all bind groups will be set before any - // draw / dispatch. Still clear the dirty sets to avoid leftover dirty sets - // from previous passes. - mDirtySets.reset(); - } else { - // Bindgroups that are not inherited will be set again before any draw or - // dispatch. Resetting the bits also makes sure we don't have leftover dirty - // bindgroups that don't exist in the pipeline layout. - mDirtySets &= ~layout->InheritedGroupsMask(mCurrentLayout); - } - mCurrentLayout = layout; + class RenderDescriptorSetTracker : public BindGroupTrackerBase { + public: + RenderDescriptorSetTracker() = default; + + void Apply(Device* device, + CommandRecordingContext* recordingContext, + VkPipelineBindPoint bindPoint) { + ApplyDescriptorSets(device, recordingContext->commandBuffer, bindPoint, + ToBackend(mPipelineLayout)->GetHandle(), + mDirtyBindGroupsObjectChangedOrIsDynamic, mBindGroups, + mDynamicOffsetCounts, mDynamicOffsets); + DidApply(); } + }; - void Flush(Device* device, VkCommandBuffer commands, VkPipelineBindPoint bindPoint) { - for (uint32_t dirtyIndex : IterateBitSet(mDirtySets)) { - device->fn.CmdBindDescriptorSets( - commands, bindPoint, mCurrentLayout->GetHandle(), dirtyIndex, 1, - &mSets[dirtyIndex], mDynamicOffsetCounts[dirtyIndex], - mDynamicOffsetCounts[dirtyIndex] > 0 ? mDynamicOffsets[dirtyIndex].data() - : nullptr); + class ComputeDescriptorSetTracker + : public BindGroupAndStorageBarrierTrackerBase { + public: + ComputeDescriptorSetTracker() = default; + + void Apply(Device* device, + CommandRecordingContext* recordingContext, + VkPipelineBindPoint bindPoint) { + ApplyDescriptorSets(device, recordingContext->commandBuffer, bindPoint, + ToBackend(mPipelineLayout)->GetHandle(), + mDirtyBindGroupsObjectChangedOrIsDynamic, mBindGroups, + mDynamicOffsetCounts, mDynamicOffsets); + + for (BindGroupIndex index : IterateBitSet(mBindGroupLayoutsMask)) { + for (BindingIndex bindingIndex : + IterateBitSet(mBindingsNeedingBarrier[index])) { + switch (mBindingTypes[index][bindingIndex]) { + case wgpu::BindingType::StorageBuffer: + static_cast(mBindings[index][bindingIndex]) + ->TransitionUsageNow(recordingContext, + wgpu::BufferUsage::Storage); + break; + + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: { + TextureViewBase* view = + static_cast(mBindings[index][bindingIndex]); + ToBackend(view->GetTexture()) + ->TransitionUsageNow(recordingContext, + wgpu::TextureUsage::Storage, + view->GetSubresourceRange()); + break; + } + case wgpu::BindingType::StorageTexture: + // Not implemented. + + case wgpu::BindingType::UniformBuffer: + case wgpu::BindingType::ReadonlyStorageBuffer: + case wgpu::BindingType::Sampler: + case wgpu::BindingType::ComparisonSampler: + case wgpu::BindingType::SampledTexture: + // Don't require barriers. + + default: + UNREACHABLE(); + break; + } + } } - mDirtySets.reset(); + DidApply(); } - - private: - PipelineLayout* mCurrentLayout = nullptr; - std::array mSets; - std::bitset mDirtySets; - std::array mDynamicOffsetCounts; - std::array, kMaxBindGroups> mDynamicOffsets; }; - void RecordBeginRenderPass(VkCommandBuffer commands, - Device* device, - BeginRenderPassCmd* renderPass) { + MaybeError RecordBeginRenderPass(CommandRecordingContext* recordingContext, + Device* device, + BeginRenderPassCmd* renderPass) { + VkCommandBuffer commands = recordingContext->commandBuffer; + // Query a VkRenderPass from the cache VkRenderPass renderPassVK = VK_NULL_HANDLE; { RenderPassCacheQuery query; - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { + for (uint32_t i : + IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { const auto& attachmentInfo = renderPass->colorAttachments[i]; - bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr; - dawn::LoadOp loadOp = attachmentInfo.loadOp; - if (loadOp == dawn::LoadOp::Load && attachmentInfo.view->GetTexture() && - !attachmentInfo.view->GetTexture()->IsSubresourceContentInitialized( - attachmentInfo.view->GetBaseMipLevel(), 1, - attachmentInfo.view->GetBaseArrayLayer(), 1)) { - loadOp = dawn::LoadOp::Clear; - } + bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr; + wgpu::LoadOp loadOp = attachmentInfo.loadOp; - query.SetColor(i, attachmentInfo.view->GetFormat(), loadOp, hasResolveTarget); + query.SetColor(i, attachmentInfo.view->GetFormat().format, loadOp, + hasResolveTarget); } - if (renderPass->hasDepthStencilAttachment) { + if (renderPass->attachmentState->HasDepthStencilAttachment()) { const auto& attachmentInfo = renderPass->depthStencilAttachment; - query.SetDepthStencil(attachmentInfo.view->GetTexture()->GetFormat(), + + query.SetDepthStencil(attachmentInfo.view->GetTexture()->GetFormat().format, attachmentInfo.depthLoadOp, attachmentInfo.stencilLoadOp); } - query.SetSampleCount(renderPass->sampleCount); + query.SetSampleCount(renderPass->attachmentState->GetSampleCount()); - renderPassVK = device->GetRenderPassCache()->GetRenderPass(query); + DAWN_TRY_ASSIGN(renderPassVK, device->GetRenderPassCache()->GetRenderPass(query)); } // Create a framebuffer that will be used once for the render pass and gather the clear @@ -203,7 +227,8 @@ namespace dawn_native { namespace vulkan { // Fill in the attachment info that will be chained in the framebuffer create info. std::array attachments; - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { + for (uint32_t i : + IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { auto& attachmentInfo = renderPass->colorAttachments[i]; TextureView* view = ToBackend(attachmentInfo.view.Get()); @@ -217,7 +242,7 @@ namespace dawn_native { namespace vulkan { attachmentCount++; } - if (renderPass->hasDepthStencilAttachment) { + if (renderPass->attachmentState->HasDepthStencilAttachment()) { auto& attachmentInfo = renderPass->depthStencilAttachment; TextureView* view = ToBackend(attachmentInfo.view.Get()); @@ -229,7 +254,8 @@ namespace dawn_native { namespace vulkan { attachmentCount++; } - for (uint32_t i : IterateBitSet(renderPass->colorAttachmentsSet)) { + for (uint32_t i : + IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { if (renderPass->colorAttachments[i].resolveTarget.Get() != nullptr) { TextureView* view = ToBackend(renderPass->colorAttachments[i].resolveTarget.Get()); @@ -247,15 +273,15 @@ namespace dawn_native { namespace vulkan { createInfo.flags = 0; createInfo.renderPass = renderPassVK; createInfo.attachmentCount = attachmentCount; - createInfo.pAttachments = attachments.data(); + createInfo.pAttachments = AsVkArray(attachments.data()); createInfo.width = renderPass->width; createInfo.height = renderPass->height; createInfo.layers = 1; - if (device->fn.CreateFramebuffer(device->GetVkDevice(), &createInfo, nullptr, - &framebuffer) != VK_SUCCESS) { - ASSERT(false); - } + DAWN_TRY( + CheckVkSuccess(device->fn.CreateFramebuffer(device->GetVkDevice(), &createInfo, + nullptr, &*framebuffer), + "CreateFramebuffer")); // We don't reuse VkFramebuffers so mark the framebuffer for deletion as soon as the // commands currently being recorded are finished. @@ -275,34 +301,114 @@ namespace dawn_native { namespace vulkan { beginInfo.pClearValues = clearValues.data(); device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE); + + return {}; } } // anonymous namespace - CommandBuffer::CommandBuffer(Device* device, CommandEncoderBase* encoder) - : CommandBufferBase(device, encoder), mCommands(encoder->AcquireCommands()) { + // static + CommandBuffer* CommandBuffer::Create(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) { + return new CommandBuffer(encoder, descriptor); + } + + CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) + : CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) { } CommandBuffer::~CommandBuffer() { FreeCommands(&mCommands); } - void CommandBuffer::RecordCommands(VkCommandBuffer commands) { + void CommandBuffer::RecordCopyImageWithTemporaryBuffer( + CommandRecordingContext* recordingContext, + const TextureCopy& srcCopy, + const TextureCopy& dstCopy, + const Extent3D& copySize) { + ASSERT(srcCopy.texture->GetFormat().format == dstCopy.texture->GetFormat().format); + dawn_native::Format format = srcCopy.texture->GetFormat(); + ASSERT(copySize.width % format.blockWidth == 0); + ASSERT(copySize.height % format.blockHeight == 0); + + // Create the temporary buffer. Note that We don't need to respect WebGPU's 256 alignment + // because it isn't a hard constraint in Vulkan. + uint64_t tempBufferSize = + (copySize.width / format.blockWidth * copySize.height / format.blockHeight) * + format.blockByteSize; + BufferDescriptor tempBufferDescriptor; + tempBufferDescriptor.size = tempBufferSize; + tempBufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + Device* device = ToBackend(GetDevice()); + Ref tempBuffer = AcquireRef(ToBackend(device->CreateBuffer(&tempBufferDescriptor))); + + BufferCopy tempBufferCopy; + tempBufferCopy.buffer = tempBuffer.Get(); + tempBufferCopy.rowsPerImage = copySize.height; + tempBufferCopy.offset = 0; + tempBufferCopy.bytesPerRow = copySize.width / format.blockWidth * format.blockByteSize; + + VkCommandBuffer commands = recordingContext->commandBuffer; + VkImage srcImage = ToBackend(srcCopy.texture)->GetHandle(); + VkImage dstImage = ToBackend(dstCopy.texture)->GetHandle(); + + tempBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); + VkBufferImageCopy srcToTempBufferRegion = + ComputeBufferImageCopyRegion(tempBufferCopy, srcCopy, copySize); + + // The Dawn CopySrc usage is always mapped to GENERAL + device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, + tempBuffer->GetHandle(), 1, &srcToTempBufferRegion); + + tempBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); + VkBufferImageCopy tempBufferToDstRegion = + ComputeBufferImageCopyRegion(tempBufferCopy, dstCopy, copySize); + + // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the + // copy command. + device->fn.CmdCopyBufferToImage(commands, tempBuffer->GetHandle(), dstImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, + &tempBufferToDstRegion); + + recordingContext->tempBuffers.emplace_back(tempBuffer); + } + + MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* recordingContext) { + Device* device = ToBackend(GetDevice()); + VkCommandBuffer commands = recordingContext->commandBuffer; // Records the necessary barriers for the resource usage pre-computed by the frontend - auto TransitionForPass = [](VkCommandBuffer commands, const PassResourceUsage& usages) { + auto TransitionForPass = [](Device* device, CommandRecordingContext* recordingContext, + const PassResourceUsage& usages) { + std::vector bufferBarriers; + std::vector imageBarriers; + VkPipelineStageFlags srcStages = 0; + VkPipelineStageFlags dstStages = 0; + for (size_t i = 0; i < usages.buffers.size(); ++i) { Buffer* buffer = ToBackend(usages.buffers[i]); - buffer->TransitionUsageNow(commands, usages.bufferUsages[i]); + buffer->TransitionUsageNow(recordingContext, usages.bufferUsages[i], + &bufferBarriers, &srcStages, &dstStages); } + for (size_t i = 0; i < usages.textures.size(); ++i) { Texture* texture = ToBackend(usages.textures[i]); + // Clear textures that are not output attachments. Output attachments will be + // cleared in RecordBeginRenderPass by setting the loadop to clear when the + // texture subresource has not been initialized before the render pass. + if (!(usages.textureUsages[i].usage & wgpu::TextureUsage::OutputAttachment)) { + texture->EnsureSubresourceContentInitialized(recordingContext, + texture->GetAllSubresources()); + } + texture->TransitionUsageForPass(recordingContext, usages.textureUsages[i], + &imageBarriers, &srcStages, &dstStages); + } - // TODO(natlee@microsoft.com): Update clearing here when subresource tracking is - // implemented - texture->EnsureSubresourceContentInitialized( - commands, 0, texture->GetNumMipLevels(), 0, texture->GetArrayLayers()); - texture->TransitionUsageNow(commands, usages.textureUsages[i]); + if (bufferBarriers.size() || imageBarriers.size()) { + device->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, + 0, 0, nullptr, bufferBarriers.size(), + bufferBarriers.data(), imageBarriers.size(), + imageBarriers.data()); } }; @@ -314,11 +420,16 @@ namespace dawn_native { namespace vulkan { switch (type) { case Command::CopyBufferToBuffer: { CopyBufferToBufferCmd* copy = mCommands.NextCommand(); + Buffer* srcBuffer = ToBackend(copy->source.Get()); Buffer* dstBuffer = ToBackend(copy->destination.Get()); - srcBuffer->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferSrc); - dstBuffer->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferDst); + srcBuffer->EnsureDataInitialized(recordingContext); + dstBuffer->EnsureDataInitializedAsDestination( + recordingContext, copy->destinationOffset, copy->size); + + srcBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); + dstBuffer->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); VkBufferCopy region; region.srcOffset = copy->sourceOffset; @@ -328,166 +439,235 @@ namespace dawn_native { namespace vulkan { VkBuffer srcHandle = srcBuffer->GetHandle(); VkBuffer dstHandle = dstBuffer->GetHandle(); device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, ®ion); - } break; + break; + } case Command::CopyBufferToTexture: { CopyBufferToTextureCmd* copy = mCommands.NextCommand(); auto& src = copy->source; auto& dst = copy->destination; + ToBackend(src.buffer)->EnsureDataInitialized(recordingContext); + VkBufferImageCopy region = ComputeBufferImageCopyRegion(src, dst, copy->copySize); VkImageSubresourceLayers subresource = region.imageSubresource; + ASSERT(dst.texture->GetDimension() == wgpu::TextureDimension::e2D); + SubresourceRange range = {subresource.mipLevel, 1, subresource.baseArrayLayer, + subresource.layerCount}; if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, subresource.mipLevel)) { // Since texture has been overwritten, it has been "initialized" - dst.texture->SetIsSubresourceContentInitialized( - subresource.mipLevel, 1, subresource.baseArrayLayer, 1); + dst.texture->SetIsSubresourceContentInitialized(true, range); } else { ToBackend(dst.texture) - ->EnsureSubresourceContentInitialized(commands, subresource.mipLevel, 1, - subresource.baseArrayLayer, 1); + ->EnsureSubresourceContentInitialized(recordingContext, range); } ToBackend(src.buffer) - ->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferSrc); + ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopySrc); ToBackend(dst.texture) - ->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferDst); + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range); VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle(); VkImage dstImage = ToBackend(dst.texture)->GetHandle(); - // The image is written to so the Dawn guarantees make sure it is in the - // TRANSFER_DST_OPTIMAL layout + // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the + // copy command. device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - } break; + break; + } case Command::CopyTextureToBuffer: { CopyTextureToBufferCmd* copy = mCommands.NextCommand(); auto& src = copy->source; auto& dst = copy->destination; + ToBackend(dst.buffer) + ->EnsureDataInitializedAsDestination(recordingContext, copy); + VkBufferImageCopy region = ComputeBufferImageCopyRegion(dst, src, copy->copySize); VkImageSubresourceLayers subresource = region.imageSubresource; + ASSERT(src.texture->GetDimension() == wgpu::TextureDimension::e2D); + const SubresourceRange range = {subresource.mipLevel, 1, + subresource.baseArrayLayer, + subresource.layerCount}; ToBackend(src.texture) - ->EnsureSubresourceContentInitialized(commands, subresource.mipLevel, 1, - subresource.baseArrayLayer, 1); + ->EnsureSubresourceContentInitialized(recordingContext, range); ToBackend(src.texture) - ->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferSrc); + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc, range); ToBackend(dst.buffer) - ->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferDst); + ->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); VkImage srcImage = ToBackend(src.texture)->GetHandle(); VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle(); - // The Dawn TransferSrc usage is always mapped to GENERAL + // The Dawn CopySrc usage is always mapped to GENERAL device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, dstBuffer, 1, ®ion); - } break; + break; + } case Command::CopyTextureToTexture: { CopyTextureToTextureCmd* copy = mCommands.NextCommand(); TextureCopy& src = copy->source; TextureCopy& dst = copy->destination; - - VkImageCopy region = ComputeImageCopyRegion(src, dst, copy->copySize); - VkImageSubresourceLayers dstSubresource = region.dstSubresource; - VkImageSubresourceLayers srcSubresource = region.srcSubresource; + SubresourceRange srcRange = GetSubresourcesAffectedByCopy(src, copy->copySize); + SubresourceRange dstRange = GetSubresourcesAffectedByCopy(dst, copy->copySize); ToBackend(src.texture) - ->EnsureSubresourceContentInitialized(commands, srcSubresource.mipLevel, 1, - srcSubresource.baseArrayLayer, 1); + ->EnsureSubresourceContentInitialized(recordingContext, srcRange); if (IsCompleteSubresourceCopiedTo(dst.texture.Get(), copy->copySize, - dstSubresource.mipLevel)) { + dst.mipLevel)) { // Since destination texture has been overwritten, it has been "initialized" - dst.texture->SetIsSubresourceContentInitialized( - dstSubresource.mipLevel, 1, dstSubresource.baseArrayLayer, 1); + dst.texture->SetIsSubresourceContentInitialized(true, dstRange); } else { ToBackend(dst.texture) - ->EnsureSubresourceContentInitialized(commands, dstSubresource.mipLevel, - 1, dstSubresource.baseArrayLayer, - 1); + ->EnsureSubresourceContentInitialized(recordingContext, dstRange); } + + if (src.texture.Get() == dst.texture.Get() && src.mipLevel == dst.mipLevel) { + // When there are overlapped subresources, the layout of the overlapped + // subresources should all be GENERAL instead of what we set now. Currently + // it is not allowed to copy with overlapped subresources, but we still + // add the ASSERT here as a reminder for this possible misuse. + ASSERT( + !IsRangeOverlapped(src.origin.z, dst.origin.z, copy->copySize.depth)); + } + + // TODO after Yunchao's CL ToBackend(src.texture) - ->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferSrc); + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopySrc, + srcRange); ToBackend(dst.texture) - ->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferDst); - VkImage srcImage = ToBackend(src.texture)->GetHandle(); - VkImage dstImage = ToBackend(dst.texture)->GetHandle(); + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, + dstRange); + + // In some situations we cannot do texture-to-texture copies with vkCmdCopyImage + // because as Vulkan SPEC always validates image copies with the virtual size of + // the image subresource, when the extent that fits in the copy region of one + // subresource but does not fit in the one of another subresource, we will fail + // to find a valid extent to satisfy the requirements on both source and + // destination image subresource. For example, when the source is the first + // level of a 16x16 texture in BC format, and the destination is the third level + // of a 60x60 texture in the same format, neither 16x16 nor 15x15 is valid as + // the extent of vkCmdCopyImage. + // Our workaround for this issue is replacing the texture-to-texture copy with + // one texture-to-buffer copy and one buffer-to-texture copy. + bool copyUsingTemporaryBuffer = + device->IsToggleEnabled( + Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy) && + src.texture->GetFormat().isCompressed && + !HasSameTextureCopyExtent(src, dst, copy->copySize); + + if (!copyUsingTemporaryBuffer) { + VkImage srcImage = ToBackend(src.texture)->GetHandle(); + VkImage dstImage = ToBackend(dst.texture)->GetHandle(); + VkImageCopy region = ComputeImageCopyRegion(src, dst, copy->copySize); + + // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the + // copy command. + device->fn.CmdCopyImage(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, + dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, + ®ion); + } else { + RecordCopyImageWithTemporaryBuffer(recordingContext, src, dst, + copy->copySize); + } - // The dstImage is written to so the Dawn guarantees make sure it is in the - // TRANSFER_DST_OPTIMAL layout - device->fn.CmdCopyImage(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL, dstImage, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - } break; + break; + } case Command::BeginRenderPass: { BeginRenderPassCmd* cmd = mCommands.NextCommand(); - TransitionForPass(commands, passResourceUsages[nextPassNumber]); - RecordRenderPass(commands, cmd); + TransitionForPass(device, recordingContext, passResourceUsages[nextPassNumber]); + + LazyClearRenderPassAttachments(cmd); + DAWN_TRY(RecordRenderPass(recordingContext, cmd)); nextPassNumber++; - } break; + break; + } case Command::BeginComputePass: { mCommands.NextCommand(); - TransitionForPass(commands, passResourceUsages[nextPassNumber]); - RecordComputePass(commands); + TransitionForPass(device, recordingContext, passResourceUsages[nextPassNumber]); + DAWN_TRY(RecordComputePass(recordingContext)); nextPassNumber++; - } break; + break; + } + + case Command::ResolveQuerySet: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } - default: { UNREACHABLE(); } break; + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } + + default: { + UNREACHABLE(); + break; + } } } + + return {}; } - void CommandBuffer::RecordComputePass(VkCommandBuffer commands) { + MaybeError CommandBuffer::RecordComputePass(CommandRecordingContext* recordingContext) { Device* device = ToBackend(GetDevice()); + VkCommandBuffer commands = recordingContext->commandBuffer; - DescriptorSetTracker descriptorSets; + ComputeDescriptorSetTracker descriptorSets = {}; Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::EndComputePass: { mCommands.NextCommand(); - return; - } break; + return {}; + } case Command::Dispatch: { DispatchCmd* dispatch = mCommands.NextCommand(); - descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_COMPUTE); + + descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_COMPUTE); device->fn.CmdDispatch(commands, dispatch->x, dispatch->y, dispatch->z); - } break; + break; + } case Command::DispatchIndirect: { DispatchIndirectCmd* dispatch = mCommands.NextCommand(); VkBuffer indirectBuffer = ToBackend(dispatch->indirectBuffer)->GetHandle(); - descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_COMPUTE); + descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_COMPUTE); device->fn.CmdDispatchIndirect( commands, indirectBuffer, static_cast(dispatch->indirectOffset)); - } break; + break; + } case Command::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); - VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle(); - uint64_t* dynamicOffsets = nullptr; + + BindGroup* bindGroup = ToBackend(cmd->group.Get()); + uint32_t* dynamicOffsets = nullptr; if (cmd->dynamicOffsetCount > 0) { - dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); } - descriptorSets.OnSetBindGroup(cmd->index, set, cmd->dynamicOffsetCount, + descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount, dynamicOffsets); - } break; + break; + } case Command::SetComputePipeline: { SetComputePipelineCmd* cmd = mCommands.NextCommand(); @@ -495,21 +675,81 @@ namespace dawn_native { namespace vulkan { device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline->GetHandle()); - descriptorSets.OnPipelineLayoutChange(ToBackend(pipeline->GetLayout())); - } break; + descriptorSets.OnSetPipeline(pipeline); + break; + } + + case Command::InsertDebugMarker: { + if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { + InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); + const char* label = mCommands.NextData(cmd->length + 1); + VkDebugMarkerMarkerInfoEXT markerInfo; + markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + markerInfo.pNext = nullptr; + markerInfo.pMarkerName = label; + // Default color to black + markerInfo.color[0] = 0.0; + markerInfo.color[1] = 0.0; + markerInfo.color[2] = 0.0; + markerInfo.color[3] = 1.0; + device->fn.CmdDebugMarkerInsertEXT(commands, &markerInfo); + } else { + SkipCommand(&mCommands, Command::InsertDebugMarker); + } + break; + } + + case Command::PopDebugGroup: { + if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { + mCommands.NextCommand(); + device->fn.CmdDebugMarkerEndEXT(commands); + } else { + SkipCommand(&mCommands, Command::PopDebugGroup); + } + break; + } - default: { UNREACHABLE(); } break; + case Command::PushDebugGroup: { + if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { + PushDebugGroupCmd* cmd = mCommands.NextCommand(); + const char* label = mCommands.NextData(cmd->length + 1); + VkDebugMarkerMarkerInfoEXT markerInfo; + markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; + markerInfo.pNext = nullptr; + markerInfo.pMarkerName = label; + // Default color to black + markerInfo.color[0] = 0.0; + markerInfo.color[1] = 0.0; + markerInfo.color[2] = 0.0; + markerInfo.color[3] = 1.0; + device->fn.CmdDebugMarkerBeginEXT(commands, &markerInfo); + } else { + SkipCommand(&mCommands, Command::PushDebugGroup); + } + break; + } + + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } + + default: { + UNREACHABLE(); + break; + } } } // EndComputePass should have been called UNREACHABLE(); } - void CommandBuffer::RecordRenderPass(VkCommandBuffer commands, - BeginRenderPassCmd* renderPassCmd) { + + MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* recordingContext, + BeginRenderPassCmd* renderPassCmd) { Device* device = ToBackend(GetDevice()); + VkCommandBuffer commands = recordingContext->commandBuffer; - RecordBeginRenderPass(commands, device, renderPassCmd); + DAWN_TRY(RecordBeginRenderPass(recordingContext, device, renderPassCmd)); // Set the default value for the dynamic state { @@ -529,9 +769,9 @@ namespace dawn_native { namespace vulkan { // The viewport and scissor default to cover all of the attachments VkViewport viewport; viewport.x = 0.0f; - viewport.y = 0.0f; + viewport.y = static_cast(renderPassCmd->height); viewport.width = static_cast(renderPassCmd->width); - viewport.height = static_cast(renderPassCmd->height); + viewport.height = -static_cast(renderPassCmd->height); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; device->fn.CmdSetViewport(commands, 0, 1, &viewport); @@ -544,73 +784,56 @@ namespace dawn_native { namespace vulkan { device->fn.CmdSetScissor(commands, 0, 1, &scissorRect); } - DescriptorSetTracker descriptorSets; + RenderDescriptorSetTracker descriptorSets = {}; RenderPipeline* lastPipeline = nullptr; - Command type; - while (mCommands.NextCommandId(&type)) { + auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) { switch (type) { - case Command::EndRenderPass: { - mCommands.NextCommand(); - device->fn.CmdEndRenderPass(commands); - for (uint32_t i : IterateBitSet(renderPassCmd->colorAttachmentsSet)) { - auto& attachmentInfo = renderPassCmd->colorAttachments[i]; - TextureView* view = ToBackend(attachmentInfo.view.Get()); - switch (attachmentInfo.storeOp) { - case dawn::StoreOp::Store: { - attachmentInfo.view->GetTexture() - ->SetIsSubresourceContentInitialized( - view->GetBaseMipLevel(), view->GetLevelCount(), - view->GetBaseArrayLayer(), view->GetLayerCount()); - } break; - - default: { UNREACHABLE(); } break; - } - } - return; - } break; - case Command::Draw: { - DrawCmd* draw = mCommands.NextCommand(); + DrawCmd* draw = iter->NextCommand(); - descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS); + descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount, draw->firstVertex, draw->firstInstance); - } break; + break; + } case Command::DrawIndexed: { - DrawIndexedCmd* draw = mCommands.NextCommand(); + DrawIndexedCmd* draw = iter->NextCommand(); - descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS); + descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount, draw->firstIndex, draw->baseVertex, draw->firstInstance); - } break; + break; + } case Command::DrawIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle(); - descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS); + descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); device->fn.CmdDrawIndirect(commands, indirectBuffer, static_cast(draw->indirectOffset), 1, 0); - } break; + break; + } case Command::DrawIndexedIndirect: { - DrawIndirectCmd* draw = mCommands.NextCommand(); + DrawIndirectCmd* draw = iter->NextCommand(); VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle(); - descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS); + descriptorSets.Apply(device, recordingContext, VK_PIPELINE_BIND_POINT_GRAPHICS); device->fn.CmdDrawIndexedIndirect( commands, indirectBuffer, static_cast(draw->indirectOffset), 1, 0); - } break; + break; + } case Command::InsertDebugMarker: { - if (device->GetDeviceInfo().debugMarker) { - InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); - const char* label = mCommands.NextData(cmd->length + 1); + if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { + InsertDebugMarkerCmd* cmd = iter->NextCommand(); + const char* label = iter->NextData(cmd->length + 1); VkDebugMarkerMarkerInfoEXT markerInfo; markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; markerInfo.pNext = nullptr; @@ -622,23 +845,25 @@ namespace dawn_native { namespace vulkan { markerInfo.color[3] = 1.0; device->fn.CmdDebugMarkerInsertEXT(commands, &markerInfo); } else { - SkipCommand(&mCommands, Command::InsertDebugMarker); + SkipCommand(iter, Command::InsertDebugMarker); } - } break; + break; + } case Command::PopDebugGroup: { - if (device->GetDeviceInfo().debugMarker) { - mCommands.NextCommand(); + if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { + iter->NextCommand(); device->fn.CmdDebugMarkerEndEXT(commands); } else { - SkipCommand(&mCommands, Command::PopDebugGroup); + SkipCommand(iter, Command::PopDebugGroup); } - } break; + break; + } case Command::PushDebugGroup: { - if (device->GetDeviceInfo().debugMarker) { - PushDebugGroupCmd* cmd = mCommands.NextCommand(); - const char* label = mCommands.NextData(cmd->length + 1); + if (device->GetDeviceInfo().HasExt(DeviceExt::DebugMarker)) { + PushDebugGroupCmd* cmd = iter->NextCommand(); + const char* label = iter->NextData(cmd->length + 1); VkDebugMarkerMarkerInfoEXT markerInfo; markerInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT; markerInfo.pNext = nullptr; @@ -650,62 +875,106 @@ namespace dawn_native { namespace vulkan { markerInfo.color[3] = 1.0; device->fn.CmdDebugMarkerBeginEXT(commands, &markerInfo); } else { - SkipCommand(&mCommands, Command::PushDebugGroup); + SkipCommand(iter, Command::PushDebugGroup); } - } break; + break; + } case Command::SetBindGroup: { - SetBindGroupCmd* cmd = mCommands.NextCommand(); - VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle(); - uint64_t* dynamicOffsets = nullptr; + SetBindGroupCmd* cmd = iter->NextCommand(); + BindGroup* bindGroup = ToBackend(cmd->group.Get()); + uint32_t* dynamicOffsets = nullptr; if (cmd->dynamicOffsetCount > 0) { - dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); + dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); } - descriptorSets.OnSetBindGroup(cmd->index, set, cmd->dynamicOffsetCount, + descriptorSets.OnSetBindGroup(cmd->index, bindGroup, cmd->dynamicOffsetCount, dynamicOffsets); - } break; - - case Command::SetBlendColor: { - SetBlendColorCmd* cmd = mCommands.NextCommand(); - float blendConstants[4] = { - cmd->color.r, - cmd->color.g, - cmd->color.b, - cmd->color.a, - }; - device->fn.CmdSetBlendConstants(commands, blendConstants); - } break; + break; + } case Command::SetIndexBuffer: { - SetIndexBufferCmd* cmd = mCommands.NextCommand(); + SetIndexBufferCmd* cmd = iter->NextCommand(); VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle(); // TODO(cwallez@chromium.org): get the index type from the last render pipeline // and rebind if needed on pipeline change ASSERT(lastPipeline != nullptr); VkIndexType indexType = - VulkanIndexType(lastPipeline->GetVertexInputDescriptor()->indexFormat); + VulkanIndexType(lastPipeline->GetVertexStateDescriptor()->indexFormat); device->fn.CmdBindIndexBuffer( commands, indexBuffer, static_cast(cmd->offset), indexType); - } break; + break; + } case Command::SetRenderPipeline: { - SetRenderPipelineCmd* cmd = mCommands.NextCommand(); + SetRenderPipelineCmd* cmd = iter->NextCommand(); RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->GetHandle()); lastPipeline = pipeline; - descriptorSets.OnPipelineLayoutChange(ToBackend(pipeline->GetLayout())); - } break; + descriptorSets.OnSetPipeline(pipeline); + break; + } + + case Command::SetVertexBuffer: { + SetVertexBufferCmd* cmd = iter->NextCommand(); + VkBuffer buffer = ToBackend(cmd->buffer)->GetHandle(); + VkDeviceSize offset = static_cast(cmd->offset); + + device->fn.CmdBindVertexBuffers(commands, cmd->slot, 1, &*buffer, &offset); + break; + } + + default: + UNREACHABLE(); + break; + } + }; + + Command type; + while (mCommands.NextCommandId(&type)) { + switch (type) { + case Command::EndRenderPass: { + mCommands.NextCommand(); + device->fn.CmdEndRenderPass(commands); + return {}; + } + + case Command::SetBlendColor: { + SetBlendColorCmd* cmd = mCommands.NextCommand(); + float blendConstants[4] = { + cmd->color.r, + cmd->color.g, + cmd->color.b, + cmd->color.a, + }; + device->fn.CmdSetBlendConstants(commands, blendConstants); + break; + } case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, cmd->reference); - } break; + break; + } + + case Command::SetViewport: { + SetViewportCmd* cmd = mCommands.NextCommand(); + VkViewport viewport; + viewport.x = cmd->x; + viewport.y = cmd->y + cmd->height; + viewport.width = cmd->width; + viewport.height = -cmd->height; + viewport.minDepth = cmd->minDepth; + viewport.maxDepth = cmd->maxDepth; + + device->fn.CmdSetViewport(commands, 0, 1, &viewport); + break; + } case Command::SetScissorRect: { SetScissorRectCmd* cmd = mCommands.NextCommand(); @@ -716,27 +985,31 @@ namespace dawn_native { namespace vulkan { rect.extent.height = cmd->height; device->fn.CmdSetScissor(commands, 0, 1, &rect); - } break; - - case Command::SetVertexBuffers: { - SetVertexBuffersCmd* cmd = mCommands.NextCommand(); - auto buffers = mCommands.NextData>(cmd->count); - auto offsets = mCommands.NextData(cmd->count); + break; + } - std::array vkBuffers; - std::array vkOffsets; + case Command::ExecuteBundles: { + ExecuteBundlesCmd* cmd = mCommands.NextCommand(); + auto bundles = mCommands.NextData>(cmd->count); for (uint32_t i = 0; i < cmd->count; ++i) { - Buffer* buffer = ToBackend(buffers[i].Get()); - vkBuffers[i] = buffer->GetHandle(); - vkOffsets[i] = static_cast(offsets[i]); + CommandIterator* iter = bundles[i]->GetCommands(); + iter->Reset(); + while (iter->NextCommandId(&type)) { + EncodeRenderBundleCommand(iter, type); + } } + break; + } - device->fn.CmdBindVertexBuffers(commands, cmd->startSlot, cmd->count, - vkBuffers.data(), vkOffsets.data()); - } break; + case Command::WriteTimestamp: { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation."); + } - default: { UNREACHABLE(); } break; + default: { + EncodeRenderBundleCommand(&mCommands, type); + break; + } } } diff --git a/third_party/dawn/src/dawn_native/vulkan/CommandBufferVk.h b/third_party/dawn/src/dawn_native/vulkan/CommandBufferVk.h index df6499d6770..fa2dd4b2d24 100644 --- a/third_party/dawn/src/dawn_native/vulkan/CommandBufferVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/CommandBufferVk.h @@ -17,27 +17,38 @@ #include "dawn_native/CommandAllocator.h" #include "dawn_native/CommandBuffer.h" +#include "dawn_native/Error.h" #include "common/vulkan_platform.h" namespace dawn_native { struct BeginRenderPassCmd; + struct TextureCopy; } // namespace dawn_native namespace dawn_native { namespace vulkan { + struct CommandRecordingContext; class Device; - class CommandBuffer : public CommandBufferBase { + class CommandBuffer final : public CommandBufferBase { public: - CommandBuffer(Device* device, CommandEncoderBase* encoder); - ~CommandBuffer(); + static CommandBuffer* Create(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor); - void RecordCommands(VkCommandBuffer commands); + MaybeError RecordCommands(CommandRecordingContext* recordingContext); private: - void RecordComputePass(VkCommandBuffer commands); - void RecordRenderPass(VkCommandBuffer commands, BeginRenderPassCmd* renderPass); + CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor); + ~CommandBuffer() override; + + MaybeError RecordComputePass(CommandRecordingContext* recordingContext); + MaybeError RecordRenderPass(CommandRecordingContext* recordingContext, + BeginRenderPassCmd* renderPass); + void RecordCopyImageWithTemporaryBuffer(CommandRecordingContext* recordingContext, + const TextureCopy& srcCopy, + const TextureCopy& dstCopy, + const Extent3D& copySize); CommandIterator mCommands; }; diff --git a/third_party/dawn/src/dawn_native/vulkan/CommandRecordingContext.h b/third_party/dawn/src/dawn_native/vulkan/CommandRecordingContext.h new file mode 100644 index 00000000000..2749fd28416 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/CommandRecordingContext.h @@ -0,0 +1,44 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#ifndef DAWNNATIVE_VULKAN_COMMANDRECORDINGCONTEXT_H_ +#define DAWNNATIVE_VULKAN_COMMANDRECORDINGCONTEXT_H_ + +#include "common/vulkan_platform.h" + +#include "dawn_native/vulkan/BufferVk.h" + +#include + +namespace dawn_native { namespace vulkan { + class Buffer; + + // Used to track operations that are handled after recording. + // Currently only tracks semaphores, but may be used to do barrier coalescing in the future. + struct CommandRecordingContext { + VkCommandBuffer commandBuffer = VK_NULL_HANDLE; + std::vector waitSemaphores = {}; + std::vector signalSemaphores = {}; + + // The internal buffers used in the workaround of texture-to-texture copies with compressed + // formats. + std::vector> tempBuffers; + + // For Device state tracking only. + VkCommandPool commandPool = VK_NULL_HANDLE; + bool used = false; + }; + +}} // namespace dawn_native::vulkan + +#endif // DAWNNATIVE_VULKAN_COMMANDRECORDINGCONTEXT_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/ComputePipelineVk.cpp b/third_party/dawn/src/dawn_native/vulkan/ComputePipelineVk.cpp index 8e7c7aa746d..f681547655b 100644 --- a/third_party/dawn/src/dawn_native/vulkan/ComputePipelineVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/ComputePipelineVk.cpp @@ -18,31 +18,55 @@ #include "dawn_native/vulkan/FencedDeleter.h" #include "dawn_native/vulkan/PipelineLayoutVk.h" #include "dawn_native/vulkan/ShaderModuleVk.h" +#include "dawn_native/vulkan/UtilsVulkan.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { - ComputePipeline::ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor) - : ComputePipelineBase(device, descriptor) { + // static + ResultOrError ComputePipeline::Create( + Device* device, + const ComputePipelineDescriptor* descriptor) { + Ref pipeline = AcquireRef(new ComputePipeline(device, descriptor)); + DAWN_TRY(pipeline->Initialize(descriptor)); + return pipeline.Detach(); + } + + MaybeError ComputePipeline::Initialize(const ComputePipelineDescriptor* descriptor) { VkComputePipelineCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; createInfo.layout = ToBackend(descriptor->layout)->GetHandle(); - createInfo.basePipelineHandle = VK_NULL_HANDLE; + createInfo.basePipelineHandle = ::VK_NULL_HANDLE; createInfo.basePipelineIndex = -1; createInfo.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; createInfo.stage.pNext = nullptr; createInfo.stage.flags = 0; createInfo.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT; - createInfo.stage.module = ToBackend(descriptor->computeStage->module)->GetHandle(); - createInfo.stage.pName = descriptor->computeStage->entryPoint; + createInfo.stage.module = ToBackend(descriptor->computeStage.module)->GetHandle(); + createInfo.stage.pName = descriptor->computeStage.entryPoint; createInfo.stage.pSpecializationInfo = nullptr; - if (device->fn.CreateComputePipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1, &createInfo, - nullptr, &mHandle) != VK_SUCCESS) { - ASSERT(false); + Device* device = ToBackend(GetDevice()); + + PNextChainBuilder extChain(&createInfo); + + VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT subgroupSizeInfo = {}; + uint32_t computeSubgroupSize = device->GetComputeSubgroupSize(); + if (computeSubgroupSize != 0u) { + ASSERT(device->GetDeviceInfo().HasExt(DeviceExt::SubgroupSizeControl)); + subgroupSizeInfo.requiredSubgroupSize = computeSubgroupSize; + extChain.Add( + &subgroupSizeInfo, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT); } + + return CheckVkSuccess( + device->fn.CreateComputePipelines(device->GetVkDevice(), ::VK_NULL_HANDLE, 1, + &createInfo, nullptr, &*mHandle), + "CreateComputePipeline"); } ComputePipeline::~ComputePipeline() { diff --git a/third_party/dawn/src/dawn_native/vulkan/ComputePipelineVk.h b/third_party/dawn/src/dawn_native/vulkan/ComputePipelineVk.h index d1b589c8c41..6aa98a4ef89 100644 --- a/third_party/dawn/src/dawn_native/vulkan/ComputePipelineVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/ComputePipelineVk.h @@ -18,19 +18,24 @@ #include "dawn_native/ComputePipeline.h" #include "common/vulkan_platform.h" +#include "dawn_native/Error.h" namespace dawn_native { namespace vulkan { class Device; - class ComputePipeline : public ComputePipelineBase { + class ComputePipeline final : public ComputePipelineBase { public: - ComputePipeline(Device* device, const ComputePipelineDescriptor* descriptor); - ~ComputePipeline(); + static ResultOrError Create(Device* device, + const ComputePipelineDescriptor* descriptor); VkPipeline GetHandle() const; private: + ~ComputePipeline() override; + using ComputePipelineBase::ComputePipelineBase; + MaybeError Initialize(const ComputePipelineDescriptor* descriptor); + VkPipeline mHandle = VK_NULL_HANDLE; }; diff --git a/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocation.h b/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocation.h new file mode 100644 index 00000000000..6a708e1ae37 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocation.h @@ -0,0 +1,31 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_VULKAN_DESCRIPTORSETALLOCATION_H_ +#define DAWNNATIVE_VULKAN_DESCRIPTORSETALLOCATION_H_ + +#include "common/vulkan_platform.h" + +namespace dawn_native { namespace vulkan { + + // Contains a descriptor set along with data necessary to track its allocation. + struct DescriptorSetAllocation { + VkDescriptorSet set = VK_NULL_HANDLE; + uint32_t poolIndex; + uint16_t setIndex; + }; + +}} // namespace dawn_native::vulkan + +#endif // DAWNNATIVE_VULKAN_DESCRIPTORSETALLOCATION_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocator.cpp b/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocator.cpp new file mode 100644 index 00000000000..a7b794ff424 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocator.cpp @@ -0,0 +1,181 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/vulkan/DescriptorSetAllocator.h" + +#include "dawn_native/vulkan/BindGroupLayoutVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/VulkanError.h" + +namespace dawn_native { namespace vulkan { + + // TODO(enga): Figure out this value. + static constexpr uint32_t kMaxDescriptorsPerPool = 512; + + DescriptorSetAllocator::DescriptorSetAllocator( + BindGroupLayout* layout, + std::map descriptorCountPerType) + : mLayout(layout) { + ASSERT(layout != nullptr); + + // Compute the total number of descriptors for this layout. + uint32_t totalDescriptorCount = 0; + mPoolSizes.reserve(descriptorCountPerType.size()); + for (const auto& it : descriptorCountPerType) { + ASSERT(it.second > 0); + totalDescriptorCount += it.second; + mPoolSizes.push_back(VkDescriptorPoolSize{it.first, it.second}); + } + + if (totalDescriptorCount == 0) { + // Vulkan requires that valid usage of vkCreateDescriptorPool must have a non-zero + // number of pools, each of which has non-zero descriptor counts. + // Since the descriptor set layout is empty, we should be able to allocate + // |kMaxDescriptorsPerPool| sets from this 1-sized descriptor pool. + // The type of this descriptor pool doesn't matter because it is never used. + mPoolSizes.push_back(VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1}); + mMaxSets = kMaxDescriptorsPerPool; + } else { + ASSERT(totalDescriptorCount <= kMaxBindingsPerPipelineLayout); + static_assert(kMaxBindingsPerPipelineLayout <= kMaxDescriptorsPerPool, ""); + + // Compute the total number of descriptors sets that fits given the max. + mMaxSets = kMaxDescriptorsPerPool / totalDescriptorCount; + ASSERT(mMaxSets > 0); + + // Grow the number of desciptors in the pool to fit the computed |mMaxSets|. + for (auto& poolSize : mPoolSizes) { + poolSize.descriptorCount *= mMaxSets; + } + } + } + + DescriptorSetAllocator::~DescriptorSetAllocator() { + for (auto& pool : mDescriptorPools) { + ASSERT(pool.freeSetIndices.size() == mMaxSets); + if (pool.vkPool != VK_NULL_HANDLE) { + Device* device = ToBackend(mLayout->GetDevice()); + device->GetFencedDeleter()->DeleteWhenUnused(pool.vkPool); + } + } + } + + ResultOrError DescriptorSetAllocator::Allocate() { + if (mAvailableDescriptorPoolIndices.empty()) { + DAWN_TRY(AllocateDescriptorPool()); + } + + ASSERT(!mAvailableDescriptorPoolIndices.empty()); + + const PoolIndex poolIndex = mAvailableDescriptorPoolIndices.back(); + DescriptorPool* pool = &mDescriptorPools[poolIndex]; + + ASSERT(!pool->freeSetIndices.empty()); + + SetIndex setIndex = pool->freeSetIndices.back(); + pool->freeSetIndices.pop_back(); + + if (pool->freeSetIndices.empty()) { + mAvailableDescriptorPoolIndices.pop_back(); + } + + return DescriptorSetAllocation{pool->sets[setIndex], poolIndex, setIndex}; + } + + void DescriptorSetAllocator::Deallocate(DescriptorSetAllocation* allocationInfo) { + ASSERT(allocationInfo != nullptr); + ASSERT(allocationInfo->set != VK_NULL_HANDLE); + + // We can't reuse the descriptor set right away because the Vulkan spec says in the + // documentation for vkCmdBindDescriptorSets that the set may be consumed any time between + // host execution of the command and the end of the draw/dispatch. + Device* device = ToBackend(mLayout->GetDevice()); + const Serial serial = device->GetPendingCommandSerial(); + mPendingDeallocations.Enqueue({allocationInfo->poolIndex, allocationInfo->setIndex}, + serial); + + if (mLastDeallocationSerial != serial) { + device->EnqueueDeferredDeallocation(mLayout); + mLastDeallocationSerial = serial; + } + + // Clear the content of allocation so that use after frees are more visible. + *allocationInfo = {}; + } + + void DescriptorSetAllocator::FinishDeallocation(Serial completedSerial) { + for (const Deallocation& dealloc : mPendingDeallocations.IterateUpTo(completedSerial)) { + ASSERT(dealloc.poolIndex < mDescriptorPools.size()); + + auto& freeSetIndices = mDescriptorPools[dealloc.poolIndex].freeSetIndices; + if (freeSetIndices.empty()) { + mAvailableDescriptorPoolIndices.emplace_back(dealloc.poolIndex); + } + freeSetIndices.emplace_back(dealloc.setIndex); + } + mPendingDeallocations.ClearUpTo(completedSerial); + } + + MaybeError DescriptorSetAllocator::AllocateDescriptorPool() { + VkDescriptorPoolCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = 0; + createInfo.maxSets = mMaxSets; + createInfo.poolSizeCount = static_cast(mPoolSizes.size()); + createInfo.pPoolSizes = mPoolSizes.data(); + + Device* device = ToBackend(mLayout->GetDevice()); + + VkDescriptorPool descriptorPool; + DAWN_TRY(CheckVkSuccess(device->fn.CreateDescriptorPool(device->GetVkDevice(), &createInfo, + nullptr, &*descriptorPool), + "CreateDescriptorPool")); + + std::vector layouts(mMaxSets, mLayout->GetHandle()); + + VkDescriptorSetAllocateInfo allocateInfo; + allocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocateInfo.pNext = nullptr; + allocateInfo.descriptorPool = descriptorPool; + allocateInfo.descriptorSetCount = mMaxSets; + allocateInfo.pSetLayouts = AsVkArray(layouts.data()); + + std::vector sets(mMaxSets); + MaybeError result = + CheckVkSuccess(device->fn.AllocateDescriptorSets(device->GetVkDevice(), &allocateInfo, + AsVkArray(sets.data())), + "AllocateDescriptorSets"); + if (result.IsError()) { + // On an error we can destroy the pool immediately because no command references it. + device->fn.DestroyDescriptorPool(device->GetVkDevice(), descriptorPool, nullptr); + DAWN_TRY(std::move(result)); + } + + std::vector freeSetIndices; + freeSetIndices.reserve(mMaxSets); + + for (SetIndex i = 0; i < mMaxSets; ++i) { + freeSetIndices.push_back(i); + } + + mAvailableDescriptorPoolIndices.push_back(mDescriptorPools.size()); + mDescriptorPools.emplace_back( + DescriptorPool{descriptorPool, std::move(sets), std::move(freeSetIndices)}); + + return {}; + } + +}} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocator.h b/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocator.h new file mode 100644 index 00000000000..44ea4a43252 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/DescriptorSetAllocator.h @@ -0,0 +1,70 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_VULKAN_DESCRIPTORSETALLOCATOR_H_ +#define DAWNNATIVE_VULKAN_DESCRIPTORSETALLOCATOR_H_ + +#include "common/SerialQueue.h" +#include "common/vulkan_platform.h" +#include "dawn_native/Error.h" +#include "dawn_native/vulkan/DescriptorSetAllocation.h" + +#include +#include + +namespace dawn_native { namespace vulkan { + + class BindGroupLayout; + + class DescriptorSetAllocator { + using PoolIndex = uint32_t; + using SetIndex = uint16_t; + + public: + DescriptorSetAllocator(BindGroupLayout* layout, + std::map descriptorCountPerType); + ~DescriptorSetAllocator(); + + ResultOrError Allocate(); + void Deallocate(DescriptorSetAllocation* allocationInfo); + void FinishDeallocation(Serial completedSerial); + + private: + MaybeError AllocateDescriptorPool(); + + BindGroupLayout* mLayout; + + std::vector mPoolSizes; + SetIndex mMaxSets; + + struct DescriptorPool { + VkDescriptorPool vkPool; + std::vector sets; + std::vector freeSetIndices; + }; + + std::vector mAvailableDescriptorPoolIndices; + std::vector mDescriptorPools; + + struct Deallocation { + PoolIndex poolIndex; + SetIndex setIndex; + }; + SerialQueue mPendingDeallocations; + Serial mLastDeallocationSerial = 0; + }; + +}} // namespace dawn_native::vulkan + +#endif // DAWNNATIVE_VULKAN_DESCRIPTORSETALLOCATOR_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/DeviceVk.cpp b/third_party/dawn/src/dawn_native/vulkan/DeviceVk.cpp index 629d8f9f08a..53538a8ee86 100644 --- a/third_party/dawn/src/dawn_native/vulkan/DeviceVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/DeviceVk.cpp @@ -16,9 +16,9 @@ #include "common/Platform.h" #include "dawn_native/BackendConnection.h" -#include "dawn_native/Commands.h" -#include "dawn_native/DynamicUploader.h" +#include "dawn_native/Error.h" #include "dawn_native/ErrorData.h" +#include "dawn_native/VulkanBackend.h" #include "dawn_native/vulkan/AdapterVk.h" #include "dawn_native/vulkan/BackendVk.h" #include "dawn_native/vulkan/BindGroupLayoutVk.h" @@ -31,195 +31,151 @@ #include "dawn_native/vulkan/QueueVk.h" #include "dawn_native/vulkan/RenderPassCache.h" #include "dawn_native/vulkan/RenderPipelineVk.h" +#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" #include "dawn_native/vulkan/SamplerVk.h" #include "dawn_native/vulkan/ShaderModuleVk.h" #include "dawn_native/vulkan/StagingBufferVk.h" #include "dawn_native/vulkan/SwapChainVk.h" #include "dawn_native/vulkan/TextureVk.h" +#include "dawn_native/vulkan/UtilsVulkan.h" #include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { + // static + ResultOrError Device::Create(Adapter* adapter, const DeviceDescriptor* descriptor) { + Ref device = AcquireRef(new Device(adapter, descriptor)); + DAWN_TRY(device->Initialize()); + return device.Detach(); + } + Device::Device(Adapter* adapter, const DeviceDescriptor* descriptor) : DeviceBase(adapter, descriptor) { - if (descriptor != nullptr) { - ApplyToggleOverrides(descriptor); - } + InitTogglesFromDriver(); } MaybeError Device::Initialize() { // Copy the adapter's device info to the device so that we can change the "knobs" mDeviceInfo = ToBackend(GetAdapter())->GetDeviceInfo(); + // Initialize the "instance" procs of our local function table. VulkanFunctions* functions = GetMutableFunctions(); *functions = ToBackend(GetAdapter())->GetBackend()->GetFunctions(); - VkPhysicalDevice physicalDevice = ToBackend(GetAdapter())->GetPhysicalDevice(); - - VulkanDeviceKnobs usedDeviceKnobs = {}; - DAWN_TRY_ASSIGN(usedDeviceKnobs, CreateDevice(physicalDevice)); - *static_cast(&mDeviceInfo) = usedDeviceKnobs; - - DAWN_TRY(functions->LoadDeviceProcs(mVkDevice, mDeviceInfo)); - - GatherQueueFromDevice(); - mDeleter = std::make_unique(this); - mMapRequestTracker = std::make_unique(this); - mMemoryAllocator = std::make_unique(this); - mRenderPassCache = std::make_unique(this); - - return {}; - } - - Device::~Device() { - // Immediately forget about all pending commands so we don't try to submit them in Tick - FreeCommands(&mPendingCommands); - - if (fn.QueueWaitIdle(mQueue) != VK_SUCCESS) { - ASSERT(false); - } - CheckPassedFences(); - - // Make sure all fences are complete by explicitly waiting on them all - while (!mFencesInFlight.empty()) { - VkFence fence = mFencesInFlight.front().first; - Serial fenceSerial = mFencesInFlight.front().second; - ASSERT(fenceSerial > mCompletedSerial); + // Two things are crucial if device initialization fails: the function pointers to destroy + // objects, and the fence deleter that calls these functions. Do not do anything before + // these two are set up, so that a failed initialization doesn't cause a crash in + // ShutDownImpl() + { + VkPhysicalDevice physicalDevice = ToBackend(GetAdapter())->GetPhysicalDevice(); - VkResult result = VK_TIMEOUT; - do { - result = fn.WaitForFences(mVkDevice, 1, &fence, true, UINT64_MAX); - } while (result == VK_TIMEOUT); - fn.DestroyFence(mVkDevice, fence, nullptr); + VulkanDeviceKnobs usedDeviceKnobs = {}; + DAWN_TRY_ASSIGN(usedDeviceKnobs, CreateDevice(physicalDevice)); + *static_cast(&mDeviceInfo) = usedDeviceKnobs; - mFencesInFlight.pop(); - mCompletedSerial = fenceSerial; - } + DAWN_TRY(functions->LoadDeviceProcs(mVkDevice, mDeviceInfo)); - // Some operations might have been started since the last submit and waiting - // on a serial that doesn't have a corresponding fence enqueued. Force all - // operations to look as if they were completed (because they were). - mCompletedSerial = mLastSubmittedSerial + 1; - Tick(); + // The queue can be loaded before the fenced deleter because their lifetime is tied to + // the device. + GatherQueueFromDevice(); - ASSERT(mCommandsInFlight.Empty()); - for (auto& commands : mUnusedCommands) { - FreeCommands(&commands); + mDeleter = std::make_unique(this); } - mUnusedCommands.clear(); - ASSERT(mWaitSemaphores.empty()); - - for (VkFence fence : mUnusedFences) { - fn.DestroyFence(mVkDevice, fence, nullptr); - } - mUnusedFences.clear(); + mRenderPassCache = std::make_unique(this); + mResourceMemoryAllocator = std::make_unique(this); - // Free services explicitly so that they can free Vulkan objects before vkDestroyDevice - mDynamicUploader = nullptr; + mExternalMemoryService = std::make_unique(this); + mExternalSemaphoreService = std::make_unique(this); - // Releasing the uploader enqueues buffers to be released. - // Call Tick() again to clear them before releasing the deleter. - mDeleter->Tick(mCompletedSerial); + DAWN_TRY(PrepareRecordingContext()); - mDeleter = nullptr; - mMapRequestTracker = nullptr; - mMemoryAllocator = nullptr; + // The environment can request to use D32S8 or D24S8 when it's not available. Override + // the decision if it is not applicable. + ApplyDepth24PlusS8Toggle(); - // The VkRenderPasses in the cache can be destroyed immediately since all commands referring - // to them are guaranteed to be finished executing. - mRenderPassCache = nullptr; + return DeviceBase::Initialize(new Queue(this)); + } - // VkQueues are destroyed when the VkDevice is destroyed - if (mVkDevice != VK_NULL_HANDLE) { - fn.DestroyDevice(mVkDevice, nullptr); - mVkDevice = VK_NULL_HANDLE; - } + Device::~Device() { + ShutDownBase(); } ResultOrError Device::CreateBindGroupImpl( const BindGroupDescriptor* descriptor) { - return new BindGroup(this, descriptor); + return BindGroup::Create(this, descriptor); } ResultOrError Device::CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) { - return new BindGroupLayout(this, descriptor); + return BindGroupLayout::Create(this, descriptor); } - ResultOrError Device::CreateBufferImpl(const BufferDescriptor* descriptor) { - return new Buffer(this, descriptor); + ResultOrError> Device::CreateBufferImpl(const BufferDescriptor* descriptor) { + return Buffer::Create(this, descriptor); } - CommandBufferBase* Device::CreateCommandBuffer(CommandEncoderBase* encoder) { - return new CommandBuffer(this, encoder); + CommandBufferBase* Device::CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) { + return CommandBuffer::Create(encoder, descriptor); } ResultOrError Device::CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) { - return new ComputePipeline(this, descriptor); + return ComputePipeline::Create(this, descriptor); } ResultOrError Device::CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) { - return new PipelineLayout(this, descriptor); + return PipelineLayout::Create(this, descriptor); } - ResultOrError Device::CreateQueueImpl() { - return new Queue(this); + ResultOrError Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) { + return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation"); } ResultOrError Device::CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) { - return new RenderPipeline(this, descriptor); + return RenderPipeline::Create(this, descriptor); } ResultOrError Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) { - return new Sampler(this, descriptor); + return Sampler::Create(this, descriptor); } ResultOrError Device::CreateShaderModuleImpl( const ShaderModuleDescriptor* descriptor) { - return new ShaderModule(this, descriptor); + return ShaderModule::Create(this, descriptor); } ResultOrError Device::CreateSwapChainImpl( const SwapChainDescriptor* descriptor) { - return new SwapChain(this, descriptor); + return SwapChain::Create(this, descriptor); + } + ResultOrError Device::CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) { + return DAWN_VALIDATION_ERROR("New swapchains not implemented."); } - ResultOrError Device::CreateTextureImpl(const TextureDescriptor* descriptor) { - return new Texture(this, descriptor); + ResultOrError> Device::CreateTextureImpl(const TextureDescriptor* descriptor) { + return Texture::Create(this, descriptor); } ResultOrError Device::CreateTextureViewImpl( TextureBase* texture, const TextureViewDescriptor* descriptor) { - return new TextureView(texture, descriptor); + return TextureView::Create(texture, descriptor); } - Serial Device::GetCompletedCommandSerial() const { - return mCompletedSerial; - } - - Serial Device::GetLastSubmittedCommandSerial() const { - return mLastSubmittedSerial; - } - - Serial Device::GetPendingCommandSerial() const { - return mLastSubmittedSerial + 1; - } - - void Device::TickImpl() { - CheckPassedFences(); + MaybeError Device::TickImpl() { RecycleCompletedCommands(); - mMapRequestTracker->Tick(mCompletedSerial); + Serial completedSerial = GetCompletedCommandSerial(); - // Uploader should tick before the resource allocator - // as it enqueues resources to be released. - mDynamicUploader->Tick(mCompletedSerial); - - mMemoryAllocator->Tick(mCompletedSerial); + for (Ref& bgl : + mBindGroupLayoutsPendingDeallocation.IterateUpTo(completedSerial)) { + bgl->FinishDeallocation(completedSerial); + } + mBindGroupLayoutsPendingDeallocation.ClearUpTo(completedSerial); - mDeleter->Tick(mCompletedSerial); + mResourceMemoryAllocator->Tick(completedSerial); + mDeleter->Tick(completedSerial); - if (mPendingCommands.pool != VK_NULL_HANDLE) { - SubmitPendingCommands(); - } else if (mCompletedSerial == mLastSubmittedSerial) { - // If there's no GPU work in flight we still need to artificially increment the serial - // so that CPU operations waiting on GPU completion can know they don't have to wait. - mCompletedSerial++; - mLastSubmittedSerial++; + if (mRecordingContext.used) { + DAWN_TRY(SubmitPendingCommands()); } + + return {}; } VkInstance Device::GetVkInstance() const { @@ -241,14 +197,6 @@ namespace dawn_native { namespace vulkan { return mQueue; } - MapRequestTracker* Device::GetMapRequestTracker() const { - return mMapRequestTracker.get(); - } - - MemoryAllocator* Device::GetMemoryAllocator() const { - return mMemoryAllocator.get(); - } - FencedDeleter* Device::GetFencedDeleter() const { return mDeleter.get(); } @@ -257,94 +205,139 @@ namespace dawn_native { namespace vulkan { return mRenderPassCache.get(); } - VkCommandBuffer Device::GetPendingCommandBuffer() { - if (mPendingCommands.pool == VK_NULL_HANDLE) { - mPendingCommands = GetUnusedCommands(); - - VkCommandBufferBeginInfo beginInfo; - beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - beginInfo.pNext = nullptr; - beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - beginInfo.pInheritanceInfo = nullptr; - - if (fn.BeginCommandBuffer(mPendingCommands.commandBuffer, &beginInfo) != VK_SUCCESS) { - ASSERT(false); - } - } + void Device::EnqueueDeferredDeallocation(BindGroupLayout* bindGroupLayout) { + mBindGroupLayoutsPendingDeallocation.Enqueue(bindGroupLayout, GetPendingCommandSerial()); + } - return mPendingCommands.commandBuffer; + CommandRecordingContext* Device::GetPendingRecordingContext() { + ASSERT(mRecordingContext.commandBuffer != VK_NULL_HANDLE); + mRecordingContext.used = true; + return &mRecordingContext; } - void Device::SubmitPendingCommands() { - if (mPendingCommands.pool == VK_NULL_HANDLE) { - return; + MaybeError Device::SubmitPendingCommands() { + if (!mRecordingContext.used) { + return {}; } - if (fn.EndCommandBuffer(mPendingCommands.commandBuffer) != VK_SUCCESS) { - ASSERT(false); - } + DAWN_TRY(CheckVkSuccess(fn.EndCommandBuffer(mRecordingContext.commandBuffer), + "vkEndCommandBuffer")); - std::vector dstStageMasks(mWaitSemaphores.size(), + std::vector dstStageMasks(mRecordingContext.waitSemaphores.size(), VK_PIPELINE_STAGE_ALL_COMMANDS_BIT); VkSubmitInfo submitInfo; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; - submitInfo.waitSemaphoreCount = static_cast(mWaitSemaphores.size()); - submitInfo.pWaitSemaphores = mWaitSemaphores.data(); + submitInfo.waitSemaphoreCount = + static_cast(mRecordingContext.waitSemaphores.size()); + submitInfo.pWaitSemaphores = AsVkArray(mRecordingContext.waitSemaphores.data()); submitInfo.pWaitDstStageMask = dstStageMasks.data(); submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &mPendingCommands.commandBuffer; - submitInfo.signalSemaphoreCount = 0; - submitInfo.pSignalSemaphores = 0; - - VkFence fence = GetUnusedFence(); - if (fn.QueueSubmit(mQueue, 1, &submitInfo, fence) != VK_SUCCESS) { - ASSERT(false); - } + submitInfo.pCommandBuffers = &mRecordingContext.commandBuffer; + submitInfo.signalSemaphoreCount = + static_cast(mRecordingContext.signalSemaphores.size()); + submitInfo.pSignalSemaphores = AsVkArray(mRecordingContext.signalSemaphores.data()); - mLastSubmittedSerial++; - mCommandsInFlight.Enqueue(mPendingCommands, mLastSubmittedSerial); - mPendingCommands = CommandPoolAndBuffer(); - mFencesInFlight.emplace(fence, mLastSubmittedSerial); + VkFence fence = VK_NULL_HANDLE; + DAWN_TRY_ASSIGN(fence, GetUnusedFence()); + DAWN_TRY(CheckVkSuccess(fn.QueueSubmit(mQueue, 1, &submitInfo, fence), "vkQueueSubmit")); - for (VkSemaphore semaphore : mWaitSemaphores) { + // Enqueue the semaphores before incrementing the serial, so that they can be deleted as + // soon as the current submission is finished. + for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) { + mDeleter->DeleteWhenUnused(semaphore); + } + for (VkSemaphore semaphore : mRecordingContext.signalSemaphores) { mDeleter->DeleteWhenUnused(semaphore); } - mWaitSemaphores.clear(); - } - void Device::AddWaitSemaphore(VkSemaphore semaphore) { - mWaitSemaphores.push_back(semaphore); + IncrementLastSubmittedCommandSerial(); + Serial lastSubmittedSerial = GetLastSubmittedCommandSerial(); + mFencesInFlight.emplace(fence, lastSubmittedSerial); + + CommandPoolAndBuffer submittedCommands = {mRecordingContext.commandPool, + mRecordingContext.commandBuffer}; + mCommandsInFlight.Enqueue(submittedCommands, lastSubmittedSerial); + mRecordingContext = CommandRecordingContext(); + DAWN_TRY(PrepareRecordingContext()); + + return {}; } ResultOrError Device::CreateDevice(VkPhysicalDevice physicalDevice) { VulkanDeviceKnobs usedKnobs = {}; - float zero = 0.0f; - std::vector layersToRequest; - std::vector extensionsToRequest; - std::vector queuesToRequest; + // Default to asking for all avilable known extensions. + usedKnobs.extensions = mDeviceInfo.extensions; - if (mDeviceInfo.debugMarker) { - extensionsToRequest.push_back(kExtensionNameExtDebugMarker); - usedKnobs.debugMarker = true; - } + // However only request the extensions that haven't been promoted in the device's apiVersion + std::vector extensionNames; + for (uint32_t ext : IterateBitSet(usedKnobs.extensions.extensionBitSet)) { + const DeviceExtInfo& info = GetDeviceExtInfo(static_cast(ext)); - if (mDeviceInfo.swapchain) { - extensionsToRequest.push_back(kExtensionNameKhrSwapchain); - usedKnobs.swapchain = true; + if (info.versionPromoted > mDeviceInfo.properties.apiVersion) { + extensionNames.push_back(info.name); + } } + // Some device features can only be enabled using a VkPhysicalDeviceFeatures2 struct, which + // is supported by the VK_EXT_get_physical_properties2 instance extension, which was + // promoted as a core API in Vulkan 1.1. + // + // Prepare a VkPhysicalDeviceFeatures2 struct for this use case, it will only be populated + // if HasExt(DeviceExt::GetPhysicalDeviceProperties2) is true. + VkPhysicalDeviceFeatures2 features2 = {}; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + PNextChainBuilder featuresChain(&features2); + // Always require independentBlend because it is a core Dawn feature usedKnobs.features.independentBlend = VK_TRUE; // Always require imageCubeArray because it is a core Dawn feature usedKnobs.features.imageCubeArray = VK_TRUE; + // Always require fragmentStoresAndAtomics because it is required by end2end tests. + usedKnobs.features.fragmentStoresAndAtomics = VK_TRUE; + + if (IsRobustnessEnabled()) { + usedKnobs.features.robustBufferAccess = VK_TRUE; + } + + if (mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) { + ASSERT(usedKnobs.HasExt(DeviceExt::SubgroupSizeControl)); + + // Always request all the features from VK_EXT_subgroup_size_control when available. + usedKnobs.subgroupSizeControlFeatures = mDeviceInfo.subgroupSizeControlFeatures; + featuresChain.Add(&usedKnobs.subgroupSizeControlFeatures); + + mComputeSubgroupSize = FindComputeSubgroupSize(); + } + + if (IsExtensionEnabled(Extension::TextureCompressionBC)) { + ASSERT(ToBackend(GetAdapter())->GetDeviceInfo().features.textureCompressionBC == + VK_TRUE); + usedKnobs.features.textureCompressionBC = VK_TRUE; + } + + if (IsExtensionEnabled(Extension::ShaderFloat16)) { + const VulkanDeviceInfo& deviceInfo = ToBackend(GetAdapter())->GetDeviceInfo(); + ASSERT(deviceInfo.HasExt(DeviceExt::ShaderFloat16Int8) && + deviceInfo.shaderFloat16Int8Features.shaderFloat16 == VK_TRUE && + deviceInfo.HasExt(DeviceExt::_16BitStorage) && + deviceInfo._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess == VK_TRUE); + + usedKnobs.shaderFloat16Int8Features.shaderFloat16 = VK_TRUE; + usedKnobs._16BitStorageFeatures.uniformAndStorageBuffer16BitAccess = VK_TRUE; + + featuresChain.Add(&usedKnobs.shaderFloat16Int8Features, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR); + featuresChain.Add(&usedKnobs._16BitStorageFeatures, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES); + } // Find a universal queue family { - constexpr uint32_t kUniversalFlags = - VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT; + // Note that GRAPHICS and COMPUTE imply TRANSFER so we don't need to check for it. + constexpr uint32_t kUniversalFlags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT; int universalQueueFamily = -1; for (unsigned int i = 0; i < mDeviceInfo.queueFamilies.size(); ++i) { if ((mDeviceInfo.queueFamilies[i].queueFlags & kUniversalFlags) == @@ -355,12 +348,14 @@ namespace dawn_native { namespace vulkan { } if (universalQueueFamily == -1) { - return DAWN_CONTEXT_LOST_ERROR("No universal queue family"); + return DAWN_INTERNAL_ERROR("No universal queue family"); } mQueueFamily = static_cast(universalQueueFamily); } // Choose to create a single universal queue + std::vector queuesToRequest; + float zero = 0.0f; { VkDeviceQueueCreateInfo queueCreateInfo; queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; @@ -379,11 +374,21 @@ namespace dawn_native { namespace vulkan { createInfo.flags = 0; createInfo.queueCreateInfoCount = static_cast(queuesToRequest.size()); createInfo.pQueueCreateInfos = queuesToRequest.data(); - createInfo.enabledLayerCount = static_cast(layersToRequest.size()); - createInfo.ppEnabledLayerNames = layersToRequest.data(); - createInfo.enabledExtensionCount = static_cast(extensionsToRequest.size()); - createInfo.ppEnabledExtensionNames = extensionsToRequest.data(); - createInfo.pEnabledFeatures = &usedKnobs.features; + createInfo.enabledLayerCount = 0; + createInfo.ppEnabledLayerNames = nullptr; + createInfo.enabledExtensionCount = static_cast(extensionNames.size()); + createInfo.ppEnabledExtensionNames = extensionNames.data(); + + // When we have DeviceExt::GetPhysicalDeviceProperties2, use features2 so that features not + // covered by VkPhysicalDeviceFeatures can be enabled. + if (mDeviceInfo.HasExt(DeviceExt::GetPhysicalDeviceProperties2)) { + features2.features = usedKnobs.features; + createInfo.pNext = &features2; + createInfo.pEnabledFeatures = nullptr; + } else { + ASSERT(features2.pNext == nullptr); + createInfo.pEnabledFeatures = &usedKnobs.features; + } DAWN_TRY(CheckVkSuccess(fn.CreateDevice(physicalDevice, &createInfo, nullptr, &mVkDevice), "vkCreateDevice")); @@ -391,17 +396,85 @@ namespace dawn_native { namespace vulkan { return usedKnobs; } + uint32_t Device::FindComputeSubgroupSize() const { + if (!mDeviceInfo.HasExt(DeviceExt::SubgroupSizeControl)) { + return 0; + } + + const VkPhysicalDeviceSubgroupSizeControlPropertiesEXT& ext = + mDeviceInfo.subgroupSizeControlProperties; + + if (ext.minSubgroupSize == ext.maxSubgroupSize) { + return 0; + } + + // At the moment, only Intel devices support varying subgroup sizes and 16, which is the + // next value after the minimum of 8, is the sweet spot according to [1]. Hence the + // following heuristics, which may need to be adjusted in the future for other + // architectures, or if a specific API is added to let client code select the size.. + // + // [1] https://bugs.freedesktop.org/show_bug.cgi?id=108875 + uint32_t subgroupSize = ext.minSubgroupSize * 2; + if (subgroupSize <= ext.maxSubgroupSize) { + return subgroupSize; + } else { + return ext.minSubgroupSize; + } + } + void Device::GatherQueueFromDevice() { fn.GetDeviceQueue(mVkDevice, mQueueFamily, 0, &mQueue); } + void Device::InitTogglesFromDriver() { + // TODO(jiawei.shao@intel.com): tighten this workaround when this issue is fixed in both + // Vulkan SPEC and drivers. + SetToggle(Toggle::UseTemporaryBufferInCompressedTextureToTextureCopy, true); + + // By default try to use D32S8 for Depth24PlusStencil8 + SetToggle(Toggle::VulkanUseD32S8, true); + } + + void Device::ApplyDepth24PlusS8Toggle() { + VkPhysicalDevice physicalDevice = ToBackend(GetAdapter())->GetPhysicalDevice(); + + bool supportsD32s8 = false; + { + VkFormatProperties properties; + fn.GetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_D32_SFLOAT_S8_UINT, + &properties); + supportsD32s8 = + properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + + bool supportsD24s8 = false; + { + VkFormatProperties properties; + fn.GetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_D24_UNORM_S8_UINT, + &properties); + supportsD24s8 = + properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + } + + ASSERT(supportsD32s8 || supportsD24s8); + + if (!supportsD24s8) { + ForceSetToggle(Toggle::VulkanUseD32S8, true); + } + if (!supportsD32s8) { + ForceSetToggle(Toggle::VulkanUseD32S8, false); + } + } + VulkanFunctions* Device::GetMutableFunctions() { return const_cast(&fn); } - VkFence Device::GetUnusedFence() { + ResultOrError Device::GetUnusedFence() { if (!mUnusedFences.empty()) { VkFence fence = mUnusedFences.back(); + DAWN_TRY(CheckVkSuccess(fn.ResetFences(mVkDevice, 1, &*fence), "vkResetFences")); + mUnusedFences.pop_back(); return fence; } @@ -412,96 +485,98 @@ namespace dawn_native { namespace vulkan { createInfo.flags = 0; VkFence fence = VK_NULL_HANDLE; - if (fn.CreateFence(mVkDevice, &createInfo, nullptr, &fence) != VK_SUCCESS) { - ASSERT(false); - } + DAWN_TRY(CheckVkSuccess(fn.CreateFence(mVkDevice, &createInfo, nullptr, &*fence), + "vkCreateFence")); return fence; } - void Device::CheckPassedFences() { + Serial Device::CheckAndUpdateCompletedSerials() { + Serial fenceSerial = 0; while (!mFencesInFlight.empty()) { VkFence fence = mFencesInFlight.front().first; - Serial fenceSerial = mFencesInFlight.front().second; - - VkResult result = fn.GetFenceStatus(mVkDevice, fence); + Serial tentativeSerial = mFencesInFlight.front().second; + VkResult result = VkResult::WrapUnsafe( + INJECT_ERROR_OR_RUN(fn.GetFenceStatus(mVkDevice, fence), VK_ERROR_DEVICE_LOST)); + // TODO: Handle DeviceLost error. ASSERT(result == VK_SUCCESS || result == VK_NOT_READY); // Fence are added in order, so we can stop searching as soon // as we see one that's not ready. if (result == VK_NOT_READY) { - return; + return fenceSerial; } + // Update fenceSerial since fence is ready. + fenceSerial = tentativeSerial; - if (fn.ResetFences(mVkDevice, 1, &fence) != VK_SUCCESS) { - ASSERT(false); - } mUnusedFences.push_back(fence); + ASSERT(fenceSerial > GetCompletedCommandSerial()); mFencesInFlight.pop(); - - ASSERT(fenceSerial > mCompletedSerial); - mCompletedSerial = fenceSerial; } + return fenceSerial; } - Device::CommandPoolAndBuffer Device::GetUnusedCommands() { + MaybeError Device::PrepareRecordingContext() { + ASSERT(!mRecordingContext.used); + ASSERT(mRecordingContext.commandBuffer == VK_NULL_HANDLE); + ASSERT(mRecordingContext.commandPool == VK_NULL_HANDLE); + + // First try to recycle unused command pools. if (!mUnusedCommands.empty()) { CommandPoolAndBuffer commands = mUnusedCommands.back(); mUnusedCommands.pop_back(); - return commands; - } - - CommandPoolAndBuffer commands; - - VkCommandPoolCreateInfo createInfo; - createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - createInfo.pNext = nullptr; - createInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; - createInfo.queueFamilyIndex = mQueueFamily; - - if (fn.CreateCommandPool(mVkDevice, &createInfo, nullptr, &commands.pool) != VK_SUCCESS) { - ASSERT(false); + DAWN_TRY(CheckVkSuccess(fn.ResetCommandPool(mVkDevice, commands.pool, 0), + "vkResetCommandPool")); + + mRecordingContext.commandBuffer = commands.commandBuffer; + mRecordingContext.commandPool = commands.pool; + } else { + // Create a new command pool for our commands and allocate the command buffer. + VkCommandPoolCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; + createInfo.queueFamilyIndex = mQueueFamily; + + DAWN_TRY(CheckVkSuccess(fn.CreateCommandPool(mVkDevice, &createInfo, nullptr, + &*mRecordingContext.commandPool), + "vkCreateCommandPool")); + + VkCommandBufferAllocateInfo allocateInfo; + allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + allocateInfo.pNext = nullptr; + allocateInfo.commandPool = mRecordingContext.commandPool; + allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + allocateInfo.commandBufferCount = 1; + + DAWN_TRY(CheckVkSuccess(fn.AllocateCommandBuffers(mVkDevice, &allocateInfo, + &mRecordingContext.commandBuffer), + "vkAllocateCommandBuffers")); } - VkCommandBufferAllocateInfo allocateInfo; - allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - allocateInfo.pNext = nullptr; - allocateInfo.commandPool = commands.pool; - allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - allocateInfo.commandBufferCount = 1; - - if (fn.AllocateCommandBuffers(mVkDevice, &allocateInfo, &commands.commandBuffer) != - VK_SUCCESS) { - ASSERT(false); - } + // Start the recording of commands in the command buffer. + VkCommandBufferBeginInfo beginInfo; + beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + beginInfo.pNext = nullptr; + beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + beginInfo.pInheritanceInfo = nullptr; - return commands; + return CheckVkSuccess(fn.BeginCommandBuffer(mRecordingContext.commandBuffer, &beginInfo), + "vkBeginCommandBuffer"); } void Device::RecycleCompletedCommands() { - for (auto& commands : mCommandsInFlight.IterateUpTo(mCompletedSerial)) { - if (fn.ResetCommandPool(mVkDevice, commands.pool, 0) != VK_SUCCESS) { - ASSERT(false); - } + for (auto& commands : mCommandsInFlight.IterateUpTo(GetCompletedCommandSerial())) { mUnusedCommands.push_back(commands); } - mCommandsInFlight.ClearUpTo(mCompletedSerial); - } - - void Device::FreeCommands(CommandPoolAndBuffer* commands) { - if (commands->pool != VK_NULL_HANDLE) { - fn.DestroyCommandPool(mVkDevice, commands->pool, nullptr); - commands->pool = VK_NULL_HANDLE; - } - - // Command buffers are implicitly destroyed when the command pool is. - commands->commandBuffer = VK_NULL_HANDLE; + mCommandsInFlight.ClearUpTo(GetCompletedCommandSerial()); } ResultOrError> Device::CreateStagingBuffer(size_t size) { std::unique_ptr stagingBuffer = std::make_unique(size, this); + DAWN_TRY(stagingBuffer->Initialize()); return std::move(stagingBuffer); } @@ -510,26 +585,331 @@ namespace dawn_native { namespace vulkan { BufferBase* destination, uint64_t destinationOffset, uint64_t size) { - // Insert memory barrier to ensure host write operations are made visible before - // copying from the staging buffer. However, this barrier can be removed (see note below). - // - // Note: Depending on the spec understanding, an explicit barrier may not be required when - // used with HOST_COHERENT as vkQueueSubmit does an implicit barrier between host and - // device. See "Availability, Visibility, and Domain Operations" in Vulkan spec for details. + // It is a validation error to do a 0-sized copy in Vulkan, check it is skipped prior to + // calling this function. + ASSERT(size != 0); + + CommandRecordingContext* recordingContext = GetPendingRecordingContext(); + + ToBackend(destination) + ->EnsureDataInitializedAsDestination(recordingContext, destinationOffset, size); + + // There is no need of a barrier to make host writes available and visible to the copy + // operation for HOST_COHERENT memory. The Vulkan spec for vkQueueSubmit describes that it + // does an implicit availability, visibility and domain operation. // Insert pipeline barrier to ensure correct ordering with previous memory operations on the // buffer. - ToBackend(destination) - ->TransitionUsageNow(GetPendingCommandBuffer(), dawn::BufferUsageBit::TransferDst); + ToBackend(destination)->TransitionUsageNow(recordingContext, wgpu::BufferUsage::CopyDst); VkBufferCopy copy; copy.srcOffset = sourceOffset; copy.dstOffset = destinationOffset; copy.size = size; - this->fn.CmdCopyBuffer(GetPendingCommandBuffer(), ToBackend(source)->GetBufferHandle(), + this->fn.CmdCopyBuffer(recordingContext->commandBuffer, + ToBackend(source)->GetBufferHandle(), ToBackend(destination)->GetHandle(), 1, ©); return {}; } + + MaybeError Device::CopyFromStagingToTexture(StagingBufferBase* source, + const TextureDataLayout& src, + TextureCopy* dst, + const Extent3D copySize) { + // There is no need of a barrier to make host writes available and visible to the copy + // operation for HOST_COHERENT memory. The Vulkan spec for vkQueueSubmit describes that it + // does an implicit availability, visibility and domain operation. + + CommandRecordingContext* recordingContext = GetPendingRecordingContext(); + + VkBufferImageCopy region = ComputeBufferImageCopyRegion(src, *dst, copySize); + VkImageSubresourceLayers subresource = region.imageSubresource; + + ASSERT(dst->texture->GetDimension() == wgpu::TextureDimension::e2D); + SubresourceRange range = {subresource.mipLevel, 1, subresource.baseArrayLayer, + subresource.layerCount}; + if (IsCompleteSubresourceCopiedTo(dst->texture.Get(), copySize, subresource.mipLevel)) { + // Since texture has been overwritten, it has been "initialized" + dst->texture->SetIsSubresourceContentInitialized(true, range); + } else { + ToBackend(dst->texture)->EnsureSubresourceContentInitialized(recordingContext, range); + } + // Insert pipeline barrier to ensure correct ordering with previous memory operations on the + // texture. + ToBackend(dst->texture) + ->TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range); + VkImage dstImage = ToBackend(dst->texture)->GetHandle(); + + // Dawn guarantees dstImage be in the TRANSFER_DST_OPTIMAL layout after the + // copy command. + this->fn.CmdCopyBufferToImage(recordingContext->commandBuffer, + ToBackend(source)->GetBufferHandle(), dstImage, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + return {}; + } + + MaybeError Device::ImportExternalImage(const ExternalImageDescriptor* descriptor, + ExternalMemoryHandle memoryHandle, + VkImage image, + const std::vector& waitHandles, + VkSemaphore* outSignalSemaphore, + VkDeviceMemory* outAllocation, + std::vector* outWaitSemaphores) { + const TextureDescriptor* textureDescriptor = + reinterpret_cast(descriptor->cTextureDescriptor); + + // TODO(dawn:22): Remove once migration from GPUTextureDescriptor.arrayLayerCount to + // GPUTextureDescriptor.size.depth is done. + TextureDescriptor fixedDescriptor; + DAWN_TRY_ASSIGN(fixedDescriptor, FixTextureDescriptor(this, textureDescriptor)); + textureDescriptor = &fixedDescriptor; + + // Check services support this combination of handle type / image info + if (!mExternalSemaphoreService->Supported()) { + return DAWN_VALIDATION_ERROR("External semaphore usage not supported"); + } + if (!mExternalMemoryService->SupportsImportMemory( + VulkanImageFormat(this, textureDescriptor->format), VK_IMAGE_TYPE_2D, + VK_IMAGE_TILING_OPTIMAL, + VulkanImageUsage(textureDescriptor->usage, + GetValidInternalFormat(textureDescriptor->format)), + VK_IMAGE_CREATE_ALIAS_BIT_KHR)) { + return DAWN_VALIDATION_ERROR("External memory usage not supported"); + } + + // Create an external semaphore to signal when the texture is done being used + DAWN_TRY_ASSIGN(*outSignalSemaphore, + mExternalSemaphoreService->CreateExportableSemaphore()); + + // Import the external image's memory + external_memory::MemoryImportParams importParams; + DAWN_TRY_ASSIGN(importParams, + mExternalMemoryService->GetMemoryImportParams(descriptor, image)); + DAWN_TRY_ASSIGN(*outAllocation, + mExternalMemoryService->ImportMemory(memoryHandle, importParams, image)); + + // Import semaphores we have to wait on before using the texture + for (const ExternalSemaphoreHandle& handle : waitHandles) { + VkSemaphore semaphore = VK_NULL_HANDLE; + DAWN_TRY_ASSIGN(semaphore, mExternalSemaphoreService->ImportSemaphore(handle)); + outWaitSemaphores->push_back(semaphore); + } + + return {}; + } + + MaybeError Device::SignalAndExportExternalTexture(Texture* texture, + ExternalSemaphoreHandle* outHandle) { + DAWN_TRY(ValidateObject(texture)); + + VkSemaphore outSignalSemaphore; + DAWN_TRY(texture->SignalAndDestroy(&outSignalSemaphore)); + + // This has to happen right after SignalAndDestroy, since the semaphore will be + // deleted when the fenced deleter runs after the queue submission + DAWN_TRY_ASSIGN(*outHandle, mExternalSemaphoreService->ExportSemaphore(outSignalSemaphore)); + + return {}; + } + + TextureBase* Device::CreateTextureWrappingVulkanImage( + const ExternalImageDescriptor* descriptor, + ExternalMemoryHandle memoryHandle, + const std::vector& waitHandles) { + const TextureDescriptor* textureDescriptor = + reinterpret_cast(descriptor->cTextureDescriptor); + + // Initial validation + if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) { + return nullptr; + } + if (ConsumedError(ValidateVulkanImageCanBeWrapped(this, textureDescriptor))) { + return nullptr; + } + + VkSemaphore signalSemaphore = VK_NULL_HANDLE; + VkDeviceMemory allocation = VK_NULL_HANDLE; + std::vector waitSemaphores; + waitSemaphores.reserve(waitHandles.size()); + + // Cleanup in case of a failure, the image creation doesn't acquire the external objects + // if a failure happems. + Texture* result = nullptr; + // TODO(crbug.com/1026480): Consolidate this into a single CreateFromExternal call. + if (ConsumedError(Texture::CreateFromExternal(this, descriptor, textureDescriptor, + mExternalMemoryService.get()), + &result) || + ConsumedError(ImportExternalImage(descriptor, memoryHandle, result->GetHandle(), + waitHandles, &signalSemaphore, &allocation, + &waitSemaphores)) || + ConsumedError(result->BindExternalMemory(descriptor, signalSemaphore, allocation, + waitSemaphores))) { + // Delete the Texture if it was created + if (result != nullptr) { + result->Release(); + } + + // Clear the signal semaphore + fn.DestroySemaphore(GetVkDevice(), signalSemaphore, nullptr); + + // Clear image memory + fn.FreeMemory(GetVkDevice(), allocation, nullptr); + + // Clear any wait semaphores we were able to import + for (VkSemaphore semaphore : waitSemaphores) { + fn.DestroySemaphore(GetVkDevice(), semaphore, nullptr); + } + return nullptr; + } + + return result; + } + + ResultOrError Device::AllocateMemory( + VkMemoryRequirements requirements, + bool mappable) { + return mResourceMemoryAllocator->Allocate(requirements, mappable); + } + + void Device::DeallocateMemory(ResourceMemoryAllocation* allocation) { + mResourceMemoryAllocator->Deallocate(allocation); + } + + int Device::FindBestMemoryTypeIndex(VkMemoryRequirements requirements, bool mappable) { + return mResourceMemoryAllocator->FindBestTypeIndex(requirements, mappable); + } + + ResourceMemoryAllocator* Device::GetResourceMemoryAllocatorForTesting() const { + return mResourceMemoryAllocator.get(); + } + + uint32_t Device::GetComputeSubgroupSize() const { + return mComputeSubgroupSize; + } + + MaybeError Device::WaitForIdleForDestruction() { + // Immediately tag the recording context as unused so we don't try to submit it in Tick. + // Move the mRecordingContext.used to mUnusedCommands so it can be cleaned up in + // ShutDownImpl + if (mRecordingContext.used) { + CommandPoolAndBuffer commands = {mRecordingContext.commandPool, + mRecordingContext.commandBuffer}; + mUnusedCommands.push_back(commands); + mRecordingContext = CommandRecordingContext(); + } + + VkResult waitIdleResult = VkResult::WrapUnsafe(fn.QueueWaitIdle(mQueue)); + // Ignore the result of QueueWaitIdle: it can return OOM which we can't really do anything + // about, Device lost, which means workloads running on the GPU are no longer accessible + // (so they are as good as waited on) or success. + DAWN_UNUSED(waitIdleResult); + + // Make sure all fences are complete by explicitly waiting on them all + while (!mFencesInFlight.empty()) { + VkFence fence = mFencesInFlight.front().first; + Serial fenceSerial = mFencesInFlight.front().second; + ASSERT(fenceSerial > GetCompletedCommandSerial()); + + VkResult result = VkResult::WrapUnsafe(VK_TIMEOUT); + do { + result = VkResult::WrapUnsafe( + INJECT_ERROR_OR_RUN(fn.WaitForFences(mVkDevice, 1, &*fence, true, UINT64_MAX), + VK_ERROR_DEVICE_LOST)); + } while (result == VK_TIMEOUT); + + // TODO: Handle errors + ASSERT(result == VK_SUCCESS); + fn.DestroyFence(mVkDevice, fence, nullptr); + + mFencesInFlight.pop(); + } + return {}; + } + + void Device::ShutDownImpl() { + ASSERT(GetState() == State::Disconnected); + + // We failed during initialization so early that we don't even have a VkDevice. There is + // nothing to do. + if (mVkDevice == VK_NULL_HANDLE) { + return; + } + + // The deleter is the second thing we initialize. If it is not present, it means that + // only the VkDevice was created and nothing else. Destroy the device and do nothing else + // because the function pointers might not have been loaded (and there is nothing to + // destroy anyway). + if (mDeleter == nullptr) { + fn.DestroyDevice(mVkDevice, nullptr); + mVkDevice = VK_NULL_HANDLE; + return; + } + + // Enough of the Device's initialization happened that we can now do regular robust + // deinitialization. + + // Immediately tag the recording context as unused so we don't try to submit it in Tick. + mRecordingContext.used = false; + if (mRecordingContext.commandPool != VK_NULL_HANDLE) { + // The VkCommandBuffer memory should be wholly owned by the pool and freed when it is + // destroyed, but that's not the case in some drivers and the leak memory. + // So we call FreeCommandBuffers before DestroyCommandPool to be safe. + // TODO(enga): Only do this on a known list of bad drivers. + fn.FreeCommandBuffers(mVkDevice, mRecordingContext.commandPool, 1, + &mRecordingContext.commandBuffer); + fn.DestroyCommandPool(mVkDevice, mRecordingContext.commandPool, nullptr); + } + + for (VkSemaphore semaphore : mRecordingContext.waitSemaphores) { + fn.DestroySemaphore(mVkDevice, semaphore, nullptr); + } + mRecordingContext.waitSemaphores.clear(); + + for (VkSemaphore semaphore : mRecordingContext.signalSemaphores) { + fn.DestroySemaphore(mVkDevice, semaphore, nullptr); + } + mRecordingContext.signalSemaphores.clear(); + + ASSERT(mCommandsInFlight.Empty()); + for (const CommandPoolAndBuffer& commands : mUnusedCommands) { + // The VkCommandBuffer memory should be wholly owned by the pool and freed when it is + // destroyed, but that's not the case in some drivers and the leak memory. + // So we call FreeCommandBuffers before DestroyCommandPool to be safe. + // TODO(enga): Only do this on a known list of bad drivers. + fn.FreeCommandBuffers(mVkDevice, commands.pool, 1, &commands.commandBuffer); + fn.DestroyCommandPool(mVkDevice, commands.pool, nullptr); + } + mUnusedCommands.clear(); + + for (VkFence fence : mUnusedFences) { + fn.DestroyFence(mVkDevice, fence, nullptr); + } + mUnusedFences.clear(); + + // Releasing the uploader enqueues buffers to be released. + // Call Tick() again to clear them before releasing the deleter. + mResourceMemoryAllocator->Tick(GetCompletedCommandSerial()); + mDeleter->Tick(GetCompletedCommandSerial()); + + // The VkRenderPasses in the cache can be destroyed immediately since all commands referring + // to them are guaranteed to be finished executing. + mRenderPassCache = nullptr; + + // We need handle deleting all child objects by calling Tick() again with a large serial to + // force all operations to look as if they were completed, and delete all objects before + // destroying the Deleter and vkDevice. + ASSERT(mDeleter != nullptr); + mDeleter->Tick(std::numeric_limits::max()); + mDeleter = nullptr; + + // VkQueues are destroyed when the VkDevice is destroyed + // The VkDevice is needed to destroy child objects, so it must be destroyed last after all + // child objects have been deleted. + ASSERT(mVkDevice != VK_NULL_HANDLE); + fn.DestroyDevice(mVkDevice, nullptr); + mVkDevice = VK_NULL_HANDLE; + } + }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/DeviceVk.h b/third_party/dawn/src/dawn_native/vulkan/DeviceVk.h index f241eaa7676..851a1e723e7 100644 --- a/third_party/dawn/src/dawn_native/vulkan/DeviceVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/DeviceVk.h @@ -19,27 +19,32 @@ #include "common/Serial.h" #include "common/SerialQueue.h" +#include "dawn_native/Commands.h" #include "dawn_native/Device.h" +#include "dawn_native/vulkan/CommandRecordingContext.h" #include "dawn_native/vulkan/Forward.h" #include "dawn_native/vulkan/VulkanFunctions.h" #include "dawn_native/vulkan/VulkanInfo.h" +#include "dawn_native/vulkan/external_memory/MemoryService.h" +#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h" + #include #include namespace dawn_native { namespace vulkan { class Adapter; + class BindGroupLayout; class BufferUploader; class FencedDeleter; - class MapRequestTracker; - class MemoryAllocator; class RenderPassCache; + class ResourceMemoryAllocator; class Device : public DeviceBase { public: - Device(Adapter* adapter, const DeviceDescriptor* descriptor); - ~Device(); + static ResultOrError Create(Adapter* adapter, const DeviceDescriptor* descriptor); + ~Device() override; MaybeError Initialize(); @@ -54,21 +59,28 @@ namespace dawn_native { namespace vulkan { BufferUploader* GetBufferUploader() const; FencedDeleter* GetFencedDeleter() const; - MapRequestTracker* GetMapRequestTracker() const; - MemoryAllocator* GetMemoryAllocator() const; RenderPassCache* GetRenderPassCache() const; - VkCommandBuffer GetPendingCommandBuffer(); - Serial GetPendingCommandSerial() const override; - void SubmitPendingCommands(); - void AddWaitSemaphore(VkSemaphore semaphore); + CommandRecordingContext* GetPendingRecordingContext(); + MaybeError SubmitPendingCommands(); + + void EnqueueDeferredDeallocation(BindGroupLayout* bindGroupLayout); + + // Dawn Native API + + TextureBase* CreateTextureWrappingVulkanImage( + const ExternalImageDescriptor* descriptor, + ExternalMemoryHandle memoryHandle, + const std::vector& waitHandles); + + MaybeError SignalAndExportExternalTexture(Texture* texture, + ExternalSemaphoreHandle* outHandle); // Dawn API - CommandBufferBase* CreateCommandBuffer(CommandEncoderBase* encoder) override; + CommandBufferBase* CreateCommandBuffer(CommandEncoder* encoder, + const CommandBufferDescriptor* descriptor) override; - Serial GetCompletedCommandSerial() const final override; - Serial GetLastSubmittedCommandSerial() const final override; - void TickImpl() override; + MaybeError TickImpl() override; ResultOrError> CreateStagingBuffer(size_t size) override; MaybeError CopyFromStagingToBuffer(StagingBufferBase* source, @@ -76,18 +88,38 @@ namespace dawn_native { namespace vulkan { BufferBase* destination, uint64_t destinationOffset, uint64_t size) override; + MaybeError CopyFromStagingToTexture(StagingBufferBase* source, + const TextureDataLayout& src, + TextureCopy* dst, + const Extent3D copySize); + + ResultOrError AllocateMemory(VkMemoryRequirements requirements, + bool mappable); + void DeallocateMemory(ResourceMemoryAllocation* allocation); + + int FindBestMemoryTypeIndex(VkMemoryRequirements requirements, bool mappable); + + ResourceMemoryAllocator* GetResourceMemoryAllocatorForTesting() const; + + // Return the fixed subgroup size to use for compute shaders on this device or 0 if none + // needs to be set. + uint32_t GetComputeSubgroupSize() const; private: + Device(Adapter* adapter, const DeviceDescriptor* descriptor); + ResultOrError CreateBindGroupImpl( const BindGroupDescriptor* descriptor) override; ResultOrError CreateBindGroupLayoutImpl( const BindGroupLayoutDescriptor* descriptor) override; - ResultOrError CreateBufferImpl(const BufferDescriptor* descriptor) override; + ResultOrError> CreateBufferImpl( + const BufferDescriptor* descriptor) override; ResultOrError CreateComputePipelineImpl( const ComputePipelineDescriptor* descriptor) override; ResultOrError CreatePipelineLayoutImpl( const PipelineLayoutDescriptor* descriptor) override; - ResultOrError CreateQueueImpl() override; + ResultOrError CreateQuerySetImpl( + const QuerySetDescriptor* descriptor) override; ResultOrError CreateRenderPipelineImpl( const RenderPipelineDescriptor* descriptor) override; ResultOrError CreateSamplerImpl(const SamplerDescriptor* descriptor) override; @@ -95,7 +127,12 @@ namespace dawn_native { namespace vulkan { const ShaderModuleDescriptor* descriptor) override; ResultOrError CreateSwapChainImpl( const SwapChainDescriptor* descriptor) override; - ResultOrError CreateTextureImpl(const TextureDescriptor* descriptor) override; + ResultOrError CreateSwapChainImpl( + Surface* surface, + NewSwapChainBase* previousSwapChain, + const SwapChainDescriptor* descriptor) override; + ResultOrError> CreateTextureImpl( + const TextureDescriptor* descriptor) override; ResultOrError CreateTextureViewImpl( TextureBase* texture, const TextureViewDescriptor* descriptor) override; @@ -103,6 +140,13 @@ namespace dawn_native { namespace vulkan { ResultOrError CreateDevice(VkPhysicalDevice physicalDevice); void GatherQueueFromDevice(); + uint32_t FindComputeSubgroupSize() const; + void InitTogglesFromDriver(); + void ApplyDepth24PlusS8Toggle(); + + void ShutDownImpl() override; + MaybeError WaitForIdleForDestruction() override; + // To make it easier to use fn it is a public const member. However // the Device is allowed to mutate them through these private methods. VulkanFunctions* GetMutableFunctions(); @@ -111,39 +155,49 @@ namespace dawn_native { namespace vulkan { VkDevice mVkDevice = VK_NULL_HANDLE; uint32_t mQueueFamily = 0; VkQueue mQueue = VK_NULL_HANDLE; + uint32_t mComputeSubgroupSize = 0; + SerialQueue> mBindGroupLayoutsPendingDeallocation; std::unique_ptr mDeleter; - std::unique_ptr mMapRequestTracker; - std::unique_ptr mMemoryAllocator; + std::unique_ptr mResourceMemoryAllocator; std::unique_ptr mRenderPassCache; - VkFence GetUnusedFence(); - void CheckPassedFences(); + std::unique_ptr mExternalMemoryService; + std::unique_ptr mExternalSemaphoreService; + + ResultOrError GetUnusedFence(); + Serial CheckAndUpdateCompletedSerials() override; // We track which operations are in flight on the GPU with an increasing serial. // This works only because we have a single queue. Each submit to a queue is associated // to a serial and a fence, such that when the fence is "ready" we know the operations // have finished. std::queue> mFencesInFlight; + // Fences in the unused list aren't reset yet. std::vector mUnusedFences; - Serial mCompletedSerial = 0; - Serial mLastSubmittedSerial = 0; + + MaybeError PrepareRecordingContext(); + void RecycleCompletedCommands(); struct CommandPoolAndBuffer { VkCommandPool pool = VK_NULL_HANDLE; VkCommandBuffer commandBuffer = VK_NULL_HANDLE; }; - - CommandPoolAndBuffer GetUnusedCommands(); - void RecycleCompletedCommands(); - void FreeCommands(CommandPoolAndBuffer* commands); - SerialQueue mCommandsInFlight; + // Command pools in the unused list haven't been reset yet. std::vector mUnusedCommands; - CommandPoolAndBuffer mPendingCommands; - std::vector mWaitSemaphores; + // There is always a valid recording context stored in mRecordingContext + CommandRecordingContext mRecordingContext; + + MaybeError ImportExternalImage(const ExternalImageDescriptor* descriptor, + ExternalMemoryHandle memoryHandle, + VkImage image, + const std::vector& waitHandles, + VkSemaphore* outSignalSemaphore, + VkDeviceMemory* outAllocation, + std::vector* outWaitSemaphores); }; }} // namespace dawn_native::vulkan -#endif // DAWNNATIVE_VULKAN_DEVICEVK_H_ +#endif // DAWNNATIVE_VULKAN_DEVICEVK_H_ \ No newline at end of file diff --git a/third_party/dawn/src/dawn_native/vulkan/ExternalHandle.h b/third_party/dawn/src/dawn_native/vulkan/ExternalHandle.h new file mode 100644 index 00000000000..45206b36f6d --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/ExternalHandle.h @@ -0,0 +1,26 @@ +#ifndef DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_ +#define DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_ + +#include "common/vulkan_platform.h" + +namespace dawn_native { namespace vulkan { + +#if DAWN_PLATFORM_LINUX + // File descriptor + using ExternalMemoryHandle = int; + // File descriptor + using ExternalSemaphoreHandle = int; +#elif DAWN_PLATFORM_FUCHSIA + // Really a Zircon vmo handle. + using ExternalMemoryHandle = zx_handle_t; + // Really a Zircon event handle. + using ExternalSemaphoreHandle = zx_handle_t; +#else + // Generic types so that the Null service can compile, not used for real handles + using ExternalMemoryHandle = void*; + using ExternalSemaphoreHandle = void*; +#endif + +}} // namespace dawn_native::vulkan + +#endif // DAWNNATIVE_VULKAN_EXTERNALHANDLE_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/Forward.h b/third_party/dawn/src/dawn_native/vulkan/Forward.h index 344678a20c2..e11a74fe4aa 100644 --- a/third_party/dawn/src/dawn_native/vulkan/Forward.h +++ b/third_party/dawn/src/dawn_native/vulkan/Forward.h @@ -27,8 +27,10 @@ namespace dawn_native { namespace vulkan { class ComputePipeline; class Device; class PipelineLayout; + class QuerySet; class Queue; class RenderPipeline; + class ResourceHeap; class Sampler; class ShaderModule; class StagingBuffer; @@ -45,8 +47,10 @@ namespace dawn_native { namespace vulkan { using ComputePipelineType = ComputePipeline; using DeviceType = Device; using PipelineLayoutType = PipelineLayout; + using QuerySetType = QuerySet; using QueueType = Queue; using RenderPipelineType = RenderPipeline; + using ResourceHeapType = ResourceHeap; using SamplerType = Sampler; using ShaderModuleType = ShaderModule; using StagingBufferType = StagingBuffer; diff --git a/third_party/dawn/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp b/third_party/dawn/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp index 63b8ffbc44f..54dd06614a8 100644 --- a/third_party/dawn/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/NativeSwapChainImplVk.cpp @@ -24,26 +24,51 @@ namespace dawn_native { namespace vulkan { namespace { + bool chooseSwapPresentMode(const std::vector& availablePresentModes, + bool turnOffVsync, + VkPresentModeKHR* presentMode) { + if (turnOffVsync) { + for (const auto& availablePresentMode : availablePresentModes) { + if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { + *presentMode = availablePresentMode; + return true; + } + } + return false; + } + + *presentMode = VK_PRESENT_MODE_FIFO_KHR; + return true; + } + bool ChooseSurfaceConfig(const VulkanSurfaceInfo& info, - NativeSwapChainImpl::ChosenConfig* config) { + NativeSwapChainImpl::ChosenConfig* config, + bool turnOffVsync) { + VkPresentModeKHR presentMode; + if (!chooseSwapPresentMode(info.presentModes, turnOffVsync, &presentMode)) { + return false; + } // TODO(cwallez@chromium.org): For now this is hardcoded to what works with one NVIDIA // driver. Need to generalize config->nativeFormat = VK_FORMAT_B8G8R8A8_UNORM; config->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; - config->format = dawn::TextureFormat::B8G8R8A8Unorm; + config->format = wgpu::TextureFormat::BGRA8Unorm; config->minImageCount = 3; // TODO(cwallez@chromium.org): This is upside down compared to what we want, at least // on Linux config->preTransform = info.capabilities.currentTransform; - config->presentMode = VK_PRESENT_MODE_FIFO_KHR; + config->presentMode = presentMode; config->compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + return true; } - } // anonymous namespace NativeSwapChainImpl::NativeSwapChainImpl(Device* device, VkSurfaceKHR surface) : mSurface(surface), mDevice(device) { + // Call this immediately, so that BackendBinding::GetPreferredSwapChainTextureFormat + // will return a correct result before a SwapChain is created. + UpdateSurfaceConfig(); } NativeSwapChainImpl::~NativeSwapChainImpl() { @@ -57,30 +82,37 @@ namespace dawn_native { namespace vulkan { } } - void NativeSwapChainImpl::Init(DawnWSIContextVulkan* /*context*/) { - if (mDevice->ConsumedError( - GatherSurfaceInfo(*ToBackend(mDevice->GetAdapter()), mSurface, &mInfo))) { + void NativeSwapChainImpl::UpdateSurfaceConfig() { + if (mDevice->ConsumedError(GatherSurfaceInfo(*ToBackend(mDevice->GetAdapter()), mSurface), + &mInfo)) { ASSERT(false); } - if (!ChooseSurfaceConfig(mInfo, &mConfig)) { + if (!ChooseSurfaceConfig(mInfo, &mConfig, mDevice->IsToggleEnabled(Toggle::TurnOffVsync))) { ASSERT(false); } } - DawnSwapChainError NativeSwapChainImpl::Configure(DawnTextureFormat format, - DawnTextureUsageBit usage, + void NativeSwapChainImpl::Init(DawnWSIContextVulkan* /*context*/) { + UpdateSurfaceConfig(); + } + + DawnSwapChainError NativeSwapChainImpl::Configure(WGPUTextureFormat format, + WGPUTextureUsage usage, uint32_t width, uint32_t height) { + UpdateSurfaceConfig(); + ASSERT(mInfo.capabilities.minImageExtent.width <= width); ASSERT(mInfo.capabilities.maxImageExtent.width >= width); ASSERT(mInfo.capabilities.minImageExtent.height <= height); ASSERT(mInfo.capabilities.maxImageExtent.height >= height); - ASSERT(format == static_cast(GetPreferredFormat())); + ASSERT(format == static_cast(GetPreferredFormat())); // TODO(cwallez@chromium.org): need to check usage works too // Create the swapchain with the configuration we chose + VkSwapchainKHR oldSwapchain = mSwapChain; VkSwapchainCreateInfoKHR createInfo; createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; createInfo.pNext = nullptr; @@ -92,8 +124,8 @@ namespace dawn_native { namespace vulkan { createInfo.imageExtent.width = width; createInfo.imageExtent.height = height; createInfo.imageArrayLayers = 1; - createInfo.imageUsage = - VulkanImageUsage(static_cast(usage), mConfig.format); + createInfo.imageUsage = VulkanImageUsage(static_cast(usage), + mDevice->GetValidInternalFormat(mConfig.format)); createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.queueFamilyIndexCount = 0; createInfo.pQueueFamilyIndices = nullptr; @@ -101,10 +133,10 @@ namespace dawn_native { namespace vulkan { createInfo.compositeAlpha = mConfig.compositeAlpha; createInfo.presentMode = mConfig.presentMode; createInfo.clipped = false; - createInfo.oldSwapchain = VK_NULL_HANDLE; + createInfo.oldSwapchain = oldSwapchain; if (mDevice->fn.CreateSwapchainKHR(mDevice->GetVkDevice(), &createInfo, nullptr, - &mSwapChain) != VK_SUCCESS) { + &*mSwapChain) != VK_SUCCESS) { ASSERT(false); } @@ -119,33 +151,12 @@ namespace dawn_native { namespace vulkan { ASSERT(count >= mConfig.minImageCount); mSwapChainImages.resize(count); if (mDevice->fn.GetSwapchainImagesKHR(mDevice->GetVkDevice(), mSwapChain, &count, - mSwapChainImages.data()) != VK_SUCCESS) { + AsVkArray(mSwapChainImages.data())) != VK_SUCCESS) { ASSERT(false); } - // Do the initial layout transition for all these images from an undefined layout to - // present so that it matches the "present" usage after the first GetNextTexture. - VkCommandBuffer commands = mDevice->GetPendingCommandBuffer(); - for (VkImage image : mSwapChainImages) { - VkImageMemoryBarrier barrier; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.pNext = nullptr; - barrier.srcAccessMask = 0; - barrier.dstAccessMask = 0; - barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - barrier.srcQueueFamilyIndex = 0; - barrier.dstQueueFamilyIndex = 0; - barrier.image = image; - barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = 1; - - mDevice->fn.CmdPipelineBarrier(commands, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, - VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, 0, 0, nullptr, 0, - nullptr, 1, &barrier); + if (oldSwapchain != VK_NULL_HANDLE) { + mDevice->GetFencedDeleter()->DeleteWhenUnused(oldSwapchain); } return DAWN_SWAP_CHAIN_NO_ERROR; @@ -161,19 +172,23 @@ namespace dawn_native { namespace vulkan { createInfo.pNext = nullptr; createInfo.flags = 0; if (mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &createInfo, nullptr, - &semaphore) != VK_SUCCESS) { + &*semaphore) != VK_SUCCESS) { ASSERT(false); } } if (mDevice->fn.AcquireNextImageKHR(mDevice->GetVkDevice(), mSwapChain, std::numeric_limits::max(), semaphore, - VK_NULL_HANDLE, &mLastImageIndex) != VK_SUCCESS) { + VkFence{}, &mLastImageIndex) != VK_SUCCESS) { ASSERT(false); } - nextTexture->texture.u64 = mSwapChainImages[mLastImageIndex].GetU64(); - mDevice->AddWaitSemaphore(semaphore); + nextTexture->texture.u64 = +#if defined(DAWN_PLATFORM_64_BIT) + reinterpret_cast +#endif + (*mSwapChainImages[mLastImageIndex]); + mDevice->GetPendingRecordingContext()->waitSemaphores.push_back(semaphore); return DAWN_SWAP_CHAIN_NO_ERROR; } @@ -191,7 +206,7 @@ namespace dawn_native { namespace vulkan { presentInfo.waitSemaphoreCount = 0; presentInfo.pWaitSemaphores = nullptr; presentInfo.swapchainCount = 1; - presentInfo.pSwapchains = &mSwapChain; + presentInfo.pSwapchains = &*mSwapChain; presentInfo.pImageIndices = &mLastImageIndex; presentInfo.pResults = nullptr; @@ -203,7 +218,7 @@ namespace dawn_native { namespace vulkan { return DAWN_SWAP_CHAIN_NO_ERROR; } - dawn::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const { + wgpu::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const { return mConfig.format; } diff --git a/third_party/dawn/src/dawn_native/vulkan/NativeSwapChainImplVk.h b/third_party/dawn/src/dawn_native/vulkan/NativeSwapChainImplVk.h index f5dc27d3f88..fe7a1820f51 100644 --- a/third_party/dawn/src/dawn_native/vulkan/NativeSwapChainImplVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/NativeSwapChainImplVk.h @@ -32,18 +32,18 @@ namespace dawn_native { namespace vulkan { ~NativeSwapChainImpl(); void Init(DawnWSIContextVulkan* context); - DawnSwapChainError Configure(DawnTextureFormat format, - DawnTextureUsageBit, + DawnSwapChainError Configure(WGPUTextureFormat format, + WGPUTextureUsage, uint32_t width, uint32_t height); DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture); DawnSwapChainError Present(); - dawn::TextureFormat GetPreferredFormat() const; + wgpu::TextureFormat GetPreferredFormat() const; struct ChosenConfig { VkFormat nativeFormat; - dawn::TextureFormat format; + wgpu::TextureFormat format; VkColorSpaceKHR colorSpace; VkSurfaceTransformFlagBitsKHR preTransform; uint32_t minImageCount; @@ -52,6 +52,8 @@ namespace dawn_native { namespace vulkan { }; private: + void UpdateSurfaceConfig(); + VkSurfaceKHR mSurface = VK_NULL_HANDLE; VkSwapchainKHR mSwapChain = VK_NULL_HANDLE; std::vector mSwapChainImages; diff --git a/third_party/dawn/src/dawn_native/vulkan/PipelineLayoutVk.cpp b/third_party/dawn/src/dawn_native/vulkan/PipelineLayoutVk.cpp index 4b5615ef3be..80c5aa833e8 100644 --- a/third_party/dawn/src/dawn_native/vulkan/PipelineLayoutVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/PipelineLayoutVk.cpp @@ -14,22 +14,30 @@ #include "dawn_native/vulkan/PipelineLayoutVk.h" +#include "common/BitSetIterator.h" #include "dawn_native/vulkan/BindGroupLayoutVk.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" - -#include "common/BitSetIterator.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { - PipelineLayout::PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor) - : PipelineLayoutBase(device, descriptor) { + // static + ResultOrError PipelineLayout::Create( + Device* device, + const PipelineLayoutDescriptor* descriptor) { + Ref layout = AcquireRef(new PipelineLayout(device, descriptor)); + DAWN_TRY(layout->Initialize()); + return layout.Detach(); + } + + MaybeError PipelineLayout::Initialize() { // Compute the array of VkDescriptorSetLayouts that will be chained in the create info. // TODO(cwallez@chromium.org) Vulkan doesn't allow holes in this array, should we expose // this constraints at the Dawn level? uint32_t numSetLayouts = 0; std::array setLayouts; - for (uint32_t setIndex : IterateBitSet(GetBindGroupLayoutsMask())) { + for (BindGroupIndex setIndex : IterateBitSet(GetBindGroupLayoutsMask())) { setLayouts[numSetLayouts] = ToBackend(GetBindGroupLayout(setIndex))->GetHandle(); numSetLayouts++; } @@ -39,14 +47,14 @@ namespace dawn_native { namespace vulkan { createInfo.pNext = nullptr; createInfo.flags = 0; createInfo.setLayoutCount = numSetLayouts; - createInfo.pSetLayouts = setLayouts.data(); + createInfo.pSetLayouts = AsVkArray(setLayouts.data()); createInfo.pushConstantRangeCount = 0; createInfo.pPushConstantRanges = nullptr; - if (device->fn.CreatePipelineLayout(device->GetVkDevice(), &createInfo, nullptr, - &mHandle) != VK_SUCCESS) { - ASSERT(false); - } + Device* device = ToBackend(GetDevice()); + return CheckVkSuccess( + device->fn.CreatePipelineLayout(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + "CreatePipelineLayout"); } PipelineLayout::~PipelineLayout() { diff --git a/third_party/dawn/src/dawn_native/vulkan/PipelineLayoutVk.h b/third_party/dawn/src/dawn_native/vulkan/PipelineLayoutVk.h index a5072eb5722..fe136e4d0d6 100644 --- a/third_party/dawn/src/dawn_native/vulkan/PipelineLayoutVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/PipelineLayoutVk.h @@ -18,19 +18,24 @@ #include "dawn_native/PipelineLayout.h" #include "common/vulkan_platform.h" +#include "dawn_native/Error.h" namespace dawn_native { namespace vulkan { class Device; - class PipelineLayout : public PipelineLayoutBase { + class PipelineLayout final : public PipelineLayoutBase { public: - PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor); - ~PipelineLayout(); + static ResultOrError Create(Device* device, + const PipelineLayoutDescriptor* descriptor); VkPipelineLayout GetHandle() const; private: + ~PipelineLayout() override; + using PipelineLayoutBase::PipelineLayoutBase; + MaybeError Initialize(); + VkPipelineLayout mHandle = VK_NULL_HANDLE; }; diff --git a/third_party/dawn/src/dawn_native/vulkan/QueueVk.cpp b/third_party/dawn/src/dawn_native/vulkan/QueueVk.cpp index a5451b182b6..99a05909a83 100644 --- a/third_party/dawn/src/dawn_native/vulkan/QueueVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/QueueVk.cpp @@ -14,28 +14,142 @@ #include "dawn_native/vulkan/QueueVk.h" +#include "common/Math.h" +#include "dawn_native/Buffer.h" +#include "dawn_native/CommandValidation.h" +#include "dawn_native/Commands.h" +#include "dawn_native/DynamicUploader.h" #include "dawn_native/vulkan/CommandBufferVk.h" +#include "dawn_native/vulkan/CommandRecordingContext.h" #include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_platform/DawnPlatform.h" +#include "dawn_platform/tracing/TraceEvent.h" namespace dawn_native { namespace vulkan { - Queue::Queue(Device* device) : QueueBase(device) { + namespace { + ResultOrError UploadTextureDataAligningBytesPerRow( + DeviceBase* device, + const void* data, + size_t dataSize, + uint32_t alignedBytesPerRow, + uint32_t optimallyAlignedBytesPerRow, + uint32_t alignedRowsPerImage, + const TextureDataLayout* dataLayout, + const Format& textureFormat, + const Extent3D* writeSize) { + uint32_t newDataSize = ComputeRequiredBytesInCopy( + textureFormat, *writeSize, optimallyAlignedBytesPerRow, alignedRowsPerImage); + + uint64_t optimalOffsetAlignment = + ToBackend(device) + ->GetDeviceInfo() + .properties.limits.optimalBufferCopyOffsetAlignment; + + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, device->GetDynamicUploader()->Allocate( + newDataSize + optimalOffsetAlignment - 1, + device->GetPendingCommandSerial())); + ASSERT(uploadHandle.mappedBuffer != nullptr); + + // TODO(tommek@google.com): Add an optimization to do a single memcpy if the data + // is already correctly packed. + uint8_t* dstPointer = static_cast(uploadHandle.mappedBuffer); + const uint8_t* srcPointer = static_cast(data); + srcPointer += dataLayout->offset; + + uint32_t alignedRowsPerImageInBlock = alignedRowsPerImage / textureFormat.blockHeight; + uint32_t dataRowsPerImageInBlock = dataLayout->rowsPerImage / textureFormat.blockHeight; + if (dataRowsPerImageInBlock == 0) { + dataRowsPerImageInBlock = writeSize->height / textureFormat.blockHeight; + } + + uint64_t additionalOffset = + Align(uploadHandle.startOffset, optimalOffsetAlignment) - uploadHandle.startOffset; + uploadHandle.startOffset += additionalOffset; + dstPointer += additionalOffset; + + ASSERT(dataRowsPerImageInBlock >= alignedRowsPerImageInBlock); + uint64_t imageAdditionalStride = + dataLayout->bytesPerRow * (dataRowsPerImageInBlock - alignedRowsPerImageInBlock); + for (uint32_t d = 0; d < writeSize->depth; ++d) { + for (uint32_t h = 0; h < alignedRowsPerImageInBlock; ++h) { + memcpy(dstPointer, srcPointer, alignedBytesPerRow); + dstPointer += optimallyAlignedBytesPerRow; + srcPointer += dataLayout->bytesPerRow; + } + srcPointer += imageAdditionalStride; + } + + return uploadHandle; + } + } // namespace + + // static + Queue* Queue::Create(Device* device) { + return new Queue(device); } Queue::~Queue() { } - void Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { + MaybeError Queue::SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) { Device* device = ToBackend(GetDevice()); device->Tick(); - VkCommandBuffer commandBuffer = device->GetPendingCommandBuffer(); + TRACE_EVENT_BEGIN0(GetDevice()->GetPlatform(), Recording, + "CommandBufferVk::RecordCommands"); + CommandRecordingContext* recordingContext = device->GetPendingRecordingContext(); for (uint32_t i = 0; i < commandCount; ++i) { - ToBackend(commands[i])->RecordCommands(commandBuffer); + DAWN_TRY(ToBackend(commands[i])->RecordCommands(recordingContext)); } + TRACE_EVENT_END0(GetDevice()->GetPlatform(), Recording, "CommandBufferVk::RecordCommands"); + + DAWN_TRY(device->SubmitPendingCommands()); - device->SubmitPendingCommands(); + return {}; } -}} // namespace dawn_native::vulkan + MaybeError Queue::WriteTextureImpl(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) { + uint32_t blockSize = destination->texture->GetFormat().blockByteSize; + uint32_t blockWidth = destination->texture->GetFormat().blockWidth; + // We are only copying the part of the data that will appear in the texture. + // Note that validating texture copy range ensures that writeSize->width and + // writeSize->height are multiples of blockWidth and blockHeight respectively. + uint32_t alignedBytesPerRow = (writeSize->width) / blockWidth * blockSize; + uint32_t alignedRowsPerImage = writeSize->height; + + uint32_t optimalBytesPerRowAlignment = + ToBackend(GetDevice()) + ->GetDeviceInfo() + .properties.limits.optimalBufferCopyRowPitchAlignment; + uint32_t optimallyAlignedBytesPerRow = + Align(alignedBytesPerRow, optimalBytesPerRowAlignment); + + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN( + uploadHandle, + UploadTextureDataAligningBytesPerRow( + GetDevice(), data, dataSize, alignedBytesPerRow, optimallyAlignedBytesPerRow, + alignedRowsPerImage, dataLayout, destination->texture->GetFormat(), writeSize)); + + TextureDataLayout passDataLayout = *dataLayout; + passDataLayout.offset = uploadHandle.startOffset; + passDataLayout.bytesPerRow = optimallyAlignedBytesPerRow; + passDataLayout.rowsPerImage = alignedRowsPerImage; + + TextureCopy textureCopy; + textureCopy.texture = destination->texture; + textureCopy.mipLevel = destination->mipLevel; + textureCopy.origin = destination->origin; + + return ToBackend(GetDevice()) + ->CopyFromStagingToTexture(uploadHandle.stagingBuffer, passDataLayout, &textureCopy, + *writeSize); + } +}} // namespace dawn_native::vulkan \ No newline at end of file diff --git a/third_party/dawn/src/dawn_native/vulkan/QueueVk.h b/third_party/dawn/src/dawn_native/vulkan/QueueVk.h index 2477c5ae46c..bcfdbda0620 100644 --- a/third_party/dawn/src/dawn_native/vulkan/QueueVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/QueueVk.h @@ -22,15 +22,22 @@ namespace dawn_native { namespace vulkan { class CommandBuffer; class Device; - class Queue : public QueueBase { + class Queue final : public QueueBase { public: - Queue(Device* device); - ~Queue(); + static Queue* Create(Device* device); private: - void SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; + ~Queue() override; + using QueueBase::QueueBase; + + MaybeError SubmitImpl(uint32_t commandCount, CommandBufferBase* const* commands) override; + MaybeError WriteTextureImpl(const TextureCopyView* destination, + const void* data, + size_t dataSize, + const TextureDataLayout* dataLayout, + const Extent3D* writeSize) override; }; }} // namespace dawn_native::vulkan -#endif // DAWNNATIVE_VULKAN_QUEUEVK_H_ +#endif // DAWNNATIVE_VULKAN_QUEUEVK_H_ \ No newline at end of file diff --git a/third_party/dawn/src/dawn_native/vulkan/RenderPassCache.cpp b/third_party/dawn/src/dawn_native/vulkan/RenderPassCache.cpp index daeb5a43ab1..47330f12045 100644 --- a/third_party/dawn/src/dawn_native/vulkan/RenderPassCache.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/RenderPassCache.cpp @@ -18,15 +18,16 @@ #include "common/HashUtils.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/TextureVk.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { namespace { - VkAttachmentLoadOp VulkanAttachmentLoadOp(dawn::LoadOp op) { + VkAttachmentLoadOp VulkanAttachmentLoadOp(wgpu::LoadOp op) { switch (op) { - case dawn::LoadOp::Load: + case wgpu::LoadOp::Load: return VK_ATTACHMENT_LOAD_OP_LOAD; - case dawn::LoadOp::Clear: + case wgpu::LoadOp::Clear: return VK_ATTACHMENT_LOAD_OP_CLEAR; default: UNREACHABLE(); @@ -37,8 +38,8 @@ namespace dawn_native { namespace vulkan { // RenderPassCacheQuery void RenderPassCacheQuery::SetColor(uint32_t index, - dawn::TextureFormat format, - dawn::LoadOp loadOp, + wgpu::TextureFormat format, + wgpu::LoadOp loadOp, bool hasResolveTarget) { colorMask.set(index); colorFormats[index] = format; @@ -46,9 +47,9 @@ namespace dawn_native { namespace vulkan { resolveTargetMask[index] = hasResolveTarget; } - void RenderPassCacheQuery::SetDepthStencil(dawn::TextureFormat format, - dawn::LoadOp depthLoadOp, - dawn::LoadOp stencilLoadOp) { + void RenderPassCacheQuery::SetDepthStencil(wgpu::TextureFormat format, + wgpu::LoadOp depthLoadOp, + wgpu::LoadOp stencilLoadOp) { hasDepthStencil = true; depthStencilFormat = format; this->depthLoadOp = depthLoadOp; @@ -71,18 +72,19 @@ namespace dawn_native { namespace vulkan { mCache.clear(); } - VkRenderPass RenderPassCache::GetRenderPass(const RenderPassCacheQuery& query) { + ResultOrError RenderPassCache::GetRenderPass(const RenderPassCacheQuery& query) { auto it = mCache.find(query); if (it != mCache.end()) { - return it->second; + return VkRenderPass(it->second); } - VkRenderPass renderPass = CreateRenderPassForQuery(query); + VkRenderPass renderPass; + DAWN_TRY_ASSIGN(renderPass, CreateRenderPassForQuery(query)); mCache.emplace(query, renderPass); return renderPass; } - VkRenderPass RenderPassCache::CreateRenderPassForQuery( + ResultOrError RenderPassCache::CreateRenderPassForQuery( const RenderPassCacheQuery& query) const { // The Vulkan subpasses want to know the layout of the attachments with VkAttachmentRef. // Precompute them as they must be pointer-chained in VkSubpassDescription @@ -106,7 +108,7 @@ namespace dawn_native { namespace vulkan { attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachmentDesc.flags = 0; - attachmentDesc.format = VulkanImageFormat(query.colorFormats[i]); + attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]); attachmentDesc.samples = vkSampleCount; attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.colorLoadOp[i]); attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -127,7 +129,7 @@ namespace dawn_native { namespace vulkan { depthStencilAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; attachmentDesc.flags = 0; - attachmentDesc.format = VulkanImageFormat(query.depthStencilFormat); + attachmentDesc.format = VulkanImageFormat(mDevice, query.depthStencilFormat); attachmentDesc.samples = vkSampleCount; attachmentDesc.loadOp = VulkanAttachmentLoadOp(query.depthLoadOp); attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -148,7 +150,7 @@ namespace dawn_native { namespace vulkan { attachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; attachmentDesc.flags = 0; - attachmentDesc.format = VulkanImageFormat(query.colorFormats[i]); + attachmentDesc.format = VulkanImageFormat(mDevice, query.colorFormats[i]); attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT; attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; @@ -189,11 +191,9 @@ namespace dawn_native { namespace vulkan { // Create the render pass from the zillion parameters VkRenderPass renderPass; - if (mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, nullptr, - &renderPass) != VK_SUCCESS) { - ASSERT(false); - } - + DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateRenderPass(mDevice->GetVkDevice(), &createInfo, + nullptr, &*renderPass), + "CreateRenderPass")); return renderPass; } diff --git a/third_party/dawn/src/dawn_native/vulkan/RenderPassCache.h b/third_party/dawn/src/dawn_native/vulkan/RenderPassCache.h index 8410cea0fa0..6af675ba41c 100644 --- a/third_party/dawn/src/dawn_native/vulkan/RenderPassCache.h +++ b/third_party/dawn/src/dawn_native/vulkan/RenderPassCache.h @@ -15,9 +15,9 @@ #ifndef DAWNNATIVE_VULKAN_RENDERPASSCACHE_H_ #define DAWNNATIVE_VULKAN_RENDERPASSCACHE_H_ -#include "common/vulkan_platform.h" - #include "common/Constants.h" +#include "common/vulkan_platform.h" +#include "dawn_native/Error.h" #include "dawn_native/dawn_platform.h" #include @@ -35,23 +35,23 @@ namespace dawn_native { namespace vulkan { // Use these helpers to build the query, they make sure all relevant data is initialized and // masks set. void SetColor(uint32_t index, - dawn::TextureFormat format, - dawn::LoadOp loadOp, + wgpu::TextureFormat format, + wgpu::LoadOp loadOp, bool hasResolveTarget); - void SetDepthStencil(dawn::TextureFormat format, - dawn::LoadOp depthLoadOp, - dawn::LoadOp stencilLoadOp); + void SetDepthStencil(wgpu::TextureFormat format, + wgpu::LoadOp depthLoadOp, + wgpu::LoadOp stencilLoadOp); void SetSampleCount(uint32_t sampleCount); std::bitset colorMask; std::bitset resolveTargetMask; - std::array colorFormats; - std::array colorLoadOp; + std::array colorFormats; + std::array colorLoadOp; bool hasDepthStencil = false; - dawn::TextureFormat depthStencilFormat; - dawn::LoadOp depthLoadOp; - dawn::LoadOp stencilLoadOp; + wgpu::TextureFormat depthStencilFormat; + wgpu::LoadOp depthLoadOp; + wgpu::LoadOp stencilLoadOp; uint32_t sampleCount; }; @@ -66,11 +66,12 @@ namespace dawn_native { namespace vulkan { RenderPassCache(Device* device); ~RenderPassCache(); - VkRenderPass GetRenderPass(const RenderPassCacheQuery& query); + ResultOrError GetRenderPass(const RenderPassCacheQuery& query); private: // Does the actual VkRenderPass creation on a cache miss. - VkRenderPass CreateRenderPassForQuery(const RenderPassCacheQuery& query) const; + ResultOrError CreateRenderPassForQuery( + const RenderPassCacheQuery& query) const; // Implements the functors necessary for to use RenderPassCacheQueries as unordered_map // keys. diff --git a/third_party/dawn/src/dawn_native/vulkan/RenderPipelineVk.cpp b/third_party/dawn/src/dawn_native/vulkan/RenderPipelineVk.cpp index e35ea671729..5e406f3ccd4 100644 --- a/third_party/dawn/src/dawn_native/vulkan/RenderPipelineVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/RenderPipelineVk.cpp @@ -21,192 +21,223 @@ #include "dawn_native/vulkan/ShaderModuleVk.h" #include "dawn_native/vulkan/TextureVk.h" #include "dawn_native/vulkan/UtilsVulkan.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { namespace { - VkVertexInputRate VulkanInputRate(dawn::InputStepMode stepMode) { + VkVertexInputRate VulkanInputRate(wgpu::InputStepMode stepMode) { switch (stepMode) { - case dawn::InputStepMode::Vertex: + case wgpu::InputStepMode::Vertex: return VK_VERTEX_INPUT_RATE_VERTEX; - case dawn::InputStepMode::Instance: + case wgpu::InputStepMode::Instance: return VK_VERTEX_INPUT_RATE_INSTANCE; default: UNREACHABLE(); } } - VkFormat VulkanVertexFormat(dawn::VertexFormat format) { + VkFormat VulkanVertexFormat(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar2: + case wgpu::VertexFormat::UChar2: return VK_FORMAT_R8G8_UINT; - case dawn::VertexFormat::UChar4: + case wgpu::VertexFormat::UChar4: return VK_FORMAT_R8G8B8A8_UINT; - case dawn::VertexFormat::Char2: + case wgpu::VertexFormat::Char2: return VK_FORMAT_R8G8_SINT; - case dawn::VertexFormat::Char4: + case wgpu::VertexFormat::Char4: return VK_FORMAT_R8G8B8A8_SINT; - case dawn::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar2Norm: return VK_FORMAT_R8G8_UNORM; - case dawn::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::UChar4Norm: return VK_FORMAT_R8G8B8A8_UNORM; - case dawn::VertexFormat::Char2Norm: + case wgpu::VertexFormat::Char2Norm: return VK_FORMAT_R8G8_SNORM; - case dawn::VertexFormat::Char4Norm: + case wgpu::VertexFormat::Char4Norm: return VK_FORMAT_R8G8B8A8_SNORM; - case dawn::VertexFormat::UShort2: + case wgpu::VertexFormat::UShort2: return VK_FORMAT_R16G16_UINT; - case dawn::VertexFormat::UShort4: + case wgpu::VertexFormat::UShort4: return VK_FORMAT_R16G16B16A16_UINT; - case dawn::VertexFormat::Short2: + case wgpu::VertexFormat::Short2: return VK_FORMAT_R16G16_SINT; - case dawn::VertexFormat::Short4: + case wgpu::VertexFormat::Short4: return VK_FORMAT_R16G16B16A16_SINT; - case dawn::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort2Norm: return VK_FORMAT_R16G16_UNORM; - case dawn::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::UShort4Norm: return VK_FORMAT_R16G16B16A16_UNORM; - case dawn::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Short2Norm: return VK_FORMAT_R16G16_SNORM; - case dawn::VertexFormat::Short4Norm: + case wgpu::VertexFormat::Short4Norm: return VK_FORMAT_R16G16B16A16_SNORM; - case dawn::VertexFormat::Half2: + case wgpu::VertexFormat::Half2: return VK_FORMAT_R16G16_SFLOAT; - case dawn::VertexFormat::Half4: + case wgpu::VertexFormat::Half4: return VK_FORMAT_R16G16B16A16_SFLOAT; - case dawn::VertexFormat::Float: + case wgpu::VertexFormat::Float: return VK_FORMAT_R32_SFLOAT; - case dawn::VertexFormat::Float2: + case wgpu::VertexFormat::Float2: return VK_FORMAT_R32G32_SFLOAT; - case dawn::VertexFormat::Float3: + case wgpu::VertexFormat::Float3: return VK_FORMAT_R32G32B32_SFLOAT; - case dawn::VertexFormat::Float4: + case wgpu::VertexFormat::Float4: return VK_FORMAT_R32G32B32A32_SFLOAT; - case dawn::VertexFormat::UInt: + case wgpu::VertexFormat::UInt: return VK_FORMAT_R32_UINT; - case dawn::VertexFormat::UInt2: + case wgpu::VertexFormat::UInt2: return VK_FORMAT_R32G32_UINT; - case dawn::VertexFormat::UInt3: + case wgpu::VertexFormat::UInt3: return VK_FORMAT_R32G32B32_UINT; - case dawn::VertexFormat::UInt4: + case wgpu::VertexFormat::UInt4: return VK_FORMAT_R32G32B32A32_UINT; - case dawn::VertexFormat::Int: + case wgpu::VertexFormat::Int: return VK_FORMAT_R32_SINT; - case dawn::VertexFormat::Int2: + case wgpu::VertexFormat::Int2: return VK_FORMAT_R32G32_SINT; - case dawn::VertexFormat::Int3: + case wgpu::VertexFormat::Int3: return VK_FORMAT_R32G32B32_SINT; - case dawn::VertexFormat::Int4: + case wgpu::VertexFormat::Int4: return VK_FORMAT_R32G32B32A32_SINT; default: UNREACHABLE(); } } - VkPrimitiveTopology VulkanPrimitiveTopology(dawn::PrimitiveTopology topology) { + VkPrimitiveTopology VulkanPrimitiveTopology(wgpu::PrimitiveTopology topology) { switch (topology) { - case dawn::PrimitiveTopology::PointList: + case wgpu::PrimitiveTopology::PointList: return VK_PRIMITIVE_TOPOLOGY_POINT_LIST; - case dawn::PrimitiveTopology::LineList: + case wgpu::PrimitiveTopology::LineList: return VK_PRIMITIVE_TOPOLOGY_LINE_LIST; - case dawn::PrimitiveTopology::LineStrip: + case wgpu::PrimitiveTopology::LineStrip: return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP; - case dawn::PrimitiveTopology::TriangleList: + case wgpu::PrimitiveTopology::TriangleList: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - case dawn::PrimitiveTopology::TriangleStrip: + case wgpu::PrimitiveTopology::TriangleStrip: return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; default: UNREACHABLE(); } } - bool ShouldEnablePrimitiveRestart(dawn::PrimitiveTopology topology) { + bool ShouldEnablePrimitiveRestart(wgpu::PrimitiveTopology topology) { // Primitive restart is always enabled in WebGPU but Vulkan validation rules ask that // primitive restart be only enabled on primitive topologies that support restarting. switch (topology) { - case dawn::PrimitiveTopology::PointList: - case dawn::PrimitiveTopology::LineList: - case dawn::PrimitiveTopology::TriangleList: + case wgpu::PrimitiveTopology::PointList: + case wgpu::PrimitiveTopology::LineList: + case wgpu::PrimitiveTopology::TriangleList: return false; - case dawn::PrimitiveTopology::LineStrip: - case dawn::PrimitiveTopology::TriangleStrip: + case wgpu::PrimitiveTopology::LineStrip: + case wgpu::PrimitiveTopology::TriangleStrip: return true; default: UNREACHABLE(); } } - VkBlendFactor VulkanBlendFactor(dawn::BlendFactor factor) { + VkFrontFace VulkanFrontFace(wgpu::FrontFace face) { + switch (face) { + case wgpu::FrontFace::CCW: + return VK_FRONT_FACE_COUNTER_CLOCKWISE; + case wgpu::FrontFace::CW: + return VK_FRONT_FACE_CLOCKWISE; + default: + UNREACHABLE(); + } + } + + VkCullModeFlagBits VulkanCullMode(wgpu::CullMode mode) { + switch (mode) { + case wgpu::CullMode::None: + return VK_CULL_MODE_NONE; + case wgpu::CullMode::Front: + return VK_CULL_MODE_FRONT_BIT; + case wgpu::CullMode::Back: + return VK_CULL_MODE_BACK_BIT; + default: + UNREACHABLE(); + } + } + + VkBlendFactor VulkanBlendFactor(wgpu::BlendFactor factor) { switch (factor) { - case dawn::BlendFactor::Zero: + case wgpu::BlendFactor::Zero: return VK_BLEND_FACTOR_ZERO; - case dawn::BlendFactor::One: + case wgpu::BlendFactor::One: return VK_BLEND_FACTOR_ONE; - case dawn::BlendFactor::SrcColor: + case wgpu::BlendFactor::SrcColor: return VK_BLEND_FACTOR_SRC_COLOR; - case dawn::BlendFactor::OneMinusSrcColor: + case wgpu::BlendFactor::OneMinusSrcColor: return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR; - case dawn::BlendFactor::SrcAlpha: + case wgpu::BlendFactor::SrcAlpha: return VK_BLEND_FACTOR_SRC_ALPHA; - case dawn::BlendFactor::OneMinusSrcAlpha: + case wgpu::BlendFactor::OneMinusSrcAlpha: return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; - case dawn::BlendFactor::DstColor: + case wgpu::BlendFactor::DstColor: return VK_BLEND_FACTOR_DST_COLOR; - case dawn::BlendFactor::OneMinusDstColor: + case wgpu::BlendFactor::OneMinusDstColor: return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR; - case dawn::BlendFactor::DstAlpha: + case wgpu::BlendFactor::DstAlpha: return VK_BLEND_FACTOR_DST_ALPHA; - case dawn::BlendFactor::OneMinusDstAlpha: + case wgpu::BlendFactor::OneMinusDstAlpha: return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; - case dawn::BlendFactor::SrcAlphaSaturated: + case wgpu::BlendFactor::SrcAlphaSaturated: return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE; - case dawn::BlendFactor::BlendColor: + case wgpu::BlendFactor::BlendColor: return VK_BLEND_FACTOR_CONSTANT_COLOR; - case dawn::BlendFactor::OneMinusBlendColor: + case wgpu::BlendFactor::OneMinusBlendColor: return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR; default: UNREACHABLE(); } } - VkBlendOp VulkanBlendOperation(dawn::BlendOperation operation) { + VkBlendOp VulkanBlendOperation(wgpu::BlendOperation operation) { switch (operation) { - case dawn::BlendOperation::Add: + case wgpu::BlendOperation::Add: return VK_BLEND_OP_ADD; - case dawn::BlendOperation::Subtract: + case wgpu::BlendOperation::Subtract: return VK_BLEND_OP_SUBTRACT; - case dawn::BlendOperation::ReverseSubtract: + case wgpu::BlendOperation::ReverseSubtract: return VK_BLEND_OP_REVERSE_SUBTRACT; - case dawn::BlendOperation::Min: + case wgpu::BlendOperation::Min: return VK_BLEND_OP_MIN; - case dawn::BlendOperation::Max: + case wgpu::BlendOperation::Max: return VK_BLEND_OP_MAX; default: UNREACHABLE(); } } - VkColorComponentFlagBits VulkanColorWriteMask(dawn::ColorWriteMask mask) { + VkColorComponentFlags VulkanColorWriteMask(wgpu::ColorWriteMask mask, + bool isDeclaredInFragmentShader) { // Vulkan and Dawn color write masks match, static assert it and return the mask - static_assert(static_cast(dawn::ColorWriteMask::Red) == + static_assert(static_cast(wgpu::ColorWriteMask::Red) == VK_COLOR_COMPONENT_R_BIT, ""); - static_assert(static_cast(dawn::ColorWriteMask::Green) == + static_assert(static_cast(wgpu::ColorWriteMask::Green) == VK_COLOR_COMPONENT_G_BIT, ""); - static_assert(static_cast(dawn::ColorWriteMask::Blue) == + static_assert(static_cast(wgpu::ColorWriteMask::Blue) == VK_COLOR_COMPONENT_B_BIT, ""); - static_assert(static_cast(dawn::ColorWriteMask::Alpha) == + static_assert(static_cast(wgpu::ColorWriteMask::Alpha) == VK_COLOR_COMPONENT_A_BIT, ""); - return static_cast(mask); + // According to Vulkan SPEC (Chapter 14.3): "The input values to blending or color + // attachment writes are undefined for components which do not correspond to a fragment + // shader outputs", we set the color write mask to 0 to prevent such undefined values + // being written into the color attachments. + return isDeclaredInFragmentShader ? static_cast(mask) + : static_cast(0); } - VkPipelineColorBlendAttachmentState ComputeColorDesc( - const ColorStateDescriptor* descriptor) { + VkPipelineColorBlendAttachmentState ComputeColorDesc(const ColorStateDescriptor* descriptor, + bool isDeclaredInFragmentShader) { VkPipelineColorBlendAttachmentState attachment; attachment.blendEnable = BlendEnabled(descriptor) ? VK_TRUE : VK_FALSE; attachment.srcColorBlendFactor = VulkanBlendFactor(descriptor->colorBlend.srcFactor); @@ -215,27 +246,28 @@ namespace dawn_native { namespace vulkan { attachment.srcAlphaBlendFactor = VulkanBlendFactor(descriptor->alphaBlend.srcFactor); attachment.dstAlphaBlendFactor = VulkanBlendFactor(descriptor->alphaBlend.dstFactor); attachment.alphaBlendOp = VulkanBlendOperation(descriptor->alphaBlend.operation); - attachment.colorWriteMask = VulkanColorWriteMask(descriptor->writeMask); + attachment.colorWriteMask = + VulkanColorWriteMask(descriptor->writeMask, isDeclaredInFragmentShader); return attachment; } - VkStencilOp VulkanStencilOp(dawn::StencilOperation op) { + VkStencilOp VulkanStencilOp(wgpu::StencilOperation op) { switch (op) { - case dawn::StencilOperation::Keep: + case wgpu::StencilOperation::Keep: return VK_STENCIL_OP_KEEP; - case dawn::StencilOperation::Zero: + case wgpu::StencilOperation::Zero: return VK_STENCIL_OP_ZERO; - case dawn::StencilOperation::Replace: + case wgpu::StencilOperation::Replace: return VK_STENCIL_OP_REPLACE; - case dawn::StencilOperation::IncrementClamp: + case wgpu::StencilOperation::IncrementClamp: return VK_STENCIL_OP_INCREMENT_AND_CLAMP; - case dawn::StencilOperation::DecrementClamp: + case wgpu::StencilOperation::DecrementClamp: return VK_STENCIL_OP_DECREMENT_AND_CLAMP; - case dawn::StencilOperation::Invert: + case wgpu::StencilOperation::Invert: return VK_STENCIL_OP_INVERT; - case dawn::StencilOperation::IncrementWrap: + case wgpu::StencilOperation::IncrementWrap: return VK_STENCIL_OP_INCREMENT_AND_WRAP; - case dawn::StencilOperation::DecrementWrap: + case wgpu::StencilOperation::DecrementWrap: return VK_STENCIL_OP_DECREMENT_AND_WRAP; default: UNREACHABLE(); @@ -251,7 +283,7 @@ namespace dawn_native { namespace vulkan { // Depth writes only occur if depth is enabled depthStencilState.depthTestEnable = - (descriptor->depthCompare == dawn::CompareFunction::Always && + (descriptor->depthCompare == wgpu::CompareFunction::Always && !descriptor->depthWriteEnabled) ? VK_FALSE : VK_TRUE; @@ -291,8 +323,18 @@ namespace dawn_native { namespace vulkan { } // anonymous namespace - RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor) - : RenderPipelineBase(device, descriptor) { + // static + ResultOrError RenderPipeline::Create( + Device* device, + const RenderPipelineDescriptor* descriptor) { + Ref pipeline = AcquireRef(new RenderPipeline(device, descriptor)); + DAWN_TRY(pipeline->Initialize(descriptor)); + return pipeline.Detach(); + } + + MaybeError RenderPipeline::Initialize(const RenderPipelineDescriptor* descriptor) { + Device* device = ToBackend(GetDevice()); + VkPipelineShaderStageCreateInfo shaderStages[2]; { shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; @@ -300,8 +342,8 @@ namespace dawn_native { namespace vulkan { shaderStages[0].flags = 0; shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shaderStages[0].pSpecializationInfo = nullptr; - shaderStages[0].module = ToBackend(descriptor->vertexStage->module)->GetHandle(); - shaderStages[0].pName = descriptor->vertexStage->entryPoint; + shaderStages[0].module = ToBackend(descriptor->vertexStage.module)->GetHandle(); + shaderStages[0].pName = descriptor->vertexStage.entryPoint; shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shaderStages[1].pNext = nullptr; @@ -312,11 +354,9 @@ namespace dawn_native { namespace vulkan { shaderStages[1].pName = descriptor->fragmentStage->entryPoint; } - std::array mBindings; - std::array mAttributes; - const VertexInputDescriptor* vertexInput = GetVertexInputDescriptor(); + PipelineVertexInputStateCreateInfoTemporaryAllocations tempAllocations; VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo = - ComputeVertexInputDesc(vertexInput, &mBindings, &mAttributes); + ComputeVertexInputDesc(&tempAllocations); VkPipelineInputAssemblyStateCreateInfo inputAssembly; inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; @@ -355,8 +395,8 @@ namespace dawn_native { namespace vulkan { rasterization.depthClampEnable = VK_FALSE; rasterization.rasterizerDiscardEnable = VK_FALSE; rasterization.polygonMode = VK_POLYGON_MODE_FILL; - rasterization.cullMode = VK_CULL_MODE_NONE; - rasterization.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterization.cullMode = VulkanCullMode(GetCullMode()); + rasterization.frontFace = VulkanFrontFace(GetFrontFace()); rasterization.depthBiasEnable = VK_FALSE; rasterization.depthBiasConstantFactor = 0.0f; rasterization.depthBiasClamp = 0.0f; @@ -370,7 +410,12 @@ namespace dawn_native { namespace vulkan { multisample.rasterizationSamples = VulkanSampleCount(GetSampleCount()); multisample.sampleShadingEnable = VK_FALSE; multisample.minSampleShading = 0.0f; - multisample.pSampleMask = nullptr; + // VkPipelineMultisampleStateCreateInfo.pSampleMask is an array of length + // ceil(rasterizationSamples / 32) and since we're passing a single uint32_t + // we have to assert that this length is indeed 1. + ASSERT(multisample.rasterizationSamples <= 32); + VkSampleMask sampleMask = GetSampleMask(); + multisample.pSampleMask = &sampleMask; multisample.alphaToCoverageEnable = VK_FALSE; multisample.alphaToOneEnable = VK_FALSE; @@ -380,9 +425,13 @@ namespace dawn_native { namespace vulkan { // Initialize the "blend state info" that will be chained in the "create info" from the data // pre-computed in the ColorState std::array colorBlendAttachments; + const ShaderModuleBase::FragmentOutputBaseTypes& fragmentOutputBaseTypes = + descriptor->fragmentStage->module->GetFragmentOutputBaseTypes(); for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) { - const ColorStateDescriptor* descriptor = GetColorStateDescriptor(i); - colorBlendAttachments[i] = ComputeColorDesc(descriptor); + const ColorStateDescriptor* colorStateDescriptor = GetColorStateDescriptor(i); + bool isDeclaredInFragmentShader = fragmentOutputBaseTypes[i] != Format::Other; + colorBlendAttachments[i] = + ComputeColorDesc(colorStateDescriptor, isDeclaredInFragmentShader); } VkPipelineColorBlendStateCreateInfo colorBlend; colorBlend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; @@ -421,17 +470,17 @@ namespace dawn_native { namespace vulkan { RenderPassCacheQuery query; for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) { - query.SetColor(i, GetColorAttachmentFormat(i), dawn::LoadOp::Load, false); + query.SetColor(i, GetColorAttachmentFormat(i), wgpu::LoadOp::Load, false); } if (HasDepthStencilAttachment()) { - query.SetDepthStencil(GetDepthStencilFormat(), dawn::LoadOp::Load, - dawn::LoadOp::Load); + query.SetDepthStencil(GetDepthStencilFormat(), wgpu::LoadOp::Load, + wgpu::LoadOp::Load); } query.SetSampleCount(GetSampleCount()); - renderPass = device->GetRenderPassCache()->GetRenderPass(query); + DAWN_TRY_ASSIGN(renderPass, device->GetRenderPassCache()->GetRenderPass(query)); } // The create info chains in a bunch of things created on the stack here or inside state @@ -454,42 +503,41 @@ namespace dawn_native { namespace vulkan { createInfo.layout = ToBackend(GetLayout())->GetHandle(); createInfo.renderPass = renderPass; createInfo.subpass = 0; - createInfo.basePipelineHandle = VK_NULL_HANDLE; + createInfo.basePipelineHandle = VkPipeline{}; createInfo.basePipelineIndex = -1; - if (device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VK_NULL_HANDLE, 1, - &createInfo, nullptr, &mHandle) != VK_SUCCESS) { - ASSERT(false); - } + return CheckVkSuccess( + device->fn.CreateGraphicsPipelines(device->GetVkDevice(), VkPipelineCache{}, 1, + &createInfo, nullptr, &*mHandle), + "CreateGraphicsPipeline"); } VkPipelineVertexInputStateCreateInfo RenderPipeline::ComputeVertexInputDesc( - const VertexInputDescriptor* vertexInput, - std::array* mBindings, - std::array* mAttributes) { + PipelineVertexInputStateCreateInfoTemporaryAllocations* tempAllocations) { // Fill in the "binding info" that will be chained in the create info uint32_t bindingCount = 0; - for (uint32_t i : IterateBitSet(GetInputsSetMask())) { - const auto& bindingInfo = GetInput(i); + for (uint32_t i : IterateBitSet(GetVertexBufferSlotsUsed())) { + const VertexBufferInfo& bindingInfo = GetVertexBuffer(i); - auto& bindingDesc = (*mBindings)[bindingCount]; - bindingDesc.binding = i; - bindingDesc.stride = bindingInfo.stride; - bindingDesc.inputRate = VulkanInputRate(bindingInfo.stepMode); + VkVertexInputBindingDescription* bindingDesc = &tempAllocations->bindings[bindingCount]; + bindingDesc->binding = i; + bindingDesc->stride = bindingInfo.arrayStride; + bindingDesc->inputRate = VulkanInputRate(bindingInfo.stepMode); bindingCount++; } // Fill in the "attribute info" that will be chained in the create info uint32_t attributeCount = 0; - for (uint32_t i : IterateBitSet(GetAttributesSetMask())) { - const auto& attributeInfo = GetAttribute(i); + for (uint32_t i : IterateBitSet(GetAttributeLocationsUsed())) { + const VertexAttributeInfo& attributeInfo = GetAttribute(i); - auto& attributeDesc = (*mAttributes)[attributeCount]; - attributeDesc.location = i; - attributeDesc.binding = attributeInfo.inputSlot; - attributeDesc.format = VulkanVertexFormat(attributeInfo.format); - attributeDesc.offset = attributeInfo.offset; + VkVertexInputAttributeDescription* attributeDesc = + &tempAllocations->attributes[attributeCount]; + attributeDesc->location = i; + attributeDesc->binding = attributeInfo.vertexBufferSlot; + attributeDesc->format = VulkanVertexFormat(attributeInfo.format); + attributeDesc->offset = attributeInfo.offset; attributeCount++; } @@ -500,9 +548,9 @@ namespace dawn_native { namespace vulkan { mCreateInfo.pNext = nullptr; mCreateInfo.flags = 0; mCreateInfo.vertexBindingDescriptionCount = bindingCount; - mCreateInfo.pVertexBindingDescriptions = &(*mBindings)[0]; + mCreateInfo.pVertexBindingDescriptions = tempAllocations->bindings.data(); mCreateInfo.vertexAttributeDescriptionCount = attributeCount; - mCreateInfo.pVertexAttributeDescriptions = &(*mAttributes)[0]; + mCreateInfo.pVertexAttributeDescriptions = tempAllocations->attributes.data(); return mCreateInfo; } diff --git a/third_party/dawn/src/dawn_native/vulkan/RenderPipelineVk.h b/third_party/dawn/src/dawn_native/vulkan/RenderPipelineVk.h index 083c3abb99b..4613f322c35 100644 --- a/third_party/dawn/src/dawn_native/vulkan/RenderPipelineVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/RenderPipelineVk.h @@ -18,23 +18,30 @@ #include "dawn_native/RenderPipeline.h" #include "common/vulkan_platform.h" +#include "dawn_native/Error.h" namespace dawn_native { namespace vulkan { class Device; - class RenderPipeline : public RenderPipelineBase { + class RenderPipeline final : public RenderPipelineBase { public: - RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor); - ~RenderPipeline(); + static ResultOrError Create(Device* device, + const RenderPipelineDescriptor* descriptor); VkPipeline GetHandle() const; private: + ~RenderPipeline() override; + using RenderPipelineBase::RenderPipelineBase; + MaybeError Initialize(const RenderPipelineDescriptor* descriptor); + + struct PipelineVertexInputStateCreateInfoTemporaryAllocations { + std::array bindings; + std::array attributes; + }; VkPipelineVertexInputStateCreateInfo ComputeVertexInputDesc( - const VertexInputDescriptor* vertexInput, - std::array* mBindings, - std::array* mAttributes); + PipelineVertexInputStateCreateInfoTemporaryAllocations* temporaryAllocations); VkPipeline mHandle = VK_NULL_HANDLE; }; diff --git a/third_party/dawn/src/dawn_native/vulkan/ResourceHeapVk.cpp b/third_party/dawn/src/dawn_native/vulkan/ResourceHeapVk.cpp new file mode 100644 index 00000000000..bf3b947bd44 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/ResourceHeapVk.cpp @@ -0,0 +1,31 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/vulkan/ResourceHeapVk.h" + +namespace dawn_native { namespace vulkan { + + ResourceHeap::ResourceHeap(VkDeviceMemory memory, size_t memoryType) + : mMemory(memory), mMemoryType(memoryType) { + } + + VkDeviceMemory ResourceHeap::GetMemory() const { + return mMemory; + } + + size_t ResourceHeap::GetMemoryType() const { + return mMemoryType; + } + +}} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/ResourceHeapVk.h b/third_party/dawn/src/dawn_native/vulkan/ResourceHeapVk.h new file mode 100644 index 00000000000..2bb909b5c8f --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/ResourceHeapVk.h @@ -0,0 +1,39 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_VULKAN_RESOURCEHEAPVK_H_ +#define DAWNNATIVE_VULKAN_RESOURCEHEAPVK_H_ + +#include "common/vulkan_platform.h" +#include "dawn_native/ResourceHeap.h" + +namespace dawn_native { namespace vulkan { + + // Wrapper for physical memory used with or without a resource object. + class ResourceHeap : public ResourceHeapBase { + public: + ResourceHeap(VkDeviceMemory memory, size_t memoryType); + ~ResourceHeap() = default; + + VkDeviceMemory GetMemory() const; + size_t GetMemoryType() const; + + private: + VkDeviceMemory mMemory = VK_NULL_HANDLE; + size_t mMemoryType = 0; + }; + +}} // namespace dawn_native::vulkan + +#endif // DAWNNATIVE_VULKAN_RESOURCEHEAPVK_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp b/third_party/dawn/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp new file mode 100644 index 00000000000..22523a36874 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp @@ -0,0 +1,261 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" + +#include "common/Math.h" +#include "dawn_native/BuddyMemoryAllocator.h" +#include "dawn_native/ResourceHeapAllocator.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/ResourceHeapVk.h" +#include "dawn_native/vulkan/VulkanError.h" + +namespace dawn_native { namespace vulkan { + + namespace { + + // TODO(cwallez@chromium.org): This is a hardcoded heurstic to choose when to + // suballocate but it should ideally depend on the size of the memory heaps and other + // factors. + constexpr uint64_t kMaxSizeForSubAllocation = 4ull * 1024ull * 1024ull; // 4MiB + + // Have each bucket of the buddy system allocate at least some resource of the maximum + // size + constexpr uint64_t kBuddyHeapsSize = 2 * kMaxSizeForSubAllocation; + + } // anonymous namespace + + // SingleTypeAllocator is a combination of a BuddyMemoryAllocator and its client and can + // service suballocation requests, but for a single Vulkan memory type. + + class ResourceMemoryAllocator::SingleTypeAllocator : public ResourceHeapAllocator { + public: + SingleTypeAllocator(Device* device, size_t memoryTypeIndex, VkDeviceSize memoryHeapSize) + : mDevice(device), + mMemoryTypeIndex(memoryTypeIndex), + mMemoryHeapSize(memoryHeapSize), + mBuddySystem( + // Round down to a power of 2 that's <= mMemoryHeapSize. This will always + // be a multiple of kBuddyHeapsSize because kBuddyHeapsSize is a power of 2. + uint64_t(1) << Log2(mMemoryHeapSize), + // Take the min in the very unlikely case the memory heap is tiny. + std::min(uint64_t(1) << Log2(mMemoryHeapSize), kBuddyHeapsSize), + this) { + ASSERT(IsPowerOfTwo(kBuddyHeapsSize)); + } + ~SingleTypeAllocator() override = default; + + ResultOrError AllocateMemory( + const VkMemoryRequirements& requirements) { + return mBuddySystem.Allocate(requirements.size, requirements.alignment); + } + + void DeallocateMemory(const ResourceMemoryAllocation& allocation) { + mBuddySystem.Deallocate(allocation); + } + + // Implementation of the MemoryAllocator interface to be a client of BuddyMemoryAllocator + + ResultOrError> AllocateResourceHeap( + uint64_t size) override { + if (size > mMemoryHeapSize) { + return DAWN_OUT_OF_MEMORY_ERROR("Allocation size too large"); + } + + VkMemoryAllocateInfo allocateInfo; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.pNext = nullptr; + allocateInfo.allocationSize = size; + allocateInfo.memoryTypeIndex = mMemoryTypeIndex; + + VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; + + // First check OOM that we want to surface to the application. + DAWN_TRY(CheckVkOOMThenSuccess( + mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, nullptr, + &*allocatedMemory), + "vkAllocateMemory")); + + ASSERT(allocatedMemory != VK_NULL_HANDLE); + return {std::make_unique(allocatedMemory, mMemoryTypeIndex)}; + } + + void DeallocateResourceHeap(std::unique_ptr allocation) override { + mDevice->GetFencedDeleter()->DeleteWhenUnused(ToBackend(allocation.get())->GetMemory()); + } + + private: + Device* mDevice; + size_t mMemoryTypeIndex; + VkDeviceSize mMemoryHeapSize; + BuddyMemoryAllocator mBuddySystem; + }; + + // Implementation of ResourceMemoryAllocator + + ResourceMemoryAllocator::ResourceMemoryAllocator(Device* device) : mDevice(device) { + const VulkanDeviceInfo& info = mDevice->GetDeviceInfo(); + mAllocatorsPerType.reserve(info.memoryTypes.size()); + + for (size_t i = 0; i < info.memoryTypes.size(); i++) { + mAllocatorsPerType.emplace_back(std::make_unique( + mDevice, i, info.memoryHeaps[info.memoryTypes[i].heapIndex].size)); + } + } + + ResourceMemoryAllocator::~ResourceMemoryAllocator() = default; + + ResultOrError ResourceMemoryAllocator::Allocate( + const VkMemoryRequirements& requirements, + bool mappable) { + // The Vulkan spec guarantees at least on memory type is valid. + int memoryType = FindBestTypeIndex(requirements, mappable); + ASSERT(memoryType >= 0); + + VkDeviceSize size = requirements.size; + + // Sub-allocate non-mappable resources because at the moment the mapped pointer + // is part of the resource and not the heap, which doesn't match the Vulkan model. + // TODO(cwallez@chromium.org): allow sub-allocating mappable resources, maybe. + if (requirements.size < kMaxSizeForSubAllocation && !mappable) { + ResourceMemoryAllocation subAllocation; + DAWN_TRY_ASSIGN(subAllocation, + mAllocatorsPerType[memoryType]->AllocateMemory(requirements)); + if (subAllocation.GetInfo().mMethod != AllocationMethod::kInvalid) { + return std::move(subAllocation); + } + } + + // If sub-allocation failed, allocate memory just for it. + std::unique_ptr resourceHeap; + DAWN_TRY_ASSIGN(resourceHeap, mAllocatorsPerType[memoryType]->AllocateResourceHeap(size)); + + void* mappedPointer = nullptr; + if (mappable) { + DAWN_TRY( + CheckVkSuccess(mDevice->fn.MapMemory(mDevice->GetVkDevice(), + ToBackend(resourceHeap.get())->GetMemory(), 0, + size, 0, &mappedPointer), + "vkMapMemory")); + } + + AllocationInfo info; + info.mMethod = AllocationMethod::kDirect; + return ResourceMemoryAllocation(info, /*offset*/ 0, resourceHeap.release(), + static_cast(mappedPointer)); + } + + void ResourceMemoryAllocator::Deallocate(ResourceMemoryAllocation* allocation) { + switch (allocation->GetInfo().mMethod) { + // Some memory allocation can never be initialized, for example when wrapping + // swapchain VkImages with a Texture. + case AllocationMethod::kInvalid: + break; + + // For direct allocation we can put the memory for deletion immediately and the fence + // deleter will make sure the resources are freed before the memory. + case AllocationMethod::kDirect: { + ResourceHeap* heap = ToBackend(allocation->GetResourceHeap()); + allocation->Invalidate(); + mDevice->GetFencedDeleter()->DeleteWhenUnused(heap->GetMemory()); + delete heap; + break; + } + + // Suballocations aren't freed immediately, otherwise another resource allocation could + // happen just after that aliases the old one and would require a barrier. + // TODO(cwallez@chromium.org): Maybe we can produce the correct barriers to reduce the + // latency to reclaim memory. + case AllocationMethod::kSubAllocated: + mSubAllocationsToDelete.Enqueue(*allocation, mDevice->GetPendingCommandSerial()); + break; + + default: + UNREACHABLE(); + break; + } + + // Invalidate the underlying resource heap in case the client accidentally + // calls DeallocateMemory again using the same allocation. + allocation->Invalidate(); + } + + void ResourceMemoryAllocator::Tick(Serial completedSerial) { + for (const ResourceMemoryAllocation& allocation : + mSubAllocationsToDelete.IterateUpTo(completedSerial)) { + ASSERT(allocation.GetInfo().mMethod == AllocationMethod::kSubAllocated); + size_t memoryType = ToBackend(allocation.GetResourceHeap())->GetMemoryType(); + + mAllocatorsPerType[memoryType]->DeallocateMemory(allocation); + } + + mSubAllocationsToDelete.ClearUpTo(completedSerial); + } + + int ResourceMemoryAllocator::FindBestTypeIndex(VkMemoryRequirements requirements, + bool mappable) { + const VulkanDeviceInfo& info = mDevice->GetDeviceInfo(); + + // Find a suitable memory type for this allocation + int bestType = -1; + for (size_t i = 0; i < info.memoryTypes.size(); ++i) { + // Resource must support this memory type + if ((requirements.memoryTypeBits & (1 << i)) == 0) { + continue; + } + + // Mappable resource must be host visible + if (mappable && + (info.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) { + continue; + } + + // Mappable must also be host coherent. + if (mappable && + (info.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0) { + continue; + } + + // Found the first candidate memory type + if (bestType == -1) { + bestType = static_cast(i); + continue; + } + + // For non-mappable resources, favor device local memory. + if (!mappable) { + if ((info.memoryTypes[bestType].propertyFlags & + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0 && + (info.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) != + 0) { + bestType = static_cast(i); + continue; + } + } + + // All things equal favor the memory in the biggest heap + VkDeviceSize bestTypeHeapSize = + info.memoryHeaps[info.memoryTypes[bestType].heapIndex].size; + VkDeviceSize candidateHeapSize = info.memoryHeaps[info.memoryTypes[i].heapIndex].size; + if (candidateHeapSize > bestTypeHeapSize) { + bestType = static_cast(i); + continue; + } + } + + return bestType; + } + +}} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.h b/third_party/dawn/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.h new file mode 100644 index 00000000000..88f6d4e0c66 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.h @@ -0,0 +1,54 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_VULKAN_RESOURCEMEMORYALLOCATORVK_H_ +#define DAWNNATIVE_VULKAN_RESOURCEMEMORYALLOCATORVK_H_ + +#include "common/SerialQueue.h" +#include "common/vulkan_platform.h" +#include "dawn_native/Error.h" +#include "dawn_native/ResourceMemoryAllocation.h" + +#include +#include + +namespace dawn_native { namespace vulkan { + + class Device; + + class ResourceMemoryAllocator { + public: + ResourceMemoryAllocator(Device* device); + ~ResourceMemoryAllocator(); + + ResultOrError Allocate(const VkMemoryRequirements& requirements, + bool mappable); + void Deallocate(ResourceMemoryAllocation* allocation); + + void Tick(Serial completedSerial); + + int FindBestTypeIndex(VkMemoryRequirements requirements, bool mappable); + + private: + Device* mDevice; + + class SingleTypeAllocator; + std::vector> mAllocatorsPerType; + + SerialQueue mSubAllocationsToDelete; + }; + +}} // namespace dawn_native::vulkan + +#endif // DAWNNATIVE_VULKAN_RESOURCEMEMORYALLOCATORVK_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/SamplerVk.cpp b/third_party/dawn/src/dawn_native/vulkan/SamplerVk.cpp index 7f242d834f2..f2a71288c5f 100644 --- a/third_party/dawn/src/dawn_native/vulkan/SamplerVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/SamplerVk.cpp @@ -17,39 +17,40 @@ #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" #include "dawn_native/vulkan/UtilsVulkan.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { namespace { - VkSamplerAddressMode VulkanSamplerAddressMode(dawn::AddressMode mode) { + VkSamplerAddressMode VulkanSamplerAddressMode(wgpu::AddressMode mode) { switch (mode) { - case dawn::AddressMode::Repeat: + case wgpu::AddressMode::Repeat: return VK_SAMPLER_ADDRESS_MODE_REPEAT; - case dawn::AddressMode::MirroredRepeat: + case wgpu::AddressMode::MirrorRepeat: return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT; - case dawn::AddressMode::ClampToEdge: + case wgpu::AddressMode::ClampToEdge: return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; default: UNREACHABLE(); } } - VkFilter VulkanSamplerFilter(dawn::FilterMode filter) { + VkFilter VulkanSamplerFilter(wgpu::FilterMode filter) { switch (filter) { - case dawn::FilterMode::Linear: + case wgpu::FilterMode::Linear: return VK_FILTER_LINEAR; - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: return VK_FILTER_NEAREST; default: UNREACHABLE(); } } - VkSamplerMipmapMode VulkanMipMapMode(dawn::FilterMode filter) { + VkSamplerMipmapMode VulkanMipMapMode(wgpu::FilterMode filter) { switch (filter) { - case dawn::FilterMode::Linear: + case wgpu::FilterMode::Linear: return VK_SAMPLER_MIPMAP_MODE_LINEAR; - case dawn::FilterMode::Nearest: + case wgpu::FilterMode::Nearest: return VK_SAMPLER_MIPMAP_MODE_NEAREST; default: UNREACHABLE(); @@ -57,8 +58,14 @@ namespace dawn_native { namespace vulkan { } } // anonymous namespace - Sampler::Sampler(Device* device, const SamplerDescriptor* descriptor) - : SamplerBase(device, descriptor), mDevice(device) { + // static + ResultOrError Sampler::Create(Device* device, const SamplerDescriptor* descriptor) { + Ref sampler = AcquireRef(new Sampler(device, descriptor)); + DAWN_TRY(sampler->Initialize(descriptor)); + return sampler.Detach(); + } + + MaybeError Sampler::Initialize(const SamplerDescriptor* descriptor) { VkSamplerCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; createInfo.pNext = nullptr; @@ -72,21 +79,27 @@ namespace dawn_native { namespace vulkan { createInfo.mipLodBias = 0.0f; createInfo.anisotropyEnable = VK_FALSE; createInfo.maxAnisotropy = 1.0f; - createInfo.compareOp = ToVulkanCompareOp(descriptor->compareFunction); - createInfo.compareEnable = createInfo.compareOp == VK_COMPARE_OP_NEVER ? VK_FALSE : VK_TRUE; + if (descriptor->compare != wgpu::CompareFunction::Undefined) { + createInfo.compareOp = ToVulkanCompareOp(descriptor->compare); + createInfo.compareEnable = VK_TRUE; + } else { + // Still set the compareOp so it's not garbage. + createInfo.compareOp = VK_COMPARE_OP_NEVER; + createInfo.compareEnable = VK_FALSE; + } createInfo.minLod = descriptor->lodMinClamp; createInfo.maxLod = descriptor->lodMaxClamp; createInfo.unnormalizedCoordinates = VK_FALSE; - if (device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &mHandle) != - VK_SUCCESS) { - ASSERT(false); - } + Device* device = ToBackend(GetDevice()); + return CheckVkSuccess( + device->fn.CreateSampler(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + "CreateSampler"); } Sampler::~Sampler() { if (mHandle != VK_NULL_HANDLE) { - mDevice->GetFencedDeleter()->DeleteWhenUnused(mHandle); + ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle); mHandle = VK_NULL_HANDLE; } } diff --git a/third_party/dawn/src/dawn_native/vulkan/SamplerVk.h b/third_party/dawn/src/dawn_native/vulkan/SamplerVk.h index 2bd51f17924..72f7d79b15b 100644 --- a/third_party/dawn/src/dawn_native/vulkan/SamplerVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/SamplerVk.h @@ -18,20 +18,24 @@ #include "dawn_native/Sampler.h" #include "common/vulkan_platform.h" -#include "dawn_native/vulkan/MemoryAllocator.h" +#include "dawn_native/Error.h" namespace dawn_native { namespace vulkan { - class Sampler : public SamplerBase { + class Device; + + class Sampler final : public SamplerBase { public: - Sampler(Device* device, const SamplerDescriptor* descriptor); - ~Sampler(); + static ResultOrError Create(Device* device, const SamplerDescriptor* descriptor); VkSampler GetHandle() const; private: + ~Sampler() override; + using SamplerBase::SamplerBase; + MaybeError Initialize(const SamplerDescriptor* descriptor); + VkSampler mHandle = VK_NULL_HANDLE; - Device* mDevice = nullptr; }; }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/ShaderModuleVk.cpp b/third_party/dawn/src/dawn_native/vulkan/ShaderModuleVk.cpp index 0dd8810d232..d76e789b441 100644 --- a/third_party/dawn/src/dawn_native/vulkan/ShaderModuleVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/ShaderModuleVk.cpp @@ -16,29 +16,70 @@ #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/VulkanError.h" -#include +#include namespace dawn_native { namespace vulkan { + // static + ResultOrError ShaderModule::Create(Device* device, + const ShaderModuleDescriptor* descriptor) { + Ref module = AcquireRef(new ShaderModule(device, descriptor)); + if (!module) + return DAWN_VALIDATION_ERROR("Unable to create ShaderModule"); + DAWN_TRY(module->Initialize()); + return module.Detach(); + } + ShaderModule::ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor) : ShaderModuleBase(device, descriptor) { + } + + MaybeError ShaderModule::Initialize() { + DAWN_TRY(InitializeBase()); + const std::vector& spirv = GetSpirv(); + // Use SPIRV-Cross to extract info from the SPIRV even if Vulkan consumes SPIRV. We want to // have a translation step eventually anyway. - spirv_cross::Compiler compiler(descriptor->code, descriptor->codeSize); - ExtractSpirvInfo(compiler); + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc::CompileOptions options = GetCompileOptions(); + + DAWN_TRY(CheckSpvcSuccess( + mSpvcContext.InitializeForVulkan(spirv.data(), spirv.size(), options), + "Unable to initialize instance of spvc")); + + spirv_cross::Compiler* compiler; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.GetCompiler(reinterpret_cast(&compiler)), + "Unable to get cross compiler")); + DAWN_TRY(ExtractSpirvInfo(*compiler)); + } else { + spirv_cross::Compiler compiler(spirv); + DAWN_TRY(ExtractSpirvInfo(compiler)); + } VkShaderModuleCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; - createInfo.codeSize = descriptor->codeSize * sizeof(uint32_t); - createInfo.pCode = descriptor->code; - - if (device->fn.CreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &mHandle) != - VK_SUCCESS) { - ASSERT(false); + std::vector vulkanSource; + if (GetDevice()->IsToggleEnabled(Toggle::UseSpvc)) { + shaderc_spvc::CompilationResult result; + DAWN_TRY(CheckSpvcSuccess(mSpvcContext.CompileShader(&result), + "Unable to generate Vulkan shader")); + DAWN_TRY(CheckSpvcSuccess(result.GetBinaryOutput(&vulkanSource), + "Unable to get binary output of Vulkan shader")); + createInfo.codeSize = vulkanSource.size() * sizeof(uint32_t); + createInfo.pCode = vulkanSource.data(); + } else { + createInfo.codeSize = spirv.size() * sizeof(uint32_t); + createInfo.pCode = spirv.data(); } + + Device* device = ToBackend(GetDevice()); + return CheckVkSuccess( + device->fn.CreateShaderModule(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + "CreateShaderModule"); } ShaderModule::~ShaderModule() { diff --git a/third_party/dawn/src/dawn_native/vulkan/ShaderModuleVk.h b/third_party/dawn/src/dawn_native/vulkan/ShaderModuleVk.h index 8c904d20ee7..720cc5e232c 100644 --- a/third_party/dawn/src/dawn_native/vulkan/ShaderModuleVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/ShaderModuleVk.h @@ -18,19 +18,24 @@ #include "dawn_native/ShaderModule.h" #include "common/vulkan_platform.h" +#include "dawn_native/Error.h" namespace dawn_native { namespace vulkan { class Device; - class ShaderModule : public ShaderModuleBase { + class ShaderModule final : public ShaderModuleBase { public: - ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor); - ~ShaderModule(); + static ResultOrError Create(Device* device, + const ShaderModuleDescriptor* descriptor); VkShaderModule GetHandle() const; private: + ShaderModule(Device* device, const ShaderModuleDescriptor* descriptor); + ~ShaderModule() override; + MaybeError Initialize(); + VkShaderModule mHandle = VK_NULL_HANDLE; }; diff --git a/third_party/dawn/src/dawn_native/vulkan/StagingBufferVk.cpp b/third_party/dawn/src/dawn_native/vulkan/StagingBufferVk.cpp index 4e96f85f9b8..dfdb9786d7b 100644 --- a/third_party/dawn/src/dawn_native/vulkan/StagingBufferVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/StagingBufferVk.cpp @@ -15,7 +15,8 @@ #include "dawn_native/vulkan/StagingBufferVk.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" -#include "dawn_native/vulkan/MemoryAllocator.h" +#include "dawn_native/vulkan/ResourceHeapVk.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { @@ -34,26 +35,24 @@ namespace dawn_native { namespace vulkan { createInfo.queueFamilyIndexCount = 0; createInfo.pQueueFamilyIndices = 0; - if (mDevice->fn.CreateBuffer(mDevice->GetVkDevice(), &createInfo, nullptr, &mBuffer) != - VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("Unable to create staging buffer."); - } + DAWN_TRY(CheckVkSuccess( + mDevice->fn.CreateBuffer(mDevice->GetVkDevice(), &createInfo, nullptr, &*mBuffer), + "vkCreateBuffer")); VkMemoryRequirements requirements; mDevice->fn.GetBufferMemoryRequirements(mDevice->GetVkDevice(), mBuffer, &requirements); - if (!mDevice->GetMemoryAllocator()->Allocate(requirements, true, &mAllocation)) { - return DAWN_CONTEXT_LOST_ERROR("Unable to allocate memory for staging buffer."); - } + DAWN_TRY_ASSIGN(mAllocation, mDevice->AllocateMemory(requirements, true)); - if (mDevice->fn.BindBufferMemory(mDevice->GetVkDevice(), mBuffer, mAllocation.GetMemory(), - mAllocation.GetMemoryOffset()) != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("Unable to attach memory to the staging buffer."); - } + DAWN_TRY(CheckVkSuccess( + mDevice->fn.BindBufferMemory(mDevice->GetVkDevice(), mBuffer, + ToBackend(mAllocation.GetResourceHeap())->GetMemory(), + mAllocation.GetOffset()), + "vkBindBufferMemory")); mMappedPointer = mAllocation.GetMappedPointer(); if (mMappedPointer == nullptr) { - return DAWN_CONTEXT_LOST_ERROR("Unable to map staging buffer."); + return DAWN_INTERNAL_ERROR("Unable to map staging buffer."); } return {}; @@ -62,11 +61,11 @@ namespace dawn_native { namespace vulkan { StagingBuffer::~StagingBuffer() { mMappedPointer = nullptr; mDevice->GetFencedDeleter()->DeleteWhenUnused(mBuffer); - mDevice->GetMemoryAllocator()->Free(&mAllocation); + mDevice->DeallocateMemory(&mAllocation); } VkBuffer StagingBuffer::GetBufferHandle() const { return mBuffer; } -}} // namespace dawn_native::vulkan \ No newline at end of file +}} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/StagingBufferVk.h b/third_party/dawn/src/dawn_native/vulkan/StagingBufferVk.h index 618c5ed7ffc..50b773a0dac 100644 --- a/third_party/dawn/src/dawn_native/vulkan/StagingBufferVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/StagingBufferVk.h @@ -15,8 +15,9 @@ #ifndef DAWNNATIVE_STAGINGBUFFERVK_H_ #define DAWNNATIVE_STAGINGBUFFERVK_H_ +#include "common/vulkan_platform.h" +#include "dawn_native/ResourceMemoryAllocation.h" #include "dawn_native/StagingBuffer.h" -#include "dawn_native/vulkan/MemoryAllocator.h" namespace dawn_native { namespace vulkan { @@ -25,7 +26,7 @@ namespace dawn_native { namespace vulkan { class StagingBuffer : public StagingBufferBase { public: StagingBuffer(size_t size, Device* device); - ~StagingBuffer(); + ~StagingBuffer() override; VkBuffer GetBufferHandle() const; @@ -34,7 +35,7 @@ namespace dawn_native { namespace vulkan { private: Device* mDevice; VkBuffer mBuffer; - DeviceMemoryAllocation mAllocation; + ResourceMemoryAllocation mAllocation; }; }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/SwapChainVk.cpp b/third_party/dawn/src/dawn_native/vulkan/SwapChainVk.cpp index f4e857d4d85..30d774d195e 100644 --- a/third_party/dawn/src/dawn_native/vulkan/SwapChainVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/SwapChainVk.cpp @@ -19,14 +19,19 @@ namespace dawn_native { namespace vulkan { + // static + SwapChain* SwapChain::Create(Device* device, const SwapChainDescriptor* descriptor) { + return new SwapChain(device, descriptor); + } + SwapChain::SwapChain(Device* device, const SwapChainDescriptor* descriptor) - : SwapChainBase(device, descriptor) { + : OldSwapChainBase(device, descriptor) { const auto& im = GetImplementation(); DawnWSIContextVulkan wsiContext = {}; im.Init(im.userData, &wsiContext); - ASSERT(im.textureUsage != DAWN_TEXTURE_USAGE_BIT_NONE); - mTextureUsage = static_cast(im.textureUsage); + ASSERT(im.textureUsage != WGPUTextureUsage_None); + mTextureUsage = static_cast(im.textureUsage); } SwapChain::~SwapChain() { @@ -38,23 +43,28 @@ namespace dawn_native { namespace vulkan { DawnSwapChainError error = im.GetNextTexture(im.userData, &next); if (error) { - GetDevice()->HandleError(error); + GetDevice()->HandleError(InternalErrorType::Internal, error); return nullptr; } - VkImage nativeTexture = VkImage::CreateFromU64(next.texture.u64); - return new Texture(ToBackend(GetDevice()), descriptor, nativeTexture); + ::VkImage image = NativeNonDispatachableHandleFromU64<::VkImage>(next.texture.u64); + VkImage nativeTexture = VkImage::CreateFromHandle(image); + return Texture::CreateForSwapChain(ToBackend(GetDevice()), descriptor, nativeTexture) + .Detach(); } - void SwapChain::OnBeforePresent(TextureBase* texture) { + MaybeError SwapChain::OnBeforePresent(TextureViewBase* view) { Device* device = ToBackend(GetDevice()); // Perform the necessary pipeline barriers for the texture to be used with the usage // requested by the implementation. - VkCommandBuffer commands = device->GetPendingCommandBuffer(); - ToBackend(texture)->TransitionUsageNow(commands, mTextureUsage); + CommandRecordingContext* recordingContext = device->GetPendingRecordingContext(); + ToBackend(view->GetTexture()) + ->TransitionUsageNow(recordingContext, mTextureUsage, view->GetSubresourceRange()); + + DAWN_TRY(device->SubmitPendingCommands()); - device->SubmitPendingCommands(); + return {}; } }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/SwapChainVk.h b/third_party/dawn/src/dawn_native/vulkan/SwapChainVk.h index e546c34067b..9ee1792f60c 100644 --- a/third_party/dawn/src/dawn_native/vulkan/SwapChainVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/SwapChainVk.h @@ -23,17 +23,19 @@ namespace dawn_native { namespace vulkan { class Device; - class SwapChain : public SwapChainBase { + class SwapChain final : public OldSwapChainBase { public: - SwapChain(Device* device, const SwapChainDescriptor* descriptor); - ~SwapChain(); + static SwapChain* Create(Device* device, const SwapChainDescriptor* descriptor); protected: + SwapChain(Device* device, const SwapChainDescriptor* descriptor); + ~SwapChain() override; + TextureBase* GetNextTextureImpl(const TextureDescriptor* descriptor) override; - void OnBeforePresent(TextureBase* texture) override; + MaybeError OnBeforePresent(TextureViewBase* view) override; private: - dawn::TextureUsageBit mTextureUsage; + wgpu::TextureUsage mTextureUsage; }; }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/TextureVk.cpp b/third_party/dawn/src/dawn_native/vulkan/TextureVk.cpp index e73ecb60341..d710c95e258 100644 --- a/third_party/dawn/src/dawn_native/vulkan/TextureVk.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/TextureVk.cpp @@ -14,36 +14,35 @@ #include "dawn_native/vulkan/TextureVk.h" +#include "common/Assert.h" +#include "common/Math.h" +#include "dawn_native/DynamicUploader.h" +#include "dawn_native/Error.h" +#include "dawn_native/VulkanBackend.h" #include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/BufferVk.h" +#include "dawn_native/vulkan/CommandRecordingContext.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/ResourceHeapVk.h" +#include "dawn_native/vulkan/StagingBufferVk.h" +#include "dawn_native/vulkan/UtilsVulkan.h" +#include "dawn_native/vulkan/VulkanError.h" namespace dawn_native { namespace vulkan { namespace { - // Converts an Dawn texture dimension to a Vulkan image type. - // Note that in Vulkan dimensionality is only 1D, 2D, 3D. Arrays and cube maps are expressed - // via the array size and a "cubemap compatible" flag. - VkImageType VulkanImageType(dawn::TextureDimension dimension) { - switch (dimension) { - case dawn::TextureDimension::e2D: - return VK_IMAGE_TYPE_2D; - default: - UNREACHABLE(); - } - } - // Converts an Dawn texture dimension to a Vulkan image view type. // Contrary to image types, image view types include arrayness and cubemapness - VkImageViewType VulkanImageViewType(dawn::TextureViewDimension dimension) { + VkImageViewType VulkanImageViewType(wgpu::TextureViewDimension dimension) { switch (dimension) { - case dawn::TextureViewDimension::e2D: + case wgpu::TextureViewDimension::e2D: return VK_IMAGE_VIEW_TYPE_2D; - case dawn::TextureViewDimension::e2DArray: + case wgpu::TextureViewDimension::e2DArray: return VK_IMAGE_VIEW_TYPE_2D_ARRAY; - case dawn::TextureViewDimension::Cube: + case wgpu::TextureViewDimension::Cube: return VK_IMAGE_VIEW_TYPE_CUBE; - case dawn::TextureViewDimension::CubeArray: + case wgpu::TextureViewDimension::CubeArray: return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY; default: UNREACHABLE(); @@ -51,23 +50,23 @@ namespace dawn_native { namespace vulkan { } // Computes which vulkan access type could be required for the given Dawn usage. - VkAccessFlags VulkanAccessFlags(dawn::TextureUsageBit usage, dawn::TextureFormat format) { + VkAccessFlags VulkanAccessFlags(wgpu::TextureUsage usage, const Format& format) { VkAccessFlags flags = 0; - if (usage & dawn::TextureUsageBit::TransferSrc) { + if (usage & wgpu::TextureUsage::CopySrc) { flags |= VK_ACCESS_TRANSFER_READ_BIT; } - if (usage & dawn::TextureUsageBit::TransferDst) { + if (usage & wgpu::TextureUsage::CopyDst) { flags |= VK_ACCESS_TRANSFER_WRITE_BIT; } - if (usage & dawn::TextureUsageBit::Sampled) { + if (usage & wgpu::TextureUsage::Sampled) { flags |= VK_ACCESS_SHADER_READ_BIT; } - if (usage & dawn::TextureUsageBit::Storage) { + if (usage & wgpu::TextureUsage::Storage) { flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } - if (usage & dawn::TextureUsageBit::OutputAttachment) { - if (TextureFormatHasDepthOrStencil(format)) { + if (usage & wgpu::TextureUsage::OutputAttachment) { + if (format.HasDepthOrStencil()) { flags |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; } else { @@ -75,12 +74,22 @@ namespace dawn_native { namespace vulkan { VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; } } - if (usage & dawn::TextureUsageBit::Present) { - // There is no access flag for present because the VK_KHR_SWAPCHAIN extension says - // that vkQueuePresentKHR makes the memory of the image visible to the presentation - // engine. There's also a note explicitly saying dstAccessMask should be 0. On the - // other side srcAccessMask can also be 0 because synchronization is required to - // happen with a semaphore instead. + if (usage & kPresentTextureUsage) { + // The present usage is only used internally by the swapchain and is never used in + // combination with other usages. + ASSERT(usage == kPresentTextureUsage); + // The Vulkan spec has the following note: + // + // When transitioning the image to VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR or + // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, there is no need to delay subsequent + // processing, or perform any visibility operations (as vkQueuePresentKHR performs + // automatic visibility operations). To achieve this, the dstAccessMask member of + // the VkImageMemoryBarrier should be set to 0, and the dstStageMask parameter + // should be set to VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT. + // + // So on the transition to Present we don't need an access flag. The other + // direction doesn't matter because swapchain textures always start a new frame + // as uninitialized. flags |= 0; } @@ -88,38 +97,40 @@ namespace dawn_native { namespace vulkan { } // Chooses which Vulkan image layout should be used for the given Dawn usage - VkImageLayout VulkanImageLayout(dawn::TextureUsageBit usage, dawn::TextureFormat format) { - if (usage == dawn::TextureUsageBit::None) { + VkImageLayout VulkanImageLayout(wgpu::TextureUsage usage, const Format& format) { + if (usage == wgpu::TextureUsage::None) { return VK_IMAGE_LAYOUT_UNDEFINED; } - if (!dawn::HasZeroOrOneBits(usage)) { + if (!wgpu::HasZeroOrOneBits(usage)) { return VK_IMAGE_LAYOUT_GENERAL; } // Usage has a single bit so we can switch on its value directly. switch (usage) { - case dawn::TextureUsageBit::TransferDst: + case wgpu::TextureUsage::CopyDst: return VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - case dawn::TextureUsageBit::Sampled: + case wgpu::TextureUsage::Sampled: return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; // Vulkan texture copy functions require the image to be in _one_ known layout. // Depending on whether parts of the texture have been transitioned to only - // TransferSrc or a combination with something else, the texture could be in a + // CopySrc or a combination with something else, the texture could be in a // combination of GENERAL and TRANSFER_SRC_OPTIMAL. This would be a problem, so we - // make TransferSrc use GENERAL. - case dawn::TextureUsageBit::TransferSrc: - // Writable storage textures must use general. If we could know the texture is read - // only we could use SHADER_READ_ONLY_OPTIMAL - case dawn::TextureUsageBit::Storage: + // make CopySrc use GENERAL. + case wgpu::TextureUsage::CopySrc: + // Read-only and write-only storage textures must use general layout because load + // and store operations on storage images can only be done on the images in + // VK_IMAGE_LAYOUT_GENERAL layout. + case wgpu::TextureUsage::Storage: + case kReadonlyStorageTexture: return VK_IMAGE_LAYOUT_GENERAL; - case dawn::TextureUsageBit::OutputAttachment: - if (TextureFormatHasDepthOrStencil(format)) { + case wgpu::TextureUsage::OutputAttachment: + if (format.HasDepthOrStencil()) { return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; } else { return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } - case dawn::TextureUsageBit::Present: + case kPresentTextureUsage: return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; default: UNREACHABLE(); @@ -127,25 +138,28 @@ namespace dawn_native { namespace vulkan { } // Computes which Vulkan pipeline stage can access a texture in the given Dawn usage - VkPipelineStageFlags VulkanPipelineStage(dawn::TextureUsageBit usage, - dawn::TextureFormat format) { + VkPipelineStageFlags VulkanPipelineStage(wgpu::TextureUsage usage, const Format& format) { VkPipelineStageFlags flags = 0; - if (usage == dawn::TextureUsageBit::None) { + if (usage == wgpu::TextureUsage::None) { // This only happens when a texture is initially created (and for srcAccessMask) in // which case there is no need to wait on anything to stop accessing this texture. return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; } - if (usage & (dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst)) { + if (usage & (wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst)) { flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; } - if (usage & (dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::Storage)) { + if (usage & (wgpu::TextureUsage::Sampled | kReadonlyStorageTexture)) { flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; } - if (usage & dawn::TextureUsageBit::OutputAttachment) { - if (TextureFormatHasDepthOrStencil(format)) { + if (usage & wgpu::TextureUsage::Storage) { + flags |= + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + } + if (usage & wgpu::TextureUsage::OutputAttachment) { + if (format.HasDepthOrStencil()) { flags |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT; // TODO(cwallez@chromium.org): This is missing the stage where the depth and @@ -154,13 +168,22 @@ namespace dawn_native { namespace vulkan { flags |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; } } - if (usage & dawn::TextureUsageBit::Present) { - // There is no pipeline stage for present but a pipeline stage is required so we use - // "bottom of pipe" to block as little as possible and vkQueuePresentKHR will make - // the memory visible to the presentation engine. The spec explicitly mentions that - // "bottom of pipe" is ok. On the other direction, synchronization happens with a - // semaphore so bottom of pipe is ok too (but maybe it could be "top of pipe" to - // block less?) + if (usage & kPresentTextureUsage) { + // The present usage is only used internally by the swapchain and is never used in + // combination with other usages. + ASSERT(usage == kPresentTextureUsage); + // The Vulkan spec has the following note: + // + // When transitioning the image to VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR or + // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, there is no need to delay subsequent + // processing, or perform any visibility operations (as vkQueuePresentKHR performs + // automatic visibility operations). To achieve this, the dstAccessMask member of + // the VkImageMemoryBarrier should be set to 0, and the dstStageMask parameter + // should be set to VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT. + // + // So on the transition to Present we use the "bottom of pipe" stage. The other + // direction doesn't matter because swapchain textures always start a new frame + // as uninitialized. flags |= VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; } @@ -170,65 +193,191 @@ namespace dawn_native { namespace vulkan { } // Computes which Vulkan texture aspects are relevant for the given Dawn format - VkImageAspectFlags VulkanAspectMask(dawn::TextureFormat format) { - bool isDepth = TextureFormatHasDepth(format); - bool isStencil = TextureFormatHasStencil(format); - - VkImageAspectFlags flags = 0; - if (isDepth) { - flags |= VK_IMAGE_ASPECT_DEPTH_BIT; - } - if (isStencil) { - flags |= VK_IMAGE_ASPECT_STENCIL_BIT; - } - - if (flags != 0) { - return flags; + VkImageAspectFlags VulkanAspectMask(const Format& format) { + switch (format.aspect) { + case Format::Aspect::Color: + return VK_IMAGE_ASPECT_COLOR_BIT; + case Format::Aspect::Depth: + return VK_IMAGE_ASPECT_DEPTH_BIT; + case Format::Aspect::Stencil: + return VK_IMAGE_ASPECT_STENCIL_BIT; + case Format::Aspect::DepthStencil: + return VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; + default: + UNREACHABLE(); + return 0; } - return VK_IMAGE_ASPECT_COLOR_BIT; } - VkExtent3D VulkanExtent3D(const Extent3D& extent) { - return {extent.width, extent.height, extent.depth}; + VkImageMemoryBarrier BuildMemoryBarrier(const Format& format, + const VkImage& image, + wgpu::TextureUsage lastUsage, + wgpu::TextureUsage usage, + const SubresourceRange& range) { + VkImageMemoryBarrier barrier; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.pNext = nullptr; + barrier.srcAccessMask = VulkanAccessFlags(lastUsage, format); + barrier.dstAccessMask = VulkanAccessFlags(usage, format); + barrier.oldLayout = VulkanImageLayout(lastUsage, format); + barrier.newLayout = VulkanImageLayout(usage, format); + barrier.image = image; + barrier.subresourceRange.aspectMask = VulkanAspectMask(format); + barrier.subresourceRange.baseMipLevel = range.baseMipLevel; + barrier.subresourceRange.levelCount = range.levelCount; + barrier.subresourceRange.baseArrayLayer = range.baseArrayLayer; + barrier.subresourceRange.layerCount = range.layerCount; + + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + return barrier; } - bool IsSampleCountSupported(const dawn_native::vulkan::Device* device, - const VkImageCreateInfo& imageCreateInfo) { - ASSERT(device); + void FillVulkanCreateInfoSizesAndType(const Texture& texture, VkImageCreateInfo* info) { + const Extent3D& size = texture.GetSize(); - VkPhysicalDevice physicalDevice = ToBackend(device->GetAdapter())->GetPhysicalDevice(); - VkImageFormatProperties properties; - if (device->fn.GetPhysicalDeviceImageFormatProperties( - physicalDevice, imageCreateInfo.format, imageCreateInfo.imageType, - imageCreateInfo.tiling, imageCreateInfo.usage, imageCreateInfo.flags, - &properties) != VK_SUCCESS) { - UNREACHABLE(); - } + info->mipLevels = texture.GetNumMipLevels(); + info->samples = VulkanSampleCount(texture.GetSampleCount()); + + // Fill in the image type, and paper over differences in how the array layer count is + // specified between WebGPU and Vulkan. + switch (texture.GetDimension()) { + case wgpu::TextureDimension::e2D: + info->imageType = VK_IMAGE_TYPE_2D; + info->extent = {size.width, size.height, 1}; + info->arrayLayers = size.depth; + break; - return properties.sampleCounts & imageCreateInfo.samples; + default: + UNREACHABLE(); + break; + } } } // namespace // Converts Dawn texture format to Vulkan formats. - VkFormat VulkanImageFormat(dawn::TextureFormat format) { + VkFormat VulkanImageFormat(const Device* device, wgpu::TextureFormat format) { switch (format) { - case dawn::TextureFormat::R8G8B8A8Unorm: - return VK_FORMAT_R8G8B8A8_UNORM; - case dawn::TextureFormat::R8G8Unorm: - return VK_FORMAT_R8G8_UNORM; - case dawn::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Unorm: return VK_FORMAT_R8_UNORM; - case dawn::TextureFormat::R8G8B8A8Uint: - return VK_FORMAT_R8G8B8A8_UINT; - case dawn::TextureFormat::R8G8Uint: - return VK_FORMAT_R8G8_UINT; - case dawn::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Snorm: + return VK_FORMAT_R8_SNORM; + case wgpu::TextureFormat::R8Uint: return VK_FORMAT_R8_UINT; - case dawn::TextureFormat::B8G8R8A8Unorm: + case wgpu::TextureFormat::R8Sint: + return VK_FORMAT_R8_SINT; + + case wgpu::TextureFormat::R16Uint: + return VK_FORMAT_R16_UINT; + case wgpu::TextureFormat::R16Sint: + return VK_FORMAT_R16_SINT; + case wgpu::TextureFormat::R16Float: + return VK_FORMAT_R16_SFLOAT; + case wgpu::TextureFormat::RG8Unorm: + return VK_FORMAT_R8G8_UNORM; + case wgpu::TextureFormat::RG8Snorm: + return VK_FORMAT_R8G8_SNORM; + case wgpu::TextureFormat::RG8Uint: + return VK_FORMAT_R8G8_UINT; + case wgpu::TextureFormat::RG8Sint: + return VK_FORMAT_R8G8_SINT; + + case wgpu::TextureFormat::R32Uint: + return VK_FORMAT_R32_UINT; + case wgpu::TextureFormat::R32Sint: + return VK_FORMAT_R32_SINT; + case wgpu::TextureFormat::R32Float: + return VK_FORMAT_R32_SFLOAT; + case wgpu::TextureFormat::RG16Uint: + return VK_FORMAT_R16G16_UINT; + case wgpu::TextureFormat::RG16Sint: + return VK_FORMAT_R16G16_SINT; + case wgpu::TextureFormat::RG16Float: + return VK_FORMAT_R16G16_SFLOAT; + case wgpu::TextureFormat::RGBA8Unorm: + return VK_FORMAT_R8G8B8A8_UNORM; + case wgpu::TextureFormat::RGBA8UnormSrgb: + return VK_FORMAT_R8G8B8A8_SRGB; + case wgpu::TextureFormat::RGBA8Snorm: + return VK_FORMAT_R8G8B8A8_SNORM; + case wgpu::TextureFormat::RGBA8Uint: + return VK_FORMAT_R8G8B8A8_UINT; + case wgpu::TextureFormat::RGBA8Sint: + return VK_FORMAT_R8G8B8A8_SINT; + case wgpu::TextureFormat::BGRA8Unorm: return VK_FORMAT_B8G8R8A8_UNORM; - case dawn::TextureFormat::D32FloatS8Uint: - return VK_FORMAT_D32_SFLOAT_S8_UINT; + case wgpu::TextureFormat::BGRA8UnormSrgb: + return VK_FORMAT_B8G8R8A8_SRGB; + case wgpu::TextureFormat::RGB10A2Unorm: + return VK_FORMAT_A2B10G10R10_UNORM_PACK32; + case wgpu::TextureFormat::RG11B10Float: + return VK_FORMAT_B10G11R11_UFLOAT_PACK32; + + case wgpu::TextureFormat::RG32Uint: + return VK_FORMAT_R32G32_UINT; + case wgpu::TextureFormat::RG32Sint: + return VK_FORMAT_R32G32_SINT; + case wgpu::TextureFormat::RG32Float: + return VK_FORMAT_R32G32_SFLOAT; + case wgpu::TextureFormat::RGBA16Uint: + return VK_FORMAT_R16G16B16A16_UINT; + case wgpu::TextureFormat::RGBA16Sint: + return VK_FORMAT_R16G16B16A16_SINT; + case wgpu::TextureFormat::RGBA16Float: + return VK_FORMAT_R16G16B16A16_SFLOAT; + + case wgpu::TextureFormat::RGBA32Uint: + return VK_FORMAT_R32G32B32A32_UINT; + case wgpu::TextureFormat::RGBA32Sint: + return VK_FORMAT_R32G32B32A32_SINT; + case wgpu::TextureFormat::RGBA32Float: + return VK_FORMAT_R32G32B32A32_SFLOAT; + + case wgpu::TextureFormat::Depth32Float: + return VK_FORMAT_D32_SFLOAT; + case wgpu::TextureFormat::Depth24Plus: + return VK_FORMAT_D32_SFLOAT; + case wgpu::TextureFormat::Depth24PlusStencil8: + // Depth24PlusStencil8 maps to either of these two formats because only requires + // that one of the two be present. The VulkanUseD32S8 toggle combines the wish of + // the environment, default to using D32S8, and availability information so we know + // that the format is available. + if (device->IsToggleEnabled(Toggle::VulkanUseD32S8)) { + return VK_FORMAT_D32_SFLOAT_S8_UINT; + } else { + return VK_FORMAT_D24_UNORM_S8_UINT; + } + + case wgpu::TextureFormat::BC1RGBAUnorm: + return VK_FORMAT_BC1_RGBA_UNORM_BLOCK; + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + return VK_FORMAT_BC1_RGBA_SRGB_BLOCK; + case wgpu::TextureFormat::BC2RGBAUnorm: + return VK_FORMAT_BC2_UNORM_BLOCK; + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + return VK_FORMAT_BC2_SRGB_BLOCK; + case wgpu::TextureFormat::BC3RGBAUnorm: + return VK_FORMAT_BC3_UNORM_BLOCK; + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + return VK_FORMAT_BC3_SRGB_BLOCK; + case wgpu::TextureFormat::BC4RSnorm: + return VK_FORMAT_BC4_SNORM_BLOCK; + case wgpu::TextureFormat::BC4RUnorm: + return VK_FORMAT_BC4_UNORM_BLOCK; + case wgpu::TextureFormat::BC5RGSnorm: + return VK_FORMAT_BC5_SNORM_BLOCK; + case wgpu::TextureFormat::BC5RGUnorm: + return VK_FORMAT_BC5_UNORM_BLOCK; + case wgpu::TextureFormat::BC6HRGBSfloat: + return VK_FORMAT_BC6H_SFLOAT_BLOCK; + case wgpu::TextureFormat::BC6HRGBUfloat: + return VK_FORMAT_BC6H_UFLOAT_BLOCK; + case wgpu::TextureFormat::BC7RGBAUnorm: + return VK_FORMAT_BC7_UNORM_BLOCK; + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return VK_FORMAT_BC7_SRGB_BLOCK; + default: UNREACHABLE(); } @@ -236,23 +385,23 @@ namespace dawn_native { namespace vulkan { // Converts the Dawn usage flags to Vulkan usage flags. Also needs the format to choose // between color and depth attachment usages. - VkImageUsageFlags VulkanImageUsage(dawn::TextureUsageBit usage, dawn::TextureFormat format) { + VkImageUsageFlags VulkanImageUsage(wgpu::TextureUsage usage, const Format& format) { VkImageUsageFlags flags = 0; - if (usage & dawn::TextureUsageBit::TransferSrc) { + if (usage & wgpu::TextureUsage::CopySrc) { flags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } - if (usage & dawn::TextureUsageBit::TransferDst) { + if (usage & wgpu::TextureUsage::CopyDst) { flags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } - if (usage & dawn::TextureUsageBit::Sampled) { + if (usage & wgpu::TextureUsage::Sampled) { flags |= VK_IMAGE_USAGE_SAMPLED_BIT; } - if (usage & dawn::TextureUsageBit::Storage) { + if (usage & wgpu::TextureUsage::Storage) { flags |= VK_IMAGE_USAGE_STORAGE_BIT; } - if (usage & dawn::TextureUsageBit::OutputAttachment) { - if (TextureFormatHasDepthOrStencil(format)) { + if (usage & wgpu::TextureUsage::OutputAttachment) { + if (format.HasDepthOrStencil()) { flags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } else { flags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; @@ -273,21 +422,87 @@ namespace dawn_native { namespace vulkan { } } - Texture::Texture(Device* device, const TextureDescriptor* descriptor) - : TextureBase(device, descriptor, TextureState::OwnedInternal) { + MaybeError ValidateVulkanImageCanBeWrapped(const DeviceBase*, + const TextureDescriptor* descriptor) { + if (descriptor->dimension != wgpu::TextureDimension::e2D) { + return DAWN_VALIDATION_ERROR("Texture must be 2D"); + } + + if (descriptor->mipLevelCount != 1) { + return DAWN_VALIDATION_ERROR("Mip level count must be 1"); + } + + if (descriptor->size.depth != 1) { + return DAWN_VALIDATION_ERROR("Array layer count must be 1"); + } + + if (descriptor->sampleCount != 1) { + return DAWN_VALIDATION_ERROR("Sample count must be 1"); + } + + return {}; + } + + bool IsSampleCountSupported(const dawn_native::vulkan::Device* device, + const VkImageCreateInfo& imageCreateInfo) { + ASSERT(device); + + VkPhysicalDevice physicalDevice = ToBackend(device->GetAdapter())->GetPhysicalDevice(); + VkImageFormatProperties properties; + if (device->fn.GetPhysicalDeviceImageFormatProperties( + physicalDevice, imageCreateInfo.format, imageCreateInfo.imageType, + imageCreateInfo.tiling, imageCreateInfo.usage, imageCreateInfo.flags, + &properties) != VK_SUCCESS) { + UNREACHABLE(); + } + + return properties.sampleCounts & imageCreateInfo.samples; + } + + // static + ResultOrError> Texture::Create(Device* device, + const TextureDescriptor* descriptor) { + Ref texture = + AcquireRef(new Texture(device, descriptor, TextureState::OwnedInternal)); + DAWN_TRY(texture->InitializeAsInternalTexture()); + return std::move(texture); + } + + // static + ResultOrError Texture::CreateFromExternal( + Device* device, + const ExternalImageDescriptor* descriptor, + const TextureDescriptor* textureDescriptor, + external_memory::Service* externalMemoryService) { + Ref texture = + AcquireRef(new Texture(device, textureDescriptor, TextureState::OwnedInternal)); + DAWN_TRY(texture->InitializeFromExternal(descriptor, externalMemoryService)); + return texture.Detach(); + } + + // static + Ref Texture::CreateForSwapChain(Device* device, + const TextureDescriptor* descriptor, + VkImage nativeImage) { + Ref texture = + AcquireRef(new Texture(device, descriptor, TextureState::OwnedExternal)); + texture->InitializeForSwapChain(nativeImage); + return std::move(texture); + } + + MaybeError Texture::InitializeAsInternalTexture() { + Device* device = ToBackend(GetDevice()); + // Create the Vulkan image "container". We don't need to check that the format supports the // combination of sample, usage etc. because validation should have been done in the Dawn // frontend already based on the minimum supported formats in the Vulkan spec - VkImageCreateInfo createInfo; + VkImageCreateInfo createInfo = {}; + FillVulkanCreateInfoSizesAndType(*this, &createInfo); + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; - createInfo.imageType = VulkanImageType(GetDimension()); - createInfo.format = VulkanImageFormat(GetFormat()); - createInfo.extent = VulkanExtent3D(GetSize()); - createInfo.mipLevels = GetNumMipLevels(); - createInfo.arrayLayers = GetArrayLayers(); - createInfo.samples = VulkanSampleCount(GetSampleCount()); + createInfo.format = VulkanImageFormat(device, GetFormat().format); createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; createInfo.usage = VulkanImageUsage(GetUsage(), GetFormat()); createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -297,7 +512,7 @@ namespace dawn_native { namespace vulkan { ASSERT(IsSampleCountSupported(device, createInfo)); - if (GetArrayLayers() >= 6 && GetSize().width == GetSize().height) { + if (GetArrayLayers() >= 6 && GetWidth() == GetHeight()) { createInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT; } @@ -306,47 +521,115 @@ namespace dawn_native { namespace vulkan { // also required for the implementation of robust resource initialization. createInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - if (device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &mHandle) != - VK_SUCCESS) { - ASSERT(false); - } + DAWN_TRY(CheckVkSuccess( + device->fn.CreateImage(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + "CreateImage")); // Create the image memory and associate it with the container VkMemoryRequirements requirements; device->fn.GetImageMemoryRequirements(device->GetVkDevice(), mHandle, &requirements); - if (!device->GetMemoryAllocator()->Allocate(requirements, false, &mMemoryAllocation)) { - ASSERT(false); + DAWN_TRY_ASSIGN(mMemoryAllocation, device->AllocateMemory(requirements, false)); + + DAWN_TRY(CheckVkSuccess( + device->fn.BindImageMemory(device->GetVkDevice(), mHandle, + ToBackend(mMemoryAllocation.GetResourceHeap())->GetMemory(), + mMemoryAllocation.GetOffset()), + "BindImageMemory")); + + if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { + DAWN_TRY(ClearTexture(ToBackend(GetDevice())->GetPendingRecordingContext(), + GetAllSubresources(), TextureBase::ClearValue::NonZero)); } - if (device->fn.BindImageMemory(device->GetVkDevice(), mHandle, - mMemoryAllocation.GetMemory(), - mMemoryAllocation.GetMemoryOffset()) != VK_SUCCESS) { - ASSERT(false); + return {}; + } + + // Internally managed, but imported from external handle + MaybeError Texture::InitializeFromExternal(const ExternalImageDescriptor* descriptor, + external_memory::Service* externalMemoryService) { + VkFormat format = VulkanImageFormat(ToBackend(GetDevice()), GetFormat().format); + VkImageUsageFlags usage = VulkanImageUsage(GetUsage(), GetFormat()); + if (!externalMemoryService->SupportsCreateImage(descriptor, format, usage)) { + return DAWN_VALIDATION_ERROR("Creating an image from external memory is not supported"); } - if (device->IsToggleEnabled(Toggle::NonzeroClearResourcesOnCreationForTesting)) { - VkImageSubresourceRange range = {}; - range.aspectMask = GetVkAspectMask(); - range.baseMipLevel = 0; - range.levelCount = GetNumMipLevels(); - range.baseArrayLayer = 0; - range.layerCount = GetArrayLayers(); - - // TODO(natlee@microsoft.com): use correct union member depending on the texture format - VkClearColorValue clear_color = {{1.0, 1.0, 1.0, 1.0}}; - - TransitionUsageNow(ToBackend(GetDevice())->GetPendingCommandBuffer(), - dawn::TextureUsageBit::TransferDst); - ToBackend(GetDevice()) - ->fn.CmdClearColorImage(ToBackend(GetDevice())->GetPendingCommandBuffer(), - GetHandle(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, - &clear_color, 1, &range); + + mExternalState = ExternalState::PendingAcquire; + + VkImageCreateInfo baseCreateInfo = {}; + FillVulkanCreateInfoSizesAndType(*this, &baseCreateInfo); + + baseCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + baseCreateInfo.pNext = nullptr; + baseCreateInfo.format = format; + baseCreateInfo.usage = usage; + baseCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + baseCreateInfo.queueFamilyIndexCount = 0; + baseCreateInfo.pQueueFamilyIndices = nullptr; + + // We always set VK_IMAGE_USAGE_TRANSFER_DST_BIT unconditionally beause the Vulkan images + // that are used in vkCmdClearColorImage() must have been created with this flag, which is + // also required for the implementation of robust resource initialization. + baseCreateInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + DAWN_TRY_ASSIGN(mHandle, externalMemoryService->CreateImage(descriptor, baseCreateInfo)); + return {}; + } + + void Texture::InitializeForSwapChain(VkImage nativeImage) { + mHandle = nativeImage; + } + + MaybeError Texture::BindExternalMemory(const ExternalImageDescriptor* descriptor, + VkSemaphore signalSemaphore, + VkDeviceMemory externalMemoryAllocation, + std::vector waitSemaphores) { + Device* device = ToBackend(GetDevice()); + DAWN_TRY(CheckVkSuccess( + device->fn.BindImageMemory(device->GetVkDevice(), mHandle, externalMemoryAllocation, 0), + "BindImageMemory (external)")); + + // Don't clear imported texture if already cleared + if (descriptor->isCleared) { + SetIsSubresourceContentInitialized(true, {0, 1, 0, 1}); } + + // Success, acquire all the external objects. + mExternalAllocation = externalMemoryAllocation; + mSignalSemaphore = signalSemaphore; + mWaitRequirements = std::move(waitSemaphores); + return {}; } - // With this constructor, the lifetime of the resource is externally managed. - Texture::Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage) - : TextureBase(device, descriptor, TextureState::OwnedExternal), mHandle(nativeImage) { + MaybeError Texture::SignalAndDestroy(VkSemaphore* outSignalSemaphore) { + Device* device = ToBackend(GetDevice()); + + if (mExternalState == ExternalState::Released) { + return DAWN_VALIDATION_ERROR("Can't export signal semaphore from signaled texture"); + } + + if (mExternalAllocation == VK_NULL_HANDLE) { + return DAWN_VALIDATION_ERROR( + "Can't export signal semaphore from destroyed / non-external texture"); + } + + ASSERT(mSignalSemaphore != VK_NULL_HANDLE); + + // Release the texture + mExternalState = ExternalState::PendingRelease; + TransitionFullUsage(device->GetPendingRecordingContext(), wgpu::TextureUsage::None); + + // Queue submit to signal we are done with the texture + device->GetPendingRecordingContext()->signalSemaphores.push_back(mSignalSemaphore); + DAWN_TRY(device->SubmitPendingCommands()); + + // Write out the signal semaphore + *outSignalSemaphore = mSignalSemaphore; + mSignalSemaphore = VK_NULL_HANDLE; + + // Destroy the texture so it can't be used again + DestroyInternal(); + return {}; } Texture::~Texture() { @@ -354,20 +637,26 @@ namespace dawn_native { namespace vulkan { } void Texture::DestroyImpl() { - Device* device = ToBackend(GetDevice()); + if (GetTextureState() == TextureState::OwnedInternal) { + Device* device = ToBackend(GetDevice()); - // If we own the resource, release it. - if (mMemoryAllocation.GetMemory() != VK_NULL_HANDLE) { - // We need to free both the memory allocation and the container. Memory should be - // freed after the VkImage is destroyed and this is taken care of by the - // FencedDeleter. - device->GetMemoryAllocator()->Free(&mMemoryAllocation); + // For textures created from a VkImage, the allocation if kInvalid so the Device knows + // to skip the deallocation of the (absence of) VkDeviceMemory. + device->DeallocateMemory(&mMemoryAllocation); if (mHandle != VK_NULL_HANDLE) { device->GetFencedDeleter()->DeleteWhenUnused(mHandle); } + + if (mExternalAllocation != VK_NULL_HANDLE) { + device->GetFencedDeleter()->DeleteWhenUnused(mExternalAllocation); + } + + mHandle = VK_NULL_HANDLE; + mExternalAllocation = VK_NULL_HANDLE; + // If a signal semaphore exists it should be requested before we delete the texture + ASSERT(mSignalSemaphore == VK_NULL_HANDLE); } - mHandle = VK_NULL_HANDLE; } VkImage Texture::GetHandle() const { @@ -378,98 +667,336 @@ namespace dawn_native { namespace vulkan { return VulkanAspectMask(GetFormat()); } - void Texture::TransitionUsageNow(VkCommandBuffer commands, dawn::TextureUsageBit usage) { - // Avoid encoding barriers when it isn't needed. - bool lastReadOnly = (mLastUsage & kReadOnlyTextureUsages) == mLastUsage; - if (lastReadOnly && mLastUsage == usage) { - return; + void Texture::TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext, + std::vector* barriers, + size_t transitionBarrierStart) { + ASSERT(GetNumMipLevels() == 1 && GetArrayLayers() == 1); + + // transitionBarrierStart specify the index where barriers for current transition start in + // the vector. barriers->size() - transitionBarrierStart is the number of barriers that we + // have already added into the vector during current transition. + ASSERT(barriers->size() - transitionBarrierStart <= 1); + + if (mExternalState == ExternalState::PendingAcquire) { + if (barriers->size() == transitionBarrierStart) { + barriers->push_back(BuildMemoryBarrier( + GetFormat(), mHandle, wgpu::TextureUsage::None, wgpu::TextureUsage::None, + SubresourceRange::SingleSubresource(0, 0))); + } + + // Transfer texture from external queue to graphics queue + (*barriers)[transitionBarrierStart].srcQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR; + (*barriers)[transitionBarrierStart].dstQueueFamilyIndex = + ToBackend(GetDevice())->GetGraphicsQueueFamily(); + // Don't override oldLayout to leave it as VK_IMAGE_LAYOUT_UNDEFINED + // TODO(http://crbug.com/dawn/200) + mExternalState = ExternalState::Acquired; + } else if (mExternalState == ExternalState::PendingRelease) { + if (barriers->size() == transitionBarrierStart) { + barriers->push_back(BuildMemoryBarrier( + GetFormat(), mHandle, wgpu::TextureUsage::None, wgpu::TextureUsage::None, + SubresourceRange::SingleSubresource(0, 0))); + } + + // Transfer texture from graphics queue to external queue + (*barriers)[transitionBarrierStart].srcQueueFamilyIndex = + ToBackend(GetDevice())->GetGraphicsQueueFamily(); + (*barriers)[transitionBarrierStart].dstQueueFamilyIndex = VK_QUEUE_FAMILY_EXTERNAL_KHR; + (*barriers)[transitionBarrierStart].newLayout = VK_IMAGE_LAYOUT_GENERAL; + mExternalState = ExternalState::Released; } - dawn::TextureFormat format = GetFormat(); + mLastExternalState = mExternalState; - VkPipelineStageFlags srcStages = VulkanPipelineStage(mLastUsage, format); - VkPipelineStageFlags dstStages = VulkanPipelineStage(usage, format); + recordingContext->waitSemaphores.insert(recordingContext->waitSemaphores.end(), + mWaitRequirements.begin(), mWaitRequirements.end()); + mWaitRequirements.clear(); + } - VkImageMemoryBarrier barrier; - barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; - barrier.pNext = nullptr; - barrier.srcAccessMask = VulkanAccessFlags(mLastUsage, format); - barrier.dstAccessMask = VulkanAccessFlags(usage, format); - barrier.oldLayout = VulkanImageLayout(mLastUsage, format); - barrier.newLayout = VulkanImageLayout(usage, format); - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = mHandle; - // This transitions the whole resource but assumes it is a 2D texture - ASSERT(GetDimension() == dawn::TextureDimension::e2D); - barrier.subresourceRange.aspectMask = VulkanAspectMask(format); - barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = GetNumMipLevels(); - barrier.subresourceRange.baseArrayLayer = 0; - barrier.subresourceRange.layerCount = GetArrayLayers(); + bool Texture::CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage, wgpu::TextureUsage usage) { + // Reuse the texture directly and avoid encoding barriers when it isn't needed. + bool lastReadOnly = (lastUsage & kReadOnlyTextureUsages) == lastUsage; + if (lastReadOnly && lastUsage == usage && mLastExternalState == mExternalState) { + return true; + } + return false; + } + + void Texture::TransitionFullUsage(CommandRecordingContext* recordingContext, + wgpu::TextureUsage usage) { + TransitionUsageNow(recordingContext, usage, GetAllSubresources()); + } + void Texture::TransitionUsageForPass(CommandRecordingContext* recordingContext, + const PassTextureUsage& textureUsages, + std::vector* imageBarriers, + VkPipelineStageFlags* srcStages, + VkPipelineStageFlags* dstStages) { + size_t transitionBarrierStart = imageBarriers->size(); + const Format& format = GetFormat(); + + wgpu::TextureUsage allUsages = wgpu::TextureUsage::None; + wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None; + + uint32_t subresourceCount = GetSubresourceCount(); + ASSERT(textureUsages.subresourceUsages.size() == subresourceCount); + // This transitions assume it is a 2D texture + ASSERT(GetDimension() == wgpu::TextureDimension::e2D); + + // If new usages of all subresources are the same and old usages of all subresources are + // the same too, we can use one barrier to do state transition for all subresources. + // Note that if the texture has only one mip level and one array slice, it will fall into + // this category. + if (textureUsages.sameUsagesAcrossSubresources && mSameLastUsagesAcrossSubresources) { + if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], textureUsages.usage)) { + return; + } + + imageBarriers->push_back(BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[0], + textureUsages.usage, GetAllSubresources())); + allLastUsages = mSubresourceLastUsages[0]; + allUsages = textureUsages.usage; + for (uint32_t i = 0; i < subresourceCount; ++i) { + mSubresourceLastUsages[i] = textureUsages.usage; + } + } else { + for (uint32_t arrayLayer = 0; arrayLayer < GetArrayLayers(); ++arrayLayer) { + for (uint32_t mipLevel = 0; mipLevel < GetNumMipLevels(); ++mipLevel) { + uint32_t index = GetSubresourceIndex(mipLevel, arrayLayer); + + // Avoid encoding barriers when it isn't needed. + if (textureUsages.subresourceUsages[index] == wgpu::TextureUsage::None) { + continue; + } + + if (CanReuseWithoutBarrier(mSubresourceLastUsages[index], + textureUsages.subresourceUsages[index])) { + continue; + } + imageBarriers->push_back(BuildMemoryBarrier( + format, mHandle, mSubresourceLastUsages[index], + textureUsages.subresourceUsages[index], + SubresourceRange::SingleSubresource(mipLevel, arrayLayer))); + allLastUsages |= mSubresourceLastUsages[index]; + allUsages |= textureUsages.subresourceUsages[index]; + mSubresourceLastUsages[index] = textureUsages.subresourceUsages[index]; + } + } + } + + if (mExternalState != ExternalState::InternalOnly) { + TweakTransitionForExternalUsage(recordingContext, imageBarriers, + transitionBarrierStart); + } + + *srcStages |= VulkanPipelineStage(allLastUsages, format); + *dstStages |= VulkanPipelineStage(allUsages, format); + mSameLastUsagesAcrossSubresources = textureUsages.sameUsagesAcrossSubresources; + } + + void Texture::TransitionUsageNow(CommandRecordingContext* recordingContext, + wgpu::TextureUsage usage, + const SubresourceRange& range) { + std::vector barriers; + const Format& format = GetFormat(); + + wgpu::TextureUsage allLastUsages = wgpu::TextureUsage::None; + uint32_t subresourceCount = GetSubresourceCount(); + + // This transitions assume it is a 2D texture + ASSERT(GetDimension() == wgpu::TextureDimension::e2D); + + // If the usages transitions can cover all subresources, and old usages of all subresources + // are the same, then we can use one barrier to do state transition for all subresources. + // Note that if the texture has only one mip level and one array slice, it will fall into + // this category. + bool areAllSubresourcesCovered = range.levelCount * range.layerCount == subresourceCount; + if (mSameLastUsagesAcrossSubresources && areAllSubresourcesCovered) { + ASSERT(range.baseMipLevel == 0 && range.baseArrayLayer == 0); + if (CanReuseWithoutBarrier(mSubresourceLastUsages[0], usage)) { + return; + } + barriers.push_back( + BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[0], usage, range)); + allLastUsages = mSubresourceLastUsages[0]; + for (uint32_t i = 0; i < subresourceCount; ++i) { + mSubresourceLastUsages[i] = usage; + } + } else { + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + for (uint32_t level = range.baseMipLevel; + level < range.baseMipLevel + range.levelCount; ++level) { + uint32_t index = GetSubresourceIndex(level, layer); + + if (CanReuseWithoutBarrier(mSubresourceLastUsages[index], usage)) { + continue; + } + + barriers.push_back( + BuildMemoryBarrier(format, mHandle, mSubresourceLastUsages[index], usage, + SubresourceRange::SingleSubresource(level, layer))); + allLastUsages |= mSubresourceLastUsages[index]; + mSubresourceLastUsages[index] = usage; + } + } + } + + if (mExternalState != ExternalState::InternalOnly) { + TweakTransitionForExternalUsage(recordingContext, &barriers, 0); + } + + VkPipelineStageFlags srcStages = VulkanPipelineStage(allLastUsages, format); + VkPipelineStageFlags dstStages = VulkanPipelineStage(usage, format); ToBackend(GetDevice()) - ->fn.CmdPipelineBarrier(commands, srcStages, dstStages, 0, 0, nullptr, 0, nullptr, 1, - &barrier); + ->fn.CmdPipelineBarrier(recordingContext->commandBuffer, srcStages, dstStages, 0, 0, + nullptr, 0, nullptr, barriers.size(), barriers.data()); - mLastUsage = usage; + mSameLastUsagesAcrossSubresources = areAllSubresourcesCovered; } - void Texture::ClearTexture(VkCommandBuffer commands, - uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount) { - if (GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { - VkImageSubresourceRange range = {}; - range.aspectMask = GetVkAspectMask(); - range.baseMipLevel = baseMipLevel; - range.levelCount = levelCount; - range.baseArrayLayer = baseArrayLayer; - range.layerCount = layerCount; - - TransitionUsageNow(commands, dawn::TextureUsageBit::TransferDst); - if (TextureFormatHasDepthOrStencil(GetFormat())) { - VkClearDepthStencilValue clear_color[1]; - clear_color[0].depth = 0.0f; - clear_color[0].stencil = 0u; - ToBackend(GetDevice()) - ->fn.CmdClearDepthStencilImage(commands, GetHandle(), - VulkanImageLayout(GetUsage(), GetFormat()), - clear_color, 1, &range); - } else { - VkClearColorValue clear_color[1]; - clear_color[0].float32[0] = 0.0f; - clear_color[0].float32[1] = 0.0f; - clear_color[0].float32[2] = 0.0f; - clear_color[0].float32[3] = 0.0f; - ToBackend(GetDevice()) - ->fn.CmdClearColorImage(commands, GetHandle(), - VulkanImageLayout(GetUsage(), GetFormat()), clear_color, - 1, &range); - } - SetIsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, - layerCount); + MaybeError Texture::ClearTexture(CommandRecordingContext* recordingContext, + const SubresourceRange& range, + TextureBase::ClearValue clearValue) { + Device* device = ToBackend(GetDevice()); + + uint8_t clearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0 : 1; + float fClearColor = (clearValue == TextureBase::ClearValue::Zero) ? 0.f : 1.f; + + TransitionUsageNow(recordingContext, wgpu::TextureUsage::CopyDst, range); + if (GetFormat().isRenderable) { + VkImageSubresourceRange imageRange = {}; + imageRange.aspectMask = GetVkAspectMask(); + imageRange.levelCount = 1; + imageRange.layerCount = 1; + + for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; + ++level) { + imageRange.baseMipLevel = level; + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, layer))) { + // Skip lazy clears if already initialized. + continue; + } + + imageRange.baseArrayLayer = layer; + + if (GetFormat().HasDepthOrStencil()) { + VkClearDepthStencilValue clearDepthStencilValue[1]; + clearDepthStencilValue[0].depth = fClearColor; + clearDepthStencilValue[0].stencil = clearColor; + device->fn.CmdClearDepthStencilImage( + recordingContext->commandBuffer, GetHandle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, clearDepthStencilValue, 1, + &imageRange); + } else { + VkClearColorValue clearColorValue = { + {fClearColor, fClearColor, fClearColor, fClearColor}}; + device->fn.CmdClearColorImage(recordingContext->commandBuffer, GetHandle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + &clearColorValue, 1, &imageRange); + } + } + } + } else { + // TODO(natlee@microsoft.com): test compressed textures are cleared + // create temp buffer with clear color to copy to the texture image + uint32_t bytesPerRow = + Align((GetWidth() / GetFormat().blockWidth) * GetFormat().blockByteSize, + kTextureBytesPerRowAlignment); + uint64_t bufferSize64 = bytesPerRow * (GetHeight() / GetFormat().blockHeight); + if (bufferSize64 > std::numeric_limits::max()) { + return DAWN_OUT_OF_MEMORY_ERROR("Unable to allocate buffer."); + } + uint32_t bufferSize = static_cast(bufferSize64); + DynamicUploader* uploader = device->GetDynamicUploader(); + UploadHandle uploadHandle; + DAWN_TRY_ASSIGN(uploadHandle, + uploader->Allocate(bufferSize, device->GetPendingCommandSerial())); + memset(uploadHandle.mappedBuffer, clearColor, bufferSize); + + // compute the buffer image copy to set the clear region of entire texture + dawn_native::BufferCopy bufferCopy; + bufferCopy.rowsPerImage = 0; + bufferCopy.offset = uploadHandle.startOffset; + bufferCopy.bytesPerRow = bytesPerRow; + + for (uint32_t level = range.baseMipLevel; level < range.baseMipLevel + range.levelCount; + ++level) { + Extent3D copySize = GetMipLevelVirtualSize(level); + + for (uint32_t layer = range.baseArrayLayer; + layer < range.baseArrayLayer + range.layerCount; ++layer) { + if (clearValue == TextureBase::ClearValue::Zero && + IsSubresourceContentInitialized( + SubresourceRange::SingleSubresource(level, layer))) { + // Skip lazy clears if already initialized. + continue; + } + + dawn_native::TextureCopy textureCopy; + textureCopy.texture = this; + textureCopy.origin = {0, 0, layer}; + textureCopy.mipLevel = level; + + VkBufferImageCopy region = + ComputeBufferImageCopyRegion(bufferCopy, textureCopy, copySize); + + // copy the clear buffer to the texture image + device->fn.CmdCopyBufferToImage( + recordingContext->commandBuffer, + ToBackend(uploadHandle.stagingBuffer)->GetBufferHandle(), GetHandle(), + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + } + } } + if (clearValue == TextureBase::ClearValue::Zero) { + SetIsSubresourceContentInitialized(true, range); + device->IncrementLazyClearCountForTesting(); + } + return {}; } - void Texture::EnsureSubresourceContentInitialized(VkCommandBuffer commands, - uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount) { - if (!IsSubresourceContentInitialized(baseMipLevel, levelCount, baseArrayLayer, - layerCount)) { + void Texture::EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext, + const SubresourceRange& range) { + if (!GetDevice()->IsToggleEnabled(Toggle::LazyClearResourceOnFirstUse)) { + return; + } + if (!IsSubresourceContentInitialized(range)) { + // TODO(jiawei.shao@intel.com): initialize textures in BC formats with Buffer-to-Texture + // copies. + if (GetFormat().isCompressed) { + return; + } + // If subresource has not been initialized, clear it to black as it could contain dirty // bits from recycled memory - ClearTexture(commands, baseMipLevel, levelCount, baseArrayLayer, layerCount); + GetDevice()->ConsumedError( + ClearTexture(recordingContext, range, TextureBase::ClearValue::Zero)); } } - // TODO(jiawei.shao@intel.com): create texture view by TextureViewDescriptor - TextureView::TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor) - : TextureViewBase(texture, descriptor) { - Device* device = ToBackend(texture->GetDevice()); + // static + ResultOrError TextureView::Create(TextureBase* texture, + const TextureViewDescriptor* descriptor) { + Ref view = AcquireRef(new TextureView(texture, descriptor)); + DAWN_TRY(view->Initialize(descriptor)); + return view.Detach(); + } + + MaybeError TextureView::Initialize(const TextureViewDescriptor* descriptor) { + if ((GetTexture()->GetUsage() & + ~(wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst)) == 0) { + // If the texture view has no other usage than CopySrc and CopyDst, then it can't + // actually be used as a render pass attachment or sampled/storage texture. The Vulkan + // validation errors warn if you create such a vkImageView, so return early. + return {}; + } + + Device* device = ToBackend(GetTexture()->GetDevice()); VkImageViewCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -477,19 +1004,18 @@ namespace dawn_native { namespace vulkan { createInfo.flags = 0; createInfo.image = ToBackend(GetTexture())->GetHandle(); createInfo.viewType = VulkanImageViewType(descriptor->dimension); - createInfo.format = VulkanImageFormat(descriptor->format); + createInfo.format = VulkanImageFormat(device, descriptor->format); createInfo.components = VkComponentMapping{VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A}; - createInfo.subresourceRange.aspectMask = VulkanAspectMask(descriptor->format); + createInfo.subresourceRange.aspectMask = VulkanAspectMask(GetFormat()); createInfo.subresourceRange.baseMipLevel = descriptor->baseMipLevel; createInfo.subresourceRange.levelCount = descriptor->mipLevelCount; createInfo.subresourceRange.baseArrayLayer = descriptor->baseArrayLayer; createInfo.subresourceRange.layerCount = descriptor->arrayLayerCount; - if (device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &mHandle) != - VK_SUCCESS) { - ASSERT(false); - } + return CheckVkSuccess( + device->fn.CreateImageView(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), + "CreateImageView"); } TextureView::~TextureView() { diff --git a/third_party/dawn/src/dawn_native/vulkan/TextureVk.h b/third_party/dawn/src/dawn_native/vulkan/TextureVk.h index decc9975687..8a1564af72b 100644 --- a/third_party/dawn/src/dawn_native/vulkan/TextureVk.h +++ b/third_party/dawn/src/dawn_native/vulkan/TextureVk.h @@ -18,19 +18,45 @@ #include "dawn_native/Texture.h" #include "common/vulkan_platform.h" -#include "dawn_native/vulkan/MemoryAllocator.h" +#include "dawn_native/PassResourceUsage.h" +#include "dawn_native/ResourceMemoryAllocation.h" +#include "dawn_native/vulkan/ExternalHandle.h" +#include "dawn_native/vulkan/external_memory/MemoryService.h" namespace dawn_native { namespace vulkan { - VkFormat VulkanImageFormat(dawn::TextureFormat format); - VkImageUsageFlags VulkanImageUsage(dawn::TextureUsageBit usage, dawn::TextureFormat format); + struct CommandRecordingContext; + class Device; + + VkFormat VulkanImageFormat(const Device* device, wgpu::TextureFormat format); + VkImageUsageFlags VulkanImageUsage(wgpu::TextureUsage usage, const Format& format); VkSampleCountFlagBits VulkanSampleCount(uint32_t sampleCount); - class Texture : public TextureBase { + MaybeError ValidateVulkanImageCanBeWrapped(const DeviceBase* device, + const TextureDescriptor* descriptor); + + bool IsSampleCountSupported(const dawn_native::vulkan::Device* device, + const VkImageCreateInfo& imageCreateInfo); + + class Texture final : public TextureBase { public: - Texture(Device* device, const TextureDescriptor* descriptor); - Texture(Device* device, const TextureDescriptor* descriptor, VkImage nativeImage); - ~Texture(); + // Used to create a regular texture from a descriptor. + static ResultOrError> Create(Device* device, + const TextureDescriptor* descriptor); + + // Creates a texture and initializes it with a VkImage that references an external memory + // object. Before the texture can be used, the VkDeviceMemory associated with the external + // image must be bound via Texture::BindExternalMemory. + static ResultOrError CreateFromExternal( + Device* device, + const ExternalImageDescriptor* descriptor, + const TextureDescriptor* textureDescriptor, + external_memory::Service* externalMemoryService); + + // Creates a texture that wraps a swapchain-allocated VkImage. + static Ref CreateForSwapChain(Device* device, + const TextureDescriptor* descriptor, + VkImage nativeImage); VkImage GetHandle() const; VkImageAspectFlags GetVkAspectMask() const; @@ -38,37 +64,84 @@ namespace dawn_native { namespace vulkan { // Transitions the texture to be used as `usage`, recording any necessary barrier in // `commands`. // TODO(cwallez@chromium.org): coalesce barriers and do them early when possible. - void TransitionUsageNow(VkCommandBuffer commands, dawn::TextureUsageBit usage); - void EnsureSubresourceContentInitialized(VkCommandBuffer commands, - uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount); + void TransitionFullUsage(CommandRecordingContext* recordingContext, + wgpu::TextureUsage usage); + + void TransitionUsageNow(CommandRecordingContext* recordingContext, + wgpu::TextureUsage usage, + const SubresourceRange& range); + void TransitionUsageForPass(CommandRecordingContext* recordingContext, + const PassTextureUsage& textureUsages, + std::vector* imageBarriers, + VkPipelineStageFlags* srcStages, + VkPipelineStageFlags* dstStages); + + void EnsureSubresourceContentInitialized(CommandRecordingContext* recordingContext, + const SubresourceRange& range); + + MaybeError SignalAndDestroy(VkSemaphore* outSignalSemaphore); + // Binds externally allocated memory to the VkImage and on success, takes ownership of + // semaphores. + MaybeError BindExternalMemory(const ExternalImageDescriptor* descriptor, + VkSemaphore signalSemaphore, + VkDeviceMemory externalMemoryAllocation, + std::vector waitSemaphores); private: + ~Texture() override; + using TextureBase::TextureBase; + + MaybeError InitializeAsInternalTexture(); + MaybeError InitializeFromExternal(const ExternalImageDescriptor* descriptor, + external_memory::Service* externalMemoryService); + void InitializeForSwapChain(VkImage nativeImage); + void DestroyImpl() override; - void ClearTexture(VkCommandBuffer commands, - uint32_t baseMipLevel, - uint32_t levelCount, - uint32_t baseArrayLayer, - uint32_t layerCount); + MaybeError ClearTexture(CommandRecordingContext* recordingContext, + const SubresourceRange& range, + TextureBase::ClearValue); + + void TweakTransitionForExternalUsage(CommandRecordingContext* recordingContext, + std::vector* barriers, + size_t transitionBarrierStart); + bool CanReuseWithoutBarrier(wgpu::TextureUsage lastUsage, wgpu::TextureUsage usage); VkImage mHandle = VK_NULL_HANDLE; - DeviceMemoryAllocation mMemoryAllocation; + ResourceMemoryAllocation mMemoryAllocation; + VkDeviceMemory mExternalAllocation = VK_NULL_HANDLE; + + enum class ExternalState { + InternalOnly, + PendingAcquire, + Acquired, + PendingRelease, + Released + }; + ExternalState mExternalState = ExternalState::InternalOnly; + ExternalState mLastExternalState = ExternalState::InternalOnly; + + VkSemaphore mSignalSemaphore = VK_NULL_HANDLE; + std::vector mWaitRequirements; + + bool mSameLastUsagesAcrossSubresources = true; // A usage of none will make sure the texture is transitioned before its first use as - // required by the spec. - dawn::TextureUsageBit mLastUsage = dawn::TextureUsageBit::None; + // required by the Vulkan spec. + std::vector mSubresourceLastUsages = + std::vector(GetSubresourceCount(), wgpu::TextureUsage::None); }; - class TextureView : public TextureViewBase { + class TextureView final : public TextureViewBase { public: - TextureView(TextureBase* texture, const TextureViewDescriptor* descriptor); - ~TextureView(); - + static ResultOrError Create(TextureBase* texture, + const TextureViewDescriptor* descriptor); VkImageView GetHandle() const; private: + ~TextureView() override; + using TextureViewBase::TextureViewBase; + MaybeError Initialize(const TextureViewDescriptor* descriptor); + VkImageView mHandle = VK_NULL_HANDLE; }; diff --git a/third_party/dawn/src/dawn_native/vulkan/UtilsVulkan.cpp b/third_party/dawn/src/dawn_native/vulkan/UtilsVulkan.cpp index 723a6bb9c80..15011ce5270 100644 --- a/third_party/dawn/src/dawn_native/vulkan/UtilsVulkan.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/UtilsVulkan.cpp @@ -15,30 +15,104 @@ #include "dawn_native/vulkan/UtilsVulkan.h" #include "common/Assert.h" +#include "dawn_native/Format.h" +#include "dawn_native/vulkan/Forward.h" +#include "dawn_native/vulkan/TextureVk.h" namespace dawn_native { namespace vulkan { - VkCompareOp ToVulkanCompareOp(dawn::CompareFunction op) { + VkCompareOp ToVulkanCompareOp(wgpu::CompareFunction op) { switch (op) { - case dawn::CompareFunction::Always: - return VK_COMPARE_OP_ALWAYS; - case dawn::CompareFunction::Equal: - return VK_COMPARE_OP_EQUAL; - case dawn::CompareFunction::Greater: - return VK_COMPARE_OP_GREATER; - case dawn::CompareFunction::GreaterEqual: - return VK_COMPARE_OP_GREATER_OR_EQUAL; - case dawn::CompareFunction::Less: + case wgpu::CompareFunction::Never: + return VK_COMPARE_OP_NEVER; + case wgpu::CompareFunction::Less: return VK_COMPARE_OP_LESS; - case dawn::CompareFunction::LessEqual: + case wgpu::CompareFunction::LessEqual: return VK_COMPARE_OP_LESS_OR_EQUAL; - case dawn::CompareFunction::Never: - return VK_COMPARE_OP_NEVER; - case dawn::CompareFunction::NotEqual: + case wgpu::CompareFunction::Greater: + return VK_COMPARE_OP_GREATER; + case wgpu::CompareFunction::GreaterEqual: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + case wgpu::CompareFunction::Equal: + return VK_COMPARE_OP_EQUAL; + case wgpu::CompareFunction::NotEqual: return VK_COMPARE_OP_NOT_EQUAL; + case wgpu::CompareFunction::Always: + return VK_COMPARE_OP_ALWAYS; default: UNREACHABLE(); } } -}} // namespace dawn_native::vulkan + // Vulkan SPEC requires the source/destination region specified by each element of + // pRegions must be a region that is contained within srcImage/dstImage. Here the size of + // the image refers to the virtual size, while Dawn validates texture copy extent with the + // physical size, so we need to re-calculate the texture copy extent to ensure it should fit + // in the virtual size of the subresource. + Extent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy, const Extent3D& copySize) { + Extent3D validTextureCopyExtent = copySize; + const TextureBase* texture = textureCopy.texture.Get(); + Extent3D virtualSizeAtLevel = texture->GetMipLevelVirtualSize(textureCopy.mipLevel); + if (textureCopy.origin.x + copySize.width > virtualSizeAtLevel.width) { + ASSERT(texture->GetFormat().isCompressed); + validTextureCopyExtent.width = virtualSizeAtLevel.width - textureCopy.origin.x; + } + if (textureCopy.origin.y + copySize.height > virtualSizeAtLevel.height) { + ASSERT(texture->GetFormat().isCompressed); + validTextureCopyExtent.height = virtualSizeAtLevel.height - textureCopy.origin.y; + } + + return validTextureCopyExtent; + } + + VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy, + const TextureCopy& textureCopy, + const Extent3D& copySize) { + TextureDataLayout passDataLayout; + passDataLayout.offset = bufferCopy.offset; + passDataLayout.rowsPerImage = bufferCopy.rowsPerImage; + passDataLayout.bytesPerRow = bufferCopy.bytesPerRow; + return ComputeBufferImageCopyRegion(passDataLayout, textureCopy, copySize); + } + + VkBufferImageCopy ComputeBufferImageCopyRegion(const TextureDataLayout& dataLayout, + const TextureCopy& textureCopy, + const Extent3D& copySize) { + const Texture* texture = ToBackend(textureCopy.texture.Get()); + + VkBufferImageCopy region; + + region.bufferOffset = dataLayout.offset; + // In Vulkan the row length is in texels while it is in bytes for Dawn + const Format& format = texture->GetFormat(); + ASSERT(dataLayout.bytesPerRow % format.blockByteSize == 0); + region.bufferRowLength = dataLayout.bytesPerRow / format.blockByteSize * format.blockWidth; + region.bufferImageHeight = dataLayout.rowsPerImage; + + region.imageSubresource.aspectMask = texture->GetVkAspectMask(); + region.imageSubresource.mipLevel = textureCopy.mipLevel; + + switch (textureCopy.texture->GetDimension()) { + case wgpu::TextureDimension::e2D: { + region.imageOffset.x = textureCopy.origin.x; + region.imageOffset.y = textureCopy.origin.y; + region.imageOffset.z = 0; + + region.imageSubresource.baseArrayLayer = textureCopy.origin.z; + region.imageSubresource.layerCount = copySize.depth; + + Extent3D imageExtent = ComputeTextureCopyExtent(textureCopy, copySize); + region.imageExtent.width = imageExtent.width; + region.imageExtent.height = imageExtent.height; + region.imageExtent.depth = 1; + break; + } + + default: + UNREACHABLE(); + break; + } + + return region; + } +}} // namespace dawn_native::vulkan \ No newline at end of file diff --git a/third_party/dawn/src/dawn_native/vulkan/UtilsVulkan.h b/third_party/dawn/src/dawn_native/vulkan/UtilsVulkan.h index 80fa2da0479..36ed8fc6ff5 100644 --- a/third_party/dawn/src/dawn_native/vulkan/UtilsVulkan.h +++ b/third_party/dawn/src/dawn_native/vulkan/UtilsVulkan.h @@ -16,12 +16,87 @@ #define DAWNNATIVE_VULKAN_UTILSVULKAN_H_ #include "common/vulkan_platform.h" +#include "dawn_native/Commands.h" #include "dawn_native/dawn_platform.h" namespace dawn_native { namespace vulkan { - VkCompareOp ToVulkanCompareOp(dawn::CompareFunction op); + // A Helper type used to build a pNext chain of extension structs. + // Usage is: + // 1) Create instance, passing the address of the first struct in the + // chain. This will parse the existing |pNext| chain in it to find + // its tail. + // + // 2) Call Add(&vk_struct) every time a new struct needs to be appended + // to the chain. + // + // 3) Alternatively, call Add(&vk_struct, VK_STRUCTURE_TYPE_XXX) to + // initialize the struct with a given VkStructureType value while + // appending it to the chain. + // + // Examples: + // VkPhysicalFeatures2 features2 = { + // .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + // .pNext = nullptr, + // }; + // + // PNextChainBuilder featuresChain(&features2); + // + // featuresChain.Add(&featuresExtensions.subgroupSizeControl, + // VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT); + // + struct PNextChainBuilder { + // Constructor takes the address of a Vulkan structure instance, and + // walks its pNext chain to record the current location of its tail. + // + // NOTE: Some VK_STRUCT_TYPEs define their pNext field as a const void* + // which is why the VkBaseOutStructure* casts below are necessary. + template + explicit PNextChainBuilder(VK_STRUCT_TYPE* head) + : mCurrent(reinterpret_cast(head)) { + // Find the end of the current chain. + while (mCurrent->pNext != nullptr) { + mCurrent = mCurrent->pNext; + } + } + + // Add one item to the chain. |vk_struct| must be a Vulkan structure + // that is already initialized. + template + void Add(VK_STRUCT_TYPE* vkStruct) { + // Sanity checks to ensure proper type safety. + static_assert( + offsetof(VK_STRUCT_TYPE, sType) == offsetof(VkBaseOutStructure, sType) && + offsetof(VK_STRUCT_TYPE, pNext) == offsetof(VkBaseOutStructure, pNext), + "Argument type is not a proper Vulkan structure type"); + vkStruct->pNext = nullptr; + + mCurrent->pNext = reinterpret_cast(vkStruct); + mCurrent = mCurrent->pNext; + } + + // A variant of Add() above that also initializes the |sType| field in |vk_struct|. + template + void Add(VK_STRUCT_TYPE* vkStruct, VkStructureType sType) { + vkStruct->sType = sType; + Add(vkStruct); + } + + private: + VkBaseOutStructure* mCurrent; + }; + + VkCompareOp ToVulkanCompareOp(wgpu::CompareFunction op); + + Extent3D ComputeTextureCopyExtent(const TextureCopy& textureCopy, const Extent3D& copySize); + + VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy, + const TextureCopy& textureCopy, + const Extent3D& copySize); + VkBufferImageCopy ComputeBufferImageCopyRegion(const TextureDataLayout& dataLayout, + const TextureCopy& textureCopy, + const Extent3D& copySize); }} // namespace dawn_native::vulkan -#endif // DAWNNATIVE_VULKAN_UTILSVULKAN_H_ +#endif // DAWNNATIVE_VULKAN_UTILSVULKAN_H_ \ No newline at end of file diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanBackend.cpp b/third_party/dawn/src/dawn_native/vulkan/VulkanBackend.cpp index acc624a618a..c1458a00fca 100644 --- a/third_party/dawn/src/dawn_native/vulkan/VulkanBackend.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanBackend.cpp @@ -24,32 +24,86 @@ #include "common/SwapChainUtils.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/NativeSwapChainImplVk.h" +#include "dawn_native/vulkan/TextureVk.h" namespace dawn_native { namespace vulkan { - VkInstance GetInstance(DawnDevice device) { + VkInstance GetInstance(WGPUDevice device) { Device* backendDevice = reinterpret_cast(device); return backendDevice->GetVkInstance(); } + DAWN_NATIVE_EXPORT PFN_vkVoidFunction GetInstanceProcAddr(WGPUDevice device, + const char* pName) { + Device* backendDevice = reinterpret_cast(device); + return (*backendDevice->fn.GetInstanceProcAddr)(backendDevice->GetVkInstance(), pName); + } + // Explicitly export this function because it uses the "native" type for surfaces while the // header as seen in this file uses the wrapped type. DAWN_NATIVE_EXPORT DawnSwapChainImplementation - CreateNativeSwapChainImpl(DawnDevice device, VkSurfaceKHRNative surfaceNative) { + CreateNativeSwapChainImpl(WGPUDevice device, ::VkSurfaceKHR surfaceNative) { Device* backendDevice = reinterpret_cast(device); VkSurfaceKHR surface = VkSurfaceKHR::CreateFromHandle(surfaceNative); DawnSwapChainImplementation impl; impl = CreateSwapChainImplementation(new NativeSwapChainImpl(backendDevice, surface)); - impl.textureUsage = DAWN_TEXTURE_USAGE_BIT_PRESENT; + impl.textureUsage = WGPUTextureUsage_Present; return impl; } - DawnTextureFormat GetNativeSwapChainPreferredFormat( + WGPUTextureFormat GetNativeSwapChainPreferredFormat( const DawnSwapChainImplementation* swapChain) { NativeSwapChainImpl* impl = reinterpret_cast(swapChain->userData); - return static_cast(impl->GetPreferredFormat()); + return static_cast(impl->GetPreferredFormat()); + } + +#ifdef DAWN_PLATFORM_LINUX + ExternalImageDescriptorFD::ExternalImageDescriptorFD(ExternalImageDescriptorType descType) + : ExternalImageDescriptor(descType) { + } + + ExternalImageDescriptorOpaqueFD::ExternalImageDescriptorOpaqueFD() + : ExternalImageDescriptorFD(ExternalImageDescriptorType::OpaqueFD) { + } + + ExternalImageDescriptorDmaBuf::ExternalImageDescriptorDmaBuf() + : ExternalImageDescriptorFD(ExternalImageDescriptorType::DmaBuf) { + } + + int ExportSignalSemaphoreOpaqueFD(WGPUDevice cDevice, WGPUTexture cTexture) { + Device* device = reinterpret_cast(cDevice); + Texture* texture = reinterpret_cast(cTexture); + + if (!texture) { + return -1; + } + + ExternalSemaphoreHandle outHandle; + if (device->ConsumedError(device->SignalAndExportExternalTexture(texture, &outHandle))) { + return -1; + } + + return outHandle; + } + + WGPUTexture WrapVulkanImage(WGPUDevice cDevice, const ExternalImageDescriptor* descriptor) { + Device* device = reinterpret_cast(cDevice); + + switch (descriptor->type) { + case ExternalImageDescriptorType::OpaqueFD: + case ExternalImageDescriptorType::DmaBuf: { + const ExternalImageDescriptorFD* fdDescriptor = + static_cast(descriptor); + TextureBase* texture = device->CreateTextureWrappingVulkanImage( + descriptor, fdDescriptor->memoryFD, fdDescriptor->waitFDs); + return reinterpret_cast(texture); + } + default: + return nullptr; + } } +#endif }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanError.cpp b/third_party/dawn/src/dawn_native/vulkan/VulkanError.cpp index a01475c23f4..f329f0851e9 100644 --- a/third_party/dawn/src/dawn_native/vulkan/VulkanError.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanError.cpp @@ -18,8 +18,12 @@ namespace dawn_native { namespace vulkan { - const char* VkResultAsString(VkResult result) { - switch (result) { + const char* VkResultAsString(::VkResult result) { + // Convert to a int32_t to silence and MSVC warning that the fake errors don't appear in + // the original VkResult enum. + int32_t code = static_cast(result); + + switch (code) { case VK_SUCCESS: return "VK_SUCCESS"; case VK_NOT_READY: @@ -56,18 +60,44 @@ namespace dawn_native { namespace vulkan { return "VK_ERROR_FORMAT_NOT_SUPPORTED"; case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL"; + case VK_FAKE_DEVICE_OOM_FOR_TESTING: + return "VK_FAKE_DEVICE_OOM_FOR_TESTING"; + case VK_FAKE_ERROR_FOR_TESTING: + return "VK_FAKE_ERROR_FOR_TESTING"; default: return ""; } } - MaybeError CheckVkSuccess(VkResult result, const char* context) { + MaybeError CheckVkSuccessImpl(VkResult result, const char* context) { + if (DAWN_LIKELY(result == VK_SUCCESS)) { + return {}; + } + + std::string message = std::string(context) + " failed with " + VkResultAsString(result); + + if (result == VK_ERROR_DEVICE_LOST) { + return DAWN_DEVICE_LOST_ERROR(message); + } else { + return DAWN_INTERNAL_ERROR(message); + } + } + + MaybeError CheckVkOOMThenSuccessImpl(VkResult result, const char* context) { if (DAWN_LIKELY(result == VK_SUCCESS)) { return {}; } std::string message = std::string(context) + " failed with " + VkResultAsString(result); - return DAWN_CONTEXT_LOST_ERROR(message); + + if (result == VK_ERROR_OUT_OF_DEVICE_MEMORY || result == VK_ERROR_OUT_OF_HOST_MEMORY || + result == VK_FAKE_DEVICE_OOM_FOR_TESTING) { + return DAWN_OUT_OF_MEMORY_ERROR(message); + } else if (result == VK_ERROR_DEVICE_LOST) { + return DAWN_DEVICE_LOST_ERROR(message); + } else { + return DAWN_INTERNAL_ERROR(message); + } } }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanError.h b/third_party/dawn/src/dawn_native/vulkan/VulkanError.h index 3dedece1ad4..7748f56cd39 100644 --- a/third_party/dawn/src/dawn_native/vulkan/VulkanError.h +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanError.h @@ -15,19 +15,35 @@ #ifndef DAWNNATIVE_VULKAN_VULKANERROR_H_ #define DAWNNATIVE_VULKAN_VULKANERROR_H_ -#include "common/vulkan_platform.h" -#include "dawn_native/Error.h" +#include "dawn_native/ErrorInjector.h" +#include "dawn_native/vulkan/VulkanFunctions.h" + +constexpr VkResult VK_FAKE_ERROR_FOR_TESTING = VK_RESULT_MAX_ENUM; +constexpr VkResult VK_FAKE_DEVICE_OOM_FOR_TESTING = static_cast(VK_RESULT_MAX_ENUM - 1); namespace dawn_native { namespace vulkan { // Returns a string version of the result. - const char* VkResultAsString(VkResult result); + const char* VkResultAsString(::VkResult result); + + MaybeError CheckVkSuccessImpl(VkResult result, const char* context); + MaybeError CheckVkOOMThenSuccessImpl(VkResult result, const char* context); + +// Returns a success only if result if VK_SUCCESS, an error with the context and stringified +// result value instead. Can be used like this: +// +// DAWN_TRY(CheckVkSuccess(vkDoSomething, "doing something")); +#define CheckVkSuccess(resultIn, contextIn) \ + ::dawn_native::vulkan::CheckVkSuccessImpl( \ + ::dawn_native::vulkan::VkResult::WrapUnsafe( \ + INJECT_ERROR_OR_RUN(resultIn, VK_FAKE_ERROR_FOR_TESTING)), \ + contextIn) - // Returns a success only if result if VK_SUCCESS, an error with the context and stringified - // result value instead. Can be used like this: - // - // DAWN_TRY(CheckVkSuccess(vkDoSomething, "doing something")); - MaybeError CheckVkSuccess(VkResult result, const char* context); +#define CheckVkOOMThenSuccess(resultIn, contextIn) \ + ::dawn_native::vulkan::CheckVkOOMThenSuccessImpl( \ + ::dawn_native::vulkan::VkResult::WrapUnsafe(INJECT_ERROR_OR_RUN( \ + resultIn, VK_FAKE_DEVICE_OOM_FOR_TESTING, VK_FAKE_ERROR_FOR_TESTING)), \ + contextIn) }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanExtensions.cpp b/third_party/dawn/src/dawn_native/vulkan/VulkanExtensions.cpp new file mode 100644 index 00000000000..1e7f23d5e7e --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanExtensions.cpp @@ -0,0 +1,320 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/vulkan/VulkanExtensions.h" + +#include "common/Assert.h" +#include "common/vulkan_platform.h" + +#include +#include + +namespace dawn_native { namespace vulkan { + + static constexpr uint32_t VulkanVersion_1_1 = VK_MAKE_VERSION(1, 1, 0); + static constexpr uint32_t VulkanVersion_1_2 = VK_MAKE_VERSION(1, 2, 0); + static constexpr uint32_t NeverPromoted = std::numeric_limits::max(); + + // A static array for InstanceExtInfo that can be indexed with InstanceExts. + // GetInstanceExtInfo checks that "index" matches the index used to access this array so an + // assert will fire if it isn't in the correct order. + static constexpr size_t kInstanceExtCount = static_cast(InstanceExt::EnumCount); + static constexpr std::array sInstanceExtInfos{{ + // + {InstanceExt::GetPhysicalDeviceProperties2, "VK_KHR_get_physical_device_properties2", + VulkanVersion_1_1}, + {InstanceExt::ExternalMemoryCapabilities, "VK_KHR_external_memory_capabilities", + VulkanVersion_1_1}, + {InstanceExt::ExternalSemaphoreCapabilities, "VK_KHR_external_semaphore_capabilities", + VulkanVersion_1_1}, + + {InstanceExt::Surface, "VK_KHR_surface", NeverPromoted}, + {InstanceExt::FuchsiaImagePipeSurface, "VK_FUCHSIA_imagepipe_surface", NeverPromoted}, + {InstanceExt::MetalSurface, "VK_EXT_metal_surface", NeverPromoted}, + {InstanceExt::WaylandSurface, "VK_KHR_wayland_surface", NeverPromoted}, + {InstanceExt::Win32Surface, "VK_KHR_win32_surface", NeverPromoted}, + {InstanceExt::XcbSurface, "VK_KHR_xcb_surface", NeverPromoted}, + {InstanceExt::XlibSurface, "VK_KHR_xlib_surface", NeverPromoted}, + + {InstanceExt::DebugReport, "VK_EXT_debug_report", NeverPromoted} + // + }}; + + void InstanceExtSet::Set(InstanceExt extension, bool enabled) { + extensionBitSet.set(static_cast(extension), enabled); + } + + bool InstanceExtSet::Has(InstanceExt extension) const { + return extensionBitSet[static_cast(extension)]; + } + + const InstanceExtInfo& GetInstanceExtInfo(InstanceExt ext) { + uint32_t index = static_cast(ext); + ASSERT(index < sInstanceExtInfos.size()); + ASSERT(sInstanceExtInfos[index].index == ext); + return sInstanceExtInfos[index]; + } + + std::unordered_map CreateInstanceExtNameMap() { + std::unordered_map result; + for (const InstanceExtInfo& info : sInstanceExtInfos) { + result[info.name] = info.index; + } + return result; + } + + InstanceExtSet EnsureDependencies(const InstanceExtSet& advertisedExts) { + // We need to check that all transitive dependencies of extensions are advertised. + // To do that in a single pass and no data structures, the extensions are topologically + // sorted in the definition of InstanceExt. + // To ensure the order is correct, we mark visited extensions in `visitedSet` and each + // dependency check will first assert all its dependents have been visited. + InstanceExtSet visitedSet; + InstanceExtSet trimmedSet; + + auto HasDep = [&](InstanceExt ext) -> bool { + ASSERT(visitedSet.Has(ext)); + return trimmedSet.Has(ext); + }; + + for (uint32_t i = 0; i < sInstanceExtInfos.size(); i++) { + InstanceExt ext = static_cast(i); + + bool hasDependencies = false; + switch (ext) { + case InstanceExt::GetPhysicalDeviceProperties2: + case InstanceExt::Surface: + case InstanceExt::DebugReport: + hasDependencies = true; + break; + + case InstanceExt::ExternalMemoryCapabilities: + case InstanceExt::ExternalSemaphoreCapabilities: + hasDependencies = HasDep(InstanceExt::GetPhysicalDeviceProperties2); + break; + + case InstanceExt::FuchsiaImagePipeSurface: + case InstanceExt::MetalSurface: + case InstanceExt::WaylandSurface: + case InstanceExt::Win32Surface: + case InstanceExt::XcbSurface: + case InstanceExt::XlibSurface: + hasDependencies = HasDep(InstanceExt::Surface); + break; + + default: + UNREACHABLE(); + break; + } + + trimmedSet.Set(ext, hasDependencies && advertisedExts.Has(ext)); + visitedSet.Set(ext, true); + } + + return trimmedSet; + } + + void MarkPromotedExtensions(InstanceExtSet* extensions, uint32_t version) { + for (const InstanceExtInfo& info : sInstanceExtInfos) { + if (info.versionPromoted <= version) { + extensions->Set(info.index, true); + } + } + } + + static constexpr size_t kDeviceExtCount = static_cast(DeviceExt::EnumCount); + static constexpr std::array sDeviceExtInfos{{ + // + {DeviceExt::BindMemory2, "VK_KHR_bind_memory2", VulkanVersion_1_1}, + {DeviceExt::Maintenance1, "VK_KHR_maintenance1", VulkanVersion_1_1}, + {DeviceExt::StorageBufferStorageClass, "VK_KHR_storage_buffer_storage_class", + VulkanVersion_1_1}, + {DeviceExt::GetPhysicalDeviceProperties2, "VK_KHR_get_physical_device_properties2", + VulkanVersion_1_1}, + {DeviceExt::GetMemoryRequirements2, "VK_KHR_get_memory_requirements2", VulkanVersion_1_1}, + {DeviceExt::ExternalMemoryCapabilities, "VK_KHR_external_memory_capabilities", + VulkanVersion_1_1}, + {DeviceExt::ExternalSemaphoreCapabilities, "VK_KHR_external_semaphore_capabilities", + VulkanVersion_1_1}, + {DeviceExt::ExternalMemory, "VK_KHR_external_memory", VulkanVersion_1_1}, + {DeviceExt::ExternalSemaphore, "VK_KHR_external_semaphore", VulkanVersion_1_1}, + {DeviceExt::_16BitStorage, "VK_KHR_16bit_storage", VulkanVersion_1_1}, + {DeviceExt::SamplerYCbCrConversion, "VK_KHR_sampler_ycbcr_conversion", VulkanVersion_1_1}, + + {DeviceExt::ImageFormatList, "VK_KHR_image_format_list", VulkanVersion_1_2}, + {DeviceExt::ShaderFloat16Int8, "VK_KHR_shader_float16_int8", VulkanVersion_1_2}, + + {DeviceExt::ExternalMemoryFD, "VK_KHR_external_memory_fd", NeverPromoted}, + {DeviceExt::ExternalMemoryDmaBuf, "VK_EXT_external_memory_dma_buf", NeverPromoted}, + {DeviceExt::ExternalMemoryZirconHandle, "VK_FUCHSIA_external_memory", NeverPromoted}, + {DeviceExt::ExternalSemaphoreFD, "VK_KHR_external_semaphore_fd", NeverPromoted}, + {DeviceExt::ExternalSemaphoreZirconHandle, "VK_FUCHSIA_external_semaphore", NeverPromoted}, + + {DeviceExt::DebugMarker, "VK_EXT_debug_marker", NeverPromoted}, + {DeviceExt::ImageDrmFormatModifier, "VK_EXT_image_drm_format_modifier", NeverPromoted}, + {DeviceExt::Swapchain, "VK_KHR_swapchain", NeverPromoted}, + {DeviceExt::SubgroupSizeControl, "VK_EXT_subgroup_size_control", NeverPromoted}, + // + }}; + + void DeviceExtSet::Set(DeviceExt extension, bool enabled) { + extensionBitSet.set(static_cast(extension), enabled); + } + + bool DeviceExtSet::Has(DeviceExt extension) const { + return extensionBitSet[static_cast(extension)]; + } + + const DeviceExtInfo& GetDeviceExtInfo(DeviceExt ext) { + uint32_t index = static_cast(ext); + ASSERT(index < sDeviceExtInfos.size()); + ASSERT(sDeviceExtInfos[index].index == ext); + return sDeviceExtInfos[index]; + } + + std::unordered_map CreateDeviceExtNameMap() { + std::unordered_map result; + for (const DeviceExtInfo& info : sDeviceExtInfos) { + result[info.name] = info.index; + } + return result; + } + + DeviceExtSet EnsureDependencies(const DeviceExtSet& advertisedExts, + const InstanceExtSet& instanceExts, + uint32_t icdVersion) { + // This is very similar to EnsureDependencies for instanceExtSet. See comment there for + // an explanation of what happens. + DeviceExtSet visitedSet; + DeviceExtSet trimmedSet; + + auto HasDep = [&](DeviceExt ext) -> bool { + ASSERT(visitedSet.Has(ext)); + return trimmedSet.Has(ext); + }; + + for (uint32_t i = 0; i < sDeviceExtInfos.size(); i++) { + DeviceExt ext = static_cast(i); + + bool hasDependencies = false; + switch (ext) { + // Happy extensions don't need anybody else! + case DeviceExt::BindMemory2: + case DeviceExt::GetMemoryRequirements2: + case DeviceExt::Maintenance1: + case DeviceExt::ImageFormatList: + case DeviceExt::StorageBufferStorageClass: + hasDependencies = true; + break; + + // Physical device extensions technically don't require the instance to support + // them but VulkanFunctions only loads the function pointers if the instance + // advertises the extension. So if we didn't have this check, we'd risk a calling + // a nullptr. + case DeviceExt::GetPhysicalDeviceProperties2: + hasDependencies = instanceExts.Has(InstanceExt::GetPhysicalDeviceProperties2); + break; + case DeviceExt::ExternalMemoryCapabilities: + hasDependencies = instanceExts.Has(InstanceExt::ExternalMemoryCapabilities) && + HasDep(DeviceExt::GetPhysicalDeviceProperties2); + break; + case DeviceExt::ExternalSemaphoreCapabilities: + hasDependencies = + instanceExts.Has(InstanceExt::ExternalSemaphoreCapabilities) && + HasDep(DeviceExt::GetPhysicalDeviceProperties2); + break; + + case DeviceExt::DebugMarker: + // TODO(cwallez@chromium.org): VK_KHR_debug_report is deprecated, switch to + // using VK_KHR_debug_utils instead. + hasDependencies = instanceExts.Has(InstanceExt::DebugReport); + break; + + case DeviceExt::ImageDrmFormatModifier: + hasDependencies = HasDep(DeviceExt::BindMemory2) && + HasDep(DeviceExt::GetPhysicalDeviceProperties2) && + HasDep(DeviceExt::ImageFormatList) && + HasDep(DeviceExt::SamplerYCbCrConversion); + break; + + case DeviceExt::Swapchain: + hasDependencies = instanceExts.Has(InstanceExt::Surface); + break; + + case DeviceExt::SamplerYCbCrConversion: + hasDependencies = HasDep(DeviceExt::Maintenance1) && + HasDep(DeviceExt::BindMemory2) && + HasDep(DeviceExt::GetMemoryRequirements2) && + HasDep(DeviceExt::GetPhysicalDeviceProperties2); + break; + + case DeviceExt::ShaderFloat16Int8: + hasDependencies = HasDep(DeviceExt::GetPhysicalDeviceProperties2); + break; + + case DeviceExt::ExternalMemory: + hasDependencies = HasDep(DeviceExt::ExternalMemoryCapabilities); + break; + + case DeviceExt::ExternalSemaphore: + hasDependencies = HasDep(DeviceExt::ExternalSemaphoreCapabilities); + break; + + case DeviceExt::ExternalMemoryFD: + case DeviceExt::ExternalMemoryZirconHandle: + hasDependencies = HasDep(DeviceExt::ExternalMemory); + break; + + case DeviceExt::ExternalMemoryDmaBuf: + hasDependencies = HasDep(DeviceExt::ExternalMemoryFD); + break; + + case DeviceExt::ExternalSemaphoreFD: + case DeviceExt::ExternalSemaphoreZirconHandle: + hasDependencies = HasDep(DeviceExt::ExternalSemaphore); + break; + + case DeviceExt::_16BitStorage: + hasDependencies = HasDep(DeviceExt::GetPhysicalDeviceProperties2) && + HasDep(DeviceExt::StorageBufferStorageClass); + break; + + case DeviceExt::SubgroupSizeControl: + // Using the extension requires DeviceExt::GetPhysicalDeviceProperties2, but we + // don't need to check for it as it also requires Vulkan 1.1 in which + // VK_KHR_get_physical_device_properties2 was promoted. + hasDependencies = icdVersion >= VulkanVersion_1_1; + break; + + default: + UNREACHABLE(); + break; + } + + trimmedSet.Set(ext, hasDependencies && advertisedExts.Has(ext)); + visitedSet.Set(ext, true); + } + + return trimmedSet; + } + + void MarkPromotedExtensions(DeviceExtSet* extensions, uint32_t version) { + for (const DeviceExtInfo& info : sDeviceExtInfos) { + if (info.versionPromoted <= version) { + extensions->Set(info.index, true); + } + } + } + +}} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanExtensions.h b/third_party/dawn/src/dawn_native/vulkan/VulkanExtensions.h new file mode 100644 index 00000000000..ba6abc2c0af --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanExtensions.h @@ -0,0 +1,141 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_VULKAN_VULKANEXTENSIONS_H_ +#define DAWNNATIVE_VULKAN_VULKANEXTENSIONS_H_ + +#include +#include + +namespace dawn_native { namespace vulkan { + + // The list of known instance extensions. They must be in dependency order (this is checked + // inside EnsureDependencies) + enum class InstanceExt { + // Promoted to 1.1 + GetPhysicalDeviceProperties2, + ExternalMemoryCapabilities, + ExternalSemaphoreCapabilities, + + // Surface extensions + Surface, + FuchsiaImagePipeSurface, + MetalSurface, + WaylandSurface, + Win32Surface, + XcbSurface, + XlibSurface, + + // Others + DebugReport, + + EnumCount, + }; + + // A bitset wrapper that is indexed with InstanceExt. + struct InstanceExtSet { + std::bitset(InstanceExt::EnumCount)> extensionBitSet; + void Set(InstanceExt extension, bool enabled); + bool Has(InstanceExt extension) const; + }; + + // Information about a known instance extension. + struct InstanceExtInfo { + InstanceExt index; + const char* name; + // The version in which this extension was promoted as built with VK_MAKE_VERSION, + // or NeverPromoted if it was never promoted. + uint32_t versionPromoted; + }; + + // Returns the information about a known InstanceExt + const InstanceExtInfo& GetInstanceExtInfo(InstanceExt ext); + // Returns a map that maps a Vulkan extension name to its InstanceExt. + std::unordered_map CreateInstanceExtNameMap(); + + // Sets entries in `extensions` to true if that entry was promoted in Vulkan version `version` + void MarkPromotedExtensions(InstanceExtSet* extensions, uint32_t version); + // From a set of extensions advertised as supported by the instance (or promoted), remove all + // extensions that don't have all their transitive dependencies in advertisedExts. + InstanceExtSet EnsureDependencies(const InstanceExtSet& advertisedExts); + + // The list of known device extensions. They must be in dependency order (this is checked + // inside EnsureDependencies) + enum class DeviceExt { + // Promoted to 1.1 + BindMemory2, + Maintenance1, + StorageBufferStorageClass, + GetPhysicalDeviceProperties2, + GetMemoryRequirements2, + ExternalMemoryCapabilities, + ExternalSemaphoreCapabilities, + ExternalMemory, + ExternalSemaphore, + _16BitStorage, + SamplerYCbCrConversion, + + // Promoted to 1.2 + ImageFormatList, + ShaderFloat16Int8, + + // External* extensions + ExternalMemoryFD, + ExternalMemoryDmaBuf, + ExternalMemoryZirconHandle, + ExternalSemaphoreFD, + ExternalSemaphoreZirconHandle, + + // Others + DebugMarker, + ImageDrmFormatModifier, + Swapchain, + SubgroupSizeControl, + + EnumCount, + }; + + // A bitset wrapper that is indexed with DeviceExt. + struct DeviceExtSet { + std::bitset(DeviceExt::EnumCount)> extensionBitSet; + void Set(DeviceExt extension, bool enabled); + bool Has(DeviceExt extension) const; + }; + + // A bitset wrapper that is indexed with DeviceExt. + struct DeviceExtInfo { + DeviceExt index; + const char* name; + // The version in which this extension was promoted as built with VK_MAKE_VERSION, + // or NeverPromoted if it was never promoted. + uint32_t versionPromoted; + }; + + // Returns the information about a known DeviceExt + const DeviceExtInfo& GetDeviceExtInfo(DeviceExt ext); + // Returns a map that maps a Vulkan extension name to its DeviceExt. + std::unordered_map CreateDeviceExtNameMap(); + + // Sets entries in `extensions` to true if that entry was promoted in Vulkan version `version` + void MarkPromotedExtensions(DeviceExtSet* extensions, uint32_t version); + // From a set of extensions advertised as supported by the device (or promoted), remove all + // extensions that don't have all their transitive dependencies in advertisedExts or in + // instanceExts. + DeviceExtSet EnsureDependencies(const DeviceExtSet& advertisedExts, + const InstanceExtSet& instanceExts, + uint32_t icdVersion); + +}} // namespace dawn_native::vulkan + +#endif // DAWNNATIVE_VULKAN_VULKANEXTENSIONS_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanFunctions.cpp b/third_party/dawn/src/dawn_native/vulkan/VulkanFunctions.cpp index 771c5d5f1fe..cc070f236c2 100644 --- a/third_party/dawn/src/dawn_native/vulkan/VulkanFunctions.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanFunctions.cpp @@ -19,32 +19,43 @@ namespace dawn_native { namespace vulkan { -#define GET_GLOBAL_PROC(name) \ - name = reinterpret_cast(GetInstanceProcAddr(nullptr, "vk" #name)); \ - if (name == nullptr) { \ - return DAWN_CONTEXT_LOST_ERROR(std::string("Couldn't get proc vk") + #name); \ - } +#define GET_GLOBAL_PROC(name) \ + do { \ + name = reinterpret_cast(GetInstanceProcAddr(nullptr, "vk" #name)); \ + if (name == nullptr) { \ + return DAWN_INTERNAL_ERROR(std::string("Couldn't get proc vk") + #name); \ + } \ + } while (0) MaybeError VulkanFunctions::LoadGlobalProcs(const DynamicLib& vulkanLib) { if (!vulkanLib.GetProc(&GetInstanceProcAddr, "vkGetInstanceProcAddr")) { - return DAWN_CONTEXT_LOST_ERROR("Couldn't get vkGetInstanceProcAddr"); + return DAWN_INTERNAL_ERROR("Couldn't get vkGetInstanceProcAddr"); } GET_GLOBAL_PROC(CreateInstance); GET_GLOBAL_PROC(EnumerateInstanceExtensionProperties); GET_GLOBAL_PROC(EnumerateInstanceLayerProperties); + // Is not available in Vulkan 1.0, so allow nullptr + EnumerateInstanceVersion = reinterpret_cast( + GetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion")); + return {}; } -#define GET_INSTANCE_PROC(name) \ - name = reinterpret_cast(GetInstanceProcAddr(instance, "vk" #name)); \ - if (name == nullptr) { \ - return DAWN_CONTEXT_LOST_ERROR(std::string("Couldn't get proc vk") + #name); \ - } +#define GET_INSTANCE_PROC_BASE(name, procName) \ + do { \ + name = reinterpret_cast(GetInstanceProcAddr(instance, "vk" #procName)); \ + if (name == nullptr) { \ + return DAWN_INTERNAL_ERROR(std::string("Couldn't get proc vk") + #procName); \ + } \ + } while (0) + +#define GET_INSTANCE_PROC(name) GET_INSTANCE_PROC_BASE(name, name) +#define GET_INSTANCE_PROC_VENDOR(name, vendor) GET_INSTANCE_PROC_BASE(name, name##vendor) MaybeError VulkanFunctions::LoadInstanceProcs(VkInstance instance, - const VulkanGlobalKnobs& usedKnobs) { + const VulkanGlobalInfo& globalInfo) { // Load this proc first so that we can destroy the instance even if some other // GET_INSTANCE_PROC fails GET_INSTANCE_PROC(DestroyInstance); @@ -63,13 +74,45 @@ namespace dawn_native { namespace vulkan { GET_INSTANCE_PROC(GetPhysicalDeviceQueueFamilyProperties); GET_INSTANCE_PROC(GetPhysicalDeviceSparseImageFormatProperties); - if (usedKnobs.debugReport) { + if (globalInfo.HasExt(InstanceExt::DebugReport)) { GET_INSTANCE_PROC(CreateDebugReportCallbackEXT); GET_INSTANCE_PROC(DebugReportMessageEXT); GET_INSTANCE_PROC(DestroyDebugReportCallbackEXT); } - if (usedKnobs.surface) { + // Vulkan 1.1 is not required to report promoted extensions from 1.0 and is not required to + // support the vendor entrypoint in GetProcAddress. + if (globalInfo.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) { + GET_INSTANCE_PROC(GetPhysicalDeviceExternalBufferProperties); + } else if (globalInfo.HasExt(InstanceExt::ExternalMemoryCapabilities)) { + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceExternalBufferProperties, KHR); + } + + if (globalInfo.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) { + GET_INSTANCE_PROC(GetPhysicalDeviceExternalSemaphoreProperties); + } else if (globalInfo.HasExt(InstanceExt::ExternalSemaphoreCapabilities)) { + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceExternalSemaphoreProperties, KHR); + } + + if (globalInfo.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) { + GET_INSTANCE_PROC(GetPhysicalDeviceFeatures2); + GET_INSTANCE_PROC(GetPhysicalDeviceProperties2); + GET_INSTANCE_PROC(GetPhysicalDeviceFormatProperties2); + GET_INSTANCE_PROC(GetPhysicalDeviceImageFormatProperties2); + GET_INSTANCE_PROC(GetPhysicalDeviceQueueFamilyProperties2); + GET_INSTANCE_PROC(GetPhysicalDeviceMemoryProperties2); + GET_INSTANCE_PROC(GetPhysicalDeviceSparseImageFormatProperties2); + } else if (globalInfo.HasExt(InstanceExt::GetPhysicalDeviceProperties2)) { + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceFeatures2, KHR); + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceProperties2, KHR); + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceFormatProperties2, KHR); + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceImageFormatProperties2, KHR); + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceQueueFamilyProperties2, KHR); + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceMemoryProperties2, KHR); + GET_INSTANCE_PROC_VENDOR(GetPhysicalDeviceSparseImageFormatProperties2, KHR); + } + + if (globalInfo.HasExt(InstanceExt::Surface)) { GET_INSTANCE_PROC(DestroySurfaceKHR); GET_INSTANCE_PROC(GetPhysicalDeviceSurfaceSupportKHR); GET_INSTANCE_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR); @@ -77,17 +120,44 @@ namespace dawn_native { namespace vulkan { GET_INSTANCE_PROC(GetPhysicalDeviceSurfacePresentModesKHR); } +#if defined(VK_USE_PLATFORM_FUCHSIA) + if (globalInfo.HasExt(InstanceExt::FuchsiaImagePipeSurface)) { + GET_INSTANCE_PROC(CreateImagePipeSurfaceFUCHSIA); + } +#endif // defined(VK_USE_PLATFORM_FUCHSIA) + +#if defined(DAWN_ENABLE_BACKEND_METAL) + if (globalInfo.HasExt(InstanceExt::MetalSurface)) { + GET_INSTANCE_PROC(CreateMetalSurfaceEXT); + } +#endif // defined(DAWN_ENABLE_BACKEND_METAL) + +#if defined(DAWN_PLATFORM_WINDOWS) + if (globalInfo.HasExt(InstanceExt::Win32Surface)) { + GET_INSTANCE_PROC(CreateWin32SurfaceKHR); + GET_INSTANCE_PROC(GetPhysicalDeviceWin32PresentationSupportKHR); + } +#endif // defined(DAWN_PLATFORM_WINDOWS) + +#if defined(DAWN_USE_X11) + if (globalInfo.HasExt(InstanceExt::XlibSurface)) { + GET_INSTANCE_PROC(CreateXlibSurfaceKHR); + GET_INSTANCE_PROC(GetPhysicalDeviceXlibPresentationSupportKHR); + } +#endif // defined(DAWN_USE_X11) return {}; } -#define GET_DEVICE_PROC(name) \ - name = reinterpret_cast(GetDeviceProcAddr(device, "vk" #name)); \ - if (name == nullptr) { \ - return DAWN_CONTEXT_LOST_ERROR(std::string("Couldn't get proc vk") + #name); \ - } +#define GET_DEVICE_PROC(name) \ + do { \ + name = reinterpret_cast(GetDeviceProcAddr(device, "vk" #name)); \ + if (name == nullptr) { \ + return DAWN_INTERNAL_ERROR(std::string("Couldn't get proc vk") + #name); \ + } \ + } while (0) MaybeError VulkanFunctions::LoadDeviceProcs(VkDevice device, - const VulkanDeviceKnobs& usedKnobs) { + const VulkanDeviceInfo& deviceInfo) { GET_DEVICE_PROC(AllocateCommandBuffers); GET_DEVICE_PROC(AllocateDescriptorSets); GET_DEVICE_PROC(AllocateMemory); @@ -208,13 +278,35 @@ namespace dawn_native { namespace vulkan { GET_DEVICE_PROC(UpdateDescriptorSets); GET_DEVICE_PROC(WaitForFences); - if (usedKnobs.debugMarker) { + if (deviceInfo.HasExt(DeviceExt::DebugMarker)) { GET_DEVICE_PROC(CmdDebugMarkerBeginEXT); GET_DEVICE_PROC(CmdDebugMarkerEndEXT); GET_DEVICE_PROC(CmdDebugMarkerInsertEXT); } - if (usedKnobs.swapchain) { + if (deviceInfo.HasExt(DeviceExt::ExternalMemoryFD)) { + GET_DEVICE_PROC(GetMemoryFdKHR); + GET_DEVICE_PROC(GetMemoryFdPropertiesKHR); + } + + if (deviceInfo.HasExt(DeviceExt::ExternalSemaphoreFD)) { + GET_DEVICE_PROC(ImportSemaphoreFdKHR); + GET_DEVICE_PROC(GetSemaphoreFdKHR); + } + +#if VK_USE_PLATFORM_FUCHSIA + if (deviceInfo.HasExt(DeviceExt::ExternalMemoryZirconHandle)) { + GET_DEVICE_PROC(GetMemoryZirconHandleFUCHSIA); + GET_DEVICE_PROC(GetMemoryZirconHandlePropertiesFUCHSIA); + } + + if (deviceInfo.HasExt(DeviceExt::ExternalSemaphoreZirconHandle)) { + GET_DEVICE_PROC(ImportSemaphoreZirconHandleFUCHSIA); + GET_DEVICE_PROC(GetSemaphoreZirconHandleFUCHSIA); + } +#endif + + if (deviceInfo.HasExt(DeviceExt::Swapchain)) { GET_DEVICE_PROC(CreateSwapchainKHR); GET_DEVICE_PROC(DestroySwapchainKHR); GET_DEVICE_PROC(GetSwapchainImagesKHR); diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanFunctions.h b/third_party/dawn/src/dawn_native/vulkan/VulkanFunctions.h index 6dcfe7e1356..1f0a4537b43 100644 --- a/third_party/dawn/src/dawn_native/vulkan/VulkanFunctions.h +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanFunctions.h @@ -23,15 +23,15 @@ class DynamicLib; namespace dawn_native { namespace vulkan { - struct VulkanGlobalKnobs; - struct VulkanDeviceKnobs; + struct VulkanGlobalInfo; + struct VulkanDeviceInfo; // Stores the Vulkan entry points. Also loads them from the dynamic library // and the vkGet*ProcAddress entry points. struct VulkanFunctions { MaybeError LoadGlobalProcs(const DynamicLib& vulkanLib); - MaybeError LoadInstanceProcs(VkInstance instance, const VulkanGlobalKnobs& usedGlobals); - MaybeError LoadDeviceProcs(VkDevice device, const VulkanDeviceKnobs& usedKnobs); + MaybeError LoadInstanceProcs(VkInstance instance, const VulkanGlobalInfo& globalInfo); + MaybeError LoadDeviceProcs(VkDevice device, const VulkanDeviceInfo& deviceInfo); // ---------- Global procs @@ -45,6 +45,9 @@ namespace dawn_native { namespace vulkan { // before querying the instance procs in case we need to error out during initialization. PFN_vkDestroyInstance DestroyInstance = nullptr; + // Core Vulkan 1.1 + PFN_vkEnumerateInstanceVersion EnumerateInstanceVersion = nullptr; + // ---------- Instance procs // Core Vulkan 1.0 @@ -81,6 +84,53 @@ namespace dawn_native { namespace vulkan { PFN_vkGetPhysicalDeviceSurfacePresentModesKHR GetPhysicalDeviceSurfacePresentModesKHR = nullptr; + // Core Vulkan 1.1 promoted extensions, set if either the core version or the extension is + // present. + + // VK_KHR_external_memory_capabilities + PFN_vkGetPhysicalDeviceExternalBufferProperties GetPhysicalDeviceExternalBufferProperties = + nullptr; + + // VK_KHR_external_semaphore_capabilities + PFN_vkGetPhysicalDeviceExternalSemaphoreProperties + GetPhysicalDeviceExternalSemaphoreProperties = nullptr; + + // VK_KHR_get_physical_device_properties2 + PFN_vkGetPhysicalDeviceFeatures2 GetPhysicalDeviceFeatures2 = nullptr; + PFN_vkGetPhysicalDeviceProperties2 GetPhysicalDeviceProperties2 = nullptr; + PFN_vkGetPhysicalDeviceFormatProperties2 GetPhysicalDeviceFormatProperties2 = nullptr; + PFN_vkGetPhysicalDeviceImageFormatProperties2 GetPhysicalDeviceImageFormatProperties2 = + nullptr; + PFN_vkGetPhysicalDeviceQueueFamilyProperties2 GetPhysicalDeviceQueueFamilyProperties2 = + nullptr; + PFN_vkGetPhysicalDeviceMemoryProperties2 GetPhysicalDeviceMemoryProperties2 = nullptr; + PFN_vkGetPhysicalDeviceSparseImageFormatProperties2 + GetPhysicalDeviceSparseImageFormatProperties2 = nullptr; + +#if defined(VK_USE_PLATFORM_FUCHSIA) + // FUCHSIA_image_pipe_surface + PFN_vkCreateImagePipeSurfaceFUCHSIA CreateImagePipeSurfaceFUCHSIA = nullptr; +#endif // defined(VK_USE_PLATFORM_FUCHSIA) + +#if defined(DAWN_ENABLE_BACKEND_METAL) + // EXT_metal_surface + PFN_vkCreateMetalSurfaceEXT CreateMetalSurfaceEXT = nullptr; +#endif // defined(DAWN_ENABLE_BACKEND_METAL) + +#if defined(DAWN_PLATFORM_WINDOWS) + // KHR_win32_surface + PFN_vkCreateWin32SurfaceKHR CreateWin32SurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR + GetPhysicalDeviceWin32PresentationSupportKHR = nullptr; +#endif // defined(DAWN_PLATFORM_WINDOWS) + +#if defined(DAWN_USE_X11) + // KHR_xlib_surface + PFN_vkCreateXlibSurfaceKHR CreateXlibSurfaceKHR = nullptr; + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR + GetPhysicalDeviceXlibPresentationSupportKHR = nullptr; +#endif // defined(DAWN_USE_X11) + // ---------- Device procs // Core Vulkan 1.0 @@ -215,6 +265,47 @@ namespace dawn_native { namespace vulkan { PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR = nullptr; PFN_vkAcquireNextImageKHR AcquireNextImageKHR = nullptr; PFN_vkQueuePresentKHR QueuePresentKHR = nullptr; + + // VK_KHR_external_memory_fd + PFN_vkGetMemoryFdKHR GetMemoryFdKHR = nullptr; + PFN_vkGetMemoryFdPropertiesKHR GetMemoryFdPropertiesKHR = nullptr; + + // VK_KHR_external_semaphore_fd + PFN_vkImportSemaphoreFdKHR ImportSemaphoreFdKHR = nullptr; + PFN_vkGetSemaphoreFdKHR GetSemaphoreFdKHR = nullptr; + +#if VK_USE_PLATFORM_FUCHSIA + // VK_FUCHSIA_external_memory + PFN_vkGetMemoryZirconHandleFUCHSIA GetMemoryZirconHandleFUCHSIA = nullptr; + PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA GetMemoryZirconHandlePropertiesFUCHSIA = + nullptr; + + // VK_FUCHSIA_external_semaphore + PFN_vkImportSemaphoreZirconHandleFUCHSIA ImportSemaphoreZirconHandleFUCHSIA = nullptr; + PFN_vkGetSemaphoreZirconHandleFUCHSIA GetSemaphoreZirconHandleFUCHSIA = nullptr; +#endif + }; + + // Create a wrapper around VkResult in the dawn_native::vulkan namespace. This shadows the + // default VkResult (::VkResult). This ensures that assigning or creating a VkResult from a raw + // ::VkResult uses WrapUnsafe. This makes it clear that users of VkResult must be intentional + // about handling error cases. + class VkResult { + public: + constexpr static VkResult WrapUnsafe(::VkResult value) { + return VkResult(value); + } + + constexpr operator ::VkResult() const { + return mValue; + } + + private: + // Private. Use VkResult::WrapUnsafe instead. + constexpr VkResult(::VkResult value) : mValue(value) { + } + + ::VkResult mValue; }; }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanInfo.cpp b/third_party/dawn/src/dawn_native/vulkan/VulkanInfo.cpp index 747202aae0d..5a66c678fb2 100644 --- a/third_party/dawn/src/dawn_native/vulkan/VulkanInfo.cpp +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanInfo.cpp @@ -14,61 +14,88 @@ #include "dawn_native/vulkan/VulkanInfo.h" +#include "common/Log.h" #include "dawn_native/vulkan/AdapterVk.h" #include "dawn_native/vulkan/BackendVk.h" +#include "dawn_native/vulkan/UtilsVulkan.h" +#include "dawn_native/vulkan/VulkanError.h" #include -namespace { - bool IsLayerName(const VkLayerProperties& layer, const char* name) { - return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; - } +namespace dawn_native { namespace vulkan { - bool IsExtensionName(const VkExtensionProperties& extension, const char* name) { - return strncmp(extension.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; - } -} // namespace + namespace { + bool IsLayerName(const VkLayerProperties& layer, const char* name) { + return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; + } -namespace dawn_native { namespace vulkan { + bool EnumerateInstanceExtensions(const char* layerName, + const dawn_native::vulkan::VulkanFunctions& vkFunctions, + std::vector* extensions) { + uint32_t count = 0; + VkResult result = VkResult::WrapUnsafe( + vkFunctions.EnumerateInstanceExtensionProperties(layerName, &count, nullptr)); + if (result != VK_SUCCESS && result != VK_INCOMPLETE) { + return false; + } + extensions->resize(count); + result = VkResult::WrapUnsafe(vkFunctions.EnumerateInstanceExtensionProperties( + layerName, &count, extensions->data())); + return (result == VK_SUCCESS); + } - const char kLayerNameLunargStandardValidation[] = "VK_LAYER_LUNARG_standard_validation"; + } // namespace + + const char kLayerNameKhronosValidation[] = "VK_LAYER_KHRONOS_validation"; const char kLayerNameLunargVKTrace[] = "VK_LAYER_LUNARG_vktrace"; const char kLayerNameRenderDocCapture[] = "VK_LAYER_RENDERDOC_Capture"; + const char kLayerNameFuchsiaImagePipeSwapchain[] = "VK_LAYER_FUCHSIA_imagepipe_swapchain"; - const char kExtensionNameExtDebugMarker[] = "VK_EXT_debug_marker"; - const char kExtensionNameExtDebugReport[] = "VK_EXT_debug_report"; - const char kExtensionNameMvkMacosSurface[] = "VK_MVK_macos_surface"; - const char kExtensionNameKhrSurface[] = "VK_KHR_surface"; - const char kExtensionNameKhrSwapchain[] = "VK_KHR_swapchain"; - const char kExtensionNameKhrWaylandSurface[] = "VK_KHR_wayland_surface"; - const char kExtensionNameKhrWin32Surface[] = "VK_KHR_win32_surface"; - const char kExtensionNameKhrXcbSurface[] = "VK_KHR_xcb_surface"; - const char kExtensionNameKhrXlibSurface[] = "VK_KHR_xlib_surface"; + bool VulkanGlobalKnobs::HasExt(InstanceExt ext) const { + return extensions.Has(ext); + } + + bool VulkanDeviceKnobs::HasExt(DeviceExt ext) const { + return extensions.Has(ext); + } ResultOrError GatherGlobalInfo(const Backend& backend) { VulkanGlobalInfo info = {}; const VulkanFunctions& vkFunctions = backend.GetFunctions(); + // Gather info on available API version + { + uint32_t supportedAPIVersion = VK_MAKE_VERSION(1, 0, 0); + if (vkFunctions.EnumerateInstanceVersion) { + vkFunctions.EnumerateInstanceVersion(&supportedAPIVersion); + } + + // Use Vulkan 1.1 if it's available. + info.apiVersion = (supportedAPIVersion >= VK_MAKE_VERSION(1, 1, 0)) + ? VK_MAKE_VERSION(1, 1, 0) + : VK_MAKE_VERSION(1, 0, 0); + } + // Gather the info about the instance layers { uint32_t count = 0; - VkResult result = vkFunctions.EnumerateInstanceLayerProperties(&count, nullptr); + VkResult result = + VkResult::WrapUnsafe(vkFunctions.EnumerateInstanceLayerProperties(&count, nullptr)); // From the Vulkan spec result should be success if there are 0 layers, // incomplete otherwise. This means that both values represent a success. // This is the same for all Enumarte functions if (result != VK_SUCCESS && result != VK_INCOMPLETE) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumerateInstanceLayerProperties"); + return DAWN_INTERNAL_ERROR("vkEnumerateInstanceLayerProperties"); } info.layers.resize(count); - result = vkFunctions.EnumerateInstanceLayerProperties(&count, info.layers.data()); - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumerateInstanceLayerProperties"); - } + DAWN_TRY(CheckVkSuccess( + vkFunctions.EnumerateInstanceLayerProperties(&count, info.layers.data()), + "vkEnumerateInstanceLayerProperties")); for (const auto& layer : info.layers) { - if (IsLayerName(layer, kLayerNameLunargStandardValidation)) { - info.standardValidation = true; + if (IsLayerName(layer, kLayerNameKhronosValidation)) { + info.validation = true; } if (IsLayerName(layer, kLayerNameLunargVKTrace)) { info.vktrace = true; @@ -76,53 +103,56 @@ namespace dawn_native { namespace vulkan { if (IsLayerName(layer, kLayerNameRenderDocCapture)) { info.renderDocCapture = true; } + // Technical note: Fuchsia implements the swapchain through + // a layer (VK_LAYER_FUCHSIA_image_pipe_swapchain), which adds + // an instance extensions (VK_FUCHSIA_image_surface) to all ICDs. + if (IsLayerName(layer, kLayerNameFuchsiaImagePipeSwapchain)) { + info.fuchsiaImagePipeSwapchain = true; + } } } // Gather the info about the instance extensions { - uint32_t count = 0; - VkResult result = - vkFunctions.EnumerateInstanceExtensionProperties(nullptr, &count, nullptr); - if (result != VK_SUCCESS && result != VK_INCOMPLETE) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumerateInstanceExtensionProperties"); - } + std::unordered_map knownExts = CreateInstanceExtNameMap(); - info.extensions.resize(count); - result = vkFunctions.EnumerateInstanceExtensionProperties(nullptr, &count, - info.extensions.data()); - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumerateInstanceExtensionProperties"); + std::vector extensionsProperties; + if (!EnumerateInstanceExtensions(nullptr, vkFunctions, &extensionsProperties)) { + return DAWN_INTERNAL_ERROR("vkEnumerateInstanceExtensionProperties"); } - for (const auto& extension : info.extensions) { - if (IsExtensionName(extension, kExtensionNameExtDebugReport)) { - info.debugReport = true; - } - if (IsExtensionName(extension, kExtensionNameMvkMacosSurface)) { - info.macosSurface = true; - } - if (IsExtensionName(extension, kExtensionNameKhrSurface)) { - info.surface = true; - } - if (IsExtensionName(extension, kExtensionNameKhrWaylandSurface)) { - info.waylandSurface = true; + for (const VkExtensionProperties& extension : extensionsProperties) { + auto it = knownExts.find(extension.extensionName); + if (it != knownExts.end()) { + info.extensions.Set(it->second, true); } - if (IsExtensionName(extension, kExtensionNameKhrWin32Surface)) { - info.win32Surface = true; - } - if (IsExtensionName(extension, kExtensionNameKhrXcbSurface)) { - info.xcbSurface = true; + } + + // Specific handling for the Fuchsia swapchain surface creation extension + // which is normally part of the Fuchsia-specific swapchain layer. + if (info.fuchsiaImagePipeSwapchain && + !info.HasExt(InstanceExt::FuchsiaImagePipeSurface)) { + if (!EnumerateInstanceExtensions(kLayerNameFuchsiaImagePipeSwapchain, vkFunctions, + &extensionsProperties)) { + return DAWN_INTERNAL_ERROR("vkEnumerateInstanceExtensionProperties"); } - if (IsExtensionName(extension, kExtensionNameKhrXlibSurface)) { - info.xlibSurface = true; + + for (const VkExtensionProperties& extension : extensionsProperties) { + auto it = knownExts.find(extension.extensionName); + if (it != knownExts.end() && + it->second == InstanceExt::FuchsiaImagePipeSurface) { + info.extensions.Set(InstanceExt::FuchsiaImagePipeSurface, true); + } } } + + MarkPromotedExtensions(&info.extensions, info.apiVersion); + info.extensions = EnsureDependencies(info.extensions); } // TODO(cwallez@chromium:org): Each layer can expose additional extensions, query them? - return info; + return std::move(info); } ResultOrError> GetPhysicalDevices(const Backend& backend) { @@ -130,28 +160,28 @@ namespace dawn_native { namespace vulkan { const VulkanFunctions& vkFunctions = backend.GetFunctions(); uint32_t count = 0; - VkResult result = vkFunctions.EnumeratePhysicalDevices(instance, &count, nullptr); + VkResult result = + VkResult::WrapUnsafe(vkFunctions.EnumeratePhysicalDevices(instance, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumeratePhysicalDevices"); + return DAWN_INTERNAL_ERROR("vkEnumeratePhysicalDevices"); } std::vector physicalDevices(count); - result = vkFunctions.EnumeratePhysicalDevices(instance, &count, physicalDevices.data()); - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumeratePhysicalDevices"); - } + DAWN_TRY(CheckVkSuccess( + vkFunctions.EnumeratePhysicalDevices(instance, &count, physicalDevices.data()), + "vkEnumeratePhysicalDevices")); - return physicalDevices; + return std::move(physicalDevices); } ResultOrError GatherDeviceInfo(const Adapter& adapter) { VulkanDeviceInfo info = {}; VkPhysicalDevice physicalDevice = adapter.GetPhysicalDevice(); + const VulkanGlobalInfo& globalInfo = adapter.GetBackend()->GetGlobalInfo(); const VulkanFunctions& vkFunctions = adapter.GetBackend()->GetFunctions(); - // Gather general info about the device + // Query the device properties first to get the ICD's `apiVersion` vkFunctions.GetPhysicalDeviceProperties(physicalDevice, &info.properties); - vkFunctions.GetPhysicalDeviceFeatures(physicalDevice, &info.features); // Gather info about device memory. { @@ -177,120 +207,158 @@ namespace dawn_native { namespace vulkan { // Gather the info about the device layers { uint32_t count = 0; - VkResult result = - vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, nullptr); + VkResult result = VkResult::WrapUnsafe( + vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumerateDeviceLayerProperties"); + return DAWN_INTERNAL_ERROR("vkEnumerateDeviceLayerProperties"); } info.layers.resize(count); - result = vkFunctions.EnumerateDeviceLayerProperties(physicalDevice, &count, - info.layers.data()); - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumerateDeviceLayerProperties"); - } + DAWN_TRY(CheckVkSuccess(vkFunctions.EnumerateDeviceLayerProperties( + physicalDevice, &count, info.layers.data()), + "vkEnumerateDeviceLayerProperties")); } // Gather the info about the device extensions { uint32_t count = 0; - VkResult result = vkFunctions.EnumerateDeviceExtensionProperties( - physicalDevice, nullptr, &count, nullptr); + VkResult result = VkResult::WrapUnsafe(vkFunctions.EnumerateDeviceExtensionProperties( + physicalDevice, nullptr, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumerateDeviceExtensionProperties"); + return DAWN_INTERNAL_ERROR("vkEnumerateDeviceExtensionProperties"); } - info.extensions.resize(count); - result = vkFunctions.EnumerateDeviceExtensionProperties(physicalDevice, nullptr, &count, - info.extensions.data()); - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkEnumerateDeviceExtensionProperties"); - } + std::vector extensionsProperties; + extensionsProperties.resize(count); + DAWN_TRY( + CheckVkSuccess(vkFunctions.EnumerateDeviceExtensionProperties( + physicalDevice, nullptr, &count, extensionsProperties.data()), + "vkEnumerateDeviceExtensionProperties")); - for (const auto& extension : info.extensions) { - if (IsExtensionName(extension, kExtensionNameExtDebugMarker)) { - info.debugMarker = true; - } + std::unordered_map knownExts = CreateDeviceExtNameMap(); - if (IsExtensionName(extension, kExtensionNameKhrSwapchain)) { - info.swapchain = true; + for (const VkExtensionProperties& extension : extensionsProperties) { + auto it = knownExts.find(extension.extensionName); + if (it != knownExts.end()) { + info.extensions.Set(it->second, true); } } + + MarkPromotedExtensions(&info.extensions, info.properties.apiVersion); + info.extensions = EnsureDependencies(info.extensions, globalInfo.extensions, + info.properties.apiVersion); + } + + // Gather general and extension features and properties + // + // Use vkGetPhysicalDevice{Features,Properties}2 if required to gather information about + // the extensions. DeviceExt::GetPhysicalDeviceProperties2 is guaranteed to be available + // because these extensions (transitively) depend on it in `EnsureDependencies` + VkPhysicalDeviceFeatures2 features2 = {}; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + PNextChainBuilder featuresChain(&features2); + + VkPhysicalDeviceProperties2 properties2 = {}; + properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; + PNextChainBuilder propertiesChain(&properties2); + + if (info.extensions.Has(DeviceExt::ShaderFloat16Int8)) { + featuresChain.Add(&info.shaderFloat16Int8Features, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR); + } + + if (info.extensions.Has(DeviceExt::_16BitStorage)) { + featuresChain.Add(&info._16BitStorageFeatures, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES); + } + + if (info.extensions.Has(DeviceExt::SubgroupSizeControl)) { + featuresChain.Add(&info.subgroupSizeControlFeatures, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT); + propertiesChain.Add( + &info.subgroupSizeControlProperties, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT); + } + + // If we have DeviceExt::GetPhysicalDeviceProperties2, use features2 and properties2 so + // that features no covered by VkPhysicalDevice{Features,Properties} can be queried. + // + // Note that info.properties has already been filled at the start of this function to get + // `apiVersion`. + ASSERT(info.properties.apiVersion != 0); + if (info.extensions.Has(DeviceExt::GetPhysicalDeviceProperties2)) { + vkFunctions.GetPhysicalDeviceProperties2(physicalDevice, &properties2); + vkFunctions.GetPhysicalDeviceFeatures2(physicalDevice, &features2); + info.features = features2.features; + } else { + ASSERT(features2.pNext == nullptr && properties2.pNext == nullptr); + vkFunctions.GetPhysicalDeviceFeatures(physicalDevice, &info.features); } // TODO(cwallez@chromium.org): gather info about formats - return info; + return std::move(info); } - MaybeError GatherSurfaceInfo(const Adapter& adapter, - VkSurfaceKHR surface, - VulkanSurfaceInfo* info) { + ResultOrError GatherSurfaceInfo(const Adapter& adapter, + VkSurfaceKHR surface) { + VulkanSurfaceInfo info = {}; + VkPhysicalDevice physicalDevice = adapter.GetPhysicalDevice(); const VulkanFunctions& vkFunctions = adapter.GetBackend()->GetFunctions(); // Get the surface capabilities - { - VkResult result = vkFunctions.GetPhysicalDeviceSurfaceCapabilitiesKHR( - physicalDevice, surface, &info->capabilities); - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkGetPhysicalDeviceSurfaceCapabilitiesKHR"); - } - } + DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceCapabilitiesKHR( + physicalDevice, surface, &info.capabilities), + "vkGetPhysicalDeviceSurfaceCapabilitiesKHR")); // Query which queue families support presenting this surface { size_t nQueueFamilies = adapter.GetDeviceInfo().queueFamilies.size(); - info->supportedQueueFamilies.resize(nQueueFamilies, false); + info.supportedQueueFamilies.resize(nQueueFamilies, false); for (uint32_t i = 0; i < nQueueFamilies; ++i) { VkBool32 supported = VK_FALSE; - VkResult result = vkFunctions.GetPhysicalDeviceSurfaceSupportKHR( - physicalDevice, i, surface, &supported); - - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkGetPhysicalDeviceSurfaceSupportKHR"); - } + DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceSupportKHR( + physicalDevice, i, surface, &supported), + "vkGetPhysicalDeviceSurfaceSupportKHR")); - info->supportedQueueFamilies[i] = (supported == VK_TRUE); + info.supportedQueueFamilies[i] = (supported == VK_TRUE); } } // Gather supported formats { uint32_t count = 0; - VkResult result = vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR( - physicalDevice, surface, &count, nullptr); + VkResult result = VkResult::WrapUnsafe(vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, surface, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { - return DAWN_CONTEXT_LOST_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR"); + return DAWN_INTERNAL_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR"); } - info->formats.resize(count); - result = vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &count, - info->formats.data()); - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkGetPhysicalDeviceSurfaceFormatsKHR"); - } + info.formats.resize(count); + DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfaceFormatsKHR( + physicalDevice, surface, &count, info.formats.data()), + "vkGetPhysicalDeviceSurfaceFormatsKHR")); } // Gather supported presents modes { uint32_t count = 0; - VkResult result = vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( - physicalDevice, surface, &count, nullptr); + VkResult result = + VkResult::WrapUnsafe(vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( + physicalDevice, surface, &count, nullptr)); if (result != VK_SUCCESS && result != VK_INCOMPLETE) { - return DAWN_CONTEXT_LOST_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR"); + return DAWN_INTERNAL_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR"); } - info->presentModes.resize(count); - result = vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( - physicalDevice, surface, &count, info->presentModes.data()); - if (result != VK_SUCCESS) { - return DAWN_CONTEXT_LOST_ERROR("vkGetPhysicalDeviceSurfacePresentModesKHR"); - } + info.presentModes.resize(count); + DAWN_TRY(CheckVkSuccess(vkFunctions.GetPhysicalDeviceSurfacePresentModesKHR( + physicalDevice, surface, &count, info.presentModes.data()), + "vkGetPhysicalDeviceSurfacePresentModesKHR")); } - return {}; + return std::move(info); } }} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/dawn_native/vulkan/VulkanInfo.h b/third_party/dawn/src/dawn_native/vulkan/VulkanInfo.h index 4425de5148d..e0512475156 100644 --- a/third_party/dawn/src/dawn_native/vulkan/VulkanInfo.h +++ b/third_party/dawn/src/dawn_native/vulkan/VulkanInfo.h @@ -17,6 +17,7 @@ #include "common/vulkan_platform.h" #include "dawn_native/Error.h" +#include "dawn_native/vulkan/VulkanExtensions.h" #include @@ -25,61 +26,50 @@ namespace dawn_native { namespace vulkan { class Adapter; class Backend; - extern const char kLayerNameLunargStandardValidation[]; + extern const char kLayerNameKhronosValidation[]; extern const char kLayerNameLunargVKTrace[]; extern const char kLayerNameRenderDocCapture[]; - - extern const char kExtensionNameExtDebugMarker[]; - extern const char kExtensionNameExtDebugReport[]; - extern const char kExtensionNameMvkMacosSurface[]; - extern const char kExtensionNameKhrSurface[]; - extern const char kExtensionNameKhrSwapchain[]; - extern const char kExtensionNameKhrWaylandSurface[]; - extern const char kExtensionNameKhrWin32Surface[]; - extern const char kExtensionNameKhrXcbSurface[]; - extern const char kExtensionNameKhrXlibSurface[]; + extern const char kLayerNameFuchsiaImagePipeSwapchain[]; // Global information - gathered before the instance is created struct VulkanGlobalKnobs { // Layers - bool standardValidation = false; + bool validation = false; bool vktrace = false; bool renderDocCapture = false; + bool fuchsiaImagePipeSwapchain = false; - // Extensions - bool debugReport = false; - bool macosSurface = false; - bool surface = false; - bool waylandSurface = false; - bool win32Surface = false; - bool xcbSurface = false; - bool xlibSurface = false; + bool HasExt(InstanceExt ext) const; + InstanceExtSet extensions; }; struct VulkanGlobalInfo : VulkanGlobalKnobs { std::vector layers; - std::vector extensions; + uint32_t apiVersion; // TODO(cwallez@chromium.org): layer instance extensions }; // Device information - gathered before the device is created. struct VulkanDeviceKnobs { VkPhysicalDeviceFeatures features; + VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shaderFloat16Int8Features; + VkPhysicalDevice16BitStorageFeaturesKHR _16BitStorageFeatures; + VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroupSizeControlFeatures; - // Extensions - bool debugMarker = false; - bool swapchain = false; + bool HasExt(DeviceExt ext) const; + DeviceExtSet extensions; }; struct VulkanDeviceInfo : VulkanDeviceKnobs { VkPhysicalDeviceProperties properties; + VkPhysicalDeviceSubgroupSizeControlPropertiesEXT subgroupSizeControlProperties; + std::vector queueFamilies; std::vector memoryTypes; std::vector memoryHeaps; std::vector layers; - std::vector extensions; // TODO(cwallez@chromium.org): layer instance extensions }; @@ -93,9 +83,8 @@ namespace dawn_native { namespace vulkan { ResultOrError GatherGlobalInfo(const Backend& backend); ResultOrError> GetPhysicalDevices(const Backend& backend); ResultOrError GatherDeviceInfo(const Adapter& adapter); - MaybeError GatherSurfaceInfo(const Adapter& adapter, - VkSurfaceKHR surface, - VulkanSurfaceInfo* info); + ResultOrError GatherSurfaceInfo(const Adapter& adapter, + VkSurfaceKHR surface); }} // namespace dawn_native::vulkan #endif // DAWNNATIVE_VULKAN_VULKANINFO_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryService.h b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryService.h new file mode 100644 index 00000000000..0c4b64d49ad --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryService.h @@ -0,0 +1,74 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_ +#define DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_ + +#include "common/vulkan_platform.h" +#include "dawn_native/Error.h" +#include "dawn_native/VulkanBackend.h" +#include "dawn_native/vulkan/ExternalHandle.h" + +namespace dawn_native { namespace vulkan { + class Device; +}} // namespace dawn_native::vulkan + +namespace dawn_native { namespace vulkan { namespace external_memory { + + struct MemoryImportParams { + VkDeviceSize allocationSize; + uint32_t memoryTypeIndex; + }; + + class Service { + public: + explicit Service(Device* device); + ~Service(); + + // True if the device reports it supports importing external memory. + bool SupportsImportMemory(VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags); + + // True if the device reports it supports creating VkImages from external memory. + bool SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage); + + // Returns the parameters required for importing memory + ResultOrError GetMemoryImportParams( + const ExternalImageDescriptor* descriptor, + VkImage image); + + // Given an external handle pointing to memory, import it into a VkDeviceMemory + ResultOrError ImportMemory(ExternalMemoryHandle handle, + const MemoryImportParams& importParams, + VkImage image); + + // Create a VkImage for the given handle type + ResultOrError CreateImage(const ExternalImageDescriptor* descriptor, + const VkImageCreateInfo& baseCreateInfo); + + private: + Device* mDevice = nullptr; + + // True if early checks pass that determine if the service is supported + bool mSupported = false; + }; + +}}} // namespace dawn_native::vulkan::external_memory + +#endif // DAWNNATIVE_VULKAN_EXTERNALMEMORY_SERVICE_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp new file mode 100644 index 00000000000..4129745dc58 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceDmaBuf.cpp @@ -0,0 +1,268 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/BackendVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/VulkanError.h" +#include "dawn_native/vulkan/external_memory/MemoryService.h" + +namespace dawn_native { namespace vulkan { namespace external_memory { + + namespace { + + // Some modifiers use multiple planes (for example, see the comment for + // I915_FORMAT_MOD_Y_TILED_CCS in drm/drm_fourcc.h), but dma-buf import in Dawn only + // supports single-plane formats. + ResultOrError GetModifierPlaneCount(const VulkanFunctions& fn, + VkPhysicalDevice physicalDevice, + VkFormat format, + uint64_t modifier) { + VkDrmFormatModifierPropertiesListEXT formatModifierPropsList; + formatModifierPropsList.sType = + VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT; + formatModifierPropsList.pNext = nullptr; + formatModifierPropsList.drmFormatModifierCount = 0; + formatModifierPropsList.pDrmFormatModifierProperties = nullptr; + + VkFormatProperties2 formatProps; + formatProps.sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2; + formatProps.pNext = &formatModifierPropsList; + + fn.GetPhysicalDeviceFormatProperties2(physicalDevice, format, &formatProps); + + uint32_t modifierCount = formatModifierPropsList.drmFormatModifierCount; + std::vector formatModifierProps(modifierCount); + formatModifierPropsList.pDrmFormatModifierProperties = formatModifierProps.data(); + + fn.GetPhysicalDeviceFormatProperties2(physicalDevice, format, &formatProps); + for (const auto& props : formatModifierProps) { + if (props.drmFormatModifier == modifier) { + uint32_t count = props.drmFormatModifierPlaneCount; + return count; + } + } + return DAWN_VALIDATION_ERROR("DRM format modifier not supported"); + } + + } // anonymous namespace + + Service::Service(Device* device) : mDevice(device) { + const VulkanDeviceInfo& deviceInfo = mDevice->GetDeviceInfo(); + + mSupported = deviceInfo.HasExt(DeviceExt::ExternalMemoryFD) && + deviceInfo.HasExt(DeviceExt::ImageDrmFormatModifier); + } + + Service::~Service() = default; + + bool Service::SupportsImportMemory(VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags) { + return mSupported; + } + + bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage) { + // Early out before we try using extension functions + if (!mSupported) { + return false; + } + if (descriptor->type != ExternalImageDescriptorType::DmaBuf) { + return false; + } + const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = + static_cast(descriptor); + + // Verify plane count for the modifier. + VkPhysicalDevice physicalDevice = ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(); + uint32_t planeCount = 0; + if (mDevice->ConsumedError(GetModifierPlaneCount(mDevice->fn, physicalDevice, format, + dmaBufDescriptor->drmModifier), + &planeCount)) { + return false; + } + if (planeCount == 0) { + return false; + } + // TODO(hob): Support multi-plane formats like I915_FORMAT_MOD_Y_TILED_CCS. + if (planeCount > 1) { + return false; + } + + // Verify that the format modifier of the external memory and the requested Vulkan format + // are actually supported together in a dma-buf import. + VkPhysicalDeviceImageDrmFormatModifierInfoEXT drmModifierInfo; + drmModifierInfo.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT; + drmModifierInfo.pNext = nullptr; + drmModifierInfo.drmFormatModifier = dmaBufDescriptor->drmModifier; + drmModifierInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo; + externalImageFormatInfo.sType = + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO; + externalImageFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; + externalImageFormatInfo.pNext = &drmModifierInfo; + + VkPhysicalDeviceImageFormatInfo2 imageFormatInfo; + imageFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2; + imageFormatInfo.format = format; + imageFormatInfo.type = VK_IMAGE_TYPE_2D; + imageFormatInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + imageFormatInfo.usage = usage; + imageFormatInfo.flags = 0; + imageFormatInfo.pNext = &externalImageFormatInfo; + + VkExternalImageFormatProperties externalImageFormatProps; + externalImageFormatProps.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES; + externalImageFormatProps.pNext = nullptr; + + VkImageFormatProperties2 imageFormatProps; + imageFormatProps.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2; + imageFormatProps.pNext = &externalImageFormatProps; + + VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2( + physicalDevice, &imageFormatInfo, &imageFormatProps)); + if (result != VK_SUCCESS) { + return false; + } + VkExternalMemoryFeatureFlags featureFlags = + externalImageFormatProps.externalMemoryProperties.externalMemoryFeatures; + return featureFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT; + } + + ResultOrError Service::GetMemoryImportParams( + const ExternalImageDescriptor* descriptor, + VkImage image) { + if (descriptor->type != ExternalImageDescriptorType::DmaBuf) { + return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor"); + } + const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = + static_cast(descriptor); + VkDevice device = mDevice->GetVkDevice(); + + // Get the valid memory types for the VkImage. + VkMemoryRequirements memoryRequirements; + mDevice->fn.GetImageMemoryRequirements(device, image, &memoryRequirements); + + VkMemoryFdPropertiesKHR fdProperties; + fdProperties.sType = VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR; + fdProperties.pNext = nullptr; + + // Get the valid memory types that the external memory can be imported as. + mDevice->fn.GetMemoryFdPropertiesKHR(device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + dmaBufDescriptor->memoryFD, &fdProperties); + // Choose the best memory type that satisfies both the image's constraint and the import's + // constraint. + memoryRequirements.memoryTypeBits &= fdProperties.memoryTypeBits; + int memoryTypeIndex = + mDevice->FindBestMemoryTypeIndex(memoryRequirements, false /** mappable */); + if (memoryTypeIndex == -1) { + return DAWN_VALIDATION_ERROR("Unable to find appropriate memory type for import"); + } + MemoryImportParams params = {memoryRequirements.size, + static_cast(memoryTypeIndex)}; + return params; + } + + ResultOrError Service::ImportMemory(ExternalMemoryHandle handle, + const MemoryImportParams& importParams, + VkImage image) { + if (handle < 0) { + return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle"); + } + + VkMemoryDedicatedAllocateInfo memoryDedicatedAllocateInfo; + memoryDedicatedAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO; + memoryDedicatedAllocateInfo.pNext = nullptr; + memoryDedicatedAllocateInfo.image = image; + memoryDedicatedAllocateInfo.buffer = VkBuffer{}; + + VkImportMemoryFdInfoKHR importMemoryFdInfo; + importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; + importMemoryFdInfo.pNext = &memoryDedicatedAllocateInfo; + importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT, + importMemoryFdInfo.fd = handle; + + VkMemoryAllocateInfo memoryAllocateInfo; + memoryAllocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + memoryAllocateInfo.pNext = &importMemoryFdInfo; + memoryAllocateInfo.allocationSize = importParams.allocationSize; + memoryAllocateInfo.memoryTypeIndex = importParams.memoryTypeIndex; + + VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; + DAWN_TRY( + CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &memoryAllocateInfo, + nullptr, &*allocatedMemory), + "vkAllocateMemory")); + return allocatedMemory; + } + + ResultOrError Service::CreateImage(const ExternalImageDescriptor* descriptor, + const VkImageCreateInfo& baseCreateInfo) { + if (descriptor->type != ExternalImageDescriptorType::DmaBuf) { + return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not a dma-buf descriptor"); + } + const ExternalImageDescriptorDmaBuf* dmaBufDescriptor = + static_cast(descriptor); + VkPhysicalDevice physicalDevice = ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(); + VkDevice device = mDevice->GetVkDevice(); + + // Dawn currently doesn't support multi-plane formats, so we only need to create a single + // VkSubresourceLayout here. + VkSubresourceLayout planeLayout; + planeLayout.offset = 0; + planeLayout.size = 0; // VK_EXT_image_drm_format_modifier mandates size = 0. + planeLayout.rowPitch = dmaBufDescriptor->stride; + planeLayout.arrayPitch = 0; // Not an array texture + planeLayout.depthPitch = 0; // Not a depth texture + + uint32_t planeCount; + DAWN_TRY_ASSIGN(planeCount, + GetModifierPlaneCount(mDevice->fn, physicalDevice, baseCreateInfo.format, + dmaBufDescriptor->drmModifier)); + ASSERT(planeCount == 1); + + VkImageDrmFormatModifierExplicitCreateInfoEXT explicitCreateInfo; + explicitCreateInfo.sType = + VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT; + explicitCreateInfo.pNext = NULL; + explicitCreateInfo.drmFormatModifier = dmaBufDescriptor->drmModifier; + explicitCreateInfo.drmFormatModifierPlaneCount = planeCount; + explicitCreateInfo.pPlaneLayouts = &planeLayout; + + VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo; + externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; + externalMemoryImageCreateInfo.pNext = &explicitCreateInfo; + externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT; + + VkImageCreateInfo createInfo = baseCreateInfo; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + createInfo.pNext = &externalMemoryImageCreateInfo; + createInfo.flags = 0; + createInfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT; + + // Create a new VkImage with tiling equal to the DRM format modifier. + VkImage image; + DAWN_TRY(CheckVkSuccess(mDevice->fn.CreateImage(device, &createInfo, nullptr, &*image), + "CreateImage")); + return image; + } + +}}} // namespace dawn_native::vulkan::external_memory diff --git a/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp new file mode 100644 index 00000000000..14d882a56ab --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceNull.cpp @@ -0,0 +1,58 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/external_memory/MemoryService.h" + +namespace dawn_native { namespace vulkan { namespace external_memory { + + Service::Service(Device* device) : mDevice(device) { + DAWN_UNUSED(mDevice); + DAWN_UNUSED(mSupported); + } + + Service::~Service() = default; + + bool Service::SupportsImportMemory(VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags) { + return false; + } + + bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage) { + return false; + } + + ResultOrError Service::GetMemoryImportParams( + const ExternalImageDescriptor* descriptor, + VkImage image) { + return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan"); + } + + ResultOrError Service::ImportMemory(ExternalMemoryHandle handle, + const MemoryImportParams& importParams, + VkImage image) { + return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan"); + } + + ResultOrError Service::CreateImage(const ExternalImageDescriptor* descriptor, + const VkImageCreateInfo& baseCreateInfo) { + return DAWN_UNIMPLEMENTED_ERROR("Using null memory service to interop inside Vulkan"); + } + +}}} // namespace dawn_native::vulkan::external_memory diff --git a/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp new file mode 100644 index 00000000000..d43a10f7031 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceOpaqueFD.cpp @@ -0,0 +1,152 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/BackendVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/TextureVk.h" +#include "dawn_native/vulkan/VulkanError.h" +#include "dawn_native/vulkan/external_memory/MemoryService.h" + +namespace dawn_native { namespace vulkan { namespace external_memory { + + Service::Service(Device* device) : mDevice(device) { + mSupported = device->GetDeviceInfo().HasExt(DeviceExt::ExternalMemoryFD); + } + + Service::~Service() = default; + + bool Service::SupportsImportMemory(VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags) { + // Early out before we try using extension functions + if (!mSupported) { + return false; + } + + VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo; + externalFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR; + externalFormatInfo.pNext = nullptr; + externalFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + VkPhysicalDeviceImageFormatInfo2 formatInfo; + formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR; + formatInfo.pNext = &externalFormatInfo; + formatInfo.format = format; + formatInfo.type = type; + formatInfo.tiling = tiling; + formatInfo.usage = usage; + formatInfo.flags = flags; + + VkExternalImageFormatProperties externalFormatProperties; + externalFormatProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR; + externalFormatProperties.pNext = nullptr; + + VkImageFormatProperties2 formatProperties; + formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR; + formatProperties.pNext = &externalFormatProperties; + + VkResult result = VkResult::WrapUnsafe(mDevice->fn.GetPhysicalDeviceImageFormatProperties2( + ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties)); + + // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED + if (result != VK_SUCCESS) { + return false; + } + + // TODO(http://crbug.com/dawn/206): Investigate dedicated only images + VkFlags memoryFlags = + externalFormatProperties.externalMemoryProperties.externalMemoryFeatures; + return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) && + !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR); + } + + bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage) { + return mSupported; + } + + ResultOrError Service::GetMemoryImportParams( + const ExternalImageDescriptor* descriptor, + VkImage image) { + if (descriptor->type != ExternalImageDescriptorType::OpaqueFD) { + return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not an OpaqueFD descriptor"); + } + const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor = + static_cast(descriptor); + + MemoryImportParams params = {opaqueFDDescriptor->allocationSize, + opaqueFDDescriptor->memoryTypeIndex}; + return params; + } + + ResultOrError Service::ImportMemory(ExternalMemoryHandle handle, + const MemoryImportParams& importParams, + VkImage image) { + if (handle < 0) { + return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle"); + } + + VkMemoryRequirements requirements; + mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements); + if (requirements.size > importParams.allocationSize) { + return DAWN_VALIDATION_ERROR("Requested allocation size is too small for image"); + } + + VkImportMemoryFdInfoKHR importMemoryFdInfo; + importMemoryFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR; + importMemoryFdInfo.pNext = nullptr; + importMemoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + importMemoryFdInfo.fd = handle; + + VkMemoryAllocateInfo allocateInfo; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.pNext = &importMemoryFdInfo; + allocateInfo.allocationSize = importParams.allocationSize; + allocateInfo.memoryTypeIndex = importParams.memoryTypeIndex; + + VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; + DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, + nullptr, &*allocatedMemory), + "vkAllocateMemory")); + return allocatedMemory; + } + + ResultOrError Service::CreateImage(const ExternalImageDescriptor* descriptor, + const VkImageCreateInfo& baseCreateInfo) { + VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo; + externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; + externalMemoryImageCreateInfo.pNext = nullptr; + externalMemoryImageCreateInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT; + + VkImageCreateInfo createInfo = baseCreateInfo; + createInfo.pNext = &externalMemoryImageCreateInfo; + createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR; + createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + ASSERT(IsSampleCountSupported(mDevice, createInfo)); + + VkImage image; + DAWN_TRY(CheckVkSuccess( + mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &*image), + "CreateImage")); + return image; + } + +}}} // namespace dawn_native::vulkan::external_memory diff --git a/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp new file mode 100644 index 00000000000..85c4e4a8d3e --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_memory/MemoryServiceZirconHandle.cpp @@ -0,0 +1,155 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/BackendVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/TextureVk.h" +#include "dawn_native/vulkan/VulkanError.h" +#include "dawn_native/vulkan/external_memory/MemoryService.h" + +namespace dawn_native { namespace vulkan { namespace external_memory { + + Service::Service(Device* device) : mDevice(device) { + mSupported = device->GetDeviceInfo().HasExt(DeviceExt::ExternalMemoryZirconHandle); + } + + Service::~Service() = default; + + bool Service::SupportsImportMemory(VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags) { + // Early out before we try using extension functions + if (!mSupported) { + return false; + } + + VkPhysicalDeviceExternalImageFormatInfo externalFormatInfo; + externalFormatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR; + externalFormatInfo.pNext = nullptr; + externalFormatInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA; + + VkPhysicalDeviceImageFormatInfo2 formatInfo; + formatInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR; + formatInfo.pNext = &externalFormatInfo; + formatInfo.format = format; + formatInfo.type = type; + formatInfo.tiling = tiling; + formatInfo.usage = usage; + formatInfo.flags = flags; + + VkExternalImageFormatProperties externalFormatProperties; + externalFormatProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR; + externalFormatProperties.pNext = nullptr; + + VkImageFormatProperties2 formatProperties; + formatProperties.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR; + formatProperties.pNext = &externalFormatProperties; + + VkResult result = mDevice->fn.GetPhysicalDeviceImageFormatProperties2( + ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &formatInfo, &formatProperties); + + // If handle not supported, result == VK_ERROR_FORMAT_NOT_SUPPORTED + if (result != VK_SUCCESS) { + return false; + } + + // TODO(http://crbug.com/dawn/206): Investigate dedicated only images + VkFlags memoryFlags = + externalFormatProperties.externalMemoryProperties.externalMemoryFeatures; + return (memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR) && + !(memoryFlags & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR); + } + + bool Service::SupportsCreateImage(const ExternalImageDescriptor* descriptor, + VkFormat format, + VkImageUsageFlags usage) { + return mSupported; + } + + ResultOrError Service::GetMemoryImportParams( + const ExternalImageDescriptor* descriptor, + VkImage image) { + if (descriptor->type != ExternalImageDescriptorType::OpaqueFD) { + return DAWN_VALIDATION_ERROR("ExternalImageDescriptor is not an OpaqueFD descriptor"); + } + const ExternalImageDescriptorOpaqueFD* opaqueFDDescriptor = + static_cast(descriptor); + + MemoryImportParams params = {opaqueFDDescriptor->allocationSize, + opaqueFDDescriptor->memoryTypeIndex}; + return params; + } + + ResultOrError Service::ImportMemory(ExternalMemoryHandle handle, + const MemoryImportParams& importParams, + VkImage image) { + if (handle == ZX_HANDLE_INVALID) { + return DAWN_VALIDATION_ERROR("Trying to import memory with invalid handle"); + } + + VkMemoryRequirements requirements; + mDevice->fn.GetImageMemoryRequirements(mDevice->GetVkDevice(), image, &requirements); + if (requirements.size > importParams.allocationSize) { + return DAWN_VALIDATION_ERROR("Requested allocation size is too small for image"); + } + + VkImportMemoryZirconHandleInfoFUCHSIA importMemoryHandleInfo; + importMemoryHandleInfo.sType = + VK_STRUCTURE_TYPE_TEMP_MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA; + importMemoryHandleInfo.pNext = nullptr; + importMemoryHandleInfo.handleType = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA; + importMemoryHandleInfo.handle = handle; + + VkMemoryAllocateInfo allocateInfo; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.pNext = &importMemoryHandleInfo; + allocateInfo.allocationSize = importParams.allocationSize; + allocateInfo.memoryTypeIndex = importParams.memoryTypeIndex; + + VkDeviceMemory allocatedMemory = VK_NULL_HANDLE; + DAWN_TRY(CheckVkSuccess(mDevice->fn.AllocateMemory(mDevice->GetVkDevice(), &allocateInfo, + nullptr, &*allocatedMemory), + "vkAllocateMemory")); + return allocatedMemory; + } + + ResultOrError Service::CreateImage(const ExternalImageDescriptor* descriptor, + const VkImageCreateInfo& baseCreateInfo) { + VkExternalMemoryImageCreateInfo externalMemoryImageCreateInfo; + externalMemoryImageCreateInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO; + externalMemoryImageCreateInfo.pNext = nullptr; + externalMemoryImageCreateInfo.handleTypes = + VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA; + + VkImageCreateInfo createInfo = baseCreateInfo; + createInfo.pNext = &externalMemoryImageCreateInfo; + createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR; + createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + ASSERT(IsSampleCountSupported(mDevice, createInfo)); + + VkImage image; + DAWN_TRY(CheckVkSuccess( + mDevice->fn.CreateImage(mDevice->GetVkDevice(), &createInfo, nullptr, &*image), + "CreateImage")); + return image; + } + +}}} // namespace dawn_native::vulkan::external_memory diff --git a/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h b/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h new file mode 100644 index 00000000000..cceaa2da5e7 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreService.h @@ -0,0 +1,54 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_ +#define DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_ + +#include "common/vulkan_platform.h" +#include "dawn_native/Error.h" +#include "dawn_native/vulkan/ExternalHandle.h" + +namespace dawn_native { namespace vulkan { + class Device; +}} // namespace dawn_native::vulkan + +namespace dawn_native { namespace vulkan { namespace external_semaphore { + + class Service { + public: + explicit Service(Device* device); + ~Service(); + + // True if the device reports it supports this feature + bool Supported(); + + // Given an external handle, import it into a VkSemaphore + ResultOrError ImportSemaphore(ExternalSemaphoreHandle handle); + + // Create a VkSemaphore that is exportable into an external handle later + ResultOrError CreateExportableSemaphore(); + + // Export a VkSemaphore into an external handle + ResultOrError ExportSemaphore(VkSemaphore semaphore); + + private: + Device* mDevice = nullptr; + + // True if early checks pass that determine if the service is supported + bool mSupported = false; + }; + +}}} // namespace dawn_native::vulkan::external_semaphore + +#endif // DAWNNATIVE_VULKAN_EXTERNALSEMAPHORE_SERVICE_H_ diff --git a/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp b/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp new file mode 100644 index 00000000000..aca4cb12682 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceNull.cpp @@ -0,0 +1,43 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h" + +namespace dawn_native { namespace vulkan { namespace external_semaphore { + + Service::Service(Device* device) : mDevice(device) { + DAWN_UNUSED(mDevice); + DAWN_UNUSED(mSupported); + } + + Service::~Service() = default; + + bool Service::Supported() { + return false; + } + + ResultOrError Service::ImportSemaphore(ExternalSemaphoreHandle handle) { + return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan"); + } + + ResultOrError Service::CreateExportableSemaphore() { + return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan"); + } + + ResultOrError Service::ExportSemaphore(VkSemaphore semaphore) { + return DAWN_UNIMPLEMENTED_ERROR("Using null semaphore service to interop inside Vulkan"); + } + +}}} // namespace dawn_native::vulkan::external_semaphore diff --git a/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp b/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp new file mode 100644 index 00000000000..aecc8935006 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceOpaqueFD.cpp @@ -0,0 +1,127 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/BackendVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/VulkanError.h" +#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h" + +namespace dawn_native { namespace vulkan { namespace external_semaphore { + + Service::Service(Device* device) : mDevice(device) { + mSupported = device->GetDeviceInfo().HasExt(DeviceExt::ExternalSemaphoreFD); + + // Early out before we try using extension functions + if (!mSupported) { + return; + } + + VkPhysicalDeviceExternalSemaphoreInfoKHR semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR; + semaphoreInfo.pNext = nullptr; + semaphoreInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + VkExternalSemaphorePropertiesKHR semaphoreProperties; + semaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR; + semaphoreProperties.pNext = nullptr; + + mDevice->fn.GetPhysicalDeviceExternalSemaphoreProperties( + ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &semaphoreInfo, + &semaphoreProperties); + + VkFlags requiredFlags = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR; + mSupported = + mSupported && + ((semaphoreProperties.externalSemaphoreFeatures & requiredFlags) == requiredFlags); + } + + Service::~Service() = default; + + bool Service::Supported() { + return mSupported; + } + + ResultOrError Service::ImportSemaphore(ExternalSemaphoreHandle handle) { + if (handle < 0) { + return DAWN_VALIDATION_ERROR("Trying to import semaphore with invalid handle"); + } + + VkSemaphore semaphore = VK_NULL_HANDLE; + VkSemaphoreCreateInfo info; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + info.pNext = nullptr; + info.flags = 0; + + DAWN_TRY(CheckVkSuccess( + mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &info, nullptr, &*semaphore), + "vkCreateSemaphore")); + + VkImportSemaphoreFdInfoKHR importSemaphoreFdInfo; + importSemaphoreFdInfo.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR; + importSemaphoreFdInfo.pNext = nullptr; + importSemaphoreFdInfo.semaphore = semaphore; + importSemaphoreFdInfo.flags = 0; + importSemaphoreFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + importSemaphoreFdInfo.fd = handle; + + MaybeError status = CheckVkSuccess( + mDevice->fn.ImportSemaphoreFdKHR(mDevice->GetVkDevice(), &importSemaphoreFdInfo), + "vkImportSemaphoreFdKHR"); + + if (status.IsError()) { + mDevice->fn.DestroySemaphore(mDevice->GetVkDevice(), semaphore, nullptr); + DAWN_TRY(std::move(status)); + } + + return semaphore; + } + + ResultOrError Service::CreateExportableSemaphore() { + VkExportSemaphoreCreateInfoKHR exportSemaphoreInfo; + exportSemaphoreInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR; + exportSemaphoreInfo.pNext = nullptr; + exportSemaphoreInfo.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + VkSemaphoreCreateInfo semaphoreCreateInfo; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreCreateInfo.pNext = &exportSemaphoreInfo; + semaphoreCreateInfo.flags = 0; + + VkSemaphore signalSemaphore; + DAWN_TRY( + CheckVkSuccess(mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &semaphoreCreateInfo, + nullptr, &*signalSemaphore), + "vkCreateSemaphore")); + return signalSemaphore; + } + + ResultOrError Service::ExportSemaphore(VkSemaphore semaphore) { + VkSemaphoreGetFdInfoKHR semaphoreGetFdInfo; + semaphoreGetFdInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR; + semaphoreGetFdInfo.pNext = nullptr; + semaphoreGetFdInfo.semaphore = semaphore; + semaphoreGetFdInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + int fd = -1; + DAWN_TRY(CheckVkSuccess( + mDevice->fn.GetSemaphoreFdKHR(mDevice->GetVkDevice(), &semaphoreGetFdInfo, &fd), + "vkGetSemaphoreFdKHR")); + + ASSERT(fd >= 0); + return fd; + } + +}}} // namespace dawn_native::vulkan::external_semaphore diff --git a/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp b/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp new file mode 100644 index 00000000000..b4e3a62e5e3 --- /dev/null +++ b/third_party/dawn/src/dawn_native/vulkan/external_semaphore/SemaphoreServiceZirconHandle.cpp @@ -0,0 +1,132 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/BackendVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/VulkanError.h" +#include "dawn_native/vulkan/external_semaphore/SemaphoreService.h" + +namespace dawn_native { namespace vulkan { namespace external_semaphore { + + Service::Service(Device* device) : mDevice(device) { + mSupported = device->GetDeviceInfo().hasExt(DeviceExt::ExternalSemaphoreZirconHandle); + + // Early out before we try using extension functions + if (!mSupported) { + return; + } + + VkPhysicalDeviceExternalSemaphoreInfoKHR semaphoreInfo; + semaphoreInfo.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR; + semaphoreInfo.pNext = nullptr; + semaphoreInfo.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA; + + VkExternalSemaphorePropertiesKHR semaphoreProperties; + semaphoreProperties.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR; + semaphoreProperties.pNext = nullptr; + + mDevice->fn.GetPhysicalDeviceExternalSemaphoreProperties( + ToBackend(mDevice->GetAdapter())->GetPhysicalDevice(), &semaphoreInfo, + &semaphoreProperties); + + VkFlags requiredFlags = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR | + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR; + mSupported = + mSupported && + ((semaphoreProperties.externalSemaphoreFeatures & requiredFlags) == requiredFlags); + } + + Service::~Service() = default; + + bool Service::Supported() { + return mSupported; + } + + ResultOrError Service::ImportSemaphore(ExternalSemaphoreHandle handle) { + if (handle == ZX_HANDLE_INVALID) { + return DAWN_VALIDATION_ERROR("Trying to import semaphore with invalid handle"); + } + + VkSemaphore semaphore = VK_NULL_HANDLE; + VkSemaphoreCreateInfo info; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + info.pNext = nullptr; + info.flags = 0; + + DAWN_TRY(CheckVkSuccess( + mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &info, nullptr, &*semaphore), + "vkCreateSemaphore")); + + VkImportSemaphoreZirconHandleInfoFUCHSIA importSempahoreHandleInfo; + importSempahoreHandleInfo.sType = + VK_STRUCTURE_TYPE_TEMP_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA; + importSempahoreHandleInfo.pNext = nullptr; + importSempahoreHandleInfo.semaphore = semaphore; + importSempahoreHandleInfo.flags = 0; + importSempahoreHandleInfo.handleType = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA; + importSempahoreHandleInfo.handle = handle; + + MaybeError status = CheckVkSuccess(mDevice->fn.ImportSemaphoreZirconHandleFUCHSIA( + mDevice->GetVkDevice(), &importSempahoreHandleInfo), + "vkImportSemaphoreZirconHandleFUCHSIA"); + + if (status.IsError()) { + mDevice->fn.DestroySemaphore(mDevice->GetVkDevice(), semaphore, nullptr); + DAWN_TRY(std::move(status)); + } + + return semaphore; + } + + ResultOrError Service::CreateExportableSemaphore() { + VkExportSemaphoreCreateInfoKHR exportSemaphoreInfo; + exportSemaphoreInfo.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR; + exportSemaphoreInfo.pNext = nullptr; + exportSemaphoreInfo.handleTypes = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA; + + VkSemaphoreCreateInfo semaphoreCreateInfo; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphoreCreateInfo.pNext = &exportSemaphoreInfo; + semaphoreCreateInfo.flags = 0; + + VkSemaphore signalSemaphore; + DAWN_TRY( + CheckVkSuccess(mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &semaphoreCreateInfo, + nullptr, &*signalSemaphore), + "vkCreateSemaphore")); + return signalSemaphore; + } + + ResultOrError Service::ExportSemaphore(VkSemaphore semaphore) { + VkSemaphoreGetZirconHandleInfoFUCHSIA semaphoreGetHandleInfo; + semaphoreGetHandleInfo.sType = + VK_STRUCTURE_TYPE_TEMP_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA; + semaphoreGetHandleInfo.pNext = nullptr; + semaphoreGetHandleInfo.semaphore = semaphore; + semaphoreGetHandleInfo.handleType = + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA; + + zx_handle_t handle = ZX_HANDLE_INVALID; + DAWN_TRY(CheckVkSuccess(mDevice->fn.GetSemaphoreZirconHandleFUCHSIA( + mDevice->GetVkDevice(), &semaphoreGetHandleInfo, &handle), + "VkSemaphoreGetZirconHandleInfoFUCHSIA")); + + ASSERT(handle != ZX_HANDLE_INVALID); + return handle; + } + +}}} // namespace dawn_native::vulkan::external_semaphore diff --git a/third_party/dawn/src/dawn_platform/BUILD.gn b/third_party/dawn/src/dawn_platform/BUILD.gn new file mode 100644 index 00000000000..c2b10933a32 --- /dev/null +++ b/third_party/dawn/src/dawn_platform/BUILD.gn @@ -0,0 +1,28 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../scripts/dawn_overrides_with_defaults.gni") + +source_set("dawn_platform") { + configs += [ "${dawn_root}/src/common:dawn_internal" ] + + sources = [ + "${dawn_root}/src/include/dawn_platform/DawnPlatform.h", + "tracing/EventTracer.cpp", + "tracing/EventTracer.h", + "tracing/TraceEvent.h", + ] + + deps = [ "${dawn_root}/src/common" ] +} diff --git a/third_party/dawn/src/dawn_platform/CMakeLists.txt b/third_party/dawn/src/dawn_platform/CMakeLists.txt new file mode 100644 index 00000000000..d94b5525ed3 --- /dev/null +++ b/third_party/dawn/src/dawn_platform/CMakeLists.txt @@ -0,0 +1,22 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_library(dawn_platform STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawn_platform PRIVATE + "${DAWN_INCLUDE_DIR}/dawn_platform/DawnPlatform.h" + "tracing/EventTracer.cpp" + "tracing/EventTracer.h" + "tracing/TraceEvent.h" +) +target_link_libraries(dawn_platform PRIVATE dawn_internal_config dawn_common) diff --git a/third_party/dawn/src/dawn_platform/tracing/EventTracer.cpp b/third_party/dawn/src/dawn_platform/tracing/EventTracer.cpp new file mode 100644 index 00000000000..a110340f58a --- /dev/null +++ b/third_party/dawn/src/dawn_platform/tracing/EventTracer.cpp @@ -0,0 +1,58 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_platform/tracing/EventTracer.h" +#include "common/Assert.h" +#include "dawn_platform/DawnPlatform.h" + +namespace dawn_platform { namespace tracing { + + const unsigned char* GetTraceCategoryEnabledFlag(Platform* platform, TraceCategory category) { + static unsigned char disabled = 0; + if (platform == nullptr) { + return &disabled; + } + + const unsigned char* categoryEnabledFlag = platform->GetTraceCategoryEnabledFlag(category); + if (categoryEnabledFlag != nullptr) { + return categoryEnabledFlag; + } + + return &disabled; + } + + TraceEventHandle AddTraceEvent(Platform* platform, + char phase, + const unsigned char* categoryGroupEnabled, + const char* name, + uint64_t id, + int numArgs, + const char** argNames, + const unsigned char* argTypes, + const uint64_t* argValues, + unsigned char flags) { + ASSERT(platform != nullptr); + + double timestamp = platform->MonotonicallyIncreasingTime(); + if (timestamp != 0) { + TraceEventHandle handle = + platform->AddTraceEvent(phase, categoryGroupEnabled, name, id, timestamp, numArgs, + argNames, argTypes, argValues, flags); + return handle; + } + + return static_cast(0); + } + +}} // namespace dawn_platform::tracing diff --git a/third_party/dawn/src/dawn_platform/tracing/EventTracer.h b/third_party/dawn/src/dawn_platform/tracing/EventTracer.h new file mode 100644 index 00000000000..114a743d79e --- /dev/null +++ b/third_party/dawn/src/dawn_platform/tracing/EventTracer.h @@ -0,0 +1,47 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNPLATFORM_TRACING_EVENTTRACER_H_ +#define DAWNPLATFORM_TRACING_EVENTTRACER_H_ + +#include + +namespace dawn_platform { + + class Platform; + enum class TraceCategory; + + namespace tracing { + + using TraceEventHandle = uint64_t; + + const unsigned char* GetTraceCategoryEnabledFlag(Platform* platform, + TraceCategory category); + + // TODO(enga): Simplify this API. + TraceEventHandle AddTraceEvent(Platform* platform, + char phase, + const unsigned char* categoryGroupEnabled, + const char* name, + uint64_t id, + int numArgs, + const char** argNames, + const unsigned char* argTypes, + const uint64_t* argValues, + unsigned char flags); + + } // namespace tracing +} // namespace dawn_platform + +#endif // DAWNPLATFORM_TRACING_EVENTTRACER_H_ diff --git a/third_party/dawn/src/dawn_platform/tracing/TraceEvent.h b/third_party/dawn/src/dawn_platform/tracing/TraceEvent.h new file mode 100644 index 00000000000..8b384a6771b --- /dev/null +++ b/third_party/dawn/src/dawn_platform/tracing/TraceEvent.h @@ -0,0 +1,993 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Trace events are for tracking application performance and resource usage. +// Macros are provided to track: +// Begin and end of function calls +// Counters +// +// Events are issued against categories. Whereas LOG's +// categories are statically defined, TRACE categories are created +// implicitly with a string. For example: +// TRACE_EVENT_INSTANT0("MY_SUBSYSTEM", "SomeImportantEvent") +// +// Events can be INSTANT, or can be pairs of BEGIN and END in the same scope: +// TRACE_EVENT_BEGIN0("MY_SUBSYSTEM", "SomethingCostly") +// doSomethingCostly() +// TRACE_EVENT_END0("MY_SUBSYSTEM", "SomethingCostly") +// Note: our tools can't always determine the correct BEGIN/END pairs unless +// these are used in the same scope. Use ASYNC_BEGIN/ASYNC_END macros if you need them +// to be in separate scopes. +// +// A common use case is to trace entire function scopes. This +// issues a trace BEGIN and END automatically: +// void doSomethingCostly() { +// TRACE_EVENT0("MY_SUBSYSTEM", "doSomethingCostly"); +// ... +// } +// +// Additional parameters can be associated with an event: +// void doSomethingCostly2(int howMuch) { +// TRACE_EVENT1("MY_SUBSYSTEM", "doSomethingCostly", +// "howMuch", howMuch); +// ... +// } +// +// The trace system will automatically add to this information the +// current process id, thread id, and a timestamp in microseconds. +// +// To trace an asynchronous procedure such as an IPC send/receive, use ASYNC_BEGIN and +// ASYNC_END: +// [single threaded sender code] +// static int send_count = 0; +// ++send_count; +// TRACE_EVENT_ASYNC_BEGIN0("ipc", "message", send_count); +// Send(new MyMessage(send_count)); +// [receive code] +// void OnMyMessage(send_count) { +// TRACE_EVENT_ASYNC_END0("ipc", "message", send_count); +// } +// The third parameter is a unique ID to match ASYNC_BEGIN/ASYNC_END pairs. +// ASYNC_BEGIN and ASYNC_END can occur on any thread of any traced process. Pointers can +// be used for the ID parameter, and they will be mangled internally so that +// the same pointer on two different processes will not match. For example: +// class MyTracedClass { +// public: +// MyTracedClass() { +// TRACE_EVENT_ASYNC_BEGIN0("category", "MyTracedClass", this); +// } +// ~MyTracedClass() { +// TRACE_EVENT_ASYNC_END0("category", "MyTracedClass", this); +// } +// } +// +// Trace event also supports counters, which is a way to track a quantity +// as it varies over time. Counters are created with the following macro: +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter", g_myCounterValue); +// +// Counters are process-specific. The macro itself can be issued from any +// thread, however. +// +// Sometimes, you want to track two counters at once. You can do this with two +// counter macros: +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter0", g_myCounterValue[0]); +// TRACE_COUNTER1("MY_SUBSYSTEM", "myCounter1", g_myCounterValue[1]); +// Or you can do it with a combined macro: +// TRACE_COUNTER2("MY_SUBSYSTEM", "myCounter", +// "bytesPinned", g_myCounterValue[0], +// "bytesAllocated", g_myCounterValue[1]); +// This indicates to the tracing UI that these counters should be displayed +// in a single graph, as a summed area chart. +// +// Since counters are in a global namespace, you may want to disembiguate with a +// unique ID, by using the TRACE_COUNTER_ID* variations. +// +// By default, trace collection is compiled in, but turned off at runtime. +// Collecting trace data is the responsibility of the embedding +// application. In Chrome's case, navigating to about:tracing will turn on +// tracing and display data collected across all active processes. +// +// +// Memory scoping note: +// Tracing copies the pointers, not the string content, of the strings passed +// in for category, name, and arg_names. Thus, the following code will +// cause problems: +// char* str = strdup("impprtantName"); +// TRACE_EVENT_INSTANT0("SUBSYSTEM", str); // BAD! +// free(str); // Trace system now has dangling pointer +// +// To avoid this issue with the |name| and |arg_name| parameters, use the +// TRACE_EVENT_COPY_XXX overloads of the macros at additional runtime overhead. +// Notes: The category must always be in a long-lived char* (i.e. static const). +// The |arg_values|, when used, are always deep copied with the _COPY +// macros. +// +// When are string argument values copied: +// const char* arg_values are only referenced by default: +// TRACE_EVENT1("category", "name", +// "arg1", "literal string is only referenced"); +// Use TRACE_STR_COPY to force copying of a const char*: +// TRACE_EVENT1("category", "name", +// "arg1", TRACE_STR_COPY("string will be copied")); +// std::string arg_values are always copied: +// TRACE_EVENT1("category", "name", +// "arg1", std::string("string will be copied")); +// +// +// Thread Safety: +// A thread safe singleton and mutex are used for thread safety. Category +// enabled flags are used to limit the performance impact when the system +// is not enabled. +// +// TRACE_EVENT macros first cache a pointer to a category. The categories are +// statically allocated and safe at all times, even after exit. Fetching a +// category is protected by the TraceLog::lock_. Multiple threads initializing +// the static variable is safe, as they will be serialized by the lock and +// multiple calls will return the same pointer to the category. +// +// Then the category_enabled flag is checked. This is a unsigned char, and +// not intended to be multithread safe. It optimizes access to addTraceEvent +// which is threadsafe internally via TraceLog::lock_. The enabled flag may +// cause some threads to incorrectly call or skip calling addTraceEvent near +// the time of the system being enabled or disabled. This is acceptable as +// we tolerate some data loss while the system is being enabled/disabled and +// because addTraceEvent is threadsafe internally and checks the enabled state +// again under lock. +// +// Without the use of these static category pointers and enabled flags all +// trace points would carry a significant performance cost of aquiring a lock +// and resolving the category. + +#ifndef DAWNPLATFORM_TRACING_TRACEEVENT_H_ +#define DAWNPLATFORM_TRACING_TRACEEVENT_H_ + +#include + +#include "dawn_platform/tracing/EventTracer.h" + +// Records a pair of begin and end events called "name" for the current +// scope, with 0, 1 or 2 associated arguments. If the category is not +// enabled, then this does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT0(platform, category, name) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(platform, category, name, 0) +#define TRACE_EVENT1(platform, category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(platform, category, name, 0, arg1_name, arg1_val) +#define TRACE_EVENT2(platform, category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_SCOPED(platform, category, name, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) + +// Records a single event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_INSTANT0(platform, category, name) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_INSTANT, category, name, \ + TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_INSTANT1(platform, category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_INSTANT, category, name, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val) +#define TRACE_EVENT_INSTANT2(platform, category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_INSTANT, category, name, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_INSTANT0(platform, category, name) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_INSTANT, category, name, \ + TRACE_EVENT_FLAG_COPY, 0) +#define TRACE_EVENT_COPY_INSTANT1(platform, category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_INSTANT, category, name, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_INSTANT2(platform, category, name, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_INSTANT, category, name, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val, arg2_name, arg2_val) + +// Records a single BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_BEGIN0(platform, category, name) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_BEGIN, category, name, \ + TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_BEGIN1(platform, category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_BEGIN, category, name, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val) +#define TRACE_EVENT_BEGIN2(platform, category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_BEGIN, category, name, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_BEGIN0(platform, category, name) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_BEGIN, category, name, \ + TRACE_EVENT_FLAG_COPY, 0) +#define TRACE_EVENT_COPY_BEGIN1(platform, category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_BEGIN, category, name, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_BEGIN2(platform, category, name, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_BEGIN, category, name, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val, arg2_name, arg2_val) + +// Records a single END event for "name" immediately. If the category +// is not enabled, then this does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_EVENT_END0(platform, category, name) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_END, category, name, \ + TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_END1(platform, category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_END, category, name, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val) +#define TRACE_EVENT_END2(platform, category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_END, category, name, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_END0(platform, category, name) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_END, category, name, TRACE_EVENT_FLAG_COPY) +#define TRACE_EVENT_COPY_END1(platform, category, name, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_END, category, name, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_END2(platform, category, name, arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_END, category, name, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val, arg2_name, arg2_val) + +// Records the value of a counter called "name" immediately. Value +// must be representable as a 32 bit integer. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_COUNTER1(platform, category, name, value) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_COUNTER, category, name, \ + TRACE_EVENT_FLAG_NONE, 0, "value", static_cast(value)) +#define TRACE_COPY_COUNTER1(platform, category, name, value) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_COUNTER, category, name, \ + TRACE_EVENT_FLAG_COPY, 0, "value", static_cast(value)) + +// Records the values of a multi-parted counter called "name" immediately. +// The UI will treat value1 and value2 as parts of a whole, displaying their +// values as a stacked-bar chart. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +#define TRACE_COUNTER2(platform, category, name, value1_name, value1_val, value2_name, value2_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_COUNTER, category, name, \ + TRACE_EVENT_FLAG_NONE, 0, value1_name, static_cast(value1_val), \ + value2_name, static_cast(value2_val)) +#define TRACE_COPY_COUNTER2(platform, category, name, value1_name, value1_val, value2_name, \ + value2_val) \ + INTERNAL_TRACE_EVENT_ADD(platform, TRACE_EVENT_PHASE_COUNTER, category, name, \ + TRACE_EVENT_FLAG_COPY, 0, value1_name, static_cast(value1_val), \ + value2_name, static_cast(value2_val)) + +// Records the value of a counter called "name" immediately. Value +// must be representable as a 32 bit integer. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to disambiguate counters with the same name. It must either +// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits +// will be xored with a hash of the process ID so that the same pointer on +// two different processes will not collide. +#define TRACE_COUNTER_ID1(platform, category, name, id, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_COUNTER, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0, "value", static_cast(value)) +#define TRACE_COPY_COUNTER_ID1(platform, category, name, id, value) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_COUNTER, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0, "value", static_cast(value)) + +// Records the values of a multi-parted counter called "name" immediately. +// The UI will treat value1 and value2 as parts of a whole, displaying their +// values as a stacked-bar chart. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to disambiguate counters with the same name. It must either +// be a pointer or an integer value up to 64 bits. If it's a pointer, the bits +// will be xored with a hash of the process ID so that the same pointer on +// two different processes will not collide. +#define TRACE_COUNTER_ID2(platform, category, name, id, value1_name, value1_val, value2_name, \ + value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + platform, TRACE_EVENT_PHASE_COUNTER, category, name, id, TRACE_EVENT_FLAG_NONE, 0, \ + value1_name, static_cast(value1_val), value2_name, static_cast(value2_val)) +#define TRACE_COPY_COUNTER_ID2(platform, category, name, id, value1_name, value1_val, value2_name, \ + value2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID( \ + platform, TRACE_EVENT_PHASE_COUNTER, category, name, id, TRACE_EVENT_FLAG_COPY, 0, \ + value1_name, static_cast(value1_val), value2_name, static_cast(value2_val)) + +// Records a single ASYNC_BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to match the ASYNC_BEGIN event with the ASYNC_END event. ASYNC +// events are considered to match if their category, name and id values all +// match. |id| must either be a pointer or an integer value up to 64 bits. If +// it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// An asynchronous operation can consist of multiple phases. The first phase is +// defined by the ASYNC_BEGIN calls. Additional phases can be defined using the +// ASYNC_STEP_BEGIN macros. When the operation completes, call ASYNC_END. +// An async operation can span threads and processes, but all events in that +// operation must use the same |name| and |id|. Each event can have its own +// args. +#define TRACE_EVENT_ASYNC_BEGIN0(platform, category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_ASYNC_BEGIN1(platform, category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_BEGIN2(platform, category, name, id, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN0(platform, category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0) +#define TRACE_EVENT_COPY_ASYNC_BEGIN1(platform, category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_BEGIN2(platform, category, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_BEGIN, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) + +// Records a single ASYNC_STEP event for |step| immediately. If the category +// is not enabled, then this does nothing. The |name| and |id| must match the +// ASYNC_BEGIN event above. The |step| param identifies this step within the +// async event. This should be called at the beginning of the next phase of an +// asynchronous operation. +#define TRACE_EVENT_ASYNC_STEP0(platform, category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_STEP, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0, "step", step) +#define TRACE_EVENT_ASYNC_STEP1(platform, category, name, id, step, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_STEP, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0, "step", step, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_STEP0(platform, category, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_STEP, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0, "step", step) +#define TRACE_EVENT_COPY_ASYNC_STEP1(platform, category, name, id, step, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_STEP, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0, "step", step, arg1_name, arg1_val) + +// Records a single ASYNC_END event for "name" immediately. If the category +// is not enabled, then this does nothing. +#define TRACE_EVENT_ASYNC_END0(platform, category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_END, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_ASYNC_END1(platform, category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_END, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val) +#define TRACE_EVENT_ASYNC_END2(platform, category, name, id, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_END, category, name, id, \ + TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) +#define TRACE_EVENT_COPY_ASYNC_END0(platform, category, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_END, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0) +#define TRACE_EVENT_COPY_ASYNC_END1(platform, category, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_END, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_ASYNC_END2(platform, category, name, id, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_ASYNC_END, category, name, id, \ + TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) + +// NESTABLE_ASYNC_* APIs are used to describe an async operation, which can +// be nested within a NESTABLE_ASYNC event and/or have inner NESTABLE_ASYNC +// events. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - A pair of NESTABLE_ASYNC_BEGIN event and NESTABLE_ASYNC_END event is +// considered as a match if their category_group, name and id all match. +// - |id| must either be a pointer or an integer value up to 64 bits. +// If it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// - |id| is used to match a child NESTABLE_ASYNC event with its parent +// NESTABLE_ASYNC event. Therefore, events in the same nested event tree must +// be logged using the same id and category_group. +// +// Unmatched NESTABLE_ASYNC_END event will be parsed as an event that starts +// at the first NESTABLE_ASYNC event of that id, and unmatched +// NESTABLE_ASYNC_BEGIN event will be parsed as an event that ends at the last +// NESTABLE_ASYNC event of that id. Corresponding warning messages for +// unmatched events will be shown in the analysis view. + +// Records a single NESTABLE_ASYNC_BEGIN event called "name" immediately, with +// 0, 1 or 2 associated arguments. If the category is not enabled, then this +// does nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN0(platform, category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN1(platform, category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0, \ + arg1_name, arg1_val) +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN2(platform, category_group, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0, \ + arg1_name, arg1_val, arg2_name, arg2_val) +// Records a single NESTABLE_ASYNC_END event called "name" immediately, with 0 +// or 2 associated arguments. If the category is not enabled, then this does +// nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_END0(platform, category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0) +// Records a single NESTABLE_ASYNC_END event called "name" immediately, with 1 +// associated argument. If the category is not enabled, then this does nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_END1(platform, category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0, \ + arg1_name, arg1_val) +#define TRACE_EVENT_NESTABLE_ASYNC_END2(platform, category_group, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0, \ + arg1_name, arg1_val, arg2_name, arg2_val) + +// Records a single NESTABLE_ASYNC_INSTANT event called "name" immediately, +// with none, one or two associated argument. If the category is not enabled, +// then this does nothing. +#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT0(platform, category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0) + +#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT1(platform, category_group, name, id, arg1_name, \ + arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0, \ + arg1_name, arg1_val) + +#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT2(platform, category_group, name, id, arg1_name, \ + arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, \ + category_group, name, id, TRACE_EVENT_FLAG_NONE, 0, \ + arg1_name, arg1_val, arg2_name, arg2_val) + +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TTS2(platform, category_group, name, id, \ + arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_ASYNC_TTS | TRACE_EVENT_FLAG_COPY, 0, \ + arg1_name, arg1_val, arg2_name, arg2_val) +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TTS2(platform, category_group, name, id, \ + arg1_name, arg1_val, arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, \ + category_group, name, id, \ + TRACE_EVENT_FLAG_ASYNC_TTS | TRACE_EVENT_FLAG_COPY, 0, \ + arg1_name, arg1_val, arg2_name, arg2_val) + +// Similar to TRACE_EVENT_NESTABLE_ASYNC_{BEGIN,END}x but with a custom +// |timestamp| provided. +#define TRACE_EVENT_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(platform, category_group, name, id, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(platform, category_group, name, id, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_NESTABLE_ASYNC_END_WITH_TIMESTAMP1(platform, category_group, name, id, \ + timestamp, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, 0, arg1_name, \ + arg1_val) +#define TRACE_EVENT_NESTABLE_ASYNC_INSTANT_WITH_TIMESTAMP0(platform, category_group, name, id, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_BEGIN_WITH_TIMESTAMP0(platform, category_group, name, id, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY, 0) +#define TRACE_EVENT_COPY_NESTABLE_ASYNC_END_WITH_TIMESTAMP0(platform, category_group, name, id, \ + timestamp) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \ + platform, TRACE_EVENT_PHASE_NESTABLE_ASYNC_END, category_group, name, id, \ + TRACE_EVENT_API_CURRENT_THREAD_ID, timestamp, TRACE_EVENT_FLAG_COPY, 0) + +// Records a single FLOW_BEGIN event called "name" immediately, with 0, 1 or 2 +// associated arguments. If the category is not enabled, then this +// does nothing. +// - category and name strings must have application lifetime (statics or +// literals). They may not include " chars. +// - |id| is used to match the FLOW_BEGIN event with the FLOW_END event. FLOW +// events are considered to match if their category_group, name and id values +// all match. |id| must either be a pointer or an integer value up to 64 bits. +// If it's a pointer, the bits will be xored with a hash of the process ID so +// that the same pointer on two different processes will not collide. +// FLOW events are different from ASYNC events in how they are drawn by the +// tracing UI. A FLOW defines asynchronous data flow, such as posting a task +// (FLOW_BEGIN) and later executing that task (FLOW_END). Expect FLOWs to be +// drawn as lines or arrows from FLOW_BEGIN scopes to FLOW_END scopes. Similar +// to ASYNC, a FLOW can consist of multiple phases. The first phase is defined +// by the FLOW_BEGIN calls. Additional phases can be defined using the FLOW_STEP +// macros. When the operation completes, call FLOW_END. An async operation can +// span threads and processes, but all events in that operation must use the +// same |name| and |id|. Each event can have its own args. +#define TRACE_EVENT_FLOW_BEGIN0(platform, category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_FLOW_BEGIN1(platform, category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val) +#define TRACE_EVENT_FLOW_BEGIN2(platform, category_group, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) +#define TRACE_EVENT_COPY_FLOW_BEGIN0(platform, category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, \ + id, TRACE_EVENT_FLAG_COPY, 0) +#define TRACE_EVENT_COPY_FLOW_BEGIN1(platform, category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, \ + id, TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_FLOW_BEGIN2(platform, category_group, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_BEGIN, category_group, name, \ + id, TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) + +// Records a single FLOW_STEP event for |step| immediately. If the category +// is not enabled, then this does nothing. The |name| and |id| must match the +// FLOW_BEGIN event above. The |step| param identifies this step within the +// async event. This should be called at the beginning of the next phase of an +// asynchronous operation. +#define TRACE_EVENT_FLOW_STEP0(platform, category_group, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, 0, "step", step) +#define TRACE_EVENT_FLOW_STEP1(platform, category_group, name, id, step, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, 0, "step", step, arg1_name, \ + arg1_val) +#define TRACE_EVENT_COPY_FLOW_STEP0(platform, category_group, name, id, step) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, \ + id, TRACE_EVENT_FLAG_COPY, 0, "step", step) +#define TRACE_EVENT_COPY_FLOW_STEP1(platform, category_group, name, id, step, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_STEP, category_group, name, \ + id, TRACE_EVENT_FLAG_COPY, 0, "step", step, arg1_name, \ + arg1_val) + +// Records a single FLOW_END event for "name" immediately. If the category +// is not enabled, then this does nothing. +#define TRACE_EVENT_FLOW_END0(platform, category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_END, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, 0) +#define TRACE_EVENT_FLOW_END_BIND_TO_ENCLOSING0(platform, category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_END, category_group, name, \ + id, TRACE_EVENT_FLAG_BIND_TO_ENCLOSING, 0) +#define TRACE_EVENT_FLOW_END1(platform, category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_END, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val) +#define TRACE_EVENT_FLOW_END2(platform, category_group, name, id, arg1_name, arg1_val, arg2_name, \ + arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_END, category_group, name, \ + id, TRACE_EVENT_FLAG_NONE, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) +#define TRACE_EVENT_COPY_FLOW_END0(platform, category_group, name, id) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_END, category_group, name, \ + id, TRACE_EVENT_FLAG_COPY, 0) +#define TRACE_EVENT_COPY_FLOW_END1(platform, category_group, name, id, arg1_name, arg1_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_END, category_group, name, \ + id, TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val) +#define TRACE_EVENT_COPY_FLOW_END2(platform, category_group, name, id, arg1_name, arg1_val, \ + arg2_name, arg2_val) \ + INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, TRACE_EVENT_PHASE_FLOW_END, category_group, name, \ + id, TRACE_EVENT_FLAG_COPY, 0, arg1_name, arg1_val, arg2_name, \ + arg2_val) + +// Creates a scope of a sampling state with the given category and name (both must +// be constant strings). These states are intended for a sampling profiler. +// Implementation note: we store category and name together because we don't +// want the inconsistency/expense of storing two pointers. +// |thread_bucket| is [0..2] and is used to statically isolate samples in one +// thread from others. +// +// { // The sampling state is set within this scope. +// TRACE_EVENT_SAMPLING_STATE_SCOPE_FOR_BUCKET(0, "category", "name"); +// ...; +// } +#define TRACE_EVENT_SCOPED_SAMPLING_STATE_FOR_BUCKET(bucket_number, category, name) \ + TraceEvent::SamplingStateScope traceEventSamplingScope(category "\0" name); + +// Returns a current sampling state of the given bucket. +// The format of the returned string is "category\0name". +#define TRACE_EVENT_GET_SAMPLING_STATE_FOR_BUCKET(bucket_number) \ + TraceEvent::SamplingStateScope::current() + +// Sets a current sampling state of the given bucket. +// |category| and |name| have to be constant strings. +#define TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(bucket_number, category, name) \ + TraceEvent::SamplingStateScope::set(category "\0" name) + +// Sets a current sampling state of the given bucket. +// |categoryAndName| doesn't need to be a constant string. +// The format of the string is "category\0name". +#define TRACE_EVENT_SET_NONCONST_SAMPLING_STATE_FOR_BUCKET(bucket_number, categoryAndName) \ + TraceEvent::SamplingStateScope::set(categoryAndName) + +// Syntactic sugars for the sampling tracing in the main thread. +#define TRACE_EVENT_SCOPED_SAMPLING_STATE(category, name) \ + TRACE_EVENT_SCOPED_SAMPLING_STATE_FOR_BUCKET(0, category, name) +#define TRACE_EVENT_GET_SAMPLING_STATE() TRACE_EVENT_GET_SAMPLING_STATE_FOR_BUCKET(0) +#define TRACE_EVENT_SET_SAMPLING_STATE(category, name) \ + TRACE_EVENT_SET_SAMPLING_STATE_FOR_BUCKET(0, category, name) +#define TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(categoryAndName) \ + TRACE_EVENT_SET_NONCONST_SAMPLING_STATE_FOR_BUCKET(0, categoryAndName) + +//////////////////////////////////////////////////////////////////////////////// +// Implementation specific tracing API definitions. + +// Get a pointer to the enabled state of the given trace category. Only +// long-lived literal strings should be given as the category name. The returned +// pointer can be held permanently in a local static for example. If the +// unsigned char is non-zero, tracing is enabled. If tracing is enabled, +// TRACE_EVENT_API_ADD_TRACE_EVENT can be called. It's OK if tracing is disabled +// between the load of the tracing state and the call to +// TRACE_EVENT_API_ADD_TRACE_EVENT, because this flag only provides an early out +// for best performance when tracing is disabled. +// const unsigned char* +// TRACE_EVENT_API_GET_CATEGORY_ENABLED(const char* category_name) +#define TRACE_EVENT_API_GET_CATEGORY_ENABLED dawn_platform::tracing::GetTraceCategoryEnabledFlag + +// Add a trace event to the platform tracing system. +// void TRACE_EVENT_API_ADD_TRACE_EVENT( +// char phase, +// const unsigned char* category_enabled, +// const char* name, +// unsigned long long id, +// int num_args, +// const char** arg_names, +// const unsigned char* arg_types, +// const unsigned long long* arg_values, +// unsigned char flags) +#define TRACE_EVENT_API_ADD_TRACE_EVENT dawn_platform::tracing::AddTraceEvent + +//////////////////////////////////////////////////////////////////////////////// + +// Implementation detail: trace event macros create temporary variables +// to keep instrumentation overhead low. These macros give each temporary +// variable a unique name based on the line number to prevent name collissions. +#define INTERNAL_TRACE_EVENT_UID3(a, b) trace_event_unique_##a##b +#define INTERNAL_TRACE_EVENT_UID2(a, b) INTERNAL_TRACE_EVENT_UID3(a, b) +#define INTERNALTRACEEVENTUID(name_prefix) INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__) + +// Implementation detail: internal macro to create static category. +#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(platform, category) \ + static const unsigned char* INTERNALTRACEEVENTUID(catstatic) = 0; \ + if (!INTERNALTRACEEVENTUID(catstatic)) \ + INTERNALTRACEEVENTUID(catstatic) = TRACE_EVENT_API_GET_CATEGORY_ENABLED(platform, category); + +// Implementation detail: internal macro to create static category and add +// event if the category is enabled. +#define INTERNAL_TRACE_EVENT_ADD(platform, phase, category, name, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(platform, ::dawn_platform::TraceCategory::category) \ + if (*INTERNALTRACEEVENTUID(catstatic)) { \ + dawn_platform::TraceEvent::addTraceEvent( \ + platform, phase, INTERNALTRACEEVENTUID(catstatic), name, \ + dawn_platform::TraceEvent::noEventId, flags, __VA_ARGS__); \ + } \ + } while (0) + +// Implementation detail: internal macro to create static category and add begin +// event if the category is enabled. Also adds the end event when the scope +// ends. +#define INTERNAL_TRACE_EVENT_ADD_SCOPED(platform, category, name, ...) \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(platform, ::dawn_platform::TraceCategory::category) \ + dawn_platform::TraceEvent::TraceEndOnScopeClose INTERNALTRACEEVENTUID(profileScope); \ + do { \ + if (*INTERNALTRACEEVENTUID(catstatic)) { \ + dawn_platform::TraceEvent::addTraceEvent( \ + platform, TRACE_EVENT_PHASE_BEGIN, INTERNALTRACEEVENTUID(catstatic), name, \ + dawn_platform::TraceEvent::noEventId, TRACE_EVENT_FLAG_NONE, __VA_ARGS__); \ + INTERNALTRACEEVENTUID(profileScope) \ + .initialize(platform, INTERNALTRACEEVENTUID(catstatic), name); \ + } \ + } while (0) + +// Implementation detail: internal macro to create static category and add +// event if the category is enabled. +#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(platform, phase, category, name, id, flags, ...) \ + do { \ + INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(platform, ::dawn_platform::TraceCategory::category) \ + if (*INTERNALTRACEEVENTUID(catstatic)) { \ + unsigned char traceEventFlags = flags | TRACE_EVENT_FLAG_HAS_ID; \ + dawn_platform::TraceEvent::TraceID traceEventTraceID(id, &traceEventFlags); \ + dawn_platform::TraceEvent::addTraceEvent( \ + platform, phase, INTERNALTRACEEVENTUID(catstatic), name, traceEventTraceID.data(), \ + traceEventFlags, __VA_ARGS__); \ + } \ + } while (0) + +// Notes regarding the following definitions: +// New values can be added and propagated to third party libraries, but existing +// definitions must never be changed, because third party libraries may use old +// definitions. + +// Phase indicates the nature of an event entry. E.g. part of a begin/end pair. +#define TRACE_EVENT_PHASE_BEGIN ('B') +#define TRACE_EVENT_PHASE_END ('E') +#define TRACE_EVENT_PHASE_INSTANT ('I') +#define TRACE_EVENT_PHASE_ASYNC_BEGIN ('S') +#define TRACE_EVENT_PHASE_ASYNC_STEP ('T') +#define TRACE_EVENT_PHASE_ASYNC_END ('F') +#define TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN ('b') +#define TRACE_EVENT_PHASE_NESTABLE_ASYNC_END ('e') +#define TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT ('n') +#define TRACE_EVENT_PHASE_FLOW_BEGIN ('s') +#define TRACE_EVENT_PHASE_FLOW_STEP ('t') +#define TRACE_EVENT_PHASE_FLOW_END ('f') +#define TRACE_EVENT_PHASE_METADATA ('M') +#define TRACE_EVENT_PHASE_COUNTER ('C') +#define TRACE_EVENT_PHASE_SAMPLE ('P') + +// Flags for changing the behavior of TRACE_EVENT_API_ADD_TRACE_EVENT. +#define TRACE_EVENT_FLAG_NONE (static_cast(0)) +#define TRACE_EVENT_FLAG_COPY (static_cast(1 << 0)) +#define TRACE_EVENT_FLAG_HAS_ID (static_cast(1 << 1)) +#define TRACE_EVENT_FLAG_MANGLE_ID (static_cast(1 << 2)) + +// Type values for identifying types in the TraceValue union. +#define TRACE_VALUE_TYPE_BOOL (static_cast(1)) +#define TRACE_VALUE_TYPE_UINT (static_cast(2)) +#define TRACE_VALUE_TYPE_INT (static_cast(3)) +#define TRACE_VALUE_TYPE_DOUBLE (static_cast(4)) +#define TRACE_VALUE_TYPE_POINTER (static_cast(5)) +#define TRACE_VALUE_TYPE_STRING (static_cast(6)) +#define TRACE_VALUE_TYPE_COPY_STRING (static_cast(7)) + +namespace dawn_platform { namespace TraceEvent { + + // Specify these values when the corresponding argument of addTraceEvent is not + // used. + const int zeroNumArgs = 0; + const unsigned long long noEventId = 0; + + // TraceID encapsulates an ID that can either be an integer or pointer. Pointers + // are mangled with the Process ID so that they are unlikely to collide when the + // same pointer is used on different processes. + class TraceID { + public: + explicit TraceID(const void* id, unsigned char* flags) + : m_data(static_cast(reinterpret_cast(id))) { + *flags |= TRACE_EVENT_FLAG_MANGLE_ID; + } + explicit TraceID(unsigned long long id, unsigned char* flags) : m_data(id) { + (void)flags; + } + explicit TraceID(unsigned long id, unsigned char* flags) : m_data(id) { + (void)flags; + } + explicit TraceID(unsigned int id, unsigned char* flags) : m_data(id) { + (void)flags; + } + explicit TraceID(unsigned short id, unsigned char* flags) : m_data(id) { + (void)flags; + } + explicit TraceID(unsigned char id, unsigned char* flags) : m_data(id) { + (void)flags; + } + explicit TraceID(long long id, unsigned char* flags) + : m_data(static_cast(id)) { + (void)flags; + } + explicit TraceID(long id, unsigned char* flags) + : m_data(static_cast(id)) { + (void)flags; + } + explicit TraceID(int id, unsigned char* flags) + : m_data(static_cast(id)) { + (void)flags; + } + explicit TraceID(short id, unsigned char* flags) + : m_data(static_cast(id)) { + (void)flags; + } + explicit TraceID(signed char id, unsigned char* flags) + : m_data(static_cast(id)) { + (void)flags; + } + + unsigned long long data() const { + return m_data; + } + + private: + unsigned long long m_data; + }; + + // Simple union to store various types as unsigned long long. + union TraceValueUnion { + bool m_bool; + unsigned long long m_uint; + long long m_int; + double m_double; + const void* m_pointer; + const char* m_string; + }; + + // Simple container for const char* that should be copied instead of retained. + class TraceStringWithCopy { + public: + explicit TraceStringWithCopy(const char* str) : m_str(str) { + } + operator const char*() const { + return m_str; + } + + private: + const char* m_str; + }; + +// Define setTraceValue for each allowed type. It stores the type and +// value in the return arguments. This allows this API to avoid declaring any +// structures so that it is portable to third_party libraries. +#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, union_member, value_type_id) \ + static inline void setTraceValue(actual_type arg, unsigned char* type, \ + unsigned long long* value) { \ + TraceValueUnion typeValue; \ + typeValue.union_member = arg; \ + *type = value_type_id; \ + *value = typeValue.m_uint; \ + } +// Simpler form for int types that can be safely casted. +#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, value_type_id) \ + static inline void setTraceValue(actual_type arg, unsigned char* type, \ + unsigned long long* value) { \ + *type = value_type_id; \ + *value = static_cast(arg); \ + } + + INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned long long, TRACE_VALUE_TYPE_UINT) + INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned int, TRACE_VALUE_TYPE_UINT) + INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned short, TRACE_VALUE_TYPE_UINT) + INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned char, TRACE_VALUE_TYPE_UINT) + INTERNAL_DECLARE_SET_TRACE_VALUE_INT(long long, TRACE_VALUE_TYPE_INT) + INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int, TRACE_VALUE_TYPE_INT) + INTERNAL_DECLARE_SET_TRACE_VALUE_INT(short, TRACE_VALUE_TYPE_INT) + INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT) + INTERNAL_DECLARE_SET_TRACE_VALUE(bool, m_bool, TRACE_VALUE_TYPE_BOOL) + INTERNAL_DECLARE_SET_TRACE_VALUE(double, m_double, TRACE_VALUE_TYPE_DOUBLE) + INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, m_pointer, TRACE_VALUE_TYPE_POINTER) + INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, m_string, TRACE_VALUE_TYPE_STRING) + INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, + m_string, + TRACE_VALUE_TYPE_COPY_STRING) + +#undef INTERNAL_DECLARE_SET_TRACE_VALUE +#undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT + + static inline void setTraceValue(const std::string& arg, + unsigned char* type, + unsigned long long* value) { + TraceValueUnion typeValue; + typeValue.m_string = arg.data(); + *type = TRACE_VALUE_TYPE_COPY_STRING; + *value = typeValue.m_uint; + } + + // These addTraceEvent template functions are defined here instead of in the + // macro, because the arg values could be temporary string objects. In order to + // store pointers to the internal c_str and pass through to the tracing API, the + // arg values must live throughout these procedures. + + static inline dawn_platform::tracing::TraceEventHandle addTraceEvent( + dawn_platform::Platform* platform, + char phase, + const unsigned char* categoryEnabled, + const char* name, + unsigned long long id, + unsigned char flags, + int /*unused, helps avoid empty __VA_ARGS__*/) { + return TRACE_EVENT_API_ADD_TRACE_EVENT(platform, phase, categoryEnabled, name, id, + zeroNumArgs, 0, 0, 0, flags); + } + + template + static inline dawn_platform::tracing::TraceEventHandle addTraceEvent( + dawn_platform::Platform* platform, + char phase, + const unsigned char* categoryEnabled, + const char* name, + unsigned long long id, + unsigned char flags, + int /*unused, helps avoid empty __VA_ARGS__*/, + const char* arg1Name, + const ARG1_TYPE& arg1Val) { + const int numArgs = 1; + unsigned char argTypes[1]; + uint64_t argValues[1]; + setTraceValue(arg1Val, &argTypes[0], &argValues[0]); + return TRACE_EVENT_API_ADD_TRACE_EVENT(platform, phase, categoryEnabled, name, id, + numArgs, &arg1Name, argTypes, argValues, flags); + } + + template + static inline dawn_platform::tracing::TraceEventHandle addTraceEvent( + dawn_platform::Platform* platform, + char phase, + const unsigned char* categoryEnabled, + const char* name, + unsigned long long id, + unsigned char flags, + int /*unused, helps avoid empty __VA_ARGS__*/, + const char* arg1Name, + const ARG1_TYPE& arg1Val, + const char* arg2Name, + const ARG2_TYPE& arg2Val) { + const int numArgs = 2; + const char* argNames[2] = {arg1Name, arg2Name}; + unsigned char argTypes[2]; + uint64_t argValues[2]; + setTraceValue(arg1Val, &argTypes[0], &argValues[0]); + setTraceValue(arg2Val, &argTypes[1], &argValues[1]); + return TRACE_EVENT_API_ADD_TRACE_EVENT(platform, phase, categoryEnabled, name, id, + numArgs, argNames, argTypes, argValues, flags); + } + + // Used by TRACE_EVENTx macro. Do not use directly. + class TraceEndOnScopeClose { + public: + // Note: members of m_data intentionally left uninitialized. See initialize. + TraceEndOnScopeClose() : m_pdata(0) { + } + ~TraceEndOnScopeClose() { + if (m_pdata) + addEventIfEnabled(); + } + + void initialize(dawn_platform::Platform* platform, + const unsigned char* categoryEnabled, + const char* name) { + m_data.platform = platform; + m_data.categoryEnabled = categoryEnabled; + m_data.name = name; + m_pdata = &m_data; + } + + private: + // Add the end event if the category is still enabled. + void addEventIfEnabled() { + // Only called when m_pdata is non-null. + if (*m_pdata->categoryEnabled) { + TRACE_EVENT_API_ADD_TRACE_EVENT( + m_pdata->platform, TRACE_EVENT_PHASE_END, m_pdata->categoryEnabled, + m_pdata->name, noEventId, zeroNumArgs, 0, 0, 0, TRACE_EVENT_FLAG_NONE); + } + } + + // This Data struct workaround is to avoid initializing all the members + // in Data during construction of this object, since this object is always + // constructed, even when tracing is disabled. If the members of Data were + // members of this class instead, compiler warnings occur about potential + // uninitialized accesses. + struct Data { + dawn_platform::Platform* platform; + const unsigned char* categoryEnabled; + const char* name; + }; + Data* m_pdata; + Data m_data; + }; + +}} // namespace dawn_platform::TraceEvent + +#endif // DAWNPLATFORM_TRACING_TRACEEVENT_H_ diff --git a/third_party/dawn/src/dawn_wire/BUILD.gn b/third_party/dawn/src/dawn_wire/BUILD.gn index c6a6618be1b..9e463423242 100644 --- a/third_party/dawn/src/dawn_wire/BUILD.gn +++ b/third_party/dawn/src/dawn_wire/BUILD.gn @@ -14,19 +14,13 @@ import("../../scripts/dawn_overrides_with_defaults.gni") -import("${dawn_root}/scripts/dawn_component.gni") import("${dawn_root}/generator/dawn_generator.gni") +import("${dawn_root}/scripts/dawn_component.gni") -############################################################################### -# libdawn_wire -############################################################################### - -# Public libdawn_wire headers so they can be publically visible for -# dependencies of libdawn_wire -source_set("libdawn_wire_headers") { - public_deps = [ - "${dawn_root}/src/dawn:dawn_headers", - ] +# Public dawn_wire headers so they can be publically visible for +# dependencies of dawn_wire +source_set("dawn_wire_headers") { + public_deps = [ "${dawn_root}/src/dawn:dawn_headers" ] all_dependent_configs = [ "${dawn_root}/src/common:dawn_public_include_dirs" ] sources = [ "${dawn_root}/src/include/dawn_wire/Wire.h", @@ -35,3 +29,63 @@ source_set("libdawn_wire_headers") { "${dawn_root}/src/include/dawn_wire/dawn_wire_export.h", ] } + +dawn_json_generator("dawn_wire_gen") { + target = "dawn_wire" + outputs = [ + "src/dawn_wire/WireCmd_autogen.h", + "src/dawn_wire/WireCmd_autogen.cpp", + "src/dawn_wire/client/ApiObjects_autogen.h", + "src/dawn_wire/client/ApiProcs_autogen.cpp", + "src/dawn_wire/client/ClientBase_autogen.h", + "src/dawn_wire/client/ClientHandlers_autogen.cpp", + "src/dawn_wire/client/ClientPrototypes_autogen.inc", + "src/dawn_wire/server/ServerBase_autogen.h", + "src/dawn_wire/server/ServerDoers_autogen.cpp", + "src/dawn_wire/server/ServerHandlers_autogen.cpp", + "src/dawn_wire/server/ServerPrototypes_autogen.inc", + ] +} + +dawn_component("dawn_wire") { + DEFINE_PREFIX = "DAWN_WIRE" + + deps = [ + ":dawn_wire_gen", + "${dawn_root}/src/common", + ] + + configs = [ "${dawn_root}/src/common:dawn_internal" ] + sources = get_target_outputs(":dawn_wire_gen") + sources += [ + "WireClient.cpp", + "WireDeserializeAllocator.cpp", + "WireDeserializeAllocator.h", + "WireServer.cpp", + "client/ApiObjects.h", + "client/Buffer.cpp", + "client/Buffer.h", + "client/Client.cpp", + "client/Client.h", + "client/ClientDoers.cpp", + "client/ClientInlineMemoryTransferService.cpp", + "client/Device.cpp", + "client/Device.h", + "client/Fence.cpp", + "client/Fence.h", + "client/ObjectAllocator.h", + "client/Queue.cpp", + "client/Queue.h", + "server/ObjectStorage.h", + "server/Server.cpp", + "server/Server.h", + "server/ServerBuffer.cpp", + "server/ServerDevice.cpp", + "server/ServerFence.cpp", + "server/ServerInlineMemoryTransferService.cpp", + "server/ServerQueue.cpp", + ] + + # Make headers publicly visible + public_deps = [ ":dawn_wire_headers" ] +} diff --git a/third_party/dawn/src/dawn_wire/CMakeLists.txt b/third_party/dawn/src/dawn_wire/CMakeLists.txt new file mode 100644 index 00000000000..e7927fa6b56 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/CMakeLists.txt @@ -0,0 +1,58 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +DawnJSONGenerator( + TARGET "dawn_wire" + PRINT_NAME "Dawn wire" + RESULT_VARIABLE "DAWN_WIRE_GEN_SOURCES" +) + +add_library(dawn_wire STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawn_wire PRIVATE + "${DAWN_INCLUDE_DIR}/dawn_wire/Wire.h" + "${DAWN_INCLUDE_DIR}/dawn_wire/WireClient.h" + "${DAWN_INCLUDE_DIR}/dawn_wire/WireServer.h" + "${DAWN_INCLUDE_DIR}/dawn_wire/dawn_wire_export.h" + ${DAWN_WIRE_GEN_SOURCES} + "WireClient.cpp" + "WireDeserializeAllocator.cpp" + "WireDeserializeAllocator.h" + "WireServer.cpp" + "client/ApiObjects.h" + "client/Buffer.cpp" + "client/Buffer.h" + "client/Client.cpp" + "client/Client.h" + "client/ClientDoers.cpp" + "client/ClientInlineMemoryTransferService.cpp" + "client/Device.cpp" + "client/Device.h" + "client/Fence.cpp" + "client/Fence.h" + "client/ObjectAllocator.h" + "client/Queue.cpp" + "client/Queue.h" + "server/ObjectStorage.h" + "server/Server.cpp" + "server/Server.h" + "server/ServerBuffer.cpp" + "server/ServerDevice.cpp" + "server/ServerFence.cpp" + "server/ServerInlineMemoryTransferService.cpp" + "server/ServerQueue.cpp" +) +target_link_libraries(dawn_wire + PUBLIC dawn_headers + PRIVATE dawn_common dawn_internal_config +) diff --git a/third_party/dawn/src/dawn_wire/WireClient.cpp b/third_party/dawn/src/dawn_wire/WireClient.cpp index ceb1c68a33f..e6fe263767b 100644 --- a/third_party/dawn/src/dawn_wire/WireClient.cpp +++ b/third_party/dawn/src/dawn_wire/WireClient.cpp @@ -17,27 +17,51 @@ namespace dawn_wire { - WireClient::WireClient(CommandSerializer* serializer) : mImpl(new client::Client(serializer)) { + WireClient::WireClient(const WireClientDescriptor& descriptor) + : mImpl(new client::Client(descriptor.serializer, descriptor.memoryTransferService)) { } WireClient::~WireClient() { mImpl.reset(); } - DawnDevice WireClient::GetDevice() const { - return mImpl->GetDevice(); + // static + DawnProcTable WireClient::GetProcs() { + return client::GetProcs(); } - DawnProcTable WireClient::GetProcs() const { - return client::GetProcs(); + WGPUDevice WireClient::GetDevice() const { + return mImpl->GetDevice(); } - const char* WireClient::HandleCommands(const char* commands, size_t size) { + const volatile char* WireClient::HandleCommands(const volatile char* commands, size_t size) { return mImpl->HandleCommands(commands, size); } - ReservedTexture WireClient::ReserveTexture(DawnDevice device) { + ReservedTexture WireClient::ReserveTexture(WGPUDevice device) { return mImpl->ReserveTexture(device); } + void WireClient::Disconnect() { + mImpl->Disconnect(); + } + + namespace client { + MemoryTransferService::~MemoryTransferService() = default; + + MemoryTransferService::ReadHandle* + MemoryTransferService::CreateReadHandle(WGPUBuffer buffer, uint64_t offset, size_t size) { + return CreateReadHandle(size); + } + + MemoryTransferService::WriteHandle* + MemoryTransferService::CreateWriteHandle(WGPUBuffer buffer, uint64_t offset, size_t size) { + return CreateWriteHandle(size); + } + + MemoryTransferService::ReadHandle::~ReadHandle() = default; + + MemoryTransferService::WriteHandle::~WriteHandle() = default; + } // namespace client + } // namespace dawn_wire diff --git a/third_party/dawn/src/dawn_wire/WireDeserializeAllocator.h b/third_party/dawn/src/dawn_wire/WireDeserializeAllocator.h index c900a23cbd5..4c34456458e 100644 --- a/third_party/dawn/src/dawn_wire/WireDeserializeAllocator.h +++ b/third_party/dawn/src/dawn_wire/WireDeserializeAllocator.h @@ -26,7 +26,7 @@ namespace dawn_wire { class WireDeserializeAllocator : public DeserializeAllocator { public: WireDeserializeAllocator(); - ~WireDeserializeAllocator(); + virtual ~WireDeserializeAllocator(); void* GetSpace(size_t size) override; diff --git a/third_party/dawn/src/dawn_wire/WireServer.cpp b/third_party/dawn/src/dawn_wire/WireServer.cpp index bfa186f9c5a..11ed4244e02 100644 --- a/third_party/dawn/src/dawn_wire/WireServer.cpp +++ b/third_party/dawn/src/dawn_wire/WireServer.cpp @@ -17,22 +17,36 @@ namespace dawn_wire { - WireServer::WireServer(DawnDevice device, - const DawnProcTable& procs, - CommandSerializer* serializer) - : mImpl(new server::Server(device, procs, serializer)) { + WireServer::WireServer(const WireServerDescriptor& descriptor) + : mImpl(new server::Server(descriptor.device, + *descriptor.procs, + descriptor.serializer, + descriptor.memoryTransferService)) { } WireServer::~WireServer() { mImpl.reset(); } - const char* WireServer::HandleCommands(const char* commands, size_t size) { + const volatile char* WireServer::HandleCommands(const volatile char* commands, size_t size) { return mImpl->HandleCommands(commands, size); } - bool WireServer::InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation) { + bool WireServer::InjectTexture(WGPUTexture texture, uint32_t id, uint32_t generation) { return mImpl->InjectTexture(texture, id, generation); } + namespace server { + MemoryTransferService::~MemoryTransferService() = default; + + MemoryTransferService::ReadHandle::~ReadHandle() = default; + + MemoryTransferService::WriteHandle::~WriteHandle() = default; + + void MemoryTransferService::WriteHandle::SetTarget(void* data, size_t dataLength) { + mTargetData = data; + mDataLength = dataLength; + } + } // namespace server + } // namespace dawn_wire diff --git a/third_party/dawn/src/dawn_wire/client/ApiObjects.h b/third_party/dawn/src/dawn_wire/client/ApiObjects.h index b74eefe6153..f842d53f469 100644 --- a/third_party/dawn/src/dawn_wire/client/ApiObjects.h +++ b/third_party/dawn/src/dawn_wire/client/ApiObjects.h @@ -20,6 +20,7 @@ #include "dawn_wire/client/Buffer.h" #include "dawn_wire/client/Device.h" #include "dawn_wire/client/Fence.h" +#include "dawn_wire/client/Queue.h" #include "dawn_wire/client/ApiObjects_autogen.h" diff --git a/third_party/dawn/src/dawn_wire/client/Buffer.cpp b/third_party/dawn/src/dawn_wire/client/Buffer.cpp index cc7db9eadef..79dc807f8e6 100644 --- a/third_party/dawn/src/dawn_wire/client/Buffer.cpp +++ b/third_party/dawn/src/dawn_wire/client/Buffer.cpp @@ -14,27 +14,489 @@ #include "dawn_wire/client/Buffer.h" +#include "dawn_wire/client/Client.h" +#include "dawn_wire/client/Device.h" + namespace dawn_wire { namespace client { + namespace { + template + void SerializeBufferMapAsync(const Buffer* buffer, + uint32_t serial, + Handle* handle, + size_t size) { + // TODO(enga): Remove the template when Read/Write handles are combined in a tagged + // pointer. + constexpr bool isWrite = + std::is_same::value; + + // Get the serialization size of the handle. + size_t handleCreateInfoLength = handle->SerializeCreateSize(); + + BufferMapAsyncCmd cmd; + cmd.bufferId = buffer->id; + cmd.requestSerial = serial; + cmd.mode = isWrite ? WGPUMapMode_Write : WGPUMapMode_Read; + cmd.handleCreateInfoLength = handleCreateInfoLength; + cmd.handleCreateInfo = nullptr; + cmd.offset = 0; + cmd.size = size; + + char* writeHandleSpace = + buffer->device->GetClient()->SerializeCommand(cmd, handleCreateInfoLength); + + // Serialize the handle into the space after the command. + handle->SerializeCreate(writeHandleSpace); + } + } // namespace + + // static + WGPUBuffer Buffer::Create(Device* device_, const WGPUBufferDescriptor* descriptor) { + Client* wireClient = device_->GetClient(); + + if ((descriptor->usage & (WGPUBufferUsage_MapRead | WGPUBufferUsage_MapWrite)) != 0 && + descriptor->size > std::numeric_limits::max()) { + device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer is too large for map usage"); + return device_->CreateErrorBuffer(); + } + + auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device_); + Buffer* buffer = bufferObjectAndSerial->object.get(); + // Store the size of the buffer so that mapping operations can allocate a + // MemoryTransfer handle of the proper size. + buffer->mSize = descriptor->size; + + DeviceCreateBufferCmd cmd; + cmd.self = ToAPI(device_); + cmd.descriptor = descriptor; + cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation}; + + wireClient->SerializeCommand(cmd); + + return ToAPI(buffer); + } + + // static + WGPUCreateBufferMappedResult Buffer::CreateMapped(Device* device_, + const WGPUBufferDescriptor* descriptor) { + Client* wireClient = device_->GetClient(); + + WGPUCreateBufferMappedResult result; + result.data = nullptr; + result.dataLength = 0; + + // This buffer is too large to be mapped and to make a WriteHandle for. + if (descriptor->size > std::numeric_limits::max()) { + device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer is too large for mapping"); + result.buffer = device_->CreateErrorBuffer(); + return result; + } + + // Create a WriteHandle for the map request. This is the client's intent to write GPU + // memory. + std::unique_ptr writeHandle = + std::unique_ptr( + wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size)); + + if (writeHandle == nullptr) { + device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed"); + result.buffer = device_->CreateErrorBuffer(); + return result; + } + + // CreateBufferMapped is synchronous and the staging buffer for upload should be immediately + // available. + // Open the WriteHandle. This returns a pointer and size of mapped memory. + // |result.data| may be null on error. + std::tie(result.data, result.dataLength) = writeHandle->Open(); + if (result.data == nullptr) { + device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed"); + result.buffer = device_->CreateErrorBuffer(); + return result; + } + + auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device_); + Buffer* buffer = bufferObjectAndSerial->object.get(); + buffer->mSize = descriptor->size; + // Successfully created staging memory. The buffer now owns the WriteHandle. + buffer->mWriteHandle = std::move(writeHandle); + buffer->mMappedData = result.data; + buffer->mMapOffset = 0; + buffer->mMapSize = descriptor->size; + + result.buffer = ToAPI(buffer); + + // Get the serialization size of the WriteHandle. + size_t handleCreateInfoLength = buffer->mWriteHandle->SerializeCreateSize(); + + DeviceCreateBufferMappedCmd cmd; + cmd.device = ToAPI(device_); + cmd.descriptor = descriptor; + cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation}; + cmd.handleCreateInfoLength = handleCreateInfoLength; + cmd.handleCreateInfo = nullptr; + + char* writeHandleSpace = + buffer->device->GetClient()->SerializeCommand(cmd, handleCreateInfoLength); + + // Serialize the WriteHandle into the space after the command. + buffer->mWriteHandle->SerializeCreate(writeHandleSpace); + + return result; + } + + // static + WGPUBuffer Buffer::CreateError(Device* device_) { + auto* allocation = device_->GetClient()->BufferAllocator().New(device_); + + DeviceCreateErrorBufferCmd cmd; + cmd.self = ToAPI(device_); + cmd.result = ObjectHandle{allocation->object->id, allocation->generation}; + device_->GetClient()->SerializeCommand(cmd); + + return ToAPI(allocation->object.get()); + } + Buffer::~Buffer() { // Callbacks need to be fired in all cases, as they can handle freeing resources // so we call them with "Unknown" status. - ClearMapRequests(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN); - - if (mappedData) { - free(mappedData); - } + ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown); } - void Buffer::ClearMapRequests(DawnBufferMapAsyncStatus status) { - for (auto& it : requests) { - if (it.second.isWrite) { + void Buffer::ClearMapRequests(WGPUBufferMapAsyncStatus status) { + for (auto& it : mRequests) { + if (it.second.writeHandle) { it.second.writeCallback(status, nullptr, 0, it.second.userdata); } else { it.second.readCallback(status, nullptr, 0, it.second.userdata); } } - requests.clear(); + mRequests.clear(); } + void Buffer::MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata) { + uint32_t serial = mRequestSerial++; + ASSERT(mRequests.find(serial) == mRequests.end()); + + if (mSize > std::numeric_limits::max()) { + // On buffer creation, we check that mappable buffers do not exceed this size. + // So this buffer must not have mappable usage. Inject a validation error. + device->InjectError(WGPUErrorType_Validation, "Buffer needs the correct map usage bit"); + callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata); + return; + } + + // Create a ReadHandle for the map request. This is the client's intent to read GPU + // memory. + MemoryTransferService::ReadHandle* readHandle = + device->GetClient()->GetMemoryTransferService()->CreateReadHandle( + static_cast(mSize)); + if (readHandle == nullptr) { + device->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping"); + callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata); + return; + } + + Buffer::MapRequestData request = {}; + request.readCallback = callback; + request.userdata = userdata; + // The handle is owned by the MapRequest until the callback returns. + request.readHandle = std::unique_ptr(readHandle); + + // Store a mapping from serial -> MapRequest. The client can map/unmap before the map + // operations are returned by the server so multiple requests may be in flight. + mRequests[serial] = std::move(request); + + SerializeBufferMapAsync(this, serial, readHandle, mSize); + } + + void Buffer::MapWriteAsync(WGPUBufferMapWriteCallback callback, void* userdata) { + uint32_t serial = mRequestSerial++; + ASSERT(mRequests.find(serial) == mRequests.end()); + + if (mSize > std::numeric_limits::max()) { + // On buffer creation, we check that mappable buffers do not exceed this size. + // So this buffer must not have mappable usage. Inject a validation error. + device->InjectError(WGPUErrorType_Validation, "Buffer needs the correct map usage bit"); + callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata); + return; + } + + // Create a WriteHandle for the map request. This is the client's intent to write GPU + // memory. + MemoryTransferService::WriteHandle* writeHandle = + device->GetClient()->GetMemoryTransferService()->CreateWriteHandle( + static_cast(mSize)); + if (writeHandle == nullptr) { + device->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping"); + callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata); + return; + } + + Buffer::MapRequestData request = {}; + request.writeCallback = callback; + request.userdata = userdata; + // The handle is owned by the MapRequest until the callback returns. + request.writeHandle = std::unique_ptr(writeHandle); + + // Store a mapping from serial -> MapRequest. The client can map/unmap before the map + // operations are returned by the server so multiple requests may be in flight. + mRequests[serial] = std::move(request); + + SerializeBufferMapAsync(this, serial, writeHandle, mSize); + } + + void Buffer::MapAsync(WGPUMapModeFlags mode, + size_t offset, + size_t size, + WGPUBufferMapCallback callback, + void* userdata) { + // Do early validation for mode because it needs to be correct for the proxying to + // MapReadAsync or MapWriteAsync to work. + bool isReadMode = mode & WGPUMapMode_Read; + bool isWriteMode = mode & WGPUMapMode_Write; + bool modeOk = isReadMode ^ isWriteMode; + // Do early validation of offset and size because it isn't checked by MapReadAsync / + // MapWriteAsync. + bool offsetOk = (uint64_t(offset) <= mSize) && offset % 4 == 0; + bool sizeOk = (uint64_t(size) <= mSize - uint64_t(offset)) && size % 4 == 0; + + if (!(modeOk && offsetOk && sizeOk)) { + device->InjectError(WGPUErrorType_Validation, "MapAsync error (you figure out :P)"); + if (callback != nullptr) { + callback(WGPUBufferMapAsyncStatus_Error, userdata); + } + return; + } + + // The structure to keep arguments so we can forward the MapReadAsync and MapWriteAsync to + // `callback` + struct ProxyData { + WGPUBufferMapCallback callback; + void* userdata; + size_t mapOffset; + size_t mapSize; + Buffer* self; + }; + ProxyData* proxy = new ProxyData; + proxy->callback = callback; + proxy->userdata = userdata; + proxy->mapOffset = offset; + proxy->mapSize = size; + proxy->self = this; + // Note technically we should keep the buffer alive until the callback is fired but the + // client doesn't have good facilities to do that yet. + + // Call MapReadAsync or MapWriteAsync and forward the callback. + if (isReadMode) { + MapReadAsync( + [](WGPUBufferMapAsyncStatus status, const void*, uint64_t, void* userdata) { + ProxyData* proxy = static_cast(userdata); + proxy->self->mMapOffset = proxy->mapOffset; + proxy->self->mMapSize = proxy->mapSize; + if (proxy->callback) { + proxy->callback(status, proxy->userdata); + } + delete proxy; + }, + proxy); + } else { + ASSERT(isWriteMode); + MapWriteAsync( + [](WGPUBufferMapAsyncStatus status, void*, uint64_t, void* userdata) { + ProxyData* proxy = static_cast(userdata); + proxy->self->mMapOffset = proxy->mapOffset; + proxy->self->mMapSize = proxy->mapSize; + if (proxy->callback) { + proxy->callback(status, proxy->userdata); + } + delete proxy; + }, + proxy); + } + } + + bool Buffer::OnMapAsyncCallback(uint32_t requestSerial, + uint32_t status, + uint64_t readInitialDataInfoLength, + const uint8_t* readInitialDataInfo) { + // The requests can have been deleted via an Unmap so this isn't an error. + auto requestIt = mRequests.find(requestSerial); + if (requestIt == mRequests.end()) { + return true; + } + + auto request = std::move(requestIt->second); + // Delete the request before calling the callback otherwise the callback could be fired a + // second time. If, for example, buffer.Unmap() is called inside the callback. + mRequests.erase(requestIt); + + auto FailRequest = [&request]() -> bool { + if (request.readCallback != nullptr) { + request.readCallback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, + request.userdata); + } + if (request.writeCallback != nullptr) { + request.writeCallback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, + request.userdata); + } + return false; + }; + + bool isRead = request.readHandle != nullptr; + bool isWrite = request.writeHandle != nullptr; + ASSERT(isRead != isWrite); + + size_t mappedDataLength = 0; + const void* mappedData = nullptr; + if (status == WGPUBufferMapAsyncStatus_Success) { + if (mReadHandle || mWriteHandle) { + // Buffer is already mapped. + return FailRequest(); + } + + if (isRead) { + if (readInitialDataInfoLength > std::numeric_limits::max()) { + // This is the size of data deserialized from the command stream, which must be + // CPU-addressable. + return FailRequest(); + } + + // The server serializes metadata to initialize the contents of the ReadHandle. + // Deserialize the message and return a pointer and size of the mapped data for + // reading. + if (!request.readHandle->DeserializeInitialData( + readInitialDataInfo, static_cast(readInitialDataInfoLength), + &mappedData, &mappedDataLength)) { + // Deserialization shouldn't fail. This is a fatal error. + return FailRequest(); + } + ASSERT(mappedData != nullptr); + + } else { + // Open the WriteHandle. This returns a pointer and size of mapped memory. + // On failure, |mappedData| may be null. + std::tie(mappedData, mappedDataLength) = request.writeHandle->Open(); + + if (mappedData == nullptr) { + return FailRequest(); + } + } + + // The MapAsync request was successful. The buffer now owns the Read/Write handles + // until Unmap(). + mReadHandle = std::move(request.readHandle); + mWriteHandle = std::move(request.writeHandle); + } + + mMappedData = const_cast(mappedData); + + if (isRead) { + request.readCallback(static_cast(status), mMappedData, + static_cast(mappedDataLength), request.userdata); + } else { + request.writeCallback(static_cast(status), mMappedData, + static_cast(mappedDataLength), request.userdata); + } + + return true; + } + + void* Buffer::GetMappedRange(size_t offset, size_t size) { + if (!IsMappedForWriting() || !CheckGetMappedRangeOffsetSize(offset, size)) { + return nullptr; + } + return static_cast(mMappedData) + offset; + } + + const void* Buffer::GetConstMappedRange(size_t offset, size_t size) { + if (!(IsMappedForWriting() || IsMappedForReading()) || + !CheckGetMappedRangeOffsetSize(offset, size)) { + return nullptr; + } + return static_cast(mMappedData) + offset; + } + + void Buffer::Unmap() { + // Invalidate the local pointer, and cancel all other in-flight requests that would + // turn into errors anyway (you can't double map). This prevents race when the following + // happens, where the application code would have unmapped a buffer but still receive a + // callback: + // - Client -> Server: MapRequest1, Unmap, MapRequest2 + // - Server -> Client: Result of MapRequest1 + // - Unmap locally on the client + // - Server -> Client: Result of MapRequest2 + if (mWriteHandle) { + // Writes need to be flushed before Unmap is sent. Unmap calls all associated + // in-flight callbacks which may read the updated data. + ASSERT(mReadHandle == nullptr); + + // Get the serialization size of metadata to flush writes. + size_t writeFlushInfoLength = mWriteHandle->SerializeFlushSize(); + + BufferUpdateMappedDataCmd cmd; + cmd.bufferId = id; + cmd.writeFlushInfoLength = writeFlushInfoLength; + cmd.writeFlushInfo = nullptr; + + char* writeHandleSpace = + device->GetClient()->SerializeCommand(cmd, writeFlushInfoLength); + + // Serialize flush metadata into the space after the command. + // This closes the handle for writing. + mWriteHandle->SerializeFlush(writeHandleSpace); + mWriteHandle = nullptr; + + } else if (mReadHandle) { + mReadHandle = nullptr; + } + + mMappedData = nullptr; + mMapOffset = 0; + mMapSize = 0; + ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown); + + BufferUnmapCmd cmd; + cmd.self = ToAPI(this); + device->GetClient()->SerializeCommand(cmd); + } + + void Buffer::Destroy() { + // Cancel or remove all mappings + mWriteHandle = nullptr; + mReadHandle = nullptr; + mMappedData = nullptr; + ClearMapRequests(WGPUBufferMapAsyncStatus_Unknown); + + BufferDestroyCmd cmd; + cmd.self = ToAPI(this); + device->GetClient()->SerializeCommand(cmd); + } + + void Buffer::SetSubData(uint64_t start, uint64_t count, const void* data) { + BufferSetSubDataInternalCmd cmd; + cmd.bufferId = id; + cmd.start = start; + cmd.count = count; + cmd.data = static_cast(data); + + device->GetClient()->SerializeCommand(cmd); + } + + bool Buffer::IsMappedForReading() const { + return mReadHandle != nullptr; + } + + bool Buffer::IsMappedForWriting() const { + return mWriteHandle != nullptr; + } + + bool Buffer::CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const { + if (size > mMapSize || offset < mMapOffset) { + return false; + } + + size_t offsetInMappedRange = offset - mMapOffset; + return offsetInMappedRange <= mMapSize - size; + } }} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/Buffer.h b/third_party/dawn/src/dawn_wire/client/Buffer.h index 536f99edbb1..c5db8481400 100644 --- a/third_party/dawn/src/dawn_wire/client/Buffer.h +++ b/third_party/dawn/src/dawn_wire/client/Buffer.h @@ -15,37 +15,75 @@ #ifndef DAWNWIRE_CLIENT_BUFFER_H_ #define DAWNWIRE_CLIENT_BUFFER_H_ -#include +#include +#include "dawn_wire/WireClient.h" #include "dawn_wire/client/ObjectBase.h" #include namespace dawn_wire { namespace client { - struct Buffer : ObjectBase { + class Buffer : public ObjectBase { + public: using ObjectBase::ObjectBase; + static WGPUBuffer Create(Device* device, const WGPUBufferDescriptor* descriptor); + static WGPUCreateBufferMappedResult CreateMapped(Device* device, + const WGPUBufferDescriptor* descriptor); + static WGPUBuffer CreateError(Device* device); + ~Buffer(); - void ClearMapRequests(DawnBufferMapAsyncStatus status); + void ClearMapRequests(WGPUBufferMapAsyncStatus status); + + void MapReadAsync(WGPUBufferMapReadCallback callback, void* userdata); + void MapWriteAsync(WGPUBufferMapWriteCallback callback, void* userdata); + bool OnMapAsyncCallback(uint32_t requestSerial, + uint32_t status, + uint64_t readInitialDataInfoLength, + const uint8_t* readInitialDataInfo); + void MapAsync(WGPUMapModeFlags mode, + size_t offset, + size_t size, + WGPUBufferMapCallback callback, + void* userdata); + void* GetMappedRange(size_t offset, size_t size); + const void* GetConstMappedRange(size_t offset, size_t size); + void Unmap(); + + void Destroy(); + + void SetSubData(uint64_t start, uint64_t count, const void* data); + + private: + bool IsMappedForReading() const; + bool IsMappedForWriting() const; + bool CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const; // We want to defer all the validation to the server, which means we could have multiple // map request in flight at a single time and need to track them separately. // On well-behaved applications, only one request should exist at a single time. struct MapRequestData { - DawnBufferMapReadCallback readCallback = nullptr; - DawnBufferMapWriteCallback writeCallback = nullptr; + // TODO(enga): Use a tagged pointer to save space. + WGPUBufferMapReadCallback readCallback = nullptr; + WGPUBufferMapWriteCallback writeCallback = nullptr; void* userdata = nullptr; - bool isWrite = false; + // TODO(enga): Use a tagged pointer to save space. + std::unique_ptr readHandle = nullptr; + std::unique_ptr writeHandle = nullptr; }; - std::map requests; - uint32_t requestSerial = 0; + std::map mRequests; + uint32_t mRequestSerial = 0; + uint64_t mSize = 0; // Only one mapped pointer can be active at a time because Unmap clears all the in-flight // requests. - void* mappedData = nullptr; - uint64_t mappedDataSize = 0; - bool isWriteMapped = false; + // TODO(enga): Use a tagged pointer to save space. + std::unique_ptr mReadHandle = nullptr; + std::unique_ptr mWriteHandle = nullptr; + void* mMappedData = nullptr; + size_t mMapOffset = 0; + size_t mMapSize = 0; }; }} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/Client.cpp b/third_party/dawn/src/dawn_wire/client/Client.cpp index 195c55fd65f..6b5b7be7ea2 100644 --- a/third_party/dawn/src/dawn_wire/client/Client.cpp +++ b/third_party/dawn/src/dawn_wire/client/Client.cpp @@ -13,29 +13,64 @@ // limitations under the License. #include "dawn_wire/client/Client.h" + +#include "common/Compiler.h" #include "dawn_wire/client/Device.h" namespace dawn_wire { namespace client { - Client::Client(CommandSerializer* serializer) - : ClientBase(), - mDevice(DeviceAllocator().New(this)->object.get()), - mSerializer(serializer) { + Client::Client(CommandSerializer* serializer, MemoryTransferService* memoryTransferService) + : ClientBase(), mSerializer(serializer), mMemoryTransferService(memoryTransferService) { + if (mMemoryTransferService == nullptr) { + // If a MemoryTransferService is not provided, fall back to inline memory. + mOwnedMemoryTransferService = CreateInlineMemoryTransferService(); + mMemoryTransferService = mOwnedMemoryTransferService.get(); + } } Client::~Client() { - DeviceAllocator().Free(mDevice); + if (mDevice != nullptr) { + DeviceAllocator().Free(mDevice); + } + } + + WGPUDevice Client::GetDevice() { + if (mDevice == nullptr) { + mDevice = DeviceAllocator().New(this)->object.get(); + } + return reinterpret_cast(mDevice); } - ReservedTexture Client::ReserveTexture(DawnDevice cDevice) { - Device* device = reinterpret_cast(cDevice); + ReservedTexture Client::ReserveTexture(WGPUDevice cDevice) { + Device* device = FromAPI(cDevice); ObjectAllocator::ObjectAndSerial* allocation = TextureAllocator().New(device); ReservedTexture result; - result.texture = reinterpret_cast(allocation->object.get()); + result.texture = ToAPI(allocation->object.get()); result.id = allocation->object->id; - result.generation = allocation->serial; + result.generation = allocation->generation; return result; } + char* Client::GetCmdSpace(size_t size) { + if (DAWN_UNLIKELY(mIsDisconnected)) { + if (size > mDummyCmdSpace.size()) { + mDummyCmdSpace.resize(size); + } + return mDummyCmdSpace.data(); + } + return static_cast(mSerializer->GetCmdSpace(size)); + } + + void Client::Disconnect() { + if (mIsDisconnected) { + return; + } + + mIsDisconnected = true; + if (mDevice != nullptr) { + mDevice->HandleDeviceLost("GPU connection lost"); + } + } + }} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/Client.h b/third_party/dawn/src/dawn_wire/client/Client.h index c7da67b8cda..d8df86d8c98 100644 --- a/third_party/dawn/src/dawn_wire/client/Client.h +++ b/third_party/dawn/src/dawn_wire/client/Client.h @@ -15,6 +15,7 @@ #ifndef DAWNWIRE_CLIENT_CLIENT_H_ #define DAWNWIRE_CLIENT_CLIENT_H_ +#include #include #include "dawn_wire/WireClient.h" @@ -25,33 +26,52 @@ namespace dawn_wire { namespace client { class Device; + class MemoryTransferService; class Client : public ClientBase { public: - Client(CommandSerializer* serializer); + Client(CommandSerializer* serializer, MemoryTransferService* memoryTransferService); ~Client(); - const char* HandleCommands(const char* commands, size_t size); - ReservedTexture ReserveTexture(DawnDevice device); + WGPUDevice GetDevice(); - void* GetCmdSpace(size_t size) { - return mSerializer->GetCmdSpace(size); + MemoryTransferService* GetMemoryTransferService() const { + return mMemoryTransferService; } - DawnDevice GetDevice() const { - return reinterpret_cast(mDevice); + const volatile char* HandleCommands(const volatile char* commands, size_t size); + ReservedTexture ReserveTexture(WGPUDevice device); + + template + char* SerializeCommand(const Cmd& cmd, size_t extraSize = 0) { + size_t requiredSize = cmd.GetRequiredSize(); + // TODO(cwallez@chromium.org): Check for overflows and allocation success? + char* allocatedBuffer = GetCmdSpace(requiredSize + extraSize); + cmd.Serialize(allocatedBuffer, *this); + return allocatedBuffer + requiredSize; } + void Disconnect(); + private: #include "dawn_wire/client/ClientPrototypes_autogen.inc" + char* GetCmdSpace(size_t size); + Device* mDevice = nullptr; CommandSerializer* mSerializer = nullptr; WireDeserializeAllocator mAllocator; + MemoryTransferService* mMemoryTransferService = nullptr; + std::unique_ptr mOwnedMemoryTransferService = nullptr; + + std::vector mDummyCmdSpace; + bool mIsDisconnected = false; }; DawnProcTable GetProcs(); + std::unique_ptr CreateInlineMemoryTransferService(); + }} // namespace dawn_wire::client #endif // DAWNWIRE_CLIENT_CLIENT_H_ diff --git a/third_party/dawn/src/dawn_wire/client/ClientDoers.cpp b/third_party/dawn/src/dawn_wire/client/ClientDoers.cpp index 313a4255e61..7fdabc08052 100644 --- a/third_party/dawn/src/dawn_wire/client/ClientDoers.cpp +++ b/third_party/dawn/src/dawn_wire/client/ClientDoers.cpp @@ -16,112 +16,48 @@ #include "dawn_wire/client/Client.h" #include "dawn_wire/client/Device.h" -namespace dawn_wire { namespace client { - - bool Client::DoDeviceErrorCallback(const char* message) { - DAWN_ASSERT(message != nullptr); - mDevice->HandleError(message); - return true; - } - - bool Client::DoBufferMapReadAsyncCallback(Buffer* buffer, - uint32_t requestSerial, - uint32_t status, - uint64_t dataLength, - const uint8_t* data) { - // The buffer might have been deleted or recreated so this isn't an error. - if (buffer == nullptr) { - return true; - } +#include - // The requests can have been deleted via an Unmap so this isn't an error. - auto requestIt = buffer->requests.find(requestSerial); - if (requestIt == buffer->requests.end()) { - return true; - } - - // It is an error for the server to call the read callback when we asked for a map write - if (requestIt->second.isWrite) { - return false; - } - - auto request = requestIt->second; - // Delete the request before calling the callback otherwise the callback could be fired a - // second time. If, for example, buffer.Unmap() is called inside the callback. - buffer->requests.erase(requestIt); - - // On success, we copy the data locally because the IPC buffer isn't valid outside of this - // function - if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) { - ASSERT(data != nullptr); +namespace dawn_wire { namespace client { - if (buffer->mappedData != nullptr) { + bool Client::DoDeviceUncapturedErrorCallback(WGPUErrorType errorType, const char* message) { + switch (errorType) { + case WGPUErrorType_NoError: + case WGPUErrorType_Validation: + case WGPUErrorType_OutOfMemory: + case WGPUErrorType_Unknown: + case WGPUErrorType_DeviceLost: + break; + default: return false; - } - - buffer->isWriteMapped = false; - buffer->mappedDataSize = dataLength; - buffer->mappedData = malloc(dataLength); - memcpy(buffer->mappedData, data, dataLength); - - request.readCallback(static_cast(status), buffer->mappedData, - dataLength, request.userdata); - } else { - request.readCallback(static_cast(status), nullptr, 0, - request.userdata); } + mDevice->HandleError(errorType, message); + return true; + } + bool Client::DoDeviceLostCallback(char const* message) { + mDevice->HandleDeviceLost(message); return true; } - bool Client::DoBufferMapWriteAsyncCallback(Buffer* buffer, - uint32_t requestSerial, - uint32_t status, - uint64_t dataLength) { + bool Client::DoDevicePopErrorScopeCallback(uint64_t requestSerial, + WGPUErrorType errorType, + const char* message) { + return mDevice->OnPopErrorScopeCallback(requestSerial, errorType, message); + } + + bool Client::DoBufferMapAsyncCallback(Buffer* buffer, + uint32_t requestSerial, + uint32_t status, + uint64_t readInitialDataInfoLength, + const uint8_t* readInitialDataInfo) { // The buffer might have been deleted or recreated so this isn't an error. if (buffer == nullptr) { return true; } - // The requests can have been deleted via an Unmap so this isn't an error. - auto requestIt = buffer->requests.find(requestSerial); - if (requestIt == buffer->requests.end()) { - return true; - } - - // It is an error for the server to call the write callback when we asked for a map read - if (!requestIt->second.isWrite) { - return false; - } - - auto request = requestIt->second; - // Delete the request before calling the callback otherwise the callback could be fired a - // second time. If, for example, buffer.Unmap() is called inside the callback. - buffer->requests.erase(requestIt); - - // On success, we copy the data locally because the IPC buffer isn't valid outside of this - // function - if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) { - if (buffer->mappedData != nullptr) { - return false; - } - - buffer->isWriteMapped = true; - buffer->mappedDataSize = dataLength; - // |mappedData| is freed in Unmap or the Buffer destructor. - // TODO(enga): Add dependency injection for buffer mapping so staging - // memory can live in shared memory. - buffer->mappedData = malloc(dataLength); - memset(buffer->mappedData, 0, dataLength); - - request.writeCallback(static_cast(status), buffer->mappedData, - dataLength, request.userdata); - } else { - request.writeCallback(static_cast(status), nullptr, 0, - request.userdata); - } - - return true; + return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength, + readInitialDataInfo); } bool Client::DoFenceUpdateCompletedValue(Fence* fence, uint64_t value) { @@ -130,8 +66,7 @@ namespace dawn_wire { namespace client { return true; } - fence->completedValue = value; - fence->CheckPassedFences(); + fence->OnUpdateCompletedValueCallback(value); return true; } diff --git a/third_party/dawn/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp b/third_party/dawn/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp new file mode 100644 index 00000000000..e7737107274 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp @@ -0,0 +1,121 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "dawn_wire/WireClient.h" +#include "dawn_wire/client/Client.h" + +#include + +namespace dawn_wire { namespace client { + + class InlineMemoryTransferService : public MemoryTransferService { + class ReadHandleImpl : public ReadHandle { + public: + explicit ReadHandleImpl(size_t size) : mSize(size) { + } + + ~ReadHandleImpl() override = default; + + size_t SerializeCreateSize() override { + return 0; + } + + void SerializeCreate(void*) override { + } + + bool DeserializeInitialData(const void* deserializePointer, + size_t deserializeSize, + const void** data, + size_t* dataLength) override { + if (deserializeSize != mSize || deserializePointer == nullptr) { + return false; + } + + mStagingData = std::unique_ptr(new (std::nothrow) uint8_t[mSize]); + if (!mStagingData) { + return false; + } + memcpy(mStagingData.get(), deserializePointer, mSize); + + ASSERT(data != nullptr); + ASSERT(dataLength != nullptr); + *data = mStagingData.get(); + *dataLength = mSize; + + return true; + } + + private: + size_t mSize; + std::unique_ptr mStagingData; + }; + + class WriteHandleImpl : public WriteHandle { + public: + explicit WriteHandleImpl(size_t size) : mSize(size) { + } + + ~WriteHandleImpl() override = default; + + size_t SerializeCreateSize() override { + return 0; + } + + void SerializeCreate(void*) override { + } + + std::pair Open() override { + mStagingData = std::unique_ptr(new (std::nothrow) uint8_t[mSize]); + if (!mStagingData) { + return std::make_pair(nullptr, 0); + } + memset(mStagingData.get(), 0, mSize); + return std::make_pair(mStagingData.get(), mSize); + } + + size_t SerializeFlushSize() override { + return mSize; + } + + void SerializeFlush(void* serializePointer) override { + ASSERT(mStagingData != nullptr); + ASSERT(serializePointer != nullptr); + memcpy(serializePointer, mStagingData.get(), mSize); + } + + private: + size_t mSize; + std::unique_ptr mStagingData; + }; + + public: + InlineMemoryTransferService() { + } + ~InlineMemoryTransferService() override = default; + + ReadHandle* CreateReadHandle(size_t size) override { + return new ReadHandleImpl(size); + } + + WriteHandle* CreateWriteHandle(size_t size) override { + return new WriteHandleImpl(size); + } + }; + + std::unique_ptr CreateInlineMemoryTransferService() { + return std::make_unique(); + } + +}} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp b/third_party/dawn/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp new file mode 100644 index 00000000000..dd8d62f8e33 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp @@ -0,0 +1,98 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_wire/client/ClientMemoryTransferService_mock.h" + +#include +#include "common/Assert.h" + +namespace dawn_wire { namespace client { + + MockMemoryTransferService::MockReadHandle::MockReadHandle(MockMemoryTransferService* service) + : ReadHandle(), mService(service) { + } + + MockMemoryTransferService::MockReadHandle::~MockReadHandle() { + mService->OnReadHandleDestroy(this); + } + + size_t MockMemoryTransferService::MockReadHandle::SerializeCreateSize() { + return mService->OnReadHandleSerializeCreateSize(this); + } + + void MockMemoryTransferService::MockReadHandle::SerializeCreate(void* serializePointer) { + mService->OnReadHandleSerializeCreate(this, serializePointer); + } + + bool MockMemoryTransferService::MockReadHandle::DeserializeInitialData( + const void* deserializePointer, + size_t deserializeSize, + const void** data, + size_t* dataLength) { + ASSERT(deserializeSize % sizeof(uint32_t) == 0); + return mService->OnReadHandleDeserializeInitialData( + this, reinterpret_cast(deserializePointer), deserializeSize, data, + dataLength); + } + + MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service) + : WriteHandle(), mService(service) { + } + + MockMemoryTransferService::MockWriteHandle::~MockWriteHandle() { + mService->OnWriteHandleDestroy(this); + } + + size_t MockMemoryTransferService::MockWriteHandle::SerializeCreateSize() { + return mService->OnWriteHandleSerializeCreateSize(this); + } + + void MockMemoryTransferService::MockWriteHandle::SerializeCreate(void* serializePointer) { + mService->OnWriteHandleSerializeCreate(this, serializePointer); + } + + std::pair MockMemoryTransferService::MockWriteHandle::Open() { + return mService->OnWriteHandleOpen(this); + } + + size_t MockMemoryTransferService::MockWriteHandle::SerializeFlushSize() { + return mService->OnWriteHandleSerializeFlushSize(this); + } + + void MockMemoryTransferService::MockWriteHandle::SerializeFlush(void* serializePointer) { + mService->OnWriteHandleSerializeFlush(this, serializePointer); + } + + MockMemoryTransferService::MockMemoryTransferService() = default; + MockMemoryTransferService::~MockMemoryTransferService() = default; + + MockMemoryTransferService::ReadHandle* MockMemoryTransferService::CreateReadHandle( + size_t size) { + return OnCreateReadHandle(size); + } + + MockMemoryTransferService::WriteHandle* MockMemoryTransferService::CreateWriteHandle( + size_t size) { + return OnCreateWriteHandle(size); + } + + MockMemoryTransferService::MockReadHandle* MockMemoryTransferService::NewReadHandle() { + return new MockReadHandle(this); + } + + MockMemoryTransferService::MockWriteHandle* MockMemoryTransferService::NewWriteHandle() { + return new MockWriteHandle(this); + } + +}} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/ClientMemoryTransferService_mock.h b/third_party/dawn/src/dawn_wire/client/ClientMemoryTransferService_mock.h new file mode 100644 index 00000000000..2d5eba4fe37 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/client/ClientMemoryTransferService_mock.h @@ -0,0 +1,95 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNWIRE_CLIENT_CLIENTMEMORYTRANSFERSERVICE_MOCK_H_ +#define DAWNWIRE_CLIENT_CLIENTMEMORYTRANSFERSERVICE_MOCK_H_ + +#include + +#include "dawn_wire/WireClient.h" +#include "dawn_wire/client/Client.h" + +namespace dawn_wire { namespace client { + + class MockMemoryTransferService : public MemoryTransferService { + public: + class MockReadHandle : public ReadHandle { + public: + explicit MockReadHandle(MockMemoryTransferService* service); + ~MockReadHandle() override; + + size_t SerializeCreateSize() override; + void SerializeCreate(void* serializePointer) override; + bool DeserializeInitialData(const void* deserializePointer, + size_t deserializeSize, + const void** data, + size_t* dataLength) override; + + private: + MockMemoryTransferService* mService; + }; + + class MockWriteHandle : public WriteHandle { + public: + explicit MockWriteHandle(MockMemoryTransferService* service); + ~MockWriteHandle() override; + + size_t SerializeCreateSize() override; + void SerializeCreate(void* serializePointer) override; + std::pair Open() override; + size_t SerializeFlushSize() override; + void SerializeFlush(void* serializePointer) override; + + private: + MockMemoryTransferService* mService; + }; + + MockMemoryTransferService(); + ~MockMemoryTransferService() override; + + ReadHandle* CreateReadHandle(size_t) override; + WriteHandle* CreateWriteHandle(size_t) override; + + MockReadHandle* NewReadHandle(); + MockWriteHandle* NewWriteHandle(); + + MOCK_METHOD(ReadHandle*, OnCreateReadHandle, (size_t)); + MOCK_METHOD(WriteHandle*, OnCreateWriteHandle, (size_t)); + + MOCK_METHOD(size_t, OnReadHandleSerializeCreateSize, (const ReadHandle*)); + MOCK_METHOD(void, OnReadHandleSerializeCreate, (const ReadHandle*, void* serializePointer)); + MOCK_METHOD(bool, + OnReadHandleDeserializeInitialData, + (const ReadHandle*, + const uint32_t* deserializePointer, + size_t deserializeSize, + const void** data, + size_t* dataLength)); + MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle*)); + + MOCK_METHOD(size_t, OnWriteHandleSerializeCreateSize, (const void* WriteHandle)); + MOCK_METHOD(void, + OnWriteHandleSerializeCreate, + (const void* WriteHandle, void* serializePointer)); + MOCK_METHOD((std::pair), OnWriteHandleOpen, (const void* WriteHandle)); + MOCK_METHOD(size_t, OnWriteHandleSerializeFlushSize, (const void* WriteHandle)); + MOCK_METHOD(void, + OnWriteHandleSerializeFlush, + (const void* WriteHandle, void* serializePointer)); + MOCK_METHOD(void, OnWriteHandleDestroy, (const void* WriteHandle)); + }; + +}} // namespace dawn_wire::client + +#endif // DAWNWIRE_CLIENT_CLIENTMEMORYTRANSFERSERVICE_MOCK_H_ diff --git a/third_party/dawn/src/dawn_wire/client/Device.cpp b/third_party/dawn/src/dawn_wire/client/Device.cpp index 7c55cd33980..411fd5cf2f5 100644 --- a/third_party/dawn/src/dawn_wire/client/Device.cpp +++ b/third_party/dawn/src/dawn_wire/client/Device.cpp @@ -14,26 +14,157 @@ #include "dawn_wire/client/Device.h" +#include "common/Assert.h" +#include "dawn_wire/WireCmd_autogen.h" +#include "dawn_wire/client/ApiObjects_autogen.h" +#include "dawn_wire/client/Client.h" +#include "dawn_wire/client/ObjectAllocator.h" + namespace dawn_wire { namespace client { - Device::Device(Client* client, uint32_t refcount, uint32_t id) - : ObjectBase(this, refcount, id), mClient(client) { + Device::Device(Client* client, uint32_t initialRefcount, uint32_t initialId) + : ObjectBase(this, initialRefcount, initialId), mClient(client) { this->device = this; + + // Get the default queue for this device. + ObjectAllocator::ObjectAndSerial* allocation = mClient->QueueAllocator().New(this); + mDefaultQueue = allocation->object.get(); + + DeviceGetDefaultQueueCmd cmd; + cmd.self = ToAPI(this); + cmd.result = ObjectHandle{allocation->object->id, allocation->generation}; + + mClient->SerializeCommand(cmd); + } + + Device::~Device() { + // Fire pending error scopes + auto errorScopes = std::move(mErrorScopes); + for (const auto& it : errorScopes) { + it.second.callback(WGPUErrorType_Unknown, "Device destroyed", it.second.userdata); + } + + // Destroy the default queue + DestroyObjectCmd cmd; + cmd.objectType = ObjectType::Queue; + cmd.objectId = mDefaultQueue->id; + + mClient->SerializeCommand(cmd); + + mClient->QueueAllocator().Free(mDefaultQueue); } Client* Device::GetClient() { return mClient; } - void Device::HandleError(const char* message) { + void Device::HandleError(WGPUErrorType errorType, const char* message) { if (mErrorCallback) { - mErrorCallback(message, mErrorUserdata); + mErrorCallback(errorType, message, mErrorUserdata); } } - void Device::SetErrorCallback(DawnDeviceErrorCallback errorCallback, void* errorUserdata) { + void Device::HandleDeviceLost(const char* message) { + if (mDeviceLostCallback && !mDidRunLostCallback) { + mDidRunLostCallback = true; + mDeviceLostCallback(message, mDeviceLostUserdata); + } + } + + void Device::SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata) { mErrorCallback = errorCallback; mErrorUserdata = errorUserdata; } + void Device::SetDeviceLostCallback(WGPUDeviceLostCallback callback, void* userdata) { + mDeviceLostCallback = callback; + mDeviceLostUserdata = userdata; + } + + void Device::PushErrorScope(WGPUErrorFilter filter) { + mErrorScopeStackSize++; + + DevicePushErrorScopeCmd cmd; + cmd.self = ToAPI(this); + cmd.filter = filter; + + mClient->SerializeCommand(cmd); + } + + bool Device::PopErrorScope(WGPUErrorCallback callback, void* userdata) { + if (mErrorScopeStackSize == 0) { + return false; + } + mErrorScopeStackSize--; + + uint64_t serial = mErrorScopeRequestSerial++; + ASSERT(mErrorScopes.find(serial) == mErrorScopes.end()); + + mErrorScopes[serial] = {callback, userdata}; + + DevicePopErrorScopeCmd cmd; + cmd.device = ToAPI(this); + cmd.requestSerial = serial; + + mClient->SerializeCommand(cmd); + + return true; + } + + bool Device::OnPopErrorScopeCallback(uint64_t requestSerial, + WGPUErrorType type, + const char* message) { + switch (type) { + case WGPUErrorType_NoError: + case WGPUErrorType_Validation: + case WGPUErrorType_OutOfMemory: + case WGPUErrorType_Unknown: + case WGPUErrorType_DeviceLost: + break; + default: + return false; + } + + auto requestIt = mErrorScopes.find(requestSerial); + if (requestIt == mErrorScopes.end()) { + return false; + } + + ErrorScopeData request = std::move(requestIt->second); + + mErrorScopes.erase(requestIt); + request.callback(type, message, request.userdata); + return true; + } + + void Device::InjectError(WGPUErrorType type, const char* message) { + DeviceInjectErrorCmd cmd; + cmd.self = ToAPI(this); + cmd.type = type; + cmd.message = message; + mClient->SerializeCommand(cmd); + } + + WGPUBuffer Device::CreateBuffer(const WGPUBufferDescriptor* descriptor) { + if (descriptor->mappedAtCreation) { + return CreateBufferMapped(descriptor).buffer; + } else { + return Buffer::Create(this, descriptor); + } + } + + WGPUCreateBufferMappedResult Device::CreateBufferMapped( + const WGPUBufferDescriptor* descriptor) { + return Buffer::CreateMapped(this, descriptor); + } + + WGPUBuffer Device::CreateErrorBuffer() { + return Buffer::CreateError(this); + } + + WGPUQueue Device::GetDefaultQueue() { + mDefaultQueue->refcount++; + return ToAPI(mDefaultQueue); + } + }} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/Device.h b/third_party/dawn/src/dawn_wire/client/Device.h index 96f22625ad0..2b554e34e89 100644 --- a/third_party/dawn/src/dawn_wire/client/Device.h +++ b/third_party/dawn/src/dawn_wire/client/Device.h @@ -15,26 +15,57 @@ #ifndef DAWNWIRE_CLIENT_DEVICE_H_ #define DAWNWIRE_CLIENT_DEVICE_H_ -#include +#include #include "dawn_wire/client/ObjectBase.h" +#include + namespace dawn_wire { namespace client { class Client; + class Queue; class Device : public ObjectBase { public: Device(Client* client, uint32_t refcount, uint32_t id); + ~Device(); Client* GetClient(); - void HandleError(const char* message); - void SetErrorCallback(DawnDeviceErrorCallback errorCallback, void* errorUserdata); + void SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata); + void SetDeviceLostCallback(WGPUDeviceLostCallback errorCallback, void* errorUserdata); + void InjectError(WGPUErrorType type, const char* message); + void PushErrorScope(WGPUErrorFilter filter); + bool PopErrorScope(WGPUErrorCallback callback, void* userdata); + WGPUBuffer CreateBuffer(const WGPUBufferDescriptor* descriptor); + WGPUCreateBufferMappedResult CreateBufferMapped(const WGPUBufferDescriptor* descriptor); + WGPUBuffer CreateErrorBuffer(); + + void HandleError(WGPUErrorType errorType, const char* message); + void HandleDeviceLost(const char* message); + bool OnPopErrorScopeCallback(uint64_t requestSerial, + WGPUErrorType type, + const char* message); + + WGPUQueue GetDefaultQueue(); private: + struct ErrorScopeData { + WGPUErrorCallback callback = nullptr; + void* userdata = nullptr; + }; + std::map mErrorScopes; + uint64_t mErrorScopeRequestSerial = 0; + uint64_t mErrorScopeStackSize = 0; + Client* mClient = nullptr; - DawnDeviceErrorCallback mErrorCallback = nullptr; - void* mErrorUserdata; + WGPUErrorCallback mErrorCallback = nullptr; + WGPUDeviceLostCallback mDeviceLostCallback = nullptr; + bool mDidRunLostCallback = false; + void* mErrorUserdata = nullptr; + void* mDeviceLostUserdata = nullptr; + + Queue* mDefaultQueue = nullptr; }; }} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/Fence.cpp b/third_party/dawn/src/dawn_wire/client/Fence.cpp index 497b714f50c..d9e1fa7ed32 100644 --- a/third_party/dawn/src/dawn_wire/client/Fence.cpp +++ b/third_party/dawn/src/dawn_wire/client/Fence.cpp @@ -14,22 +14,74 @@ #include "dawn_wire/client/Fence.h" +#include "dawn_wire/client/Device.h" + namespace dawn_wire { namespace client { Fence::~Fence() { // Callbacks need to be fired in all cases, as they can handle freeing resources // so we call them with "Unknown" status. - for (auto& request : requests.IterateAll()) { - request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, request.userdata); + for (auto& request : mRequests.IterateAll()) { + request.completionCallback(WGPUFenceCompletionStatus_Unknown, request.userdata); } - requests.Clear(); + mRequests.Clear(); + } + + void Fence::Initialize(Queue* queue, const WGPUFenceDescriptor* descriptor) { + mQueue = queue; + + uint64_t initialValue = descriptor != nullptr ? descriptor->initialValue : 0u; + mSignaledValue = initialValue; + mCompletedValue = initialValue; } void Fence::CheckPassedFences() { - for (auto& request : requests.IterateUpTo(completedValue)) { - request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, request.userdata); + for (auto& request : mRequests.IterateUpTo(mCompletedValue)) { + request.completionCallback(WGPUFenceCompletionStatus_Success, request.userdata); + } + mRequests.ClearUpTo(mCompletedValue); + } + + void Fence::OnCompletion(uint64_t value, + WGPUFenceOnCompletionCallback callback, + void* userdata) { + if (value > mSignaledValue) { + device->InjectError(WGPUErrorType_Validation, + "Value greater than fence signaled value"); + callback(WGPUFenceCompletionStatus_Error, userdata); + return; } - requests.ClearUpTo(completedValue); + + if (value <= mCompletedValue) { + callback(WGPUFenceCompletionStatus_Success, userdata); + return; + } + + Fence::OnCompletionData request; + request.completionCallback = callback; + request.userdata = userdata; + mRequests.Enqueue(std::move(request), value); + } + + void Fence::OnUpdateCompletedValueCallback(uint64_t value) { + mCompletedValue = value; + CheckPassedFences(); + } + + uint64_t Fence::GetCompletedValue() const { + return mCompletedValue; + } + + uint64_t Fence::GetSignaledValue() const { + return mSignaledValue; + } + + Queue* Fence::GetQueue() const { + return mQueue; + } + + void Fence::SetSignaledValue(uint64_t value) { + mSignaledValue = value; } }} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/Fence.h b/third_party/dawn/src/dawn_wire/client/Fence.h index d9b5e57bee9..107b4e7f440 100644 --- a/third_party/dawn/src/dawn_wire/client/Fence.h +++ b/third_party/dawn/src/dawn_wire/client/Fence.h @@ -15,28 +15,40 @@ #ifndef DAWNWIRE_CLIENT_FENCE_H_ #define DAWNWIRE_CLIENT_FENCE_H_ -#include +#include #include "common/SerialMap.h" #include "dawn_wire/client/ObjectBase.h" namespace dawn_wire { namespace client { - struct Queue; - struct Fence : ObjectBase { + class Queue; + class Fence : public ObjectBase { + public: using ObjectBase::ObjectBase; ~Fence(); + void Initialize(Queue* queue, const WGPUFenceDescriptor* descriptor); + void CheckPassedFences(); + void OnCompletion(uint64_t value, WGPUFenceOnCompletionCallback callback, void* userdata); + void OnUpdateCompletedValueCallback(uint64_t value); + + uint64_t GetCompletedValue() const; + uint64_t GetSignaledValue() const; + Queue* GetQueue() const; + + void SetSignaledValue(uint64_t value); + private: struct OnCompletionData { - DawnFenceOnCompletionCallback completionCallback = nullptr; + WGPUFenceOnCompletionCallback completionCallback = nullptr; void* userdata = nullptr; }; - Queue* queue = nullptr; - uint64_t signaledValue = 0; - uint64_t completedValue = 0; - SerialMap requests; + Queue* mQueue = nullptr; + uint64_t mSignaledValue = 0; + uint64_t mCompletedValue = 0; + SerialMap mRequests; }; }} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/ObjectAllocator.h b/third_party/dawn/src/dawn_wire/client/ObjectAllocator.h index 7caacbe5272..215b9f4a32c 100644 --- a/third_party/dawn/src/dawn_wire/client/ObjectAllocator.h +++ b/third_party/dawn/src/dawn_wire/client/ObjectAllocator.h @@ -16,7 +16,9 @@ #define DAWNWIRE_CLIENT_OBJECTALLOCATOR_H_ #include "common/Assert.h" +#include "common/Compiler.h" +#include #include #include @@ -32,11 +34,11 @@ namespace dawn_wire { namespace client { public: struct ObjectAndSerial { - ObjectAndSerial(std::unique_ptr object, uint32_t serial) - : object(std::move(object)), serial(serial) { + ObjectAndSerial(std::unique_ptr object, uint32_t generation) + : object(std::move(object)), generation(generation) { } std::unique_ptr object; - uint32_t serial; + uint32_t generation; }; ObjectAllocator() { @@ -46,24 +48,30 @@ namespace dawn_wire { namespace client { ObjectAndSerial* New(ObjectOwner* owner) { uint32_t id = GetNewId(); - T* result = new T(owner, 1, id); - auto object = std::unique_ptr(result); + auto object = std::make_unique(owner, 1, id); if (id >= mObjects.size()) { ASSERT(id == mObjects.size()); mObjects.emplace_back(std::move(object), 0); } else { ASSERT(mObjects[id].object == nullptr); - // TODO(cwallez@chromium.org): investigate if overflows could cause bad things to - // happen - mObjects[id].serial++; + + mObjects[id].generation++; + // The generation should never overflow. We don't recycle ObjectIds that would + // overflow their next generation. + ASSERT(mObjects[id].generation != 0); + mObjects[id].object = std::move(object); } return &mObjects[id]; } void Free(T* obj) { - FreeId(obj->id); + if (DAWN_LIKELY(mObjects[obj->id].generation != std::numeric_limits::max())) { + // Only recycle this ObjectId if the generation won't overflow on the next + // allocation. + FreeId(obj->id); + } mObjects[obj->id].object = nullptr; } @@ -74,11 +82,11 @@ namespace dawn_wire { namespace client { return mObjects[id].object.get(); } - uint32_t GetSerial(uint32_t id) { + uint32_t GetGeneration(uint32_t id) { if (id >= mObjects.size()) { return 0; } - return mObjects[id].serial; + return mObjects[id].generation; } private: diff --git a/third_party/dawn/src/dawn_wire/client/ObjectBase.h b/third_party/dawn/src/dawn_wire/client/ObjectBase.h index b97b0409a28..edf18f6c87f 100644 --- a/third_party/dawn/src/dawn_wire/client/ObjectBase.h +++ b/third_party/dawn/src/dawn_wire/client/ObjectBase.h @@ -15,7 +15,7 @@ #ifndef DAWNWIRE_CLIENT_OBJECTBASE_H_ #define DAWNWIRE_CLIENT_OBJECTBASE_H_ -#include +#include namespace dawn_wire { namespace client { diff --git a/third_party/dawn/src/dawn_wire/client/Queue.cpp b/third_party/dawn/src/dawn_wire/client/Queue.cpp new file mode 100644 index 00000000000..ad116732ff3 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/client/Queue.cpp @@ -0,0 +1,91 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_wire/client/Queue.h" + +#include "dawn_wire/client/Client.h" +#include "dawn_wire/client/Device.h" + +namespace dawn_wire { namespace client { + + WGPUFence Queue::CreateFence(WGPUFenceDescriptor const* descriptor) { + auto* allocation = device->GetClient()->FenceAllocator().New(device); + + QueueCreateFenceCmd cmd; + cmd.self = ToAPI(this); + cmd.result = ObjectHandle{allocation->object->id, allocation->generation}; + cmd.descriptor = descriptor; + device->GetClient()->SerializeCommand(cmd); + + Fence* fence = allocation->object.get(); + fence->Initialize(this, descriptor); + return ToAPI(fence); + } + + void Queue::Signal(WGPUFence cFence, uint64_t signalValue) { + Fence* fence = FromAPI(cFence); + if (fence->GetQueue() != this) { + device->InjectError(WGPUErrorType_Validation, + "Fence must be signaled on the queue on which it was created."); + return; + } + if (signalValue <= fence->GetSignaledValue()) { + device->InjectError(WGPUErrorType_Validation, + "Fence value less than or equal to signaled value"); + return; + } + + fence->SetSignaledValue(signalValue); + + QueueSignalCmd cmd; + cmd.self = ToAPI(this); + cmd.fence = cFence; + cmd.signalValue = signalValue; + + device->GetClient()->SerializeCommand(cmd); + } + + void Queue::WriteBuffer(WGPUBuffer cBuffer, + uint64_t bufferOffset, + const void* data, + size_t size) { + Buffer* buffer = FromAPI(cBuffer); + + QueueWriteBufferInternalCmd cmd; + cmd.queueId = id; + cmd.bufferId = buffer->id; + cmd.bufferOffset = bufferOffset; + cmd.data = static_cast(data); + cmd.size = size; + + device->GetClient()->SerializeCommand(cmd); + } + + void Queue::WriteTexture(const WGPUTextureCopyView* destination, + const void* data, + size_t dataSize, + const WGPUTextureDataLayout* dataLayout, + const WGPUExtent3D* writeSize) { + QueueWriteTextureInternalCmd cmd; + cmd.queueId = id; + cmd.destination = destination; + cmd.data = static_cast(data); + cmd.dataSize = dataSize; + cmd.dataLayout = dataLayout; + cmd.writeSize = writeSize; + + device->GetClient()->SerializeCommand(cmd); + } + +}} // namespace dawn_wire::client diff --git a/third_party/dawn/src/dawn_wire/client/Queue.h b/third_party/dawn/src/dawn_wire/client/Queue.h new file mode 100644 index 00000000000..866bccde066 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/client/Queue.h @@ -0,0 +1,43 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNWIRE_CLIENT_QUEUE_H_ +#define DAWNWIRE_CLIENT_QUEUE_H_ + +#include + +#include "dawn_wire/WireClient.h" +#include "dawn_wire/client/ObjectBase.h" + +#include + +namespace dawn_wire { namespace client { + + class Queue : public ObjectBase { + public: + using ObjectBase::ObjectBase; + + WGPUFence CreateFence(const WGPUFenceDescriptor* descriptor); + void Signal(WGPUFence fence, uint64_t signalValue); + void WriteBuffer(WGPUBuffer cBuffer, uint64_t bufferOffset, const void* data, size_t size); + void WriteTexture(const WGPUTextureCopyView* destination, + const void* data, + size_t dataSize, + const WGPUTextureDataLayout* dataLayout, + const WGPUExtent3D* writeSize); + }; + +}} // namespace dawn_wire::client + +#endif // DAWNWIRE_CLIENT_QUEUE_H_ diff --git a/third_party/dawn/src/dawn_wire/server/ObjectStorage.h b/third_party/dawn/src/dawn_wire/server/ObjectStorage.h index 4fc3016bb25..99631586843 100644 --- a/third_party/dawn/src/dawn_wire/server/ObjectStorage.h +++ b/third_party/dawn/src/dawn_wire/server/ObjectStorage.h @@ -16,6 +16,7 @@ #define DAWNWIRE_SERVER_OBJECTSTORAGE_H_ #include "dawn_wire/WireCmd_autogen.h" +#include "dawn_wire/WireServer.h" #include #include @@ -24,9 +25,9 @@ namespace dawn_wire { namespace server { template struct ObjectDataBase { - // The backend-provided handle and serial to this object. + // The backend-provided handle and generation to this object. T handle; - uint32_t serial = 0; + uint32_t generation = 0; // Whether this object has been allocated, used by the KnownObjects queries // TODO(cwallez@chromium.org): make this an internal bit vector in KnownObjects. @@ -40,9 +41,10 @@ namespace dawn_wire { namespace server { enum class BufferMapWriteState { Unmapped, Mapped, MapError }; template <> - struct ObjectData : public ObjectDataBase { - void* mappedData = nullptr; - size_t mappedDataSize = 0; + struct ObjectData : public ObjectDataBase { + // TODO(enga): Use a tagged pointer to save space. + std::unique_ptr readHandle; + std::unique_ptr writeHandle; BufferMapWriteState mapWriteState = BufferMapWriteState::Unmapped; }; @@ -59,7 +61,7 @@ namespace dawn_wire { namespace server { Data reservation; reservation.handle = nullptr; reservation.allocated = false; - mKnown.push_back(reservation); + mKnown.push_back(std::move(reservation)); } // Get a backend objects for a given client ID. @@ -92,10 +94,10 @@ namespace dawn_wire { namespace server { } // Allocates the data for a given ID and returns it. - // Returns nullptr if the ID is already allocated, or too far ahead. - // Invalidates all the Data* + // Returns nullptr if the ID is already allocated, or too far ahead, or if ID is 0 (ID 0 is + // reserved for nullptr). Invalidates all the Data* Data* Allocate(uint32_t id) { - if (id > mKnown.size()) { + if (id == 0 || id > mKnown.size()) { return nullptr; } @@ -104,7 +106,7 @@ namespace dawn_wire { namespace server { data.handle = nullptr; if (id >= mKnown.size()) { - mKnown.push_back(data); + mKnown.push_back(std::move(data)); return &mKnown.back(); } @@ -112,7 +114,7 @@ namespace dawn_wire { namespace server { return nullptr; } - mKnown[id] = data; + mKnown[id] = std::move(data); return &mKnown[id]; } diff --git a/third_party/dawn/src/dawn_wire/server/Server.cpp b/third_party/dawn/src/dawn_wire/server/Server.cpp index 5db76640a14..0199aba42ff 100644 --- a/third_party/dawn/src/dawn_wire/server/Server.cpp +++ b/third_party/dawn/src/dawn_wire/server/Server.cpp @@ -13,34 +13,44 @@ // limitations under the License. #include "dawn_wire/server/Server.h" +#include "dawn_wire/WireServer.h" namespace dawn_wire { namespace server { - Server::Server(DawnDevice device, const DawnProcTable& procs, CommandSerializer* serializer) - : mSerializer(serializer), mProcs(procs) { + Server::Server(WGPUDevice device, + const DawnProcTable& procs, + CommandSerializer* serializer, + MemoryTransferService* memoryTransferService) + : mSerializer(serializer), mProcs(procs), mMemoryTransferService(memoryTransferService) { + if (mMemoryTransferService == nullptr) { + // If a MemoryTransferService is not provided, fallback to inline memory. + mOwnedMemoryTransferService = CreateInlineMemoryTransferService(); + mMemoryTransferService = mOwnedMemoryTransferService.get(); + } // The client-server knowledge is bootstrapped with device 1. auto* deviceData = DeviceObjects().Allocate(1); deviceData->handle = device; - mProcs.deviceSetErrorCallback(device, ForwardDeviceError, this); + mProcs.deviceSetUncapturedErrorCallback(device, ForwardUncapturedError, this); + mProcs.deviceSetDeviceLostCallback(device, ForwardDeviceLost, this); } Server::~Server() { DestroyAllObjects(mProcs); } - void* Server::GetCmdSpace(size_t size) { - return mSerializer->GetCmdSpace(size); + char* Server::GetCmdSpace(size_t size) { + return static_cast(mSerializer->GetCmdSpace(size)); } - bool Server::InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation) { - ObjectData* data = TextureObjects().Allocate(id); + bool Server::InjectTexture(WGPUTexture texture, uint32_t id, uint32_t generation) { + ObjectData* data = TextureObjects().Allocate(id); if (data == nullptr) { return false; } data->handle = texture; - data->serial = generation; + data->generation = generation; data->allocated = true; // The texture is externally owned so it shouldn't be destroyed when we receive a destroy diff --git a/third_party/dawn/src/dawn_wire/server/Server.h b/third_party/dawn/src/dawn_wire/server/Server.h index ae690750e2a..8049e80ada1 100644 --- a/third_party/dawn/src/dawn_wire/server/Server.h +++ b/third_party/dawn/src/dawn_wire/server/Server.h @@ -20,13 +20,26 @@ namespace dawn_wire { namespace server { class Server; + class MemoryTransferService; struct MapUserdata { Server* server; ObjectHandle buffer; + WGPUBuffer bufferObj; uint32_t requestSerial; + uint64_t offset; uint64_t size; - bool isWrite; + WGPUMapModeFlags mode; + // TODO(enga): Use a tagged pointer to save space. + std::unique_ptr readHandle = nullptr; + std::unique_ptr writeHandle = nullptr; + }; + + struct ErrorScopeUserdata { + Server* server; + // TODO(enga): ObjectHandle device; + // when the wire supports multiple devices. + uint64_t requestSerial; }; struct FenceCompletionUserdata { @@ -37,39 +50,42 @@ namespace dawn_wire { namespace server { class Server : public ServerBase { public: - Server(DawnDevice device, const DawnProcTable& procs, CommandSerializer* serializer); + Server(WGPUDevice device, + const DawnProcTable& procs, + CommandSerializer* serializer, + MemoryTransferService* memoryTransferService); ~Server(); - const char* HandleCommands(const char* commands, size_t size); + const volatile char* HandleCommands(const volatile char* commands, size_t size); - bool InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation); + bool InjectTexture(WGPUTexture texture, uint32_t id, uint32_t generation); private: - void* GetCmdSpace(size_t size); + template + char* SerializeCommand(const Cmd& cmd, size_t extraSize = 0) { + size_t requiredSize = cmd.GetRequiredSize(); + // TODO(cwallez@chromium.org): Check for overflows and allocation success? + char* allocatedBuffer = GetCmdSpace(requiredSize + extraSize); + cmd.Serialize(allocatedBuffer); + return allocatedBuffer + requiredSize; + } + char* GetCmdSpace(size_t size); // Forwarding callbacks - static void ForwardDeviceError(const char* message, void* userdata); - static void ForwardBufferMapReadAsync(DawnBufferMapAsyncStatus status, - const void* ptr, - uint64_t dataLength, - void* userdata); - static void ForwardBufferMapWriteAsync(DawnBufferMapAsyncStatus status, - void* ptr, - uint64_t dataLength, - void* userdata); - static void ForwardFenceCompletedValue(DawnFenceCompletionStatus status, void* userdata); + static void ForwardUncapturedError(WGPUErrorType type, const char* message, void* userdata); + static void ForwardDeviceLost(const char* message, void* userdata); + static void ForwardPopErrorScope(WGPUErrorType type, const char* message, void* userdata); + static void ForwardBufferMapAsync(WGPUBufferMapAsyncStatus status, void* userdata); + static void ForwardFenceCompletedValue(WGPUFenceCompletionStatus status, void* userdata); // Error callbacks - void OnDeviceError(const char* message); - void OnBufferMapReadAsyncCallback(DawnBufferMapAsyncStatus status, - const void* ptr, - uint64_t dataLength, - MapUserdata* userdata); - void OnBufferMapWriteAsyncCallback(DawnBufferMapAsyncStatus status, - void* ptr, - uint64_t dataLength, - MapUserdata* userdata); - void OnFenceCompletedValueUpdated(DawnFenceCompletionStatus status, + void OnUncapturedError(WGPUErrorType type, const char* message); + void OnDeviceLost(const char* message); + void OnDevicePopErrorScope(WGPUErrorType type, + const char* message, + ErrorScopeUserdata* userdata); + void OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* userdata); + void OnFenceCompletedValueUpdated(WGPUFenceCompletionStatus status, FenceCompletionUserdata* userdata); #include "dawn_wire/server/ServerPrototypes_autogen.inc" @@ -77,8 +93,12 @@ namespace dawn_wire { namespace server { CommandSerializer* mSerializer = nullptr; WireDeserializeAllocator mAllocator; DawnProcTable mProcs; + std::unique_ptr mOwnedMemoryTransferService = nullptr; + MemoryTransferService* mMemoryTransferService = nullptr; }; + std::unique_ptr CreateInlineMemoryTransferService(); + }} // namespace dawn_wire::server #endif // DAWNWIRE_SERVER_SERVER_H_ diff --git a/third_party/dawn/src/dawn_wire/server/ServerBuffer.cpp b/third_party/dawn/src/dawn_wire/server/ServerBuffer.cpp index 4dfa80ee2e8..d0c82a25604 100644 --- a/third_party/dawn/src/dawn_wire/server/ServerBuffer.cpp +++ b/third_party/dawn/src/dawn_wire/server/ServerBuffer.cpp @@ -23,7 +23,22 @@ namespace dawn_wire { namespace server { auto* buffer = BufferObjects().Get(cmd.selfId); DAWN_ASSERT(buffer != nullptr); - buffer->mappedData = nullptr; + // The buffer was unmapped. Clear the Read/WriteHandle. + buffer->readHandle = nullptr; + buffer->writeHandle = nullptr; + buffer->mapWriteState = BufferMapWriteState::Unmapped; + + return true; + } + + bool Server::PreHandleBufferDestroy(const BufferDestroyCmd& cmd) { + // Destroying a buffer does an implicit unmapping. + auto* buffer = BufferObjects().Get(cmd.selfId); + DAWN_ASSERT(buffer != nullptr); + + // The buffer was destroyed. Clear the Read/WriteHandle. + buffer->readHandle = nullptr; + buffer->writeHandle = nullptr; buffer->mapWriteState = BufferMapWriteState::Unmapped; return true; @@ -31,7 +46,11 @@ namespace dawn_wire { namespace server { bool Server::DoBufferMapAsync(ObjectId bufferId, uint32_t requestSerial, - bool isWrite) { + WGPUMapModeFlags mode, + size_t offset, + size_t size, + uint64_t handleCreateInfoLength, + const uint8_t* handleCreateInfo) { // These requests are just forwarded to the buffer, with userdata containing what the // client will require in the return command. @@ -45,43 +64,104 @@ namespace dawn_wire { namespace server { return false; } - MapUserdata* userdata = new MapUserdata; + // The server only knows how to deal with write XOR read. Validate that. + bool isReadMode = mode & WGPUMapMode_Read; + bool isWriteMode = mode & WGPUMapMode_Write; + if (!(isReadMode ^ isWriteMode)) { + return false; + } + + if (handleCreateInfoLength > std::numeric_limits::max()) { + // This is the size of data deserialized from the command stream, which must be + // CPU-addressable. + return false; + } + + std::unique_ptr userdata = std::make_unique(); userdata->server = this; - userdata->buffer = ObjectHandle{bufferId, buffer->serial}; + userdata->buffer = ObjectHandle{bufferId, buffer->generation}; + userdata->bufferObj = buffer->handle; userdata->requestSerial = requestSerial; - userdata->isWrite = isWrite; + userdata->offset = offset; + userdata->size = size; + userdata->mode = mode; + + // The handle will point to the mapped memory or staging memory for the mapping. + // Store it on the map request. + if (isWriteMode) { + // Deserialize metadata produced from the client to create a companion server handle. + MemoryTransferService::WriteHandle* writeHandle = nullptr; + if (!mMemoryTransferService->DeserializeWriteHandle( + handleCreateInfo, static_cast(handleCreateInfoLength), &writeHandle)) { + return false; + } + ASSERT(writeHandle != nullptr); - if (isWrite) { - mProcs.bufferMapWriteAsync(buffer->handle, ForwardBufferMapWriteAsync, userdata); + userdata->writeHandle = + std::unique_ptr(writeHandle); } else { - mProcs.bufferMapReadAsync(buffer->handle, ForwardBufferMapReadAsync, userdata); + ASSERT(isReadMode); + // Deserialize metadata produced from the client to create a companion server handle. + MemoryTransferService::ReadHandle* readHandle = nullptr; + if (!mMemoryTransferService->DeserializeReadHandle( + handleCreateInfo, static_cast(handleCreateInfoLength), &readHandle)) { + return false; + } + ASSERT(readHandle != nullptr); + + userdata->readHandle = std::unique_ptr(readHandle); } + mProcs.bufferMapAsync(buffer->handle, mode, offset, size, ForwardBufferMapAsync, + userdata.release()); + return true; } - bool Server::DoDeviceCreateBufferMapped(DawnDevice device, - const DawnBufferDescriptor* descriptor, - ObjectHandle bufferResult) { + bool Server::DoDeviceCreateBufferMapped(WGPUDevice device, + const WGPUBufferDescriptor* descriptor, + ObjectHandle bufferResult, + uint64_t handleCreateInfoLength, + const uint8_t* handleCreateInfo) { + if (handleCreateInfoLength > std::numeric_limits::max()) { + // This is the size of data deserialized from the command stream, which must be + // CPU-addressable. + return false; + } + auto* resultData = BufferObjects().Allocate(bufferResult.id); if (resultData == nullptr) { return false; } - resultData->serial = bufferResult.serial; + resultData->generation = bufferResult.generation; - DawnCreateBufferMappedResult result = mProcs.deviceCreateBufferMapped(device, descriptor); + WGPUCreateBufferMappedResult result = mProcs.deviceCreateBufferMapped(device, descriptor); ASSERT(result.buffer != nullptr); if (result.data == nullptr && result.dataLength != 0) { // Non-zero dataLength but null data is used to indicate an allocation error. + // Don't return false because this is not fatal. result.buffer is an ErrorBuffer + // and subsequent operations will be errors. + // This should only happen when fuzzing with the Null backend. resultData->mapWriteState = BufferMapWriteState::MapError; } else { + // Deserialize metadata produced from the client to create a companion server handle. + MemoryTransferService::WriteHandle* writeHandle = nullptr; + if (!mMemoryTransferService->DeserializeWriteHandle( + handleCreateInfo, static_cast(handleCreateInfoLength), &writeHandle)) { + return false; + } + ASSERT(writeHandle != nullptr); + + // Set the target of the WriteHandle to the mapped GPU memory. + writeHandle->SetTarget(result.data, result.dataLength); + // The buffer is mapped and has a valid mappedData pointer. // The buffer may still be an error with fake staging data. resultData->mapWriteState = BufferMapWriteState::Mapped; + resultData->writeHandle = + std::unique_ptr(writeHandle); } resultData->handle = result.buffer; - resultData->mappedData = result.data; - resultData->mappedDataSize = result.dataLength; return true; } @@ -104,14 +184,20 @@ namespace dawn_wire { namespace server { return true; } - bool Server::DoBufferUpdateMappedData(ObjectId bufferId, uint32_t count, const uint8_t* data) { + bool Server::DoBufferUpdateMappedData(ObjectId bufferId, + uint64_t writeFlushInfoLength, + const uint8_t* writeFlushInfo) { // The null object isn't valid as `self` if (bufferId == 0) { return false; } + if (writeFlushInfoLength > std::numeric_limits::max()) { + return false; + } + auto* buffer = BufferObjects().Get(bufferId); - if (buffer == nullptr || buffer->mappedDataSize != count) { + if (buffer == nullptr) { return false; } switch (buffer->mapWriteState) { @@ -124,84 +210,69 @@ namespace dawn_wire { namespace server { case BufferMapWriteState::Mapped: break; } + if (!buffer->writeHandle) { + // This check is performed after the check for the MapError state. It is permissible + // to Unmap and attempt to update mapped data of an error buffer. + return false; + } - ASSERT(data != nullptr); - ASSERT(buffer->mappedData != nullptr); - memcpy(buffer->mappedData, data, count); - - return true; - } - - void Server::ForwardBufferMapReadAsync(DawnBufferMapAsyncStatus status, - const void* ptr, - uint64_t dataLength, - void* userdata) { - auto data = static_cast(userdata); - data->server->OnBufferMapReadAsyncCallback(status, ptr, dataLength, data); + // Deserialize the flush info and flush updated data from the handle into the target + // of the handle. The target is set via WriteHandle::SetTarget. + return buffer->writeHandle->DeserializeFlush(writeFlushInfo, + static_cast(writeFlushInfoLength)); } - void Server::ForwardBufferMapWriteAsync(DawnBufferMapAsyncStatus status, - void* ptr, - uint64_t dataLength, - void* userdata) { + void Server::ForwardBufferMapAsync(WGPUBufferMapAsyncStatus status, void* userdata) { auto data = static_cast(userdata); - data->server->OnBufferMapWriteAsyncCallback(status, ptr, dataLength, data); + data->server->OnBufferMapAsyncCallback(status, data); } - void Server::OnBufferMapReadAsyncCallback(DawnBufferMapAsyncStatus status, - const void* ptr, - uint64_t dataLength, - MapUserdata* userdata) { + void Server::OnBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, MapUserdata* userdata) { std::unique_ptr data(userdata); // Skip sending the callback if the buffer has already been destroyed. auto* bufferData = BufferObjects().Get(data->buffer.id); - if (bufferData == nullptr || bufferData->serial != data->buffer.serial) { + if (bufferData == nullptr || bufferData->generation != data->buffer.generation) { return; } - ReturnBufferMapReadAsyncCallbackCmd cmd; - cmd.buffer = data->buffer; - cmd.requestSerial = data->requestSerial; - cmd.status = status; - cmd.dataLength = 0; - cmd.data = static_cast(ptr); + bool isRead = data->mode & WGPUMapMode_Read; + bool isSuccess = status == WGPUBufferMapAsyncStatus_Success; - if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) { - cmd.dataLength = dataLength; - } - - size_t requiredSize = cmd.GetRequiredSize(); - char* allocatedBuffer = static_cast(GetCmdSpace(requiredSize)); - cmd.Serialize(allocatedBuffer); - } - - void Server::OnBufferMapWriteAsyncCallback(DawnBufferMapAsyncStatus status, - void* ptr, - uint64_t dataLength, - MapUserdata* userdata) { - std::unique_ptr data(userdata); - - // Skip sending the callback if the buffer has already been destroyed. - auto* bufferData = BufferObjects().Get(data->buffer.id); - if (bufferData == nullptr || bufferData->serial != data->buffer.serial) { - return; - } - - ReturnBufferMapWriteAsyncCallbackCmd cmd; + ReturnBufferMapAsyncCallbackCmd cmd; cmd.buffer = data->buffer; cmd.requestSerial = data->requestSerial; cmd.status = status; - cmd.dataLength = dataLength; - - size_t requiredSize = cmd.GetRequiredSize(); - char* allocatedBuffer = static_cast(GetCmdSpace(requiredSize)); - cmd.Serialize(allocatedBuffer); + cmd.readInitialDataInfoLength = 0; + cmd.readInitialDataInfo = nullptr; + + const void* readData = nullptr; + if (isSuccess && isRead) { + // Get the serialization size of the message to initialize ReadHandle data. + readData = mProcs.bufferGetConstMappedRange(data->bufferObj, data->offset, data->size); + cmd.readInitialDataInfoLength = + data->readHandle->SerializeInitialDataSize(readData, data->size); + } - if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) { - bufferData->mapWriteState = BufferMapWriteState::Mapped; - bufferData->mappedData = ptr; - bufferData->mappedDataSize = dataLength; + char* readHandleSpace = SerializeCommand(cmd, cmd.readInitialDataInfoLength); + + if (isSuccess) { + if (isRead) { + // Serialize the initialization message into the space after the command. + data->readHandle->SerializeInitialData(readData, data->size, readHandleSpace); + // The in-flight map request returned successfully. + // Move the ReadHandle so it is owned by the buffer. + bufferData->readHandle = std::move(data->readHandle); + } else { + // The in-flight map request returned successfully. + // Move the WriteHandle so it is owned by the buffer. + bufferData->writeHandle = std::move(data->writeHandle); + bufferData->mapWriteState = BufferMapWriteState::Mapped; + // Set the target of the WriteHandle to the mapped buffer data. + bufferData->writeHandle->SetTarget( + mProcs.bufferGetMappedRange(data->bufferObj, data->offset, data->size), + data->size); + } } } diff --git a/third_party/dawn/src/dawn_wire/server/ServerDevice.cpp b/third_party/dawn/src/dawn_wire/server/ServerDevice.cpp index 507f5181140..64ad1bb0c50 100644 --- a/third_party/dawn/src/dawn_wire/server/ServerDevice.cpp +++ b/third_party/dawn/src/dawn_wire/server/ServerDevice.cpp @@ -16,18 +16,60 @@ namespace dawn_wire { namespace server { - void Server::ForwardDeviceError(const char* message, void* userdata) { + void Server::ForwardUncapturedError(WGPUErrorType type, const char* message, void* userdata) { auto server = static_cast(userdata); - server->OnDeviceError(message); + server->OnUncapturedError(type, message); } - void Server::OnDeviceError(const char* message) { - ReturnDeviceErrorCallbackCmd cmd; + void Server::ForwardDeviceLost(const char* message, void* userdata) { + auto server = static_cast(userdata); + server->OnDeviceLost(message); + } + + void Server::OnUncapturedError(WGPUErrorType type, const char* message) { + ReturnDeviceUncapturedErrorCallbackCmd cmd; + cmd.type = type; + cmd.message = message; + + SerializeCommand(cmd); + } + + void Server::OnDeviceLost(const char* message) { + ReturnDeviceLostCallbackCmd cmd; + cmd.message = message; + + SerializeCommand(cmd); + } + + bool Server::DoDevicePopErrorScope(WGPUDevice cDevice, uint64_t requestSerial) { + ErrorScopeUserdata* userdata = new ErrorScopeUserdata; + userdata->server = this; + userdata->requestSerial = requestSerial; + + bool success = mProcs.devicePopErrorScope(cDevice, ForwardPopErrorScope, userdata); + if (!success) { + delete userdata; + } + return success; + } + + // static + void Server::ForwardPopErrorScope(WGPUErrorType type, const char* message, void* userdata) { + auto* data = reinterpret_cast(userdata); + data->server->OnDevicePopErrorScope(type, message, data); + } + + void Server::OnDevicePopErrorScope(WGPUErrorType type, + const char* message, + ErrorScopeUserdata* userdata) { + std::unique_ptr data{userdata}; + + ReturnDevicePopErrorScopeCallbackCmd cmd; + cmd.requestSerial = data->requestSerial; + cmd.type = type; cmd.message = message; - size_t requiredSize = cmd.GetRequiredSize(); - char* allocatedBuffer = static_cast(GetCmdSpace(requiredSize)); - cmd.Serialize(allocatedBuffer); + SerializeCommand(cmd); } }} // namespace dawn_wire::server diff --git a/third_party/dawn/src/dawn_wire/server/ServerFence.cpp b/third_party/dawn/src/dawn_wire/server/ServerFence.cpp index 79e14c401d3..1a4105c38bd 100644 --- a/third_party/dawn/src/dawn_wire/server/ServerFence.cpp +++ b/third_party/dawn/src/dawn_wire/server/ServerFence.cpp @@ -18,16 +18,16 @@ namespace dawn_wire { namespace server { - void Server::ForwardFenceCompletedValue(DawnFenceCompletionStatus status, void* userdata) { + void Server::ForwardFenceCompletedValue(WGPUFenceCompletionStatus status, void* userdata) { auto data = static_cast(userdata); data->server->OnFenceCompletedValueUpdated(status, data); } - void Server::OnFenceCompletedValueUpdated(DawnFenceCompletionStatus status, + void Server::OnFenceCompletedValueUpdated(WGPUFenceCompletionStatus status, FenceCompletionUserdata* userdata) { std::unique_ptr data(userdata); - if (status != DAWN_FENCE_COMPLETION_STATUS_SUCCESS) { + if (status != WGPUFenceCompletionStatus_Success) { return; } @@ -35,9 +35,7 @@ namespace dawn_wire { namespace server { cmd.fence = data->fence; cmd.value = data->value; - size_t requiredSize = cmd.GetRequiredSize(); - char* allocatedBuffer = static_cast(GetCmdSpace(requiredSize)); - cmd.Serialize(allocatedBuffer); + SerializeCommand(cmd); } }} // namespace dawn_wire::server diff --git a/third_party/dawn/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp b/third_party/dawn/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp new file mode 100644 index 00000000000..105dee49e18 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/server/ServerInlineMemoryTransferService.cpp @@ -0,0 +1,87 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "dawn_wire/WireServer.h" +#include "dawn_wire/server/Server.h" + +#include + +namespace dawn_wire { namespace server { + + class InlineMemoryTransferService : public MemoryTransferService { + public: + class ReadHandleImpl : public ReadHandle { + public: + ReadHandleImpl() { + } + ~ReadHandleImpl() override = default; + + size_t SerializeInitialDataSize(const void* data, size_t dataLength) override { + return dataLength; + } + + void SerializeInitialData(const void* data, + size_t dataLength, + void* serializePointer) override { + if (dataLength > 0) { + ASSERT(data != nullptr); + ASSERT(serializePointer != nullptr); + memcpy(serializePointer, data, dataLength); + } + } + }; + + class WriteHandleImpl : public WriteHandle { + public: + WriteHandleImpl() { + } + ~WriteHandleImpl() override = default; + + bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override { + if (deserializeSize != mDataLength || mTargetData == nullptr || + deserializePointer == nullptr) { + return false; + } + memcpy(mTargetData, deserializePointer, mDataLength); + return true; + } + }; + + InlineMemoryTransferService() { + } + ~InlineMemoryTransferService() override = default; + + bool DeserializeReadHandle(const void* deserializePointer, + size_t deserializeSize, + ReadHandle** readHandle) override { + ASSERT(readHandle != nullptr); + *readHandle = new ReadHandleImpl(); + return true; + } + + bool DeserializeWriteHandle(const void* deserializePointer, + size_t deserializeSize, + WriteHandle** writeHandle) override { + ASSERT(writeHandle != nullptr); + *writeHandle = new WriteHandleImpl(); + return true; + } + }; + + std::unique_ptr CreateInlineMemoryTransferService() { + return std::make_unique(); + } + +}} // namespace dawn_wire::server diff --git a/third_party/dawn/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp b/third_party/dawn/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp new file mode 100644 index 00000000000..b8b1696d275 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp @@ -0,0 +1,87 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_wire/server/ServerMemoryTransferService_mock.h" + +#include "common/Assert.h" + +namespace dawn_wire { namespace server { + + MockMemoryTransferService::MockReadHandle::MockReadHandle(MockMemoryTransferService* service) + : ReadHandle(), mService(service) { + } + + MockMemoryTransferService::MockReadHandle::~MockReadHandle() { + mService->OnReadHandleDestroy(this); + } + + size_t MockMemoryTransferService::MockReadHandle::SerializeInitialDataSize(const void* data, + size_t dataLength) { + return mService->OnReadHandleSerializeInitialDataSize(this, data, dataLength); + } + + void MockMemoryTransferService::MockReadHandle::SerializeInitialData(const void* data, + size_t dataLength, + void* serializePointer) { + mService->OnReadHandleSerializeInitialData(this, data, dataLength, serializePointer); + } + + MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service) + : WriteHandle(), mService(service) { + } + + MockMemoryTransferService::MockWriteHandle::~MockWriteHandle() { + mService->OnWriteHandleDestroy(this); + } + + bool MockMemoryTransferService::MockWriteHandle::DeserializeFlush( + const void* deserializePointer, + size_t deserializeSize) { + ASSERT(deserializeSize % sizeof(uint32_t) == 0); + return mService->OnWriteHandleDeserializeFlush( + this, reinterpret_cast(deserializePointer), deserializeSize); + } + + const uint32_t* MockMemoryTransferService::MockWriteHandle::GetData() const { + return reinterpret_cast(mTargetData); + } + + MockMemoryTransferService::MockMemoryTransferService() = default; + MockMemoryTransferService::~MockMemoryTransferService() = default; + + bool MockMemoryTransferService::DeserializeReadHandle(const void* deserializePointer, + size_t deserializeSize, + ReadHandle** readHandle) { + ASSERT(deserializeSize % sizeof(uint32_t) == 0); + return OnDeserializeReadHandle(reinterpret_cast(deserializePointer), + deserializeSize, readHandle); + } + + bool MockMemoryTransferService::DeserializeWriteHandle(const void* deserializePointer, + size_t deserializeSize, + WriteHandle** writeHandle) { + ASSERT(deserializeSize % sizeof(uint32_t) == 0); + return OnDeserializeWriteHandle(reinterpret_cast(deserializePointer), + deserializeSize, writeHandle); + } + + MockMemoryTransferService::MockReadHandle* MockMemoryTransferService::NewReadHandle() { + return new MockReadHandle(this); + } + + MockMemoryTransferService::MockWriteHandle* MockMemoryTransferService::NewWriteHandle() { + return new MockWriteHandle(this); + } + +}} // namespace dawn_wire::server diff --git a/third_party/dawn/src/dawn_wire/server/ServerMemoryTransferService_mock.h b/third_party/dawn/src/dawn_wire/server/ServerMemoryTransferService_mock.h new file mode 100644 index 00000000000..f85aa6461b2 --- /dev/null +++ b/third_party/dawn/src/dawn_wire/server/ServerMemoryTransferService_mock.h @@ -0,0 +1,101 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNWIRE_SERVER_SERVERMEMORYTRANSFERSERVICE_MOCK_H_ +#define DAWNWIRE_SERVER_SERVERMEMORYTRANSFERSERVICE_MOCK_H_ + +#include + +#include "dawn_wire/WireServer.h" +#include "dawn_wire/server/Server.h" + +namespace dawn_wire { namespace server { + + class MockMemoryTransferService : public MemoryTransferService { + public: + class MockReadHandle : public ReadHandle { + public: + MockReadHandle(MockMemoryTransferService* service); + ~MockReadHandle() override; + + size_t SerializeInitialDataSize(const void* data, size_t dataLength) override; + void SerializeInitialData(const void* data, + size_t dataLength, + void* serializePointer) override; + + private: + MockMemoryTransferService* mService; + }; + + class MockWriteHandle : public WriteHandle { + public: + MockWriteHandle(MockMemoryTransferService* service); + ~MockWriteHandle() override; + + bool DeserializeFlush(const void* deserializePointer, size_t deserializeSize) override; + + const uint32_t* GetData() const; + + private: + MockMemoryTransferService* mService; + }; + + MockMemoryTransferService(); + ~MockMemoryTransferService() override; + + bool DeserializeReadHandle(const void* deserializePointer, + size_t deserializeSize, + ReadHandle** readHandle) override; + + bool DeserializeWriteHandle(const void* deserializePointer, + size_t deserializeSize, + WriteHandle** writeHandle) override; + + MockReadHandle* NewReadHandle(); + MockWriteHandle* NewWriteHandle(); + + MOCK_METHOD(bool, + OnDeserializeReadHandle, + (const uint32_t* deserializePointer, + size_t deserializeSize, + ReadHandle** readHandle)); + + MOCK_METHOD(bool, + OnDeserializeWriteHandle, + (const uint32_t* deserializePointer, + size_t deserializeSize, + WriteHandle** writeHandle)); + + MOCK_METHOD(size_t, + OnReadHandleSerializeInitialDataSize, + (const ReadHandle* readHandle, const void* data, size_t dataLength)); + MOCK_METHOD(void, + OnReadHandleSerializeInitialData, + (const ReadHandle* readHandle, + const void* data, + size_t dataLength, + void* serializePointer)); + MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle* readHandle)); + + MOCK_METHOD(bool, + OnWriteHandleDeserializeFlush, + (const WriteHandle* writeHandle, + const uint32_t* deserializePointer, + size_t deserializeSize)); + MOCK_METHOD(void, OnWriteHandleDestroy, (const WriteHandle* writeHandle)); + }; + +}} // namespace dawn_wire::server + +#endif // DAWNWIRE_SERVER_SERVERMEMORYTRANSFERSERVICE_MOCK_H_ diff --git a/third_party/dawn/src/dawn_wire/server/ServerQueue.cpp b/third_party/dawn/src/dawn_wire/server/ServerQueue.cpp index 1851ddad581..0c5a6b880f8 100644 --- a/third_party/dawn/src/dawn_wire/server/ServerQueue.cpp +++ b/third_party/dawn/src/dawn_wire/server/ServerQueue.cpp @@ -17,7 +17,7 @@ namespace dawn_wire { namespace server { - bool Server::DoQueueSignal(DawnQueue cSelf, DawnFence cFence, uint64_t signalValue) { + bool Server::DoQueueSignal(WGPUQueue cSelf, WGPUFence cFence, uint64_t signalValue) { if (cFence == nullptr) { return false; } @@ -31,11 +31,45 @@ namespace dawn_wire { namespace server { FenceCompletionUserdata* userdata = new FenceCompletionUserdata; userdata->server = this; - userdata->fence = ObjectHandle{fenceId, fence->serial}; + userdata->fence = ObjectHandle{fenceId, fence->generation}; userdata->value = signalValue; mProcs.fenceOnCompletion(cFence, signalValue, ForwardFenceCompletedValue, userdata); return true; } + bool Server::DoQueueWriteBufferInternal(ObjectId queueId, + ObjectId bufferId, + uint64_t bufferOffset, + const uint8_t* data, + size_t size) { + // The null object isn't valid as `self` or `buffer` so we can combine the check with the + // check that the ID is valid. + auto* queue = QueueObjects().Get(queueId); + auto* buffer = BufferObjects().Get(bufferId); + if (queue == nullptr || buffer == nullptr) { + return false; + } + + mProcs.queueWriteBuffer(queue->handle, buffer->handle, bufferOffset, data, size); + return true; + } + + bool Server::DoQueueWriteTextureInternal(ObjectId queueId, + const WGPUTextureCopyView* destination, + const uint8_t* data, + size_t dataSize, + const WGPUTextureDataLayout* dataLayout, + const WGPUExtent3D* writeSize) { + // The null object isn't valid as `self` so we can combine the check with the + // check that the ID is valid. + auto* queue = QueueObjects().Get(queueId); + if (queue == nullptr) { + return false; + } + + mProcs.queueWriteTexture(queue->handle, destination, data, dataSize, dataLayout, writeSize); + return true; + } + }} // namespace dawn_wire::server diff --git a/third_party/dawn/src/fuzzers/BUILD.gn b/third_party/dawn/src/fuzzers/BUILD.gn index 2369f94d1cf..f6400b433c2 100644 --- a/third_party/dawn/src/fuzzers/BUILD.gn +++ b/third_party/dawn/src/fuzzers/BUILD.gn @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import("../../scripts/dawn_overrides_with_defaults.gni") import("//build_overrides/build.gni") +import("../../scripts/dawn_overrides_with_defaults.gni") # We only have libfuzzer in Chromium builds but if we build fuzzer targets only # there, we would risk breaking fuzzer targets all the time when making changes @@ -74,8 +74,21 @@ static_library("dawn_spirv_cross_fuzzer_common") { "DawnSPIRVCrossFuzzer.cpp", "DawnSPIRVCrossFuzzer.h", ] + public_deps = [ "${dawn_shaderc_dir}:libshaderc_spvc" ] +} + +static_library("dawn_wire_server_fuzzer_common") { + sources = [ + "DawnWireServerFuzzer.cpp", + "DawnWireServerFuzzer.h", + ] public_deps = [ - "${dawn_shaderc_dir}:libshaderc_spvc", + "${dawn_root}/src/common", + "${dawn_root}/src/dawn:dawn_proc", + "${dawn_root}/src/dawn:dawncpp", + "${dawn_root}/src/dawn_native:dawn_native_static", + "${dawn_root}/src/dawn_wire:dawn_wire_static", + "${dawn_root}/src/utils:dawn_utils", ] } @@ -83,12 +96,8 @@ static_library("dawn_spirv_cross_fuzzer_common") { # needed. # Uses Dawn specific options and varies input data dawn_fuzzer_test("dawn_spirv_cross_glsl_fast_fuzzer") { - sources = [ - "DawnSPIRVCrossGLSLFastFuzzer.cpp", - ] - deps = [ - ":dawn_spirv_cross_fuzzer_common", - ] + sources = [ "DawnSPIRVCrossGLSLFastFuzzer.cpp" ] + deps = [ ":dawn_spirv_cross_fuzzer_common" ] asan_options = [ "allow_user_segv_handler=1" ] } @@ -96,12 +105,8 @@ dawn_fuzzer_test("dawn_spirv_cross_glsl_fast_fuzzer") { # needed. # Uses Dawn specific options and varies input data dawn_fuzzer_test("dawn_spirv_cross_hlsl_fast_fuzzer") { - sources = [ - "DawnSPIRVCrossHLSLFastFuzzer.cpp", - ] - deps = [ - ":dawn_spirv_cross_fuzzer_common", - ] + sources = [ "DawnSPIRVCrossHLSLFastFuzzer.cpp" ] + deps = [ ":dawn_spirv_cross_fuzzer_common" ] asan_options = [ "allow_user_segv_handler=1" ] } @@ -109,26 +114,67 @@ dawn_fuzzer_test("dawn_spirv_cross_hlsl_fast_fuzzer") { # needed. # Uses Dawn specific options and varies input data dawn_fuzzer_test("dawn_spirv_cross_msl_fast_fuzzer") { - sources = [ - "DawnSPIRVCrossMSLFastFuzzer.cpp", - ] - deps = [ - ":dawn_spirv_cross_fuzzer_common", - ] + sources = [ "DawnSPIRVCrossMSLFastFuzzer.cpp" ] + deps = [ ":dawn_spirv_cross_fuzzer_common" ] asan_options = [ "allow_user_segv_handler=1" ] } +dawn_fuzzer_test("dawn_spvc_glsl_fast_fuzzer") { + sources = [ "DawnSPVCglslFastFuzzer.cpp" ] + deps = [ "${dawn_shaderc_dir}:libshaderc_spvc" ] +} + +dawn_fuzzer_test("dawn_spvc_hlsl_fast_fuzzer") { + sources = [ "DawnSPVChlslFastFuzzer.cpp" ] + deps = [ "${dawn_shaderc_dir}:libshaderc_spvc" ] +} + +dawn_fuzzer_test("dawn_spvc_msl_fast_fuzzer") { + sources = [ "DawnSPVCmslFastFuzzer.cpp" ] + deps = [ "${dawn_shaderc_dir}:libshaderc_spvc" ] +} + dawn_fuzzer_test("dawn_wire_server_and_frontend_fuzzer") { - sources = [ - "DawnWireServerAndFrontendFuzzer.cpp", - ] + sources = [ "DawnWireServerAndFrontendFuzzer.cpp" ] + + deps = [ ":dawn_wire_server_fuzzer_common" ] + + additional_configs = [ "${dawn_root}/src/common:dawn_internal" ] +} + +if (is_win) { + dawn_fuzzer_test("dawn_wire_server_and_d3d12_backend_fuzzer") { + sources = [ "DawnWireServerAndD3D12BackendFuzzer.cpp" ] + + deps = [ ":dawn_wire_server_fuzzer_common" ] + additional_configs = [ "${dawn_root}/src/common:dawn_internal" ] + } +} + +dawn_fuzzer_test("dawn_wire_server_and_vulkan_backend_fuzzer") { + sources = [ "DawnWireServerAndVulkanBackendFuzzer.cpp" ] + + deps = [ ":dawn_wire_server_fuzzer_common" ] + + additional_configs = [ "${dawn_root}/src/common:dawn_internal" ] +} + +# A group target to build all the fuzzers +group("dawn_fuzzers") { + testonly = true deps = [ - "${dawn_root}/:libdawn_native_static", - "${dawn_root}/:libdawn_wire_static", - "${dawn_root}/src/common", - "${dawn_root}/src/dawn:libdawn_static", + ":dawn_spirv_cross_glsl_fast_fuzzer", + ":dawn_spirv_cross_hlsl_fast_fuzzer", + ":dawn_spirv_cross_msl_fast_fuzzer", + ":dawn_spvc_glsl_fast_fuzzer", + ":dawn_spvc_hlsl_fast_fuzzer", + ":dawn_spvc_msl_fast_fuzzer", + ":dawn_wire_server_and_frontend_fuzzer", + ":dawn_wire_server_and_vulkan_backend_fuzzer", ] - additional_configs = [ "${dawn_root}/src/common:dawn_internal" ] + if (is_win) { + deps += [ ":dawn_wire_server_and_d3d12_backend_fuzzer" ] + } } diff --git a/third_party/dawn/src/fuzzers/DawnSPIRVCrossFuzzer.h b/third_party/dawn/src/fuzzers/DawnSPIRVCrossFuzzer.h index b60fd5d77fd..49cfc97bc5c 100644 --- a/third_party/dawn/src/fuzzers/DawnSPIRVCrossFuzzer.h +++ b/third_party/dawn/src/fuzzers/DawnSPIRVCrossFuzzer.h @@ -16,7 +16,7 @@ #include #include -#include "shaderc/spvc.hpp" +#include "spvc/spvc.hpp" namespace DawnSPIRVCrossFuzzer { diff --git a/third_party/dawn/src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp index 1fdb4a69c10..7a658e0851d 100644 --- a/third_party/dawn/src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp +++ b/third_party/dawn/src/fuzzers/DawnSPIRVCrossGLSLFastFuzzer.cpp @@ -20,12 +20,13 @@ namespace { int GLSLFastFuzzTask(const std::vector& input) { - shaderc_spvc::Compiler compiler; - if (!compiler.IsValid()) { + shaderc_spvc::Context context; + if (!context.IsValid()) { return 0; } - DawnSPIRVCrossFuzzer::ExecuteWithSignalTrap([&compiler, &input]() { + DawnSPIRVCrossFuzzer::ExecuteWithSignalTrap([&context, &input]() { + shaderc_spvc::CompilationResult result; shaderc_spvc::CompileOptions options; options.SetSourceEnvironment(shaderc_target_env_webgpu, shaderc_env_version_webgpu); options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); @@ -33,7 +34,10 @@ namespace { // Using the options that are used by Dawn, they appear in ShaderModuleGL.cpp options.SetGLSLLanguageVersion(440); options.SetFixupClipspace(true); - compiler.CompileSpvToGlsl(input.data(), input.size(), options); + if (context.InitializeForGlsl(input.data(), input.size(), options) == + shaderc_spvc_status_success) { + context.CompileShader(&result); + } }); return 0; @@ -41,6 +45,10 @@ namespace { } // namespace +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return 0; +} + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return DawnSPIRVCrossFuzzer::Run(data, size, GLSLFastFuzzTask); } diff --git a/third_party/dawn/src/fuzzers/DawnSPIRVCrossHLSLFastFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnSPIRVCrossHLSLFastFuzzer.cpp index 193a7581336..ac75be1f57f 100644 --- a/third_party/dawn/src/fuzzers/DawnSPIRVCrossHLSLFastFuzzer.cpp +++ b/third_party/dawn/src/fuzzers/DawnSPIRVCrossHLSLFastFuzzer.cpp @@ -21,20 +21,30 @@ namespace { int FuzzTask(const std::vector& input) { - shaderc_spvc::Compiler compiler; - if (!compiler.IsValid()) { + shaderc_spvc::Context context; + if (!context.IsValid()) { return 0; } - DawnSPIRVCrossFuzzer::ExecuteWithSignalTrap([&compiler, &input]() { + DawnSPIRVCrossFuzzer::ExecuteWithSignalTrap([&context, &input]() { + shaderc_spvc::CompilationResult result; shaderc_spvc::CompileOptions options; options.SetSourceEnvironment(shaderc_target_env_webgpu, shaderc_env_version_webgpu); options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); // Using the options that are used by Dawn, they appear in ShaderModuleD3D12.cpp - options.SetFlipVertY(true); - options.SetShaderModel(51); - compiler.CompileSpvToHlsl(input.data(), input.size(), options); + options.SetForceZeroInitializedVariables(true); + options.SetHLSLShaderModel(51); + // TODO (hao.x.li@intel.com): The HLSLPointCoordCompat and HLSLPointSizeCompat are + // required temporarily for https://bugs.chromium.org/p/dawn/issues/detail?id=146, + // but should be removed once WebGPU requires there is no gl_PointSize builtin. + // See https://github.com/gpuweb/gpuweb/issues/332 + options.SetHLSLPointCoordCompat(true); + options.SetHLSLPointSizeCompat(true); + if (context.InitializeForHlsl(input.data(), input.size(), options) == + shaderc_spvc_status_success) { + context.CompileShader(&result); + } }); return 0; @@ -42,6 +52,10 @@ namespace { } // namespace +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return 0; +} + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return DawnSPIRVCrossFuzzer::Run(data, size, FuzzTask); } diff --git a/third_party/dawn/src/fuzzers/DawnSPIRVCrossMSLFastFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnSPIRVCrossMSLFastFuzzer.cpp index 13c8391352c..e4ff96457d8 100644 --- a/third_party/dawn/src/fuzzers/DawnSPIRVCrossMSLFastFuzzer.cpp +++ b/third_party/dawn/src/fuzzers/DawnSPIRVCrossMSLFastFuzzer.cpp @@ -21,19 +21,22 @@ namespace { int FuzzTask(const std::vector& input) { - shaderc_spvc::Compiler compiler; - if (!compiler.IsValid()) { + shaderc_spvc::Context context; + if (!context.IsValid()) { return 0; } - DawnSPIRVCrossFuzzer::ExecuteWithSignalTrap([&compiler, &input]() { + DawnSPIRVCrossFuzzer::ExecuteWithSignalTrap([&context, &input]() { + shaderc_spvc::CompilationResult result; shaderc_spvc::CompileOptions options; options.SetSourceEnvironment(shaderc_target_env_webgpu, shaderc_env_version_webgpu); options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); // Using the options that are used by Dawn, they appear in ShaderModuleMTL.mm - options.SetFlipVertY(true); - compiler.CompileSpvToMsl(input.data(), input.size(), options); + if (context.InitializeForMsl(input.data(), input.size(), options) == + shaderc_spvc_status_success) { + context.CompileShader(&result); + } }); return 0; @@ -41,6 +44,10 @@ namespace { } // namespace +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return 0; +} + extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { return DawnSPIRVCrossFuzzer::Run(data, size, FuzzTask); } diff --git a/third_party/dawn/src/fuzzers/DawnSPVCglslFastFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnSPVCglslFastFuzzer.cpp new file mode 100644 index 00000000000..6fbaf82ff72 --- /dev/null +++ b/third_party/dawn/src/fuzzers/DawnSPVCglslFastFuzzer.cpp @@ -0,0 +1,57 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "spvc/spvc.hpp" + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + shaderc_spvc::Context context; + if (!context.IsValid()) { + return 0; + } + + shaderc_spvc::CompilationResult result; + shaderc_spvc::CompileOptions options; + options.SetSourceEnvironment(shaderc_target_env_webgpu, shaderc_env_version_webgpu); + options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); + // Using the options that are used by Dawn, they appear in ShaderModuleGL.cpp + // TODO(sarahM0): double check these option after completion of spvc integration + options.SetFlipVertY(true); + options.SetFixupClipspace(true); +#if defined(DAWN_PLATFORM_APPLE) + options.SetGLSLLanguageVersion(410); +#else + options.SetGLSLLanguageVersion(440); +#endif + + size_t sizeInU32 = size / sizeof(uint32_t); + const uint32_t* u32Data = reinterpret_cast(data); + std::vector input(u32Data, u32Data + sizeInU32); + + if (input.size() != 0) { + if (context.InitializeForGlsl(input.data(), input.size(), options) == + shaderc_spvc_status_success) { + context.SetUseSpvcParser(true); + context.CompileShader(&result); + } + } + return 0; +} diff --git a/third_party/dawn/src/fuzzers/DawnSPVChlslFastFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnSPVChlslFastFuzzer.cpp new file mode 100644 index 00000000000..540ac6567ac --- /dev/null +++ b/third_party/dawn/src/fuzzers/DawnSPVChlslFastFuzzer.cpp @@ -0,0 +1,57 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "spvc/spvc.hpp" + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + shaderc_spvc::Context context; + if (!context.IsValid()) { + return 0; + } + + shaderc_spvc::CompilationResult result; + shaderc_spvc::CompileOptions options; + options.SetSourceEnvironment(shaderc_target_env_webgpu, shaderc_env_version_webgpu); + options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); + + // Using the options that are used by Dawn, they appear in ShaderModuleD3D12.cpp + // TODO(sarahM0): double check these option after completion of spvc integration + options.SetHLSLShaderModel(51); + // TODO (hao.x.li@intel.com): The HLSLPointCoordCompat and HLSLPointSizeCompat are + // required temporarily for https://bugs.chromium.org/p/dawn/issues/detail?id=146, + // but should be removed once WebGPU requires there is no gl_PointSize builtin. + // See https://github.com/gpuweb/gpuweb/issues/332 + options.SetHLSLPointCoordCompat(true); + options.SetHLSLPointSizeCompat(true); + + size_t sizeInU32 = size / sizeof(uint32_t); + const uint32_t* u32Data = reinterpret_cast(data); + std::vector input(u32Data, u32Data + sizeInU32); + + if (input.size() != 0) { + if (context.InitializeForHlsl(input.data(), input.size(), options) == + shaderc_spvc_status_success) { + context.CompileShader(&result); + } + } + return 0; +} diff --git a/third_party/dawn/src/fuzzers/DawnSPVCmslFastFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnSPVCmslFastFuzzer.cpp new file mode 100644 index 00000000000..70cb21e48f2 --- /dev/null +++ b/third_party/dawn/src/fuzzers/DawnSPVCmslFastFuzzer.cpp @@ -0,0 +1,49 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include "spvc/spvc.hpp" + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + shaderc_spvc::Context context; + if (!context.IsValid()) { + return 0; + } + + shaderc_spvc::CompilationResult result; + shaderc_spvc::CompileOptions options; + options.SetSourceEnvironment(shaderc_target_env_webgpu, shaderc_env_version_webgpu); + options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_1); + + // Using the options that are used by Dawn, they appear in ShaderModuleMTL.mm + // TODO(sarahM0): double check these option after completion of spvc integration + size_t sizeInU32 = size / sizeof(uint32_t); + const uint32_t* u32Data = reinterpret_cast(data); + std::vector input(u32Data, u32Data + sizeInU32); + + if (input.size() != 0) { + if (context.InitializeForMsl(input.data(), input.size(), options) == + shaderc_spvc_status_success) { + context.CompileShader(&result); + } + } + return 0; +} diff --git a/third_party/dawn/src/fuzzers/DawnWireServerAndD3D12BackendFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnWireServerAndD3D12BackendFuzzer.cpp new file mode 100644 index 00000000000..79211361a81 --- /dev/null +++ b/third_party/dawn/src/fuzzers/DawnWireServerAndD3D12BackendFuzzer.cpp @@ -0,0 +1,44 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "DawnWireServerFuzzer.h" + +#include "dawn_native/DawnNative.h" +#include "testing/libfuzzer/libfuzzer_exports.h" + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return DawnWireServerFuzzer::Initialize(argc, argv); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + return DawnWireServerFuzzer::Run( + data, size, + [](dawn_native::Instance* instance) { + std::vector adapters = instance->GetAdapters(); + + wgpu::Device device; + for (dawn_native::Adapter adapter : adapters) { + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); + + if (properties.backendType == wgpu::BackendType::D3D12 && + properties.adapterType == wgpu::AdapterType::CPU) { + device = wgpu::Device::Acquire(adapter.CreateDevice()); + break; + } + } + return device; + }, + true /* supportsErrorInjection */); +} diff --git a/third_party/dawn/src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp index 0fb24de9946..f41b8cb1955 100644 --- a/third_party/dawn/src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp +++ b/third_party/dawn/src/fuzzers/DawnWireServerAndFrontendFuzzer.cpp @@ -12,79 +12,35 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "DawnWireServerFuzzer.h" + #include "common/Assert.h" -#include "dawn/dawncpp.h" #include "dawn_native/DawnNative.h" -#include "dawn_wire/WireServer.h" - -#include - -class DevNull : public dawn_wire::CommandSerializer { - public: - void* GetCmdSpace(size_t size) override { - if (size > buf.size()) { - buf.resize(size); - } - return buf.data(); - } - bool Flush() override { - return true; - } - - private: - std::vector buf; -}; +#include "testing/libfuzzer/libfuzzer_exports.h" -static DawnProcDeviceCreateSwapChain originalDeviceCreateSwapChain = nullptr; - -DawnSwapChain ErrorDeviceCreateSwapChain(DawnDevice device, const DawnSwapChainDescriptor*) { - DawnSwapChainDescriptor desc; - desc.nextInChain = nullptr; - // A 0 implementation will trigger a swapchain creation error. - desc.implementation = 0; - return originalDeviceCreateSwapChain(device, &desc); +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return DawnWireServerFuzzer::Initialize(argc, argv); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { - DawnProcTable procs = dawn_native::GetProcs(); - - // Swapchains receive a pointer to an implementation. The fuzzer will pass garbage in so we - // intercept calls to create swapchains and make sure they always return error swapchains. - // This is ok for fuzzing because embedders of dawn_wire would always define their own - // swapchain handling. - originalDeviceCreateSwapChain = procs.deviceCreateSwapChain; - procs.deviceCreateSwapChain = ErrorDeviceCreateSwapChain; - - dawnSetProcs(&procs); - - // Create an instance and find the null adapter to create a device with. - std::unique_ptr instance = std::make_unique(); - instance->DiscoverDefaultAdapters(); - - std::vector adapters = instance->GetAdapters(); - - dawn::Device nullDevice; - for (dawn_native::Adapter adapter : adapters) { - if (adapter.GetBackendType() == dawn_native::BackendType::Null) { - nullDevice = dawn::Device::Acquire(adapter.CreateDevice()); - break; - } - } - ASSERT(nullDevice.Get() != nullptr); - - DevNull devNull; - std::unique_ptr wireServer( - new dawn_wire::WireServer(nullDevice.Get(), procs, &devNull)); - - wireServer->HandleCommands(reinterpret_cast(data), size); - - // Fake waiting for all previous commands before destroying the server. - nullDevice.Tick(); - - // Destroy the server before the device because it needs to free all objects. - wireServer = nullptr; - nullDevice = nullptr; - instance = nullptr; - - return 0; + return DawnWireServerFuzzer::Run( + data, size, + [](dawn_native::Instance* instance) { + std::vector adapters = instance->GetAdapters(); + + wgpu::Device nullDevice; + for (dawn_native::Adapter adapter : adapters) { + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); + + if (properties.backendType == wgpu::BackendType::Null) { + nullDevice = wgpu::Device::Acquire(adapter.CreateDevice()); + break; + } + } + + ASSERT(nullDevice.Get() != nullptr); + return nullDevice; + }, + false /* supportsErrorInjection */); } diff --git a/third_party/dawn/src/fuzzers/DawnWireServerAndVulkanBackendFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnWireServerAndVulkanBackendFuzzer.cpp new file mode 100644 index 00000000000..b23b132e8c4 --- /dev/null +++ b/third_party/dawn/src/fuzzers/DawnWireServerAndVulkanBackendFuzzer.cpp @@ -0,0 +1,44 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "DawnWireServerFuzzer.h" + +#include "dawn_native/DawnNative.h" +#include "testing/libfuzzer/libfuzzer_exports.h" + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + return DawnWireServerFuzzer::Initialize(argc, argv); +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + return DawnWireServerFuzzer::Run( + data, size, + [](dawn_native::Instance* instance) { + std::vector adapters = instance->GetAdapters(); + + wgpu::Device device; + for (dawn_native::Adapter adapter : adapters) { + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); + + if (properties.backendType == wgpu::BackendType::Vulkan && + properties.adapterType == wgpu::AdapterType::CPU) { + device = wgpu::Device::Acquire(adapter.CreateDevice()); + break; + } + } + return device; + }, + true /* supportsErrorInjection */); +} diff --git a/third_party/dawn/src/fuzzers/DawnWireServerFuzzer.cpp b/third_party/dawn/src/fuzzers/DawnWireServerFuzzer.cpp new file mode 100644 index 00000000000..1a70f186027 --- /dev/null +++ b/third_party/dawn/src/fuzzers/DawnWireServerFuzzer.cpp @@ -0,0 +1,199 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "DawnWireServerFuzzer.h" + +#include "common/Assert.h" +#include "common/Log.h" +#include "common/SystemUtils.h" +#include "dawn/dawn_proc.h" +#include "dawn/webgpu_cpp.h" +#include "dawn_native/DawnNative.h" +#include "dawn_wire/WireServer.h" +#include "utils/SystemUtils.h" + +#include +#include + +namespace { + + class DevNull : public dawn_wire::CommandSerializer { + public: + void* GetCmdSpace(size_t size) override { + if (size > buf.size()) { + buf.resize(size); + } + return buf.data(); + } + bool Flush() override { + return true; + } + + private: + std::vector buf; + }; + + std::unique_ptr sInstance; + WGPUProcDeviceCreateSwapChain sOriginalDeviceCreateSwapChain = nullptr; + + std::string sInjectedErrorTestcaseOutDir; + uint64_t sOutputFileNumber = 0; + + bool sCommandsComplete = false; + + WGPUSwapChain ErrorDeviceCreateSwapChain(WGPUDevice device, + WGPUSurface surface, + const WGPUSwapChainDescriptor*) { + WGPUSwapChainDescriptor desc = {}; + // A 0 implementation will trigger a swapchain creation error. + desc.implementation = 0; + return sOriginalDeviceCreateSwapChain(device, surface, &desc); + } + + void CommandsCompleteCallback(WGPUFenceCompletionStatus status, void* userdata) { + sCommandsComplete = true; + } + +} // namespace + +int DawnWireServerFuzzer::Initialize(int* argc, char*** argv) { + ASSERT(argc != nullptr && argv != nullptr); + + // The first argument (the fuzzer binary) always stays the same. + int argcOut = 1; + + for (int i = 1; i < *argc; ++i) { + constexpr const char kInjectedErrorTestcaseDirArg[] = "--injected-error-testcase-dir="; + if (strstr((*argv)[i], kInjectedErrorTestcaseDirArg) == (*argv)[i]) { + sInjectedErrorTestcaseOutDir = (*argv)[i] + strlen(kInjectedErrorTestcaseDirArg); + const char* sep = GetPathSeparator(); + if (sInjectedErrorTestcaseOutDir.back() != *sep) { + sInjectedErrorTestcaseOutDir += sep; + } + // Log so that it's clear the fuzzer found the argument. + dawn::InfoLog() << "Generating injected errors, output dir is: \"" + << sInjectedErrorTestcaseOutDir << "\""; + continue; + } + + // Move any unconsumed arguments to the next slot in the output array. + (*argv)[argcOut++] = (*argv)[i]; + } + + // Write the argument count + *argc = argcOut; + + // TODO(crbug.com/1038952): The Instance must be static because destructing the vkInstance with + // Swiftshader crashes libFuzzer. When this is fixed, move this into Run so that error injection + // for adapter discovery can be fuzzed. + sInstance = std::make_unique(); + sInstance->DiscoverDefaultAdapters(); + + return 0; +} + +int DawnWireServerFuzzer::Run(const uint8_t* data, + size_t size, + MakeDeviceFn MakeDevice, + bool supportsErrorInjection) { + bool didInjectError = false; + + if (supportsErrorInjection) { + dawn_native::EnableErrorInjector(); + + // Clear the error injector since it has the previous run's call counts. + dawn_native::ClearErrorInjector(); + + // If we're outputing testcases with injected errors, we run the fuzzer on the original + // input data, and prepend injected errors to it. In the case, where we're NOT outputing, + // we use the first bytes as the injected error index. + if (sInjectedErrorTestcaseOutDir.empty() && size >= sizeof(uint64_t)) { + // Otherwise, use the first bytes as the injected error index. + dawn_native::InjectErrorAt(*reinterpret_cast(data)); + didInjectError = true; + + data += sizeof(uint64_t); + size -= sizeof(uint64_t); + } + } + + DawnProcTable procs = dawn_native::GetProcs(); + + // Swapchains receive a pointer to an implementation. The fuzzer will pass garbage in so we + // intercept calls to create swapchains and make sure they always return error swapchains. + // This is ok for fuzzing because embedders of dawn_wire would always define their own + // swapchain handling. + sOriginalDeviceCreateSwapChain = procs.deviceCreateSwapChain; + procs.deviceCreateSwapChain = ErrorDeviceCreateSwapChain; + + dawnProcSetProcs(&procs); + + wgpu::Device device = MakeDevice(sInstance.get()); + if (!device) { + // We should only ever fail device creation if an error was injected. + ASSERT(didInjectError); + return 0; + } + + DevNull devNull; + dawn_wire::WireServerDescriptor serverDesc = {}; + serverDesc.device = device.Get(); + serverDesc.procs = &procs; + serverDesc.serializer = &devNull; + + std::unique_ptr wireServer(new dawn_wire::WireServer(serverDesc)); + + wireServer->HandleCommands(reinterpret_cast(data), size); + + // Wait for all previous commands before destroying the server. + // TODO(enga): Improve this when we improve/finalize how processing events happens. + { + wgpu::Queue queue = device.GetDefaultQueue(); + wgpu::Fence fence = queue.CreateFence(); + queue.Signal(fence, 1u); + fence.OnCompletion(1u, CommandsCompleteCallback, 0); + while (!sCommandsComplete) { + device.Tick(); + utils::USleep(100); + } + } + + // Destroy the server before the device because it needs to free all objects. + wireServer = nullptr; + device = nullptr; + + // If we support error injection, and an output directory was provided, output copies of the + // original testcase data, prepended with the injected error index. + if (supportsErrorInjection && !sInjectedErrorTestcaseOutDir.empty()) { + const uint64_t injectedCallCount = dawn_native::AcquireErrorInjectorCallCount(); + + auto WriteTestcase = [&](uint64_t i) { + std::ofstream outFile( + sInjectedErrorTestcaseOutDir + "injected_error_testcase_" + + std::to_string(sOutputFileNumber++), + std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + outFile.write(reinterpret_cast(&i), sizeof(i)); + outFile.write(reinterpret_cast(data), size); + }; + + for (uint64_t i = 0; i < injectedCallCount; ++i) { + WriteTestcase(i); + } + + // Also add a testcase where the injected error is so large no errors should occur. + WriteTestcase(std::numeric_limits::max()); + } + + return 0; +} diff --git a/third_party/dawn/src/fuzzers/DawnWireServerFuzzer.h b/third_party/dawn/src/fuzzers/DawnWireServerFuzzer.h new file mode 100644 index 00000000000..c2e176765f3 --- /dev/null +++ b/third_party/dawn/src/fuzzers/DawnWireServerFuzzer.h @@ -0,0 +1,34 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn/webgpu_cpp.h" + +#include +#include + +namespace dawn_native { + + class Instance; + +} // namespace dawn_native + +namespace DawnWireServerFuzzer { + + using MakeDeviceFn = std::function; + + int Initialize(int* argc, char*** argv); + + int Run(const uint8_t* data, size_t size, MakeDeviceFn MakeDevice, bool supportsErrorInjection); + +} // namespace DawnWireServerFuzzer diff --git a/third_party/dawn/src/fuzzers/StandaloneFuzzerMain.cpp b/third_party/dawn/src/fuzzers/StandaloneFuzzerMain.cpp index ba8d94b99db..33411991e9f 100644 --- a/third_party/dawn/src/fuzzers/StandaloneFuzzerMain.cpp +++ b/third_party/dawn/src/fuzzers/StandaloneFuzzerMain.cpp @@ -17,11 +17,17 @@ #include #include +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv); extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); int main(int argc, char** argv) { + if (LLVMFuzzerInitialize(&argc, &argv)) { + std::cerr << "Failed to initialize fuzzer target" << std::endl; + return 1; + } + if (argc != 2) { - std::cout << "Usage: [FILE]" << std::endl; + std::cout << "Usage: [options] FILE" << std::endl; return 1; } diff --git a/third_party/dawn/src/include/dawn/EnumClassBitmasks.h b/third_party/dawn/src/include/dawn/EnumClassBitmasks.h index 7e1d769a9b0..93d2be4574d 100644 --- a/third_party/dawn/src/include/dawn/EnumClassBitmasks.h +++ b/third_party/dawn/src/include/dawn/EnumClassBitmasks.h @@ -17,26 +17,7 @@ #include -namespace dawn { - -// std::underlying_type doesn't work in old GLIBC still used in Chrome -#define CR_GLIBCXX_4_7_0 20120322 -#define CR_GLIBCXX_4_5_4 20120702 -#define CR_GLIBCXX_4_6_4 20121127 -#if defined(__GLIBCXX__) && (__GLIBCXX__ < CR_GLIBCXX_4_7_0 || __GLIBCXX__ == CR_GLIBCXX_4_5_4 || \ - __GLIBCXX__ == CR_GLIBCXX_4_6_4) -# define CR_USE_FALLBACKS_FOR_OLD_GLIBCXX -#endif - -#if defined(CR_USE_FALLBACKS_FOR_OLD_GLIBCXX) - template - struct UnderlyingType { - using type = __underlying_type(T); - }; -#else - template - using UnderlyingType = std::underlying_type; -#endif +namespace wgpu { template struct IsDawnBitmask { @@ -59,7 +40,7 @@ namespace dawn { template struct BoolConvertible { - using Integral = typename UnderlyingType::type; + using Integral = typename std::underlying_type::type; constexpr BoolConvertible(Integral value) : value(value) { } @@ -82,19 +63,13 @@ namespace dawn { } }; - template - constexpr bool HasZeroOrOneBits(T value) { - using Integral = typename UnderlyingType::type; - return (static_cast(value) & (static_cast(value) - 1)) == 0; - } - template ::enable && LowerBitmask::enable>::type> constexpr BoolConvertible::type> operator|(T1 left, T2 right) { using T = typename LowerBitmask::type; - using Integral = typename UnderlyingType::type; + using Integral = typename std::underlying_type::type; return static_cast(LowerBitmask::Lower(left)) | static_cast(LowerBitmask::Lower(right)); } @@ -105,7 +80,7 @@ namespace dawn { LowerBitmask::enable>::type> constexpr BoolConvertible::type> operator&(T1 left, T2 right) { using T = typename LowerBitmask::type; - using Integral = typename UnderlyingType::type; + using Integral = typename std::underlying_type::type; return static_cast(LowerBitmask::Lower(left)) & static_cast(LowerBitmask::Lower(right)); } @@ -116,7 +91,7 @@ namespace dawn { LowerBitmask::enable>::type> constexpr BoolConvertible::type> operator^(T1 left, T2 right) { using T = typename LowerBitmask::type; - using Integral = typename UnderlyingType::type; + using Integral = typename std::underlying_type::type; return static_cast(LowerBitmask::Lower(left)) ^ static_cast(LowerBitmask::Lower(right)); } @@ -124,7 +99,7 @@ namespace dawn { template constexpr BoolConvertible::type> operator~(T1 t) { using T = typename LowerBitmask::type; - using Integral = typename UnderlyingType::type; + using Integral = typename std::underlying_type::type; return ~static_cast(LowerBitmask::Lower(t)); } @@ -157,6 +132,13 @@ namespace dawn { l = l ^ r; return l; } -} // namespace dawn + + template + constexpr bool HasZeroOrOneBits(T value) { + using Integral = typename std::underlying_type::type; + return (static_cast(value) & (static_cast(value) - 1)) == 0; + } + +} // namespace wgpu #endif // DAWN_ENUM_CLASS_BITMASKS_H_ diff --git a/third_party/dawn/src/include/dawn/dawn_proc.h b/third_party/dawn/src/include/dawn/dawn_proc.h new file mode 100644 index 00000000000..adeec463352 --- /dev/null +++ b/third_party/dawn/src/include/dawn/dawn_proc.h @@ -0,0 +1,36 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWN_DAWN_PROC_H_ +#define DAWN_DAWN_PROC_H_ + +#include "dawn/dawn_proc_table.h" +#include "dawn/webgpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// Sets the static proctable used by libdawn_proc to implement the Dawn entrypoints. Passing NULL +// for `procs` sets up the null proctable that contains only null function pointers. It is the +// default value of the proctable. Setting the proctable back to null is good practice when you +// are done using libdawn_proc since further usage will cause a segfault instead of calling an +// unexpected function. +WGPU_EXPORT void dawnProcSetProcs(const DawnProcTable* procs); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // DAWN_DAWN_PROC_H_ diff --git a/third_party/dawn/src/include/dawn/dawn_wsi.h b/third_party/dawn/src/include/dawn/dawn_wsi.h index 1a2c4dfae66..f1a6047b5b3 100644 --- a/third_party/dawn/src/include/dawn/dawn_wsi.h +++ b/third_party/dawn/src/include/dawn/dawn_wsi.h @@ -15,7 +15,7 @@ #ifndef DAWN_DAWN_WSI_H_ #define DAWN_DAWN_WSI_H_ -#include +#include // Error message (or nullptr if there was no error) typedef const char* DawnSwapChainError; @@ -40,8 +40,8 @@ typedef struct { /// Configure/reconfigure the swap chain. DawnSwapChainError (*Configure)(void* userData, - DawnTextureFormat format, - DawnTextureUsageBit allowedUsage, + WGPUTextureFormat format, + WGPUTextureUsage allowedUsage, uint32_t width, uint32_t height); @@ -55,21 +55,22 @@ typedef struct { void* userData; /// For use by the D3D12 and Vulkan backends: how the swapchain will use the texture. - DawnTextureUsageBit textureUsage; + WGPUTextureUsage textureUsage; } DawnSwapChainImplementation; #if defined(DAWN_ENABLE_BACKEND_D3D12) && defined(__cplusplus) -typedef struct { - DawnDevice device = nullptr; -} DawnWSIContextD3D12; +struct DawnWSIContextD3D12 { + WGPUDevice device = nullptr; +}; #endif #if defined(DAWN_ENABLE_BACKEND_METAL) && defined(__OBJC__) # import -typedef struct { +struct DawnWSIContextMetal { id device = nil; -} DawnWSIContextMetal; + id queue = nil; +}; #endif #ifdef DAWN_ENABLE_BACKEND_OPENGL diff --git a/third_party/dawn/src/include/dawn_native/D3D12Backend.h b/third_party/dawn/src/include/dawn_native/D3D12Backend.h index 43debedfb85..035ad96188e 100644 --- a/third_party/dawn/src/include/dawn_native/D3D12Backend.h +++ b/third_party/dawn/src/include/dawn_native/D3D12Backend.h @@ -18,13 +18,47 @@ #include #include +#include #include +#include + +struct ID3D12Device; namespace dawn_native { namespace d3d12 { - DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(DawnDevice device, + DAWN_NATIVE_EXPORT Microsoft::WRL::ComPtr GetD3D12Device(WGPUDevice device); + DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device, HWND window); - DAWN_NATIVE_EXPORT DawnTextureFormat + DAWN_NATIVE_EXPORT WGPUTextureFormat GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain); + + enum MemorySegment { + Local, + NonLocal, + }; + + DAWN_NATIVE_EXPORT uint64_t SetExternalMemoryReservation(WGPUDevice device, + uint64_t requestedReservationSize, + MemorySegment memorySegment); + + struct DAWN_NATIVE_EXPORT ExternalImageDescriptorDXGISharedHandle : ExternalImageDescriptor { + public: + ExternalImageDescriptorDXGISharedHandle(); + + HANDLE sharedHandle; + uint64_t acquireMutexKey; + bool isSwapChainTexture = false; + }; + + // Note: SharedHandle must be a handle to a texture object. + DAWN_NATIVE_EXPORT WGPUTexture + WrapSharedHandle(WGPUDevice device, const ExternalImageDescriptorDXGISharedHandle* descriptor); + + struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptions : public AdapterDiscoveryOptionsBase { + AdapterDiscoveryOptions(Microsoft::WRL::ComPtr adapter); + + Microsoft::WRL::ComPtr dxgiAdapter; + }; + }} // namespace dawn_native::d3d12 #endif // DAWNNATIVE_D3D12BACKEND_H_ diff --git a/third_party/dawn/src/include/dawn_native/DawnNative.h b/third_party/dawn/src/include/dawn_native/DawnNative.h index 76c1159a9c2..219e53fa94b 100644 --- a/third_party/dawn/src/include/dawn_native/DawnNative.h +++ b/third_party/dawn/src/include/dawn_native/DawnNative.h @@ -15,20 +15,31 @@ #ifndef DAWNNATIVE_DAWNNATIVE_H_ #define DAWNNATIVE_DAWNNATIVE_H_ -#include +#include +#include #include #include #include +namespace dawn_platform { + class Platform; +} // namespace dawn_platform + +namespace wgpu { + struct AdapterProperties; +} + namespace dawn_native { + // DEPRECATED: use WGPUAdapterProperties instead. struct PCIInfo { uint32_t deviceId = 0; uint32_t vendorId = 0; std::string name; }; + // DEPRECATED: use WGPUBackendType instead. enum class BackendType { D3D12, Metal, @@ -37,6 +48,7 @@ namespace dawn_native { Vulkan, }; + // DEPRECATED: use WGPUAdapterType instead. enum class DeviceType { DiscreteGPU, IntegratedGPU, @@ -50,6 +62,7 @@ namespace dawn_native { // An optional parameter of Adapter::CreateDevice() to send additional information when creating // a Device. For example, we can use it to enable a workaround, optimization or feature. struct DAWN_NATIVE_EXPORT DeviceDescriptor { + std::vector requiredExtensions; std::vector forceEnabledToggles; std::vector forceDisabledToggles; }; @@ -63,6 +76,11 @@ namespace dawn_native { const char* url; }; + // A struct to record the information of an extension. An extension is a GPU feature that is not + // required to be supported by all Dawn backends and can only be used when it is enabled on the + // creation of device. + using ExtensionInfo = ToggleInfo; + // An adapter is an object that represent on possibility of creating devices in the system. // Most of the time it will represent a combination of a physical GPU and an API. Not that the // same GPU can be represented by multiple adapters but on different APIs. @@ -75,16 +93,24 @@ namespace dawn_native { Adapter(AdapterBase* impl); ~Adapter(); + // DEPRECATED: use GetProperties instead. BackendType GetBackendType() const; DeviceType GetDeviceType() const; const PCIInfo& GetPCIInfo() const; + // Essentially webgpu.h's wgpuAdapterGetProperties while we don't have WGPUAdapter in + // dawn.json + void GetProperties(wgpu::AdapterProperties* properties) const; + + std::vector GetSupportedExtensions() const; + WGPUDeviceProperties GetAdapterProperties() const; + explicit operator bool() const; // Create a device on this adapter, note that the interface will change to include at least // a device descriptor and a pointer to backend specific options. // On an error, nullptr is returned. - DawnDevice CreateDevice(const DeviceDescriptor* deviceDescriptor = nullptr); + WGPUDevice CreateDevice(const DeviceDescriptor* deviceDescriptor = nullptr); private: AdapterBase* mImpl = nullptr; @@ -93,10 +119,10 @@ namespace dawn_native { // Base class for options passed to Instance::DiscoverAdapters. struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptionsBase { public: - const BackendType backendType; + const WGPUBackendType backendType; protected: - AdapterDiscoveryOptionsBase(BackendType type); + AdapterDiscoveryOptionsBase(WGPUBackendType type); }; // Represents a connection to dawn_native and is used for dependency injection, discovering @@ -127,7 +153,14 @@ namespace dawn_native { // Enable backend's validation layers if it has. void EnableBackendValidation(bool enableBackendValidation); - bool IsBackendValidationEnabled(); + + // Enable debug capture on Dawn startup + void EnableBeginCaptureOnStartup(bool beginCaptureOnStartup); + + void SetPlatform(dawn_platform::Platform* platform); + + // Returns the underlying WGPUInstance object. + WGPUInstance Get() const; private: InstanceBase* mImpl = nullptr; @@ -137,8 +170,49 @@ namespace dawn_native { DAWN_NATIVE_EXPORT DawnProcTable GetProcs(); // Query the names of all the toggles that are enabled in device - DAWN_NATIVE_EXPORT std::vector GetTogglesUsed(DawnDevice device); + DAWN_NATIVE_EXPORT std::vector GetTogglesUsed(WGPUDevice device); + + // Backdoor to get the number of lazy clears for testing + DAWN_NATIVE_EXPORT size_t GetLazyClearCountForTesting(WGPUDevice device); + + // Backdoor to get the number of deprecation warnings for testing + DAWN_NATIVE_EXPORT size_t GetDeprecationWarningCountForTesting(WGPUDevice device); + + // Query if texture has been initialized + DAWN_NATIVE_EXPORT bool IsTextureSubresourceInitialized(WGPUTexture texture, + uint32_t baseMipLevel, + uint32_t levelCount, + uint32_t baseArrayLayer, + uint32_t layerCount); + + // Backdoor to get the order of the ProcMap for testing + DAWN_NATIVE_EXPORT std::vector GetProcMapNamesForTesting(); + + // ErrorInjector functions used for testing only. Defined in dawn_native/ErrorInjector.cpp + DAWN_NATIVE_EXPORT void EnableErrorInjector(); + DAWN_NATIVE_EXPORT void DisableErrorInjector(); + DAWN_NATIVE_EXPORT void ClearErrorInjector(); + DAWN_NATIVE_EXPORT uint64_t AcquireErrorInjectorCallCount(); + DAWN_NATIVE_EXPORT void InjectErrorAt(uint64_t index); + + // The different types of ExternalImageDescriptors + enum ExternalImageDescriptorType { + OpaqueFD, + DmaBuf, + IOSurface, + DXGISharedHandle, + }; + + // Common properties of external images + struct DAWN_NATIVE_EXPORT ExternalImageDescriptor { + public: + const ExternalImageDescriptorType type; + const WGPUTextureDescriptor* cTextureDescriptor; // Must match image creation params + bool isCleared; // Sets whether the texture will be cleared before use + protected: + ExternalImageDescriptor(ExternalImageDescriptorType type); + }; } // namespace dawn_native #endif // DAWNNATIVE_DAWNNATIVE_H_ diff --git a/third_party/dawn/src/include/dawn_native/MetalBackend.h b/third_party/dawn/src/include/dawn_native/MetalBackend.h index 7588b978965..90884ee7f24 100644 --- a/third_party/dawn/src/include/dawn_native/MetalBackend.h +++ b/third_party/dawn/src/include/dawn_native/MetalBackend.h @@ -33,22 +33,28 @@ typedef __IOSurface* IOSurfaceRef; #endif //__OBJC__ namespace dawn_native { namespace metal { - DAWN_NATIVE_EXPORT DawnTexture WrapIOSurface(DawnDevice device, - const DawnTextureDescriptor* descriptor, - IOSurfaceRef ioSurface, - uint32_t plane); + struct DAWN_NATIVE_EXPORT ExternalImageDescriptorIOSurface : ExternalImageDescriptor { + public: + ExternalImageDescriptorIOSurface(); + + IOSurfaceRef ioSurface; + uint32_t plane; + }; + + DAWN_NATIVE_EXPORT WGPUTexture + WrapIOSurface(WGPUDevice device, const ExternalImageDescriptorIOSurface* descriptor); // When making Metal interop with other APIs, we need to be careful that QueueSubmit doesn't // mean that the operations will be visible to other APIs/Metal devices right away. macOS // does have a global queue of graphics operations, but the command buffers are inserted there // when they are "scheduled". Submitting other operations before the command buffer is // scheduled could lead to races in who gets scheduled first and incorrect rendering. - DAWN_NATIVE_EXPORT void WaitForCommandsToBeScheduled(DawnDevice device); + DAWN_NATIVE_EXPORT void WaitForCommandsToBeScheduled(WGPUDevice device); }} // namespace dawn_native::metal #ifdef __OBJC__ namespace dawn_native { namespace metal { - DAWN_NATIVE_EXPORT id GetMetalDevice(DawnDevice device); + DAWN_NATIVE_EXPORT id GetMetalDevice(WGPUDevice device); }} // namespace dawn_native::metal #endif // __OBJC__ diff --git a/third_party/dawn/src/include/dawn_native/OpenGLBackend.h b/third_party/dawn/src/include/dawn_native/OpenGLBackend.h index 10b00004a55..05896716a3f 100644 --- a/third_party/dawn/src/include/dawn_native/OpenGLBackend.h +++ b/third_party/dawn/src/include/dawn_native/OpenGLBackend.h @@ -15,6 +15,7 @@ #ifndef DAWNNATIVE_OPENGLBACKEND_H_ #define DAWNNATIVE_OPENGLBACKEND_H_ +#include #include namespace dawn_native { namespace opengl { @@ -25,6 +26,12 @@ namespace dawn_native { namespace opengl { void* (*getProc)(const char*); }; + using PresentCallback = void (*)(void*); + DAWN_NATIVE_EXPORT DawnSwapChainImplementation + CreateNativeSwapChainImpl(WGPUDevice device, PresentCallback present, void* presentUserdata); + DAWN_NATIVE_EXPORT WGPUTextureFormat + GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain); + }} // namespace dawn_native::opengl #endif // DAWNNATIVE_OPENGLBACKEND_H_ diff --git a/third_party/dawn/src/include/dawn_native/VulkanBackend.h b/third_party/dawn/src/include/dawn_native/VulkanBackend.h index eeb2b0b1ec2..4e5aee995e8 100644 --- a/third_party/dawn/src/include/dawn_native/VulkanBackend.h +++ b/third_party/dawn/src/include/dawn_native/VulkanBackend.h @@ -23,12 +23,60 @@ #include namespace dawn_native { namespace vulkan { - DAWN_NATIVE_EXPORT VkInstance GetInstance(DawnDevice device); - DAWN_NATIVE_EXPORT DawnSwapChainImplementation CreateNativeSwapChainImpl(DawnDevice device, - VkSurfaceKHR surface); - DAWN_NATIVE_EXPORT DawnTextureFormat + DAWN_NATIVE_EXPORT VkInstance GetInstance(WGPUDevice device); + + DAWN_NATIVE_EXPORT PFN_vkVoidFunction GetInstanceProcAddr(WGPUDevice device, const char* pName); + + DAWN_NATIVE_EXPORT DawnSwapChainImplementation + CreateNativeSwapChainImpl(WGPUDevice device, ::VkSurfaceKHR surface); + DAWN_NATIVE_EXPORT WGPUTextureFormat GetNativeSwapChainPreferredFormat(const DawnSwapChainImplementation* swapChain); + +// Can't use DAWN_PLATFORM_LINUX since header included in both dawn and chrome +#ifdef __linux__ + // Common properties of external images represented by FDs. On successful import the file + // descriptor's ownership is transferred to the Dawn implementation and they shouldn't be + // used outside of Dawn again. TODO(enga): Also transfer ownership in the error case so the + // caller can assume the FD is always consumed. + struct DAWN_NATIVE_EXPORT ExternalImageDescriptorFD : ExternalImageDescriptor { + public: + int memoryFD; // A file descriptor from an export of the memory of the image + std::vector waitFDs; // File descriptors of semaphores which will be waited on + + protected: + ExternalImageDescriptorFD(ExternalImageDescriptorType type); + }; + + // Descriptor for opaque file descriptor image import + struct DAWN_NATIVE_EXPORT ExternalImageDescriptorOpaqueFD : ExternalImageDescriptorFD { + ExternalImageDescriptorOpaqueFD(); + + VkDeviceSize allocationSize; // Must match VkMemoryAllocateInfo from image creation + uint32_t memoryTypeIndex; // Must match VkMemoryAllocateInfo from image creation + }; + + // Descriptor for dma-buf file descriptor image import + struct DAWN_NATIVE_EXPORT ExternalImageDescriptorDmaBuf : ExternalImageDescriptorFD { + ExternalImageDescriptorDmaBuf(); + + uint32_t stride; // Stride of the buffer in bytes + uint64_t drmModifier; // DRM modifier of the buffer + }; + + // Exports a signal semaphore from a wrapped texture. This must be called on wrapped + // textures before they are destroyed. On failure, returns -1 + DAWN_NATIVE_EXPORT int ExportSignalSemaphoreOpaqueFD(WGPUDevice cDevice, + WGPUTexture cTexture); + + // Imports external memory into a Vulkan image. Internally, this uses external memory / + // semaphore extensions to import the image and wait on the provided synchronizaton + // primitives before the texture can be used. + // On failure, returns a nullptr. + DAWN_NATIVE_EXPORT WGPUTexture WrapVulkanImage(WGPUDevice cDevice, + const ExternalImageDescriptor* descriptor); +#endif // __linux__ + }} // namespace dawn_native::vulkan #endif // DAWNNATIVE_VULKANBACKEND_H_ diff --git a/third_party/dawn/src/include/dawn_platform/DawnPlatform.h b/third_party/dawn/src/include/dawn_platform/DawnPlatform.h new file mode 100644 index 00000000000..61b029e986b --- /dev/null +++ b/third_party/dawn/src/include/dawn_platform/DawnPlatform.h @@ -0,0 +1,53 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef DAWNPLATFORM_DAWNPLATFORM_H_ +#define DAWNPLATFORM_DAWNPLATFORM_H_ + +#include + +#include + +namespace dawn_platform { + + enum class TraceCategory { + General, // General trace events + Validation, // Dawn validation + Recording, // Native command recording + GPUWork, // Actual GPU work + }; + + class DAWN_NATIVE_EXPORT Platform { + public: + virtual ~Platform() { + } + virtual const unsigned char* GetTraceCategoryEnabledFlag(TraceCategory category) = 0; + + virtual double MonotonicallyIncreasingTime() = 0; + + virtual uint64_t AddTraceEvent(char phase, + const unsigned char* categoryGroupEnabled, + const char* name, + uint64_t id, + double timestamp, + int numArgs, + const char** argNames, + const unsigned char* argTypes, + const uint64_t* argValues, + unsigned char flags) = 0; + }; + +} // namespace dawn_platform + +#endif // DAWNPLATFORM_DAWNPLATFORM_H_ diff --git a/third_party/dawn/src/include/dawn_wire/Wire.h b/third_party/dawn/src/include/dawn_wire/Wire.h index b5ee54d15cb..6120f8b9ce6 100644 --- a/third_party/dawn/src/include/dawn_wire/Wire.h +++ b/third_party/dawn/src/include/dawn_wire/Wire.h @@ -17,7 +17,7 @@ #include -#include "dawn/dawn.h" +#include "dawn/webgpu.h" #include "dawn_wire/dawn_wire_export.h" namespace dawn_wire { @@ -32,9 +32,19 @@ namespace dawn_wire { class DAWN_WIRE_EXPORT CommandHandler { public: virtual ~CommandHandler() = default; - virtual const char* HandleCommands(const char* commands, size_t size) = 0; + virtual const volatile char* HandleCommands(const volatile char* commands, size_t size) = 0; }; + DAWN_WIRE_EXPORT size_t + SerializedWGPUDevicePropertiesSize(const WGPUDeviceProperties* deviceProperties); + + DAWN_WIRE_EXPORT void SerializeWGPUDeviceProperties( + const WGPUDeviceProperties* deviceProperties, + char* serializeBuffer); + + DAWN_WIRE_EXPORT bool DeserializeWGPUDeviceProperties(WGPUDeviceProperties* deviceProperties, + const volatile char* deserializeBuffer); + } // namespace dawn_wire #endif // DAWNWIRE_WIRE_H_ diff --git a/third_party/dawn/src/include/dawn_wire/WireClient.h b/third_party/dawn/src/include/dawn_wire/WireClient.h index 4e9e56a4222..815b66b877a 100644 --- a/third_party/dawn/src/include/dawn_wire/WireClient.h +++ b/third_party/dawn/src/include/dawn_wire/WireClient.h @@ -15,37 +15,121 @@ #ifndef DAWNWIRE_WIRECLIENT_H_ #define DAWNWIRE_WIRECLIENT_H_ -#include - +#include "dawn/dawn_proc_table.h" #include "dawn_wire/Wire.h" +#include +#include + namespace dawn_wire { namespace client { class Client; - } + class MemoryTransferService; + } // namespace client struct ReservedTexture { - DawnTexture texture; + WGPUTexture texture; uint32_t id; uint32_t generation; }; + struct DAWN_WIRE_EXPORT WireClientDescriptor { + CommandSerializer* serializer; + client::MemoryTransferService* memoryTransferService = nullptr; + }; + class DAWN_WIRE_EXPORT WireClient : public CommandHandler { public: - WireClient(CommandSerializer* serializer); - ~WireClient(); + WireClient(const WireClientDescriptor& descriptor); + ~WireClient() override; - DawnDevice GetDevice() const; - DawnProcTable GetProcs() const; - const char* HandleCommands(const char* commands, size_t size) override final; + static DawnProcTable GetProcs(); - ReservedTexture ReserveTexture(DawnDevice device); + WGPUDevice GetDevice() const; + const volatile char* HandleCommands(const volatile char* commands, + size_t size) override final; + + ReservedTexture ReserveTexture(WGPUDevice device); + + // Disconnects the client. + // Commands allocated after this point will not be sent. + void Disconnect(); private: std::unique_ptr mImpl; }; + namespace client { + class DAWN_WIRE_EXPORT MemoryTransferService { + public: + class ReadHandle; + class WriteHandle; + + virtual ~MemoryTransferService(); + + // Create a handle for reading server data. + // This may fail and return nullptr. + virtual ReadHandle* CreateReadHandle(size_t) = 0; + + // Create a handle for writing server data. + // This may fail and return nullptr. + virtual WriteHandle* CreateWriteHandle(size_t) = 0; + + // Imported memory implementation needs to override these to create Read/Write + // handles associated with a particular buffer. The client should receive a file + // descriptor for the buffer out-of-band. + virtual ReadHandle* CreateReadHandle(WGPUBuffer, uint64_t offset, size_t size); + virtual WriteHandle* CreateWriteHandle(WGPUBuffer, uint64_t offset, size_t size); + + class DAWN_WIRE_EXPORT ReadHandle { + public: + // Get the required serialization size for SerializeCreate + virtual size_t SerializeCreateSize() = 0; + + // Serialize the handle into |serializePointer| so it can be received by the server. + virtual void SerializeCreate(void* serializePointer) = 0; + + // Load initial data and open the handle for reading. + // This function takes in the serialized result of + // server::MemoryTransferService::ReadHandle::SerializeInitialData. + // This function should write to |data| and |dataLength| the pointer and size of the + // mapped data for reading. It must live at least until the ReadHandle is + // destructed. + virtual bool DeserializeInitialData(const void* deserializePointer, + size_t deserializeSize, + const void** data, + size_t* dataLength) = 0; + virtual ~ReadHandle(); + }; + + class DAWN_WIRE_EXPORT WriteHandle { + public: + // Get the required serialization size for SerializeCreate + virtual size_t SerializeCreateSize() = 0; + + // Serialize the handle into |serializePointer| so it can be received by the server. + virtual void SerializeCreate(void* serializePointer) = 0; + + // Open the handle for reading. The data returned should be zero-initialized. + // The data returned must live at least until the WriteHandle is destructed. + // On failure, the pointer returned should be null. + virtual std::pair Open() = 0; + + // Get the required serialization size for SerializeFlush + virtual size_t SerializeFlushSize() = 0; + + // Flush writes to the handle. This should serialize info to send updates to the + // server. + virtual void SerializeFlush(void* serializePointer) = 0; + + virtual ~WriteHandle(); + }; + }; + + // Backdoor to get the order of the ProcMap for testing + DAWN_WIRE_EXPORT std::vector GetProcMapNamesForTesting(); + } // namespace client } // namespace dawn_wire #endif // DAWNWIRE_WIRECLIENT_H_ diff --git a/third_party/dawn/src/include/dawn_wire/WireServer.h b/third_party/dawn/src/include/dawn_wire/WireServer.h index 084ab54e086..b3d1e62a1ff 100644 --- a/third_party/dawn/src/include/dawn_wire/WireServer.h +++ b/third_party/dawn/src/include/dawn_wire/WireServer.h @@ -19,25 +19,85 @@ #include "dawn_wire/Wire.h" +struct DawnProcTable; + namespace dawn_wire { namespace server { class Server; - } + class MemoryTransferService; + } // namespace server + + struct DAWN_WIRE_EXPORT WireServerDescriptor { + WGPUDevice device; + const DawnProcTable* procs; + CommandSerializer* serializer; + server::MemoryTransferService* memoryTransferService = nullptr; + }; class DAWN_WIRE_EXPORT WireServer : public CommandHandler { public: - WireServer(DawnDevice device, const DawnProcTable& procs, CommandSerializer* serializer); - ~WireServer(); + WireServer(const WireServerDescriptor& descriptor); + ~WireServer() override; - const char* HandleCommands(const char* commands, size_t size) override final; + const volatile char* HandleCommands(const volatile char* commands, + size_t size) override final; - bool InjectTexture(DawnTexture texture, uint32_t id, uint32_t generation); + bool InjectTexture(WGPUTexture texture, uint32_t id, uint32_t generation); private: std::unique_ptr mImpl; }; + namespace server { + class DAWN_WIRE_EXPORT MemoryTransferService { + public: + class ReadHandle; + class WriteHandle; + + virtual ~MemoryTransferService(); + + // Deserialize data to create Read/Write handles. These handles are for the client + // to Read/Write data. + virtual bool DeserializeReadHandle(const void* deserializePointer, + size_t deserializeSize, + ReadHandle** readHandle) = 0; + virtual bool DeserializeWriteHandle(const void* deserializePointer, + size_t deserializeSize, + WriteHandle** writeHandle) = 0; + + class DAWN_WIRE_EXPORT ReadHandle { + public: + // Get the required serialization size for SerializeInitialData + virtual size_t SerializeInitialDataSize(const void* data, size_t dataLength) = 0; + + // Initialize the handle data. + // Serialize into |serializePointer| so the client can update handle data. + virtual void SerializeInitialData(const void* data, + size_t dataLength, + void* serializePointer) = 0; + virtual ~ReadHandle(); + }; + + class DAWN_WIRE_EXPORT WriteHandle { + public: + // Set the target for writes from the client. DeserializeFlush should copy data + // into the target. + void SetTarget(void* data, size_t dataLength); + + // This function takes in the serialized result of + // client::MemoryTransferService::WriteHandle::SerializeFlush. + virtual bool DeserializeFlush(const void* deserializePointer, + size_t deserializeSize) = 0; + virtual ~WriteHandle(); + + protected: + void* mTargetData = nullptr; + size_t mDataLength = 0; + }; + }; + } // namespace server + } // namespace dawn_wire #endif // DAWNWIRE_WIRESERVER_H_ diff --git a/third_party/dawn/src/tests/BUILD.gn b/third_party/dawn/src/tests/BUILD.gn new file mode 100644 index 00000000000..06875e68d74 --- /dev/null +++ b/third_party/dawn/src/tests/BUILD.gn @@ -0,0 +1,494 @@ +# Copyright 2012 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../scripts/dawn_overrides_with_defaults.gni") + +import("//testing/test.gni") +import("${dawn_root}/generator/dawn_generator.gni") +import("${dawn_root}/scripts/dawn_features.gni") + +group("dawn_tests") { + testonly = true + deps = [ + ":dawn_end2end_tests", + ":dawn_perf_tests", + ":dawn_unittests", + ] +} + +############################################################################### +# Gtest Gmock - Handle building inside and outside of Chromium. +############################################################################### + +# When building outside of Chromium we need to define our own targets for GTest +# and GMock. However when compiling inside of Chromium we need to reuse the +# existing targets, both because Chromium has a special harness for swarming +# and because otherwise the "gn check" fails. + +if (!build_with_chromium) { + # When we aren't in Chromium we define out own targets based on the location + # of the googletest repo. + googletest_dir = dawn_googletest_dir + + config("gtest_config") { + include_dirs = [ + "${googletest_dir}/googletest", + "${googletest_dir}/googletest/include", + ] + } + + static_library("gtest") { + testonly = true + sources = [ "${googletest_dir}/googletest/src/gtest-all.cc" ] + public_configs = [ ":gtest_config" ] + } + + config("gmock_config") { + include_dirs = [ + "${googletest_dir}/googlemock", + "${googletest_dir}/googlemock/include", + "${googletest_dir}/googletest/include", + ] + } + + static_library("gmock") { + testonly = true + sources = [ "${googletest_dir}/googlemock/src/gmock-all.cc" ] + public_configs = [ ":gmock_config" ] + } + + group("gmock_and_gtest") { + testonly = true + public_deps = [ + ":gmock", + ":gtest", + ] + } +} else { + # When we are in Chromium we reuse its targets, and also add some deps that + # are needed to launch the test in swarming mode. + group("gmock_and_gtest") { + testonly = true + public_deps = [ + "//base", + "//base/test:test_support", + "//testing/gmock", + "//testing/gtest", + ] + } +} + +############################################################################### +# Wrapping of Chromium targets +############################################################################### + +# These targets are separated because they are Chromium sources files that +# can't use the dawn_internal config, otherwise Dawn's warning flags get +# applied while compiling a bunch of Chromium's //base (via header inclusion) +if (build_with_chromium) { + source_set("dawn_unittests_main") { + testonly = true + deps = [ ":gmock_and_gtest" ] + sources = [ "//gpu/dawn_unittests_main.cc" ] + } + source_set("dawn_end2end_tests_main") { + testonly = true + deps = [ ":gmock_and_gtest" ] + sources = [ "//gpu/dawn_end2end_tests_main.cc" ] + } + source_set("dawn_perf_tests_main") { + testonly = true + deps = [ ":gmock_and_gtest" ] + sources = [ "//gpu/dawn_perf_tests_main.cc" ] + } +} + +############################################################################### +# Dawn unittests +############################################################################### + +dawn_json_generator("mock_webgpu_gen") { + target = "mock_webgpu" + outputs = [ + "src/dawn/mock_webgpu.h", + "src/dawn/mock_webgpu.cpp", + ] +} + +test("dawn_unittests") { + configs += [ "${dawn_root}/src/common:dawn_internal" ] + + deps = [ + ":gmock_and_gtest", + ":mock_webgpu_gen", + "${dawn_root}/src/common", + "${dawn_root}/src/dawn:dawn_proc", + "${dawn_root}/src/dawn:dawncpp", + "${dawn_root}/src/dawn_native", + "${dawn_root}/src/dawn_native:dawn_native_sources", + "${dawn_root}/src/dawn_wire", + "${dawn_root}/src/utils:dawn_utils", + ] + + # Add internal dawn_native config for internal unittests. + configs += [ "${dawn_root}/src/dawn_native:dawn_native_internal" ] + + sources = get_target_outputs(":mock_webgpu_gen") + sources += [ + "${dawn_root}/src/dawn_wire/client/ClientMemoryTransferService_mock.cpp", + "${dawn_root}/src/dawn_wire/client/ClientMemoryTransferService_mock.h", + "${dawn_root}/src/dawn_wire/server/ServerMemoryTransferService_mock.cpp", + "${dawn_root}/src/dawn_wire/server/ServerMemoryTransferService_mock.h", + "MockCallback.h", + "unittests/BitSetIteratorTests.cpp", + "unittests/BuddyAllocatorTests.cpp", + "unittests/BuddyMemoryAllocatorTests.cpp", + "unittests/CommandAllocatorTests.cpp", + "unittests/EnumClassBitmasksTests.cpp", + "unittests/ErrorTests.cpp", + "unittests/ExtensionTests.cpp", + "unittests/GetProcAddressTests.cpp", + "unittests/ITypArrayTests.cpp", + "unittests/ITypBitsetTests.cpp", + "unittests/ITypSpanTests.cpp", + "unittests/ITypVectorTests.cpp", + "unittests/LinkedListTests.cpp", + "unittests/MathTests.cpp", + "unittests/ObjectBaseTests.cpp", + "unittests/PerStageTests.cpp", + "unittests/PlacementAllocatedTests.cpp", + "unittests/RefCountedTests.cpp", + "unittests/ResultTests.cpp", + "unittests/RingBufferAllocatorTests.cpp", + "unittests/SerialMapTests.cpp", + "unittests/SerialQueueTests.cpp", + "unittests/SlabAllocatorTests.cpp", + "unittests/StackContainerTests.cpp", + "unittests/SystemUtilsTests.cpp", + "unittests/ToBackendTests.cpp", + "unittests/TypedIntegerTests.cpp", + "unittests/validation/BindGroupValidationTests.cpp", + "unittests/validation/BufferValidationTests.cpp", + "unittests/validation/CommandBufferValidationTests.cpp", + "unittests/validation/ComputeIndirectValidationTests.cpp", + "unittests/validation/ComputeValidationTests.cpp", + "unittests/validation/CopyCommandsValidationTests.cpp", + "unittests/validation/DebugMarkerValidationTests.cpp", + "unittests/validation/DrawIndirectValidationTests.cpp", + "unittests/validation/DynamicStateCommandValidationTests.cpp", + "unittests/validation/ErrorScopeValidationTests.cpp", + "unittests/validation/FenceValidationTests.cpp", + "unittests/validation/GetBindGroupLayoutValidationTests.cpp", + "unittests/validation/IndexBufferValidationTests.cpp", + "unittests/validation/MinimumBufferSizeValidationTests.cpp", + "unittests/validation/QuerySetValidationTests.cpp", + "unittests/validation/QueueSubmitValidationTests.cpp", + "unittests/validation/QueueWriteTextureValidationTests.cpp", + "unittests/validation/RenderBundleValidationTests.cpp", + "unittests/validation/RenderPassDescriptorValidationTests.cpp", + "unittests/validation/RenderPipelineValidationTests.cpp", + "unittests/validation/ResourceUsageTrackingTests.cpp", + "unittests/validation/SamplerValidationTests.cpp", + "unittests/validation/ShaderModuleValidationTests.cpp", + "unittests/validation/StorageTextureValidationTests.cpp", + "unittests/validation/TextureSubresourceTests.cpp", + "unittests/validation/TextureValidationTests.cpp", + "unittests/validation/TextureViewValidationTests.cpp", + "unittests/validation/ToggleValidationTests.cpp", + "unittests/validation/ValidationTest.cpp", + "unittests/validation/ValidationTest.h", + "unittests/validation/VertexBufferValidationTests.cpp", + "unittests/validation/VertexStateValidationTests.cpp", + "unittests/wire/WireArgumentTests.cpp", + "unittests/wire/WireBasicTests.cpp", + "unittests/wire/WireBufferMappingTests.cpp", + "unittests/wire/WireDisconnectTests.cpp", + "unittests/wire/WireErrorCallbackTests.cpp", + "unittests/wire/WireExtensionTests.cpp", + "unittests/wire/WireFenceTests.cpp", + "unittests/wire/WireInjectTextureTests.cpp", + "unittests/wire/WireMemoryTransferServiceTests.cpp", + "unittests/wire/WireMultipleDeviceTests.cpp", + "unittests/wire/WireOptionalTests.cpp", + "unittests/wire/WireTest.cpp", + "unittests/wire/WireTest.h", + "unittests/wire/WireWGPUDevicePropertiesTests.cpp", + ] + + if (dawn_enable_d3d12) { + sources += [ "unittests/d3d12/CopySplitTests.cpp" ] + } + + # When building inside Chromium, use their gtest main function because it is + # needed to run in swarming correctly. + if (build_with_chromium) { + deps += [ ":dawn_unittests_main" ] + } else { + sources += [ "UnittestsMain.cpp" ] + } +} + +############################################################################### +# Dawn end2end tests targets +############################################################################### + +source_set("dawn_end2end_tests_sources") { + configs += [ "${dawn_root}/src/common:dawn_internal" ] + testonly = true + + deps = [ + ":gmock_and_gtest", + "${dawn_root}/src/common", + "${dawn_root}/src/dawn:dawn_proc", + "${dawn_root}/src/dawn:dawncpp", + "${dawn_root}/src/dawn_native", + "${dawn_root}/src/dawn_wire", + "${dawn_root}/src/utils:dawn_utils", + ] + + sources = [ + "DawnTest.h", + "end2end/BasicTests.cpp", + "end2end/BindGroupTests.cpp", + "end2end/BufferTests.cpp", + "end2end/BufferZeroInitTests.cpp", + "end2end/ClipSpaceTests.cpp", + "end2end/ColorStateTests.cpp", + "end2end/CompressedTextureFormatTests.cpp", + "end2end/ComputeCopyStorageBufferTests.cpp", + "end2end/ComputeIndirectTests.cpp", + "end2end/ComputeSharedMemoryTests.cpp", + "end2end/ComputeStorageBufferBarrierTests.cpp", + "end2end/CopyTests.cpp", + "end2end/CullingTests.cpp", + "end2end/DebugMarkerTests.cpp", + "end2end/DeprecatedAPITests.cpp", + "end2end/DepthSamplingTests.cpp", + "end2end/DepthStencilStateTests.cpp", + "end2end/DestroyTests.cpp", + "end2end/DeviceLostTests.cpp", + "end2end/DrawIndexedIndirectTests.cpp", + "end2end/DrawIndexedTests.cpp", + "end2end/DrawIndirectTests.cpp", + "end2end/DrawTests.cpp", + "end2end/DynamicBufferOffsetTests.cpp", + "end2end/FenceTests.cpp", + "end2end/GpuMemorySynchronizationTests.cpp", + "end2end/IndexFormatTests.cpp", + "end2end/MultisampledRenderingTests.cpp", + "end2end/MultisampledSamplingTests.cpp", + "end2end/NonzeroBufferCreationTests.cpp", + "end2end/NonzeroTextureCreationTests.cpp", + "end2end/ObjectCachingTests.cpp", + "end2end/OpArrayLengthTests.cpp", + "end2end/PipelineLayoutTests.cpp", + "end2end/PrimitiveTopologyTests.cpp", + "end2end/QueryTests.cpp", + "end2end/QueueTests.cpp", + "end2end/RenderBundleTests.cpp", + "end2end/RenderPassLoadOpTests.cpp", + "end2end/RenderPassTests.cpp", + "end2end/SamplerTests.cpp", + "end2end/ScissorTests.cpp", + "end2end/ShaderFloat16Tests.cpp", + "end2end/StorageTextureTests.cpp", + "end2end/SubresourceOutputAttachmentTests.cpp", + "end2end/TextureFormatTests.cpp", + "end2end/TextureSubresourceTests.cpp", + "end2end/TextureViewTests.cpp", + "end2end/TextureZeroInitTests.cpp", + "end2end/VertexFormatTests.cpp", + "end2end/VertexStateTests.cpp", + "end2end/ViewportOrientationTests.cpp", + "end2end/ViewportTests.cpp", + ] + + # Validation tests that need OS windows live in end2end tests. + sources += [ + "unittests/validation/ValidationTest.cpp", + "unittests/validation/ValidationTest.h", + ] + + libs = [] + + if (dawn_enable_d3d12) { + sources += [ "end2end/D3D12ResourceWrappingTests.cpp" ] + libs += [ + "d3d11.lib", + "dxgi.lib", + ] + } + + if (dawn_enable_metal) { + sources += [ "end2end/IOSurfaceWrappingTests.cpp" ] + frameworks = [ "IOSurface.framework" ] + } + + if (dawn_enable_opengl) { + assert(dawn_supports_glfw_for_windowing) + } + + if (dawn_supports_glfw_for_windowing) { + sources += [ + "end2end/SwapChainTests.cpp", + "end2end/SwapChainValidationTests.cpp", + "end2end/WindowSurfaceTests.cpp", + ] + deps += [ "${dawn_root}/src/utils:dawn_glfw" ] + } +} + +source_set("dawn_white_box_tests_sources") { + configs += [ "${dawn_root}/src/dawn_native:dawn_native_internal" ] + testonly = true + + deps = [ + ":gmock_and_gtest", + "${dawn_root}/src/common", + "${dawn_root}/src/dawn:dawn_proc", + "${dawn_root}/src/dawn:dawncpp", + "${dawn_root}/src/dawn_native:dawn_native_sources", + "${dawn_root}/src/dawn_wire", + "${dawn_root}/src/utils:dawn_utils", + + # Static because the tests both link against and have dawn_native + # sources. MSVC errors when both importing and exporting symbols. + "${dawn_root}/src/dawn_native:dawn_native_static", + ] + + sources = [ "DawnTest.h" ] + + if (dawn_enable_vulkan) { + deps += [ "${dawn_root}/third_party/khronos:vulkan_headers" ] + + if (is_chromeos) { + sources += [ "white_box/VulkanImageWrappingTestsDmaBuf.cpp" ] + } else if (is_linux) { + sources += [ "white_box/VulkanImageWrappingTestsOpaqueFD.cpp" ] + } + + if (dawn_enable_error_injection) { + sources += [ "white_box/VulkanErrorInjectorTests.cpp" ] + } + } + + sources += [ "white_box/InternalResourceUsageTests.cpp" ] + + if (dawn_enable_d3d12) { + sources += [ + "white_box/D3D12DescriptorHeapTests.cpp", + "white_box/D3D12ResidencyTests.cpp", + "white_box/D3D12SmallTextureTests.cpp", + ] + } + + if (dawn_enable_metal) { + sources += [ "white_box/MetalAutoreleasePoolTests.mm" ] + } + + if (dawn_enable_opengl) { + deps += [ "${dawn_root}/src/utils:dawn_glfw" ] + } + + libs = [] +} + +test("dawn_end2end_tests") { + configs += [ "${dawn_root}/src/common:dawn_internal" ] + + deps = [ + ":dawn_end2end_tests_sources", + ":dawn_white_box_tests_sources", + ":gmock_and_gtest", + "${dawn_root}/src/common", + "${dawn_root}/src/dawn:dawn_proc", + "${dawn_root}/src/dawn:dawncpp", + "${dawn_root}/src/dawn_native", + "${dawn_root}/src/dawn_wire", + "${dawn_root}/src/utils:dawn_utils", + ] + + sources = [ + "DawnTest.cpp", + "DawnTest.h", + ] + + libs = [] + + # When building inside Chromium, use their gtest main function because it is + # needed to run in swarming correctly. + if (build_with_chromium) { + deps += [ ":dawn_end2end_tests_main" ] + } else { + sources += [ "End2EndTestsMain.cpp" ] + } + + if (dawn_enable_opengl) { + deps += [ "${dawn_root}/src/utils:dawn_glfw" ] + } + + if (is_chromeos) { + libs += [ "gbm" ] + } +} + +############################################################################### +# Dawn perf tests +############################################################################### + +test("dawn_perf_tests") { + configs += [ "${dawn_root}/src/common:dawn_internal" ] + + deps = [ + ":gmock_and_gtest", + "${dawn_root}/src/common", + "${dawn_root}/src/dawn:dawn_proc", + "${dawn_root}/src/dawn:dawncpp", + "${dawn_root}/src/dawn_native", + "${dawn_root}/src/dawn_platform", + "${dawn_root}/src/dawn_wire", + "${dawn_root}/src/utils:dawn_utils", + ] + + sources = [ + "DawnTest.cpp", + "DawnTest.h", + "ParamGenerator.h", + "perf_tests/BufferUploadPerf.cpp", + "perf_tests/DawnPerfTest.cpp", + "perf_tests/DawnPerfTest.h", + "perf_tests/DawnPerfTestPlatform.cpp", + "perf_tests/DawnPerfTestPlatform.h", + "perf_tests/DrawCallPerf.cpp", + ] + + libs = [] + + # When building inside Chromium, use their gtest main function and the + # other perf test scaffolding in order to run in swarming correctly. + if (build_with_chromium) { + deps += [ ":dawn_perf_tests_main" ] + data_deps = [ "//testing:run_perf_test" ] + } else { + sources += [ "PerfTestsMain.cpp" ] + } + + if (dawn_enable_metal) { + frameworks = [ "IOSurface.framework" ] + } + + if (dawn_enable_opengl) { + deps += [ "${dawn_root}/src/utils:dawn_glfw" ] + } +} diff --git a/third_party/dawn/src/tests/DawnTest.cpp b/third_party/dawn/src/tests/DawnTest.cpp index c501cf0e2a5..d396e25b6a6 100644 --- a/third_party/dawn/src/tests/DawnTest.cpp +++ b/third_party/dawn/src/tests/DawnTest.cpp @@ -15,52 +15,59 @@ #include "tests/DawnTest.h" #include "common/Assert.h" -#include "common/Constants.h" +#include "common/GPUInfo.h" +#include "common/Log.h" #include "common/Math.h" #include "common/Platform.h" +#include "common/SystemUtils.h" +#include "dawn/dawn_proc.h" #include "dawn_native/DawnNative.h" #include "dawn_wire/WireClient.h" #include "dawn_wire/WireServer.h" -#include "utils/BackendBinding.h" -#include "utils/DawnHelpers.h" #include "utils/SystemUtils.h" #include "utils/TerribleCommandBuffer.h" +#include "utils/WGPUHelpers.h" #include +#include #include -#include +#include #include #include -#include "GLFW/glfw3.h" + +#ifdef DAWN_ENABLE_BACKEND_OPENGL +# include "GLFW/glfw3.h" +# include "dawn_native/OpenGLBackend.h" +#endif // DAWN_ENABLE_BACKEND_OPENGL namespace { - std::string ParamName(dawn_native::BackendType type) { + std::string ParamName(wgpu::BackendType type) { switch (type) { - case dawn_native::BackendType::D3D12: + case wgpu::BackendType::D3D12: return "D3D12"; - case dawn_native::BackendType::Metal: + case wgpu::BackendType::Metal: return "Metal"; - case dawn_native::BackendType::Null: + case wgpu::BackendType::Null: return "Null"; - case dawn_native::BackendType::OpenGL: + case wgpu::BackendType::OpenGL: return "OpenGL"; - case dawn_native::BackendType::Vulkan: + case wgpu::BackendType::Vulkan: return "Vulkan"; default: UNREACHABLE(); } } - const char* DeviceTypeName(dawn_native::DeviceType type) { + const char* AdapterTypeName(wgpu::AdapterType type) { switch (type) { - case dawn_native::DeviceType::DiscreteGPU: + case wgpu::AdapterType::DiscreteGPU: return "Discrete GPU"; - case dawn_native::DeviceType::IntegratedGPU: + case wgpu::AdapterType::IntegratedGPU: return "Integrated GPU"; - case dawn_native::DeviceType::CPU: + case wgpu::AdapterType::CPU: return "CPU"; - case dawn_native::DeviceType::Unknown: + case wgpu::AdapterType::Unknown: return "Unknown"; default: UNREACHABLE(); @@ -68,7 +75,7 @@ namespace { } struct MapReadUserdata { - DawnTest* test; + DawnTestBase* test; size_t slot; }; @@ -76,18 +83,92 @@ namespace { } // namespace -const DawnTestParam D3D12Backend(dawn_native::BackendType::D3D12); -const DawnTestParam MetalBackend(dawn_native::BackendType::Metal); -const DawnTestParam OpenGLBackend(dawn_native::BackendType::OpenGL); -const DawnTestParam VulkanBackend(dawn_native::BackendType::Vulkan); +const RGBA8 RGBA8::kZero = RGBA8(0, 0, 0, 0); +const RGBA8 RGBA8::kBlack = RGBA8(0, 0, 0, 255); +const RGBA8 RGBA8::kRed = RGBA8(255, 0, 0, 255); +const RGBA8 RGBA8::kGreen = RGBA8(0, 255, 0, 255); +const RGBA8 RGBA8::kBlue = RGBA8(0, 0, 255, 255); +const RGBA8 RGBA8::kYellow = RGBA8(255, 255, 0, 255); +const RGBA8 RGBA8::kWhite = RGBA8(255, 255, 255, 255); + +BackendTestConfig::BackendTestConfig(wgpu::BackendType backendType, + std::initializer_list forceEnabledWorkarounds, + std::initializer_list forceDisabledWorkarounds) + : backendType(backendType), + forceEnabledWorkarounds(forceEnabledWorkarounds), + forceDisabledWorkarounds(forceDisabledWorkarounds) { +} -DawnTestParam ForceWorkarounds(const DawnTestParam& originParam, - std::initializer_list forceEnabledWorkarounds, +BackendTestConfig D3D12Backend(std::initializer_list forceEnabledWorkarounds, std::initializer_list forceDisabledWorkarounds) { - DawnTestParam newTestParam = originParam; - newTestParam.forceEnabledWorkarounds = forceEnabledWorkarounds; - newTestParam.forceDisabledWorkarounds = forceDisabledWorkarounds; - return newTestParam; + return BackendTestConfig(wgpu::BackendType::D3D12, forceEnabledWorkarounds, + forceDisabledWorkarounds); +} + +BackendTestConfig MetalBackend(std::initializer_list forceEnabledWorkarounds, + std::initializer_list forceDisabledWorkarounds) { + return BackendTestConfig(wgpu::BackendType::Metal, forceEnabledWorkarounds, + forceDisabledWorkarounds); +} + +BackendTestConfig NullBackend(std::initializer_list forceEnabledWorkarounds, + std::initializer_list forceDisabledWorkarounds) { + return BackendTestConfig(wgpu::BackendType::Null, forceEnabledWorkarounds, + forceDisabledWorkarounds); +} + +BackendTestConfig OpenGLBackend(std::initializer_list forceEnabledWorkarounds, + std::initializer_list forceDisabledWorkarounds) { + return BackendTestConfig(wgpu::BackendType::OpenGL, forceEnabledWorkarounds, + forceDisabledWorkarounds); +} + +BackendTestConfig VulkanBackend(std::initializer_list forceEnabledWorkarounds, + std::initializer_list forceDisabledWorkarounds) { + return BackendTestConfig(wgpu::BackendType::Vulkan, forceEnabledWorkarounds, + forceDisabledWorkarounds); +} + +TestAdapterProperties::TestAdapterProperties(const wgpu::AdapterProperties& properties, + bool selected) + : wgpu::AdapterProperties(properties), adapterName(properties.name), selected(selected) { +} + +AdapterTestParam::AdapterTestParam(const BackendTestConfig& config, + const TestAdapterProperties& adapterProperties) + : adapterProperties(adapterProperties), + forceEnabledWorkarounds(config.forceEnabledWorkarounds), + forceDisabledWorkarounds(config.forceDisabledWorkarounds) { +} + +std::ostream& operator<<(std::ostream& os, const AdapterTestParam& param) { + // Sanitize the adapter name for GoogleTest + std::string sanitizedName = + std::regex_replace(param.adapterProperties.adapterName, std::regex("[^a-zA-Z0-9]+"), "_"); + + // Strip trailing underscores, if any. + if (sanitizedName.back() == '_') { + sanitizedName.back() = '\0'; + } + + os << ParamName(param.adapterProperties.backendType) << "_" << sanitizedName.c_str(); + + // In a Windows Remote Desktop session there are two adapters named "Microsoft Basic Render + // Driver" with different adapter types. We must differentiate them to avoid any tests using the + // same name. + if (param.adapterProperties.deviceID == 0x008C) { + std::string adapterType = AdapterTypeName(param.adapterProperties.adapterType); + std::replace(adapterType.begin(), adapterType.end(), ' ', '_'); + os << "_" << adapterType; + } + + for (const char* forceEnabledWorkaround : param.forceEnabledWorkarounds) { + os << "__e_" << forceEnabledWorkaround; + } + for (const char* forceDisabledWorkaround : param.forceDisabledWorkarounds) { + os << "__d_" << forceDisabledWorkaround; + } + return os; } // Implementation of DawnTestEnvironment @@ -97,7 +178,29 @@ void InitDawnEnd2EndTestEnvironment(int argc, char** argv) { testing::AddGlobalTestEnvironment(gTestEnv); } +// static +void DawnTestEnvironment::SetEnvironment(DawnTestEnvironment* env) { + gTestEnv = env; +} + DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) { + ParseArgs(argc, argv); + + // Create a temporary instance to select available and preferred adapters. This is done before + // test instantiation so GetAvailableAdapterTestParamsForBackends can generate test + // parameterizations all selected adapters. We drop the instance at the end of this function + // because the Vulkan validation layers use static global mutexes which behave badly when + // Chromium's test launcher forks the test process. The instance will be recreated on test + // environment setup. + std::unique_ptr instance = CreateInstanceAndDiscoverAdapters(); + ASSERT(instance); + + SelectPreferredAdapterProperties(instance.get()); + PrintTestConfigurationAndAdapterInfo(); +} + +void DawnTestEnvironment::ParseArgs(int argc, char** argv) { + size_t argLen = 0; // Set when parsing --arg=X arguments for (int i = 1; i < argc; ++i) { if (strcmp("-w", argv[i]) == 0 || strcmp("--use-wire", argv[i]) == 0) { mUseWire = true; @@ -109,101 +212,382 @@ DawnTestEnvironment::DawnTestEnvironment(int argc, char** argv) { continue; } + if (strcmp("-c", argv[i]) == 0 || strcmp("--begin-capture-on-startup", argv[i]) == 0) { + mBeginCaptureOnStartup = true; + continue; + } + + if (strcmp("--skip-validation", argv[i]) == 0) { + mSkipDawnValidation = true; + continue; + } + + if (strcmp("--use-spvc", argv[i]) == 0) { + if (mSpvcFlagSeen) { + dawn::WarningLog() << "Multiple flags passed in that force whether or not to use " + "the spvc. This may lead to unexpected behaviour."; + } + ASSERT(!mSpvcFlagSeen); + + mUseSpvc = true; + mSpvcFlagSeen = true; + continue; + } + + if (strcmp("--no-use-spvc", argv[i]) == 0) { + if (mSpvcFlagSeen) { + dawn::WarningLog() << "Multiple flags passed in that force whether or not to use " + "the spvc. This may lead to unexpected behaviour."; + } + ASSERT(!mSpvcFlagSeen); + + mUseSpvc = false; + mSpvcFlagSeen = true; + continue; + } + + if (strcmp("--use-spvc-parser", argv[i]) == 0) { + if (mSpvcParserFlagSeen) { + dawn::WarningLog() << "Multiple flags passed in that force whether or not to use " + "the spvc parser. This may cause unexpected behaviour."; + } + ASSERT(!mSpvcParserFlagSeen); + + if (!mUseSpvc) { + if (mSpvcFlagSeen) { + dawn::ErrorLog() + << "Overriding force disabling of spvc since it is required for using the " + "spvc parser. This indicates a likely misconfiguration."; + } else { + dawn::InfoLog() + << "Enabling spvc since it is required for using the spvc parser."; + } + ASSERT(!mSpvcFlagSeen); + } + + mUseSpvc = true; // It's impossible to use the spvc parser without using spvc, so + // turning on mUseSpvc implicitly. + mUseSpvcParser = true; + mSpvcParserFlagSeen = true; + continue; + } + + if (strcmp("--no-use-spvc-parser", argv[i]) == 0) { + if (mSpvcParserFlagSeen) { + dawn::WarningLog() << "Multiple flags passed in that force whether or not to use " + "the spvc parser. This may cause unexpected behaviour."; + } + ASSERT(!mSpvcParserFlagSeen); + + // Intentionally not changing mUseSpvc, since the dependency is one-way. This will + // not correctly handle the case where spvc is off by default, then there is a spvc + // parser on flag followed by a off flag, but that is already being indicated as a + // misuse. + mUseSpvcParser = false; + mSpvcParserFlagSeen = true; + continue; + } + + constexpr const char kVendorIdFilterArg[] = "--adapter-vendor-id="; + argLen = sizeof(kVendorIdFilterArg) - 1; + if (strncmp(argv[i], kVendorIdFilterArg, argLen) == 0) { + const char* vendorIdFilter = argv[i] + argLen; + if (vendorIdFilter[0] != '\0') { + mVendorIdFilter = strtoul(vendorIdFilter, nullptr, 16); + // Set filter flag if vendor id is non-zero. + mHasVendorIdFilter = mVendorIdFilter != 0; + } + continue; + } + + constexpr const char kExclusiveDeviceTypePreferenceArg[] = + "--exclusive-device-type-preference="; + argLen = sizeof(kExclusiveDeviceTypePreferenceArg) - 1; + if (strncmp(argv[i], kExclusiveDeviceTypePreferenceArg, argLen) == 0) { + const char* preference = argv[i] + argLen; + if (preference[0] != '\0') { + std::istringstream ss(preference); + std::string type; + while (std::getline(ss, type, ',')) { + if (strcmp(type.c_str(), "discrete") == 0) { + mDevicePreferences.push_back(dawn_native::DeviceType::DiscreteGPU); + } else if (strcmp(type.c_str(), "integrated") == 0) { + mDevicePreferences.push_back(dawn_native::DeviceType::IntegratedGPU); + } else if (strcmp(type.c_str(), "cpu") == 0) { + mDevicePreferences.push_back(dawn_native::DeviceType::CPU); + } else { + dawn::ErrorLog() << "Invalid device type preference: " << type; + UNREACHABLE(); + } + } + } + } + + constexpr const char kWireTraceDirArg[] = "--wire-trace-dir="; + argLen = sizeof(kWireTraceDirArg) - 1; + if (strncmp(argv[i], kWireTraceDirArg, argLen) == 0) { + const char* wireTraceDir = argv[i] + argLen; + if (wireTraceDir[0] != '\0') { + const char* sep = GetPathSeparator(); + mWireTraceDir = wireTraceDir; + if (mWireTraceDir.back() != *sep) { + mWireTraceDir += sep; + } + } + continue; + } + if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { - std::cout << "\n\nUsage: " << argv[0] - << " [GTEST_FLAGS...] [-w] [--enable-validation-layers]\n"; - std::cout << " -w, --use-wire: Run the tests through the wire (defaults to no wire)\n"; - std::cout << " -d, --enable-backend-validation: Enable backend validation (defaults" - << " to disabled)\n"; - std::cout << std::endl; + dawn::InfoLog() + << "\n\nUsage: " << argv[0] + << " [GTEST_FLAGS...] [-w] [-d] [-c] [--adapter-vendor-id=x]" + " [--exclusive-device-type-preference=integrated,cpu,discrete]\n" + " -w, --use-wire: Run the tests through the wire (defaults to no wire)\n" + " -d, --enable-backend-validation: Enable backend validation (defaults" + " to disabled)\n" + " -c, --begin-capture-on-startup: Begin debug capture on startup " + "(defaults to no capture)\n" + " --skip-validation: Skip Dawn validation\n" + " --use-spvc: Use spvc for accessing spirv-cross\n" + " --no-use-spvc: Do not use spvc for accessing spirv-cross\n" + " --use-spvc-parser: Use spvc's spir-v parsing insteads of spirv-cross's, " + "implies --use-spvc\n" + " --no-use-spvc-parser: Do no use spvc's spir-v parsing insteads of " + "spirv-cross's\n" + " --adapter-vendor-id: Select adapter by vendor id to run end2end tests" + "on multi-GPU systems \n" + " --exclusive-device-type-preference: Comma-delimited list of preferred device " + "types. For each backend, tests will run only on adapters that match the first " + "available device type\n"; continue; } } } -void DawnTestEnvironment::SetUp() { - ASSERT_TRUE(glfwInit()); +std::unique_ptr DawnTestEnvironment::CreateInstanceAndDiscoverAdapters() + const { + auto instance = std::make_unique(); + instance->EnableBackendValidation(mEnableBackendValidation); + instance->EnableBeginCaptureOnStartup(mBeginCaptureOnStartup); - mInstance = std::make_unique(); - mInstance->EnableBackendValidation(mEnableBackendValidation); + instance->DiscoverDefaultAdapters(); - static constexpr dawn_native::BackendType kAllBackends[] = { - dawn_native::BackendType::D3D12, - dawn_native::BackendType::Metal, - dawn_native::BackendType::OpenGL, - dawn_native::BackendType::Vulkan, - }; +#ifdef DAWN_ENABLE_BACKEND_OPENGL + if (!glfwInit()) { + return instance; + } + glfwDefaultWindowHints(); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - // Create a test window for each backend and discover an adapter using it. - for (dawn_native::BackendType backend : kAllBackends) { - if (detail::IsBackendAvailable(backend)) { - CreateBackendWindow(backend); - utils::DiscoverAdapter(mInstance.get(), mWindows[backend], backend); + std::string windowName = "Dawn OpenGL test window"; + GLFWwindow* window = glfwCreateWindow(400, 400, windowName.c_str(), nullptr, nullptr); + + glfwMakeContextCurrent(window); + dawn_native::opengl::AdapterDiscoveryOptions adapterOptions; + adapterOptions.getProc = reinterpret_cast(glfwGetProcAddress); + instance->DiscoverAdapters(&adapterOptions); +#endif // DAWN_ENABLE_BACKEND_OPENGL + + return instance; +} + +void DawnTestEnvironment::SelectPreferredAdapterProperties(const dawn_native::Instance* instance) { + // Get the first available preferred device type. + dawn_native::DeviceType preferredDeviceType = static_cast(-1); + bool hasDevicePreference = false; + for (dawn_native::DeviceType devicePreference : mDevicePreferences) { + for (const dawn_native::Adapter& adapter : instance->GetAdapters()) { + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); + + if (adapter.GetDeviceType() == devicePreference) { + preferredDeviceType = devicePreference; + hasDevicePreference = true; + break; + } + } + if (hasDevicePreference) { + break; } } - std::cout << "Testing configuration\n"; - std::cout << "---------------------\n"; - std::cout << "UseWire: " << (mUseWire ? "true" : "false") << "\n"; - std::cout << "EnableBackendValidation: " << (mEnableBackendValidation ? "true" : "false") - << "\n"; - std::cout << "\n"; + for (const dawn_native::Adapter& adapter : instance->GetAdapters()) { + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); - // Preparing for outputting hex numbers - std::cout << std::showbase << std::hex << std::setfill('0') << std::setw(4); + // The adapter is selected if: + bool selected = false; + if (mHasVendorIdFilter) { + // It matches the vendor id, if present. + selected = mVendorIdFilter == properties.vendorID; - std::cout << "System adapters: \n"; - for (const dawn_native::Adapter& adapter : mInstance->GetAdapters()) { - const dawn_native::PCIInfo& pci = adapter.GetPCIInfo(); + if (!mDevicePreferences.empty()) { + dawn::WarningLog() << "Vendor ID filter provided. Ignoring device type preference."; + } + } else if (hasDevicePreference) { + // There is a device preference and: + selected = + // The device type matches the first available preferred type for that backend, if + // present. + (adapter.GetDeviceType() == preferredDeviceType) || + // Always select Unknown OpenGL adapters if we don't want a CPU adapter. + // OpenGL will usually be unknown because we can't query the device type. + // If we ever have Swiftshader GL (unlikely), we could set the DeviceType properly. + (preferredDeviceType != dawn_native::DeviceType::CPU && + adapter.GetDeviceType() == dawn_native::DeviceType::Unknown && + properties.backendType == wgpu::BackendType::OpenGL) || + // Always select the Null backend. There are few tests on this backend, and they run + // quickly. This is temporary as to not lose coverage. We can group it with + // Swiftshader as a CPU adapter when we have Swiftshader tests. + (properties.backendType == wgpu::BackendType::Null); + } else { + // No vendor id or device preference was provided (select all). + selected = true; + } + + mAdapterProperties.emplace_back(properties, selected); + } +} +std::vector DawnTestEnvironment::GetAvailableAdapterTestParamsForBackends( + const BackendTestConfig* params, + size_t numParams) { + std::vector testParams; + for (size_t i = 0; i < numParams; ++i) { + for (const auto& adapterProperties : mAdapterProperties) { + if (params[i].backendType == adapterProperties.backendType && + adapterProperties.selected) { + testParams.push_back(AdapterTestParam(params[i], adapterProperties)); + } + } + } + return testParams; +} + +void DawnTestEnvironment::PrintTestConfigurationAndAdapterInfo() const { + dawn::LogMessage log = dawn::InfoLog(); + log << "Testing configuration\n" + "---------------------\n" + "UseWire: " + << (mUseWire ? "true" : "false") + << "\n" + "EnableBackendValidation: " + << (mEnableBackendValidation ? "true" : "false") + << "\n" + "SkipDawnValidation: " + << (mSkipDawnValidation ? "true" : "false") + << "\n" + "UseSpvc: " + << (mUseSpvc ? "true" : "false") + << "\n" + "UseSpvcParser: " + << (mUseSpvcParser ? "true" : "false") + << "\n" + "BeginCaptureOnStartup: " + << (mBeginCaptureOnStartup ? "true" : "false") + << "\n" + "\n" + << "System adapters: \n"; + + for (const TestAdapterProperties& properties : mAdapterProperties) { std::ostringstream vendorId; std::ostringstream deviceId; vendorId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4) - << pci.vendorId; + << properties.vendorID; deviceId << std::setfill('0') << std::uppercase << std::internal << std::hex << std::setw(4) - << pci.deviceId; + << properties.deviceID; + + // Preparing for outputting hex numbers + log << std::showbase << std::hex << std::setfill('0') << std::setw(4) - std::cout << " - \"" << pci.name << "\"\n"; - std::cout << " type: " << DeviceTypeName(adapter.GetDeviceType()) - << ", backend: " << ParamName(adapter.GetBackendType()) << "\n"; - std::cout << " vendorId: 0x" << vendorId.str() << ", deviceId: 0x" << deviceId.str() - << "\n"; + << " - \"" << properties.adapterName << "\"\n" + << " type: " << AdapterTypeName(properties.adapterType) + << ", backend: " << ParamName(properties.backendType) << "\n" + << " vendorId: 0x" << vendorId.str() << ", deviceId: 0x" << deviceId.str() + << (properties.selected ? " [Selected]" : "") << "\n"; } - std::cout << std::endl; +} + +void DawnTestEnvironment::SetUp() { + mInstance = CreateInstanceAndDiscoverAdapters(); + ASSERT(mInstance); +} + +void DawnTestEnvironment::TearDown() { + // When Vulkan validation layers are enabled, it's unsafe to call Vulkan APIs in the destructor + // of a static/global variable, so the instance must be manually released beforehand. + mInstance.reset(); } bool DawnTestEnvironment::UsesWire() const { return mUseWire; } +bool DawnTestEnvironment::IsBackendValidationEnabled() const { + return mEnableBackendValidation; +} + +bool DawnTestEnvironment::IsDawnValidationSkipped() const { + return mSkipDawnValidation; +} + +bool DawnTestEnvironment::IsSpvcBeingUsed() const { + return mUseSpvc; +} + +bool DawnTestEnvironment::IsSpvcParserBeingUsed() const { + return mUseSpvcParser; +} + dawn_native::Instance* DawnTestEnvironment::GetInstance() const { return mInstance.get(); } -GLFWwindow* DawnTestEnvironment::GetWindowForBackend(dawn_native::BackendType type) const { - return mWindows.at(type); +bool DawnTestEnvironment::HasVendorIdFilter() const { + return mHasVendorIdFilter; } -void DawnTestEnvironment::CreateBackendWindow(dawn_native::BackendType type) { - glfwDefaultWindowHints(); - utils::SetupGLFWWindowHintsForBackend(type); - - std::string windowName = "Dawn " + ParamName(type) + " test window"; - GLFWwindow* window = glfwCreateWindow(400, 400, windowName.c_str(), nullptr, nullptr); +uint32_t DawnTestEnvironment::GetVendorIdFilter() const { + return mVendorIdFilter; +} - mWindows[type] = window; +const char* DawnTestEnvironment::GetWireTraceDir() const { + if (mWireTraceDir.length() == 0) { + return nullptr; + } + return mWireTraceDir.c_str(); } +class WireServerTraceLayer : public dawn_wire::CommandHandler { + public: + WireServerTraceLayer(const char* file, dawn_wire::CommandHandler* handler) + : dawn_wire::CommandHandler(), mHandler(handler) { + mFile.open(file, std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); + } + + const volatile char* HandleCommands(const volatile char* commands, size_t size) override { + mFile.write(const_cast(commands), size); + return mHandler->HandleCommands(commands, size); + } + + private: + dawn_wire::CommandHandler* mHandler; + std::ofstream mFile; +}; + // Implementation of DawnTest -DawnTest::DawnTest() = default; +DawnTestBase::DawnTestBase(const AdapterTestParam& param) : mParam(param) { +} -DawnTest::~DawnTest() { +DawnTestBase::~DawnTestBase() { // We need to destroy child objects before the Device mReadbackSlots.clear(); - queue = dawn::Queue(); - swapchain = dawn::SwapChain(); - device = dawn::Device(); + queue = wgpu::Queue(); + device = wgpu::Device(); mWireClient = nullptr; mWireServer = nullptr; @@ -211,50 +595,63 @@ DawnTest::~DawnTest() { backendProcs.deviceRelease(backendDevice); } - dawnSetProcs(nullptr); + dawnProcSetProcs(nullptr); +} + +bool DawnTestBase::IsD3D12() const { + return mParam.adapterProperties.backendType == wgpu::BackendType::D3D12; +} + +bool DawnTestBase::IsMetal() const { + return mParam.adapterProperties.backendType == wgpu::BackendType::Metal; } -bool DawnTest::IsD3D12() const { - return GetParam().backendType == dawn_native::BackendType::D3D12; +bool DawnTestBase::IsNull() const { + return mParam.adapterProperties.backendType == wgpu::BackendType::Null; } -bool DawnTest::IsMetal() const { - return GetParam().backendType == dawn_native::BackendType::Metal; +bool DawnTestBase::IsOpenGL() const { + return mParam.adapterProperties.backendType == wgpu::BackendType::OpenGL; } -bool DawnTest::IsOpenGL() const { - return GetParam().backendType == dawn_native::BackendType::OpenGL; +bool DawnTestBase::IsVulkan() const { + return mParam.adapterProperties.backendType == wgpu::BackendType::Vulkan; } -bool DawnTest::IsVulkan() const { - return GetParam().backendType == dawn_native::BackendType::Vulkan; +bool DawnTestBase::IsAMD() const { + return gpu_info::IsAMD(mParam.adapterProperties.vendorID); } -bool DawnTest::IsAMD() const { - return mPCIInfo.vendorId == kVendorID_AMD; +bool DawnTestBase::IsARM() const { + return gpu_info::IsARM(mParam.adapterProperties.vendorID); } -bool DawnTest::IsARM() const { - return mPCIInfo.vendorId == kVendorID_ARM; +bool DawnTestBase::IsImgTec() const { + return gpu_info::IsImgTec(mParam.adapterProperties.vendorID); } -bool DawnTest::IsImgTec() const { - return mPCIInfo.vendorId == kVendorID_ImgTec; +bool DawnTestBase::IsIntel() const { + return gpu_info::IsIntel(mParam.adapterProperties.vendorID); } -bool DawnTest::IsIntel() const { - return mPCIInfo.vendorId == kVendorID_Intel; +bool DawnTestBase::IsNvidia() const { + return gpu_info::IsNvidia(mParam.adapterProperties.vendorID); } -bool DawnTest::IsNvidia() const { - return mPCIInfo.vendorId == kVendorID_Nvidia; +bool DawnTestBase::IsQualcomm() const { + return gpu_info::IsQualcomm(mParam.adapterProperties.vendorID); } -bool DawnTest::IsQualcomm() const { - return mPCIInfo.vendorId == kVendorID_Qualcomm; +bool DawnTestBase::IsSwiftshader() const { + return gpu_info::IsSwiftshader(mParam.adapterProperties.vendorID, + mParam.adapterProperties.deviceID); } -bool DawnTest::IsWindows() const { +bool DawnTestBase::IsWARP() const { + return gpu_info::IsWARP(mParam.adapterProperties.vendorID, mParam.adapterProperties.deviceID); +} + +bool DawnTestBase::IsWindows() const { #ifdef DAWN_PLATFORM_WINDOWS return true; #else @@ -262,7 +659,7 @@ bool DawnTest::IsWindows() const { #endif } -bool DawnTest::IsLinux() const { +bool DawnTestBase::IsLinux() const { #ifdef DAWN_PLATFORM_LINUX return true; #else @@ -270,7 +667,7 @@ bool DawnTest::IsLinux() const { #endif } -bool DawnTest::IsMacOS() const { +bool DawnTestBase::IsMacOS() const { #ifdef DAWN_PLATFORM_APPLE return true; #else @@ -278,70 +675,172 @@ bool DawnTest::IsMacOS() const { #endif } -bool DawnTest::UsesWire() const { +bool DawnTestBase::UsesWire() const { return gTestEnv->UsesWire(); } -void DawnTest::SetUp() { - // Get an adapter for the backend to use, and create the device. - dawn_native::Adapter backendAdapter; - const dawn_native::BackendType backendType = GetParam().backendType; - { - dawn_native::Instance* instance = gTestEnv->GetInstance(); - std::vector adapters = instance->GetAdapters(); - - for (const dawn_native::Adapter& adapter : adapters) { - if (adapter.GetBackendType() == backendType) { - backendAdapter = adapter; - // On Metal, select the last adapter so that the discrete GPU is tested on - // multi-GPU systems. - // TODO(cwallez@chromium.org): Replace this with command line arguments requesting - // a specific device / vendor ID once the macOS 10.13 SDK is rolled and correct - // PCI info collection is implemented on Metal. - if (backendType != dawn_native::BackendType::Metal) { - break; - } - } - } +bool DawnTestBase::IsBackendValidationEnabled() const { + return gTestEnv->IsBackendValidationEnabled(); +} + +bool DawnTestBase::IsDawnValidationSkipped() const { + return gTestEnv->IsDawnValidationSkipped(); +} + +bool DawnTestBase::IsSpvcBeingUsed() const { + return gTestEnv->IsSpvcBeingUsed(); +} + +bool DawnTestBase::IsSpvcParserBeingUsed() const { + return gTestEnv->IsSpvcParserBeingUsed(); +} + +bool DawnTestBase::IsAsan() const { +#if defined(ADDRESS_SANITIZER) + return true; +#else + return false; +#endif +} + +bool DawnTestBase::HasVendorIdFilter() const { + return gTestEnv->HasVendorIdFilter(); +} + +uint32_t DawnTestBase::GetVendorIdFilter() const { + return gTestEnv->GetVendorIdFilter(); +} + +wgpu::Instance DawnTestBase::GetInstance() const { + return gTestEnv->GetInstance()->Get(); +} + +dawn_native::Adapter DawnTestBase::GetAdapter() const { + return mBackendAdapter; +} + +std::vector DawnTestBase::GetRequiredExtensions() { + return {}; +} + +const wgpu::AdapterProperties& DawnTestBase::GetAdapterProperties() const { + return mParam.adapterProperties; +} - ASSERT(backendAdapter); +bool DawnTestBase::SupportsExtensions(const std::vector& extensions) { + ASSERT(mBackendAdapter); + std::set supportedExtensionsSet; + for (const char* supportedExtensionName : mBackendAdapter.GetSupportedExtensions()) { + supportedExtensionsSet.insert(supportedExtensionName); } - mPCIInfo = backendAdapter.GetPCIInfo(); + for (const char* extensionName : extensions) { + if (supportedExtensionsSet.find(extensionName) == supportedExtensionsSet.end()) { + return false; + } + } - for (const char* forceEnabledWorkaround : GetParam().forceEnabledWorkarounds) { + return true; +} + +void DawnTestBase::SetUp() { + { + // Find the adapter that exactly matches our adapter properties. + const auto& adapters = gTestEnv->GetInstance()->GetAdapters(); + const auto& it = std::find_if( + adapters.begin(), adapters.end(), [&](const dawn_native::Adapter& adapter) { + wgpu::AdapterProperties properties; + adapter.GetProperties(&properties); + + return (mParam.adapterProperties.selected && + properties.deviceID == mParam.adapterProperties.deviceID && + properties.vendorID == mParam.adapterProperties.vendorID && + properties.adapterType == mParam.adapterProperties.adapterType && + properties.backendType == mParam.adapterProperties.backendType && + strcmp(properties.name, mParam.adapterProperties.adapterName.c_str()) == 0); + }); + ASSERT(it != adapters.end()); + mBackendAdapter = *it; + } + + // Create the device from the adapter + for (const char* forceEnabledWorkaround : mParam.forceEnabledWorkarounds) { ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceEnabledWorkaround) != nullptr); } - for (const char* forceDisabledWorkaround : GetParam().forceDisabledWorkarounds) { + for (const char* forceDisabledWorkaround : mParam.forceDisabledWorkarounds) { ASSERT(gTestEnv->GetInstance()->GetToggleInfo(forceDisabledWorkaround) != nullptr); } dawn_native::DeviceDescriptor deviceDescriptor; - deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds; - deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds; - backendDevice = backendAdapter.CreateDevice(&deviceDescriptor); + deviceDescriptor.forceEnabledToggles = mParam.forceEnabledWorkarounds; + deviceDescriptor.forceDisabledToggles = mParam.forceDisabledWorkarounds; + deviceDescriptor.requiredExtensions = GetRequiredExtensions(); + + static constexpr char kSkipValidationToggle[] = "skip_validation"; + if (gTestEnv->IsDawnValidationSkipped()) { + ASSERT(gTestEnv->GetInstance()->GetToggleInfo(kSkipValidationToggle) != nullptr); + deviceDescriptor.forceEnabledToggles.push_back(kSkipValidationToggle); + } - backendProcs = dawn_native::GetProcs(); + static constexpr char kUseSpvcToggle[] = "use_spvc"; + static constexpr char kUseSpvcParserToggle[] = "use_spvc_parser"; + if (gTestEnv->IsSpvcBeingUsed()) { + ASSERT(gTestEnv->GetInstance()->GetToggleInfo(kUseSpvcToggle) != nullptr); + deviceDescriptor.forceEnabledToggles.push_back(kUseSpvcToggle); - // Get the test window and create the device using it (esp. for OpenGL) - GLFWwindow* testWindow = gTestEnv->GetWindowForBackend(backendType); - DAWN_ASSERT(testWindow != nullptr); - mBinding.reset(utils::CreateBinding(backendType, testWindow, backendDevice)); - DAWN_ASSERT(mBinding != nullptr); + if (gTestEnv->IsSpvcParserBeingUsed()) { + ASSERT(gTestEnv->GetInstance()->GetToggleInfo(kUseSpvcParserToggle) != nullptr); + deviceDescriptor.forceEnabledToggles.push_back(kUseSpvcParserToggle); + } + + } else { + // Turning on spvc parser should always turn on spvc. + ASSERT(!gTestEnv->IsSpvcParserBeingUsed()); + ASSERT(gTestEnv->GetInstance()->GetToggleInfo(kUseSpvcToggle) != nullptr); + deviceDescriptor.forceDisabledToggles.push_back(kUseSpvcToggle); + } + + backendDevice = mBackendAdapter.CreateDevice(&deviceDescriptor); + ASSERT_NE(nullptr, backendDevice); + + backendProcs = dawn_native::GetProcs(); // Choose whether to use the backend procs and devices directly, or set up the wire. - DawnDevice cDevice = nullptr; + WGPUDevice cDevice = nullptr; DawnProcTable procs; if (gTestEnv->UsesWire()) { mC2sBuf = std::make_unique(); mS2cBuf = std::make_unique(); - mWireServer.reset(new dawn_wire::WireServer(backendDevice, backendProcs, mS2cBuf.get())); + dawn_wire::WireServerDescriptor serverDesc = {}; + serverDesc.device = backendDevice; + serverDesc.procs = &backendProcs; + serverDesc.serializer = mS2cBuf.get(); + + mWireServer.reset(new dawn_wire::WireServer(serverDesc)); mC2sBuf->SetHandler(mWireServer.get()); - mWireClient.reset(new dawn_wire::WireClient(mC2sBuf.get())); - DawnDevice clientDevice = mWireClient->GetDevice(); - DawnProcTable clientProcs = mWireClient->GetProcs(); + if (gTestEnv->GetWireTraceDir() != nullptr) { + std::string file = + std::string( + ::testing::UnitTest::GetInstance()->current_test_info()->test_suite_name()) + + "_" + ::testing::UnitTest::GetInstance()->current_test_info()->name(); + // Replace slashes in gtest names with underscores so everything is in one directory. + std::replace(file.begin(), file.end(), '/', '_'); + + std::string fullPath = gTestEnv->GetWireTraceDir() + file; + + mWireServerTraceLayer.reset( + new WireServerTraceLayer(fullPath.c_str(), mWireServer.get())); + mC2sBuf->SetHandler(mWireServerTraceLayer.get()); + } + + dawn_wire::WireClientDescriptor clientDesc = {}; + clientDesc.serializer = mC2sBuf.get(); + + mWireClient.reset(new dawn_wire::WireClient(clientDesc)); + WGPUDevice clientDevice = mWireClient->GetDevice(); + DawnProcTable clientProcs = dawn_wire::WireClient::GetProcs(); mS2cBuf->SetHandler(mWireClient.get()); procs = clientProcs; @@ -351,27 +850,17 @@ void DawnTest::SetUp() { cDevice = backendDevice; } - // Set up the device and queue because all tests need them, and DawnTest needs them too for the - // deferred expectations. - dawnSetProcs(&procs); - device = dawn::Device::Acquire(cDevice); - queue = device.CreateQueue(); - - // The swapchain isn't used by tests but is useful when debugging with graphics debuggers that - // capture at frame boundaries. - dawn::SwapChainDescriptor swapChainDesc; - swapChainDesc.implementation = mBinding->GetSwapChainImplementation(); - swapchain = device.CreateSwapChain(&swapChainDesc); - FlushWire(); - swapchain.Configure( - static_cast(mBinding->GetPreferredSwapChainTextureFormat()), - dawn::TextureUsageBit::OutputAttachment, 400, 400); + // Set up the device and queue because all tests need them, and DawnTestBase needs them too for + // the deferred expectations. + dawnProcSetProcs(&procs); + device = wgpu::Device::Acquire(cDevice); + queue = device.GetDefaultQueue(); - device.SetErrorCallback(OnDeviceError, this); + device.SetUncapturedErrorCallback(OnDeviceError, this); + device.SetDeviceLostCallback(OnDeviceLost, this); } -void DawnTest::TearDown() { - swapchain = dawn::SwapChain(); +void DawnTestBase::TearDown() { FlushWire(); MapSlotsSynchronously(); @@ -382,38 +871,43 @@ void DawnTest::TearDown() { } } -void DawnTest::StartExpectDeviceError() { +void DawnTestBase::StartExpectDeviceError() { mExpectError = true; mError = false; } -bool DawnTest::EndExpectDeviceError() { +bool DawnTestBase::EndExpectDeviceError() { mExpectError = false; return mError; } // static -void DawnTest::OnDeviceError(const char* message, void* userdata) { - DawnTest* self = static_cast(userdata); +void DawnTestBase::OnDeviceError(WGPUErrorType type, const char* message, void* userdata) { + ASSERT(type != WGPUErrorType_NoError); + DawnTestBase* self = static_cast(userdata); ASSERT_TRUE(self->mExpectError) << "Got unexpected device error: " << message; ASSERT_FALSE(self->mError) << "Got two errors in expect block"; self->mError = true; } -std::ostringstream& DawnTest::AddBufferExpectation(const char* file, - int line, - const dawn::Buffer& buffer, - uint64_t offset, - uint64_t size, - detail::Expectation* expectation) { +void DawnTestBase::OnDeviceLost(const char* message, void* userdata) { + FAIL() << "Device Lost during test: " << message; +} + +std::ostringstream& DawnTestBase::AddBufferExpectation(const char* file, + int line, + const wgpu::Buffer& buffer, + uint64_t offset, + uint64_t size, + detail::Expectation* expectation) { auto readback = ReserveReadback(size); // We need to enqueue the copy immediately because by the time we resolve the expectation, // the buffer might have been modified. - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(buffer, offset, readback.buffer, readback.offset, size); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); DeferredExpectation deferred; @@ -423,7 +917,7 @@ std::ostringstream& DawnTest::AddBufferExpectation(const char* file, deferred.readbackOffset = readback.offset; deferred.size = size; deferred.rowBytes = size; - deferred.rowPitch = size; + deferred.bytesPerRow = size; deferred.expectation.reset(expectation); mDeferredExpectations.push_back(std::move(deferred)); @@ -431,34 +925,34 @@ std::ostringstream& DawnTest::AddBufferExpectation(const char* file, return *(mDeferredExpectations.back().message.get()); } -std::ostringstream& DawnTest::AddTextureExpectation(const char* file, - int line, - const dawn::Texture& texture, - uint32_t x, - uint32_t y, - uint32_t width, - uint32_t height, - uint32_t level, - uint32_t slice, - uint32_t pixelSize, - detail::Expectation* expectation) { - uint32_t rowPitch = Align(width * pixelSize, kTextureRowPitchAlignment); - uint32_t size = rowPitch * (height - 1) + width * pixelSize; +std::ostringstream& DawnTestBase::AddTextureExpectation(const char* file, + int line, + const wgpu::Texture& texture, + uint32_t x, + uint32_t y, + uint32_t width, + uint32_t height, + uint32_t level, + uint32_t slice, + uint32_t pixelSize, + detail::Expectation* expectation) { + uint32_t bytesPerRow = Align(width * pixelSize, kTextureBytesPerRowAlignment); + uint32_t size = bytesPerRow * (height - 1) + width * pixelSize; auto readback = ReserveReadback(size); // We need to enqueue the copy immediately because by the time we resolve the expectation, // the texture might have been modified. - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(texture, level, slice, {x, y, 0}); - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(readback.buffer, readback.offset, rowPitch, 0); - dawn::Extent3D copySize = {width, height, 1}; + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, level, {x, y, slice}); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(readback.buffer, readback.offset, bytesPerRow, 0); + wgpu::Extent3D copySize = {width, height, 1}; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); DeferredExpectation deferred; @@ -468,7 +962,7 @@ std::ostringstream& DawnTest::AddTextureExpectation(const char* file, deferred.readbackOffset = readback.offset; deferred.size = size; deferred.rowBytes = width * pixelSize; - deferred.rowPitch = rowPitch; + deferred.bytesPerRow = bytesPerRow; deferred.expectation.reset(expectation); mDeferredExpectations.push_back(std::move(deferred)); @@ -476,20 +970,14 @@ std::ostringstream& DawnTest::AddTextureExpectation(const char* file, return *(mDeferredExpectations.back().message.get()); } -void DawnTest::WaitABit() { +void DawnTestBase::WaitABit() { device.Tick(); FlushWire(); utils::USleep(100); } -void DawnTest::SwapBuffersForCapture() { - // Insert a frame boundary for API capture tools. - dawn::Texture backBuffer = swapchain.GetNextTexture(); - swapchain.Present(backBuffer); -} - -void DawnTest::FlushWire() { +void DawnTestBase::FlushWire() { if (gTestEnv->UsesWire()) { bool C2SFlushed = mC2sBuf->Flush(); bool S2CFlushed = mS2cBuf->Flush(); @@ -498,12 +986,12 @@ void DawnTest::FlushWire() { } } -DawnTest::ReadbackReservation DawnTest::ReserveReadback(uint64_t readbackSize) { +DawnTestBase::ReadbackReservation DawnTestBase::ReserveReadback(uint64_t readbackSize) { // For now create a new MapRead buffer for each readback // TODO(cwallez@chromium.org): eventually make bigger buffers and allocate linearly? - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = readbackSize; - descriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferDst; + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; ReadbackSlot slot; slot.bufferSize = readbackSize; @@ -518,7 +1006,7 @@ DawnTest::ReadbackReservation DawnTest::ReserveReadback(uint64_t readbackSize) { return reservation; } -void DawnTest::MapSlotsSynchronously() { +void DawnTestBase::MapSlotsSynchronously() { // Initialize numPendingMapOperations before mapping, just in case the callback is called // immediately. mNumPendingMapOperations = mReadbackSlots.size(); @@ -527,8 +1015,8 @@ void DawnTest::MapSlotsSynchronously() { for (size_t i = 0; i < mReadbackSlots.size(); ++i) { MapReadUserdata* userdata = new MapReadUserdata{this, i}; - auto& slot = mReadbackSlots[i]; - slot.buffer.MapReadAsync(SlotMapReadCallback, userdata); + const ReadbackSlot& slot = mReadbackSlots[i]; + slot.buffer.MapAsync(wgpu::MapMode::Read, 0, 0, SlotMapCallback, userdata); } // Busy wait until all map operations are done. @@ -538,20 +1026,18 @@ void DawnTest::MapSlotsSynchronously() { } // static -void DawnTest::SlotMapReadCallback(DawnBufferMapAsyncStatus status, - const void* data, - uint64_t, - void* userdata_) { - DAWN_ASSERT(status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS); +void DawnTestBase::SlotMapCallback(WGPUBufferMapAsyncStatus status, void* userdata_) { + DAWN_ASSERT(status == WGPUBufferMapAsyncStatus_Success); - auto userdata = static_cast(userdata_); - userdata->test->mReadbackSlots[userdata->slot].mappedData = data; - userdata->test->mNumPendingMapOperations--; + std::unique_ptr userdata(static_cast(userdata_)); + DawnTestBase* test = userdata->test; + ReadbackSlot* slot = &test->mReadbackSlots[userdata->slot]; - delete userdata; + slot->mappedData = slot->buffer.GetConstMappedRange(); + test->mNumPendingMapOperations--; } -void DawnTest::ResolveExpectations() { +void DawnTestBase::ResolveExpectations() { for (const auto& expectation : mDeferredExpectations) { DAWN_ASSERT(mReadbackSlots[expectation.readbackSlot].mappedData != nullptr); @@ -562,15 +1048,16 @@ void DawnTest::ResolveExpectations() { uint32_t size; std::vector packedData; - if (expectation.rowBytes != expectation.rowPitch) { - DAWN_ASSERT(expectation.rowPitch > expectation.rowBytes); + if (expectation.rowBytes != expectation.bytesPerRow) { + DAWN_ASSERT(expectation.bytesPerRow > expectation.rowBytes); uint32_t rowCount = - (expectation.size + expectation.rowPitch - 1) / expectation.rowPitch; + (expectation.size + expectation.bytesPerRow - 1) / expectation.bytesPerRow; uint32_t packedSize = rowCount * expectation.rowBytes; packedData.resize(packedSize); for (uint32_t r = 0; r < rowCount; ++r) { for (uint32_t i = 0; i < expectation.rowBytes; ++i) { - packedData[i + r * expectation.rowBytes] = data[i + r * expectation.rowPitch]; + packedData[i + r * expectation.rowBytes] = + data[i + r * expectation.bytesPerRow]; } } data = packedData.data(); @@ -605,47 +1092,11 @@ std::ostream& operator<<(std::ostream& stream, const RGBA8& color) { } namespace detail { - bool IsBackendAvailable(dawn_native::BackendType type) { - switch (type) { -#if defined(DAWN_ENABLE_BACKEND_D3D12) - case dawn_native::BackendType::D3D12: -#endif -#if defined(DAWN_ENABLE_BACKEND_METAL) - case dawn_native::BackendType::Metal: -#endif -#if defined(DAWN_ENABLE_BACKEND_OPENGL) - case dawn_native::BackendType::OpenGL: -#endif -#if defined(DAWN_ENABLE_BACKEND_VULKAN) - case dawn_native::BackendType::Vulkan: -#endif - return true; - - default: - return false; - } - } - - std::vector FilterBackends(const DawnTestParam* params, size_t numParams) { - std::vector backends; - - for (size_t i = 0; i < numParams; ++i) { - if (IsBackendAvailable(params[i].backendType)) { - backends.push_back(params[i]); - } - } - return backends; - } - - std::string GetParamName(const testing::TestParamInfo& info) { - std::ostringstream ostream; - ostream << ParamName(info.param.backendType); - - for (const char* forceEnabledWorkaround : info.param.forceEnabledWorkarounds) { - ostream << "_" << forceEnabledWorkaround; - } - - return ostream.str(); + std::vector GetAvailableAdapterTestParamsForBackends( + const BackendTestConfig* params, + size_t numParams) { + ASSERT(gTestEnv != nullptr); + return gTestEnv->GetAvailableAdapterTestParamsForBackends(params, numParams); } // Helper classes to set expectations @@ -666,7 +1117,6 @@ namespace detail { const T* actual = static_cast(data); - testing::AssertionResult failure = testing::AssertionFailure(); for (size_t i = 0; i < mExpected.size(); ++i) { if (actual[i] != mExpected[i]) { testing::AssertionResult result = testing::AssertionFailure() @@ -704,6 +1154,8 @@ namespace detail { } template class ExpectEq; + template class ExpectEq; template class ExpectEq; template class ExpectEq; + template class ExpectEq; } // namespace detail diff --git a/third_party/dawn/src/tests/DawnTest.h b/third_party/dawn/src/tests/DawnTest.h index f2b8c1740f5..096abcf69dd 100644 --- a/third_party/dawn/src/tests/DawnTest.h +++ b/third_party/dawn/src/tests/DawnTest.h @@ -12,7 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "dawn/dawncpp.h" +#ifndef TESTS_DAWNTEST_H_ +#define TESTS_DAWNTEST_H_ + +#include "common/Log.h" +#include "dawn/dawn_proc_table.h" +#include "dawn/webgpu_cpp.h" #include "dawn_native/DawnNative.h" #include @@ -23,33 +28,62 @@ // Getting data back from Dawn is done in an async manners so all expectations are "deferred" // until the end of the test. Also expectations use a copy to a MapRead buffer to get the data -// so resources should have the TransferSrc allowed usage bit if you want to add expectations on +// so resources should have the CopySrc allowed usage bit if you want to add expectations on // them. +#define EXPECT_BUFFER_U16_EQ(expected, buffer, offset) \ + AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint16_t), \ + new ::detail::ExpectEq(expected)) + +#define EXPECT_BUFFER_U16_RANGE_EQ(expected, buffer, offset, count) \ + AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint16_t) * (count), \ + new ::detail::ExpectEq(expected, count)) + #define EXPECT_BUFFER_U32_EQ(expected, buffer, offset) \ AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t), \ - new detail::ExpectEq(expected)) + new ::detail::ExpectEq(expected)) -#define EXPECT_BUFFER_U32_RANGE_EQ(expected, buffer, offset, count) \ - AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t) * count, \ - new detail::ExpectEq(expected, count)) +#define EXPECT_BUFFER_U32_RANGE_EQ(expected, buffer, offset, count) \ + AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t) * (count), \ + new ::detail::ExpectEq(expected, count)) + +#define EXPECT_BUFFER_FLOAT_EQ(expected, buffer, offset) \ + AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t), \ + new ::detail::ExpectEq(expected)) + +#define EXPECT_BUFFER_FLOAT_RANGE_EQ(expected, buffer, offset, count) \ + AddBufferExpectation(__FILE__, __LINE__, buffer, offset, sizeof(uint32_t) * (count), \ + new ::detail::ExpectEq(expected, count)) // Test a pixel of the mip level 0 of a 2D texture. #define EXPECT_PIXEL_RGBA8_EQ(expected, texture, x, y) \ AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(RGBA8), \ - new detail::ExpectEq(expected)) + new ::detail::ExpectEq(expected)) #define EXPECT_TEXTURE_RGBA8_EQ(expected, texture, x, y, width, height, level, slice) \ AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \ sizeof(RGBA8), \ - new detail::ExpectEq(expected, (width) * (height))) + new ::detail::ExpectEq(expected, (width) * (height))) + +#define EXPECT_PIXEL_FLOAT_EQ(expected, texture, x, y) \ + AddTextureExpectation(__FILE__, __LINE__, texture, x, y, 1, 1, 0, 0, sizeof(float), \ + new ::detail::ExpectEq(expected)) + +#define EXPECT_TEXTURE_FLOAT_EQ(expected, texture, x, y, width, height, level, slice) \ + AddTextureExpectation(__FILE__, __LINE__, texture, x, y, width, height, level, slice, \ + sizeof(float), \ + new ::detail::ExpectEq(expected, (width) * (height))) // Should only be used to test validation of function that can't be tested by regular validation // tests; -#define ASSERT_DEVICE_ERROR(statement) \ - StartExpectDeviceError(); \ - statement; \ - FlushWire(); \ - ASSERT_TRUE(EndExpectDeviceError()); +#define ASSERT_DEVICE_ERROR(statement) \ + StartExpectDeviceError(); \ + statement; \ + FlushWire(); \ + if (!EndExpectDeviceError()) { \ + FAIL() << "Expected device error in:\n " << #statement; \ + } \ + do { \ + } while (0) struct RGBA8 { constexpr RGBA8() : RGBA8(0, 0, 0, 0) { @@ -60,33 +94,65 @@ struct RGBA8 { bool operator!=(const RGBA8& other) const; uint8_t r, g, b, a; + + static const RGBA8 kZero; + static const RGBA8 kBlack; + static const RGBA8 kRed; + static const RGBA8 kGreen; + static const RGBA8 kBlue; + static const RGBA8 kYellow; + static const RGBA8 kWhite; }; std::ostream& operator<<(std::ostream& stream, const RGBA8& color); -struct DawnTestParam { - explicit DawnTestParam(dawn_native::BackendType backendType) : backendType(backendType) { - } +struct BackendTestConfig { + BackendTestConfig(wgpu::BackendType backendType, + std::initializer_list forceEnabledWorkarounds = {}, + std::initializer_list forceDisabledWorkarounds = {}); + + wgpu::BackendType backendType; + + std::vector forceEnabledWorkarounds; + std::vector forceDisabledWorkarounds; +}; - dawn_native::BackendType backendType; +struct TestAdapterProperties : wgpu::AdapterProperties { + TestAdapterProperties(const wgpu::AdapterProperties& properties, bool selected); + std::string adapterName; + bool selected; + private: + // This may be temporary, so it is copied into |adapterName| and made private. + using wgpu::AdapterProperties::name; +}; + +struct AdapterTestParam { + AdapterTestParam(const BackendTestConfig& config, + const TestAdapterProperties& adapterProperties); + + TestAdapterProperties adapterProperties; std::vector forceEnabledWorkarounds; std::vector forceDisabledWorkarounds; }; -// Shorthands for backend types used in the DAWN_INSTANTIATE_TEST -extern const DawnTestParam D3D12Backend; -extern const DawnTestParam MetalBackend; -extern const DawnTestParam OpenGLBackend; -extern const DawnTestParam VulkanBackend; +std::ostream& operator<<(std::ostream& os, const AdapterTestParam& param); + +BackendTestConfig D3D12Backend(std::initializer_list forceEnabledWorkarounds = {}, + std::initializer_list forceDisabledWorkarounds = {}); -DawnTestParam ForceWorkarounds(const DawnTestParam& originParam, - std::initializer_list forceEnabledWorkarounds, +BackendTestConfig MetalBackend(std::initializer_list forceEnabledWorkarounds = {}, std::initializer_list forceDisabledWorkarounds = {}); -struct GLFWwindow; +BackendTestConfig NullBackend(std::initializer_list forceEnabledWorkarounds = {}, + std::initializer_list forceDisabledWorkarounds = {}); + +BackendTestConfig OpenGLBackend(std::initializer_list forceEnabledWorkarounds = {}, + std::initializer_list forceDisabledWorkarounds = {}); + +BackendTestConfig VulkanBackend(std::initializer_list forceEnabledWorkarounds = {}, + std::initializer_list forceDisabledWorkarounds = {}); namespace utils { - class BackendBinding; class TerribleCommandBuffer; } // namespace utils @@ -95,6 +161,7 @@ namespace detail { } // namespace detail namespace dawn_wire { + class CommandHandler; class WireClient; class WireServer; } // namespace dawn_wire @@ -104,37 +171,64 @@ void InitDawnEnd2EndTestEnvironment(int argc, char** argv); class DawnTestEnvironment : public testing::Environment { public: DawnTestEnvironment(int argc, char** argv); - ~DawnTestEnvironment() = default; + ~DawnTestEnvironment() override = default; + + static void SetEnvironment(DawnTestEnvironment* env); + + std::vector GetAvailableAdapterTestParamsForBackends( + const BackendTestConfig* params, + size_t numParams); void SetUp() override; + void TearDown() override; bool UsesWire() const; + bool IsBackendValidationEnabled() const; + bool IsDawnValidationSkipped() const; + bool IsSpvcBeingUsed() const; + bool IsSpvcParserBeingUsed() const; dawn_native::Instance* GetInstance() const; - GLFWwindow* GetWindowForBackend(dawn_native::BackendType type) const; + bool HasVendorIdFilter() const; + uint32_t GetVendorIdFilter() const; + const char* GetWireTraceDir() const; + + protected: + std::unique_ptr mInstance; private: - void CreateBackendWindow(dawn_native::BackendType type); + void ParseArgs(int argc, char** argv); + std::unique_ptr CreateInstanceAndDiscoverAdapters() const; + void SelectPreferredAdapterProperties(const dawn_native::Instance* instance); + void PrintTestConfigurationAndAdapterInfo() const; bool mUseWire = false; bool mEnableBackendValidation = false; - std::unique_ptr mInstance; - - // Windows don't usually like to be bound to one API than the other, for example switching - // from Vulkan to OpenGL causes crashes on some drivers. Because of this, we lazily created - // a window for each backing API. - std::unordered_map mWindows; + bool mSkipDawnValidation = false; + bool mUseSpvc = false; + bool mSpvcFlagSeen = false; + bool mUseSpvcParser = false; + bool mSpvcParserFlagSeen = false; + bool mBeginCaptureOnStartup = false; + bool mHasVendorIdFilter = false; + uint32_t mVendorIdFilter = 0; + std::string mWireTraceDir; + std::vector mDevicePreferences; + std::vector mAdapterProperties; }; -class DawnTest : public ::testing::TestWithParam { +class DawnTestBase { + friend class DawnPerfTestBase; + public: - DawnTest(); - ~DawnTest(); + DawnTestBase(const AdapterTestParam& param); + virtual ~DawnTestBase(); - void SetUp() override; - void TearDown() override; + void SetUp(); + void TearDown(); bool IsD3D12() const; bool IsMetal() const; + bool IsNull() const; bool IsOpenGL() const; bool IsVulkan() const; @@ -144,34 +238,47 @@ class DawnTest : public ::testing::TestWithParam { bool IsIntel() const; bool IsNvidia() const; bool IsQualcomm() const; + bool IsSwiftshader() const; + bool IsWARP() const; bool IsWindows() const; bool IsLinux() const; bool IsMacOS() const; bool UsesWire() const; + bool IsBackendValidationEnabled() const; + bool IsDawnValidationSkipped() const; + bool IsSpvcBeingUsed() const; + bool IsSpvcParserBeingUsed() const; + + bool IsAsan() const; void StartExpectDeviceError(); bool EndExpectDeviceError(); + bool HasVendorIdFilter() const; + uint32_t GetVendorIdFilter() const; + + wgpu::Instance GetInstance() const; + dawn_native::Adapter GetAdapter() const; + protected: - dawn::Device device; - dawn::Queue queue; - dawn::SwapChain swapchain; + wgpu::Device device; + wgpu::Queue queue; DawnProcTable backendProcs = {}; - DawnDevice backendDevice = nullptr; + WGPUDevice backendDevice = nullptr; // Helper methods to implement the EXPECT_ macros std::ostringstream& AddBufferExpectation(const char* file, int line, - const dawn::Buffer& buffer, + const wgpu::Buffer& buffer, uint64_t offset, uint64_t size, detail::Expectation* expectation); std::ostringstream& AddTextureExpectation(const char* file, int line, - const dawn::Texture& texture, + const wgpu::Texture& texture, uint32_t x, uint32_t y, uint32_t width, @@ -184,23 +291,36 @@ class DawnTest : public ::testing::TestWithParam { void WaitABit(); void FlushWire(); - void SwapBuffersForCapture(); + bool SupportsExtensions(const std::vector& extensions); + + // Called in SetUp() to get the extensions required to be enabled in the tests. The tests must + // check if the required extensions are supported by the adapter in this function and guarantee + // the returned extensions are all supported by the adapter. The tests may provide different + // code path to handle the situation when not all extensions are supported. + virtual std::vector GetRequiredExtensions(); + + const wgpu::AdapterProperties& GetAdapterProperties() const; private: + AdapterTestParam mParam; + // Things used to set up testing through the Wire. std::unique_ptr mWireServer; std::unique_ptr mWireClient; std::unique_ptr mC2sBuf; std::unique_ptr mS2cBuf; + std::unique_ptr mWireServerTraceLayer; + // Tracking for validation errors - static void OnDeviceError(const char* message, void* userdata); + static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata); + static void OnDeviceLost(const char* message, void* userdata); bool mExpectError = false; bool mError = false; // MapRead buffers used to get data for the expectations struct ReadbackSlot { - dawn::Buffer buffer; + wgpu::Buffer buffer; uint64_t bufferSize; const void* mappedData = nullptr; }; @@ -208,15 +328,12 @@ class DawnTest : public ::testing::TestWithParam { // Maps all the buffers and fill ReadbackSlot::mappedData void MapSlotsSynchronously(); - static void SlotMapReadCallback(DawnBufferMapAsyncStatus status, - const void* data, - uint64_t dataLength, - void* userdata); + static void SlotMapCallback(WGPUBufferMapAsyncStatus status, void* userdata); size_t mNumPendingMapOperations = 0; // Reserve space where the data for an expectation can be copied struct ReadbackReservation { - dawn::Buffer buffer; + wgpu::Buffer buffer; size_t slot; uint64_t offset; }; @@ -229,7 +346,7 @@ class DawnTest : public ::testing::TestWithParam { uint64_t readbackOffset; uint64_t size; uint32_t rowBytes; - uint32_t rowPitch; + uint32_t bytesPerRow; std::unique_ptr expectation; // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54316 // Use unique_ptr because of missing move/copy constructors on std::basic_ostringstream @@ -240,33 +357,60 @@ class DawnTest : public ::testing::TestWithParam { // Assuming the data is mapped, checks all expectations void ResolveExpectations(); - std::unique_ptr mBinding; + dawn_native::Adapter mBackendAdapter; +}; + +// Skip a test when the given condition is satisfied. +#define DAWN_SKIP_TEST_IF(condition) \ + do { \ + if (condition) { \ + dawn::InfoLog() << "Test skipped: " #condition "."; \ + GTEST_SKIP(); \ + return; \ + } \ + } while (0) + +template +class DawnTestWithParams : public DawnTestBase, public ::testing::TestWithParam { + protected: + DawnTestWithParams(); + ~DawnTestWithParams() override = default; - dawn_native::PCIInfo mPCIInfo; + void SetUp() override { + DawnTestBase::SetUp(); + } + + void TearDown() override { + DawnTestBase::TearDown(); + } }; +template +DawnTestWithParams::DawnTestWithParams() : DawnTestBase(this->GetParam()) { +} + +using DawnTest = DawnTestWithParams<>; + +// Helpers to get the first element of a __VA_ARGS__ without triggering empty __VA_ARGS__ warnings. +#define DAWN_INTERNAL_PP_GET_HEAD(firstParam, ...) firstParam +#define DAWN_PP_GET_HEAD(...) DAWN_INTERNAL_PP_GET_HEAD(__VA_ARGS__, dummyArg) + // Instantiate the test once for each backend provided after the first argument. Use it like this: // DAWN_INSTANTIATE_TEST(MyTestFixture, MetalBackend, OpenGLBackend) -#define DAWN_INSTANTIATE_TEST(testName, firstParam, ...) \ - const decltype(firstParam) testName##params[] = {firstParam, ##__VA_ARGS__}; \ - INSTANTIATE_TEST_SUITE_P( \ - , testName, \ - testing::ValuesIn(::detail::FilterBackends( \ - testName##params, sizeof(testName##params) / sizeof(firstParam))), \ - ::detail::GetParamName) - -// Skip a test when the given condition is satisfied. -#define DAWN_SKIP_TEST_IF(condition) \ - if (condition) { \ - std::cout << "Test skipped: " #condition "." << std::endl; \ - return; \ - } +#define DAWN_INSTANTIATE_TEST(testName, ...) \ + const decltype(DAWN_PP_GET_HEAD(__VA_ARGS__)) testName##params[] = {__VA_ARGS__}; \ + INSTANTIATE_TEST_SUITE_P( \ + , testName, \ + testing::ValuesIn(::detail::GetAvailableAdapterTestParamsForBackends( \ + testName##params, sizeof(testName##params) / sizeof(testName##params[0]))), \ + testing::PrintToStringParamName()) namespace detail { // Helper functions used for DAWN_INSTANTIATE_TEST - bool IsBackendAvailable(dawn_native::BackendType type); - std::vector FilterBackends(const DawnTestParam* params, size_t numParams); - std::string GetParamName(const testing::TestParamInfo& info); + bool IsBackendAvailable(wgpu::BackendType type); + std::vector GetAvailableAdapterTestParamsForBackends( + const BackendTestConfig* params, + size_t numParams); // All classes used to implement the deferred expectations should inherit from this. class Expectation { @@ -290,6 +434,10 @@ namespace detail { std::vector mExpected; }; extern template class ExpectEq; + extern template class ExpectEq; extern template class ExpectEq; extern template class ExpectEq; + extern template class ExpectEq; } // namespace detail + +#endif // TESTS_DAWNTEST_H_ diff --git a/third_party/dawn/src/tests/End2EndTestsMain.cpp b/third_party/dawn/src/tests/End2EndTestsMain.cpp index 69a2041006f..da7bbbe0ecd 100644 --- a/third_party/dawn/src/tests/End2EndTestsMain.cpp +++ b/third_party/dawn/src/tests/End2EndTestsMain.cpp @@ -15,7 +15,7 @@ #include "tests/DawnTest.h" int main(int argc, char** argv) { - testing::InitGoogleTest(&argc, argv); InitDawnEnd2EndTestEnvironment(argc, argv); + testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/third_party/dawn/src/tests/MockCallback.h b/third_party/dawn/src/tests/MockCallback.h new file mode 100644 index 00000000000..c3dfb4ab90f --- /dev/null +++ b/third_party/dawn/src/tests/MockCallback.h @@ -0,0 +1,101 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/Assert.h" + +#include +#include + +namespace testing { + + template + class MockCallback; + + // Helper class for mocking callbacks used for Dawn callbacks with |void* userdata| + // as the last callback argument. + // + // Example Usage: + // MockCallback mock; + // + // void* foo = XYZ; // this is the callback userdata + // + // wgpuDeviceSetDeviceLostCallback(device, mock.Callback(), mock.MakeUserdata(foo)); + // EXPECT_CALL(mock, Call(_, foo)); + template + class MockCallback : public ::testing::MockFunction { + using CallbackType = R (*)(Args...); + + public: + // Helper function makes it easier to get the callback using |foo.Callback()| + // unstead of MockCallback::Callback. + static CallbackType Callback() { + return CallUnboundCallback; + } + + void* MakeUserdata(void* userdata) { + auto mockAndUserdata = + std::unique_ptr(new MockAndUserdata{this, userdata}); + + // Add the userdata to a set of userdata for this mock. We never + // remove from this set even if a callback should only be called once so that + // repeated calls to the callback still forward the userdata correctly. + // Userdata will be destroyed when the mock is destroyed. + auto it = mUserdatas.insert(std::move(mockAndUserdata)); + ASSERT(it.second); + return it.first->get(); + } + + private: + struct MockAndUserdata { + MockCallback* mock; + void* userdata; + }; + + static R CallUnboundCallback(Args... args) { + std::tuple tuple = std::make_tuple(args...); + + constexpr size_t ArgC = sizeof...(Args); + static_assert(ArgC >= 1, "Mock callback requires at least one argument (the userdata)"); + + // Get the userdata. It should be the last argument. + auto userdata = std::get(tuple); + static_assert(std::is_same::value, + "Last callback argument must be void* userdata"); + + // Extract the mock. + ASSERT(userdata != nullptr); + auto* mockAndUserdata = reinterpret_cast(userdata); + MockCallback* mock = mockAndUserdata->mock; + ASSERT(mock != nullptr); + + // Replace the userdata + std::get(tuple) = mockAndUserdata->userdata; + + // Forward the callback to the mock. + return mock->CallImpl(std::make_index_sequence{}, std::move(tuple)); + } + + // This helper cannot be inlined because we dependent on the templated index sequence + // to unpack the tuple arguments. + template + R CallImpl(const std::index_sequence&, std::tuple args) { + return this->Call(std::get(args)...); + } + + std::set> mUserdatas; + }; + +} // namespace testing diff --git a/third_party/dawn/src/tests/ParamGenerator.h b/third_party/dawn/src/tests/ParamGenerator.h new file mode 100644 index 00000000000..1497343fd23 --- /dev/null +++ b/third_party/dawn/src/tests/ParamGenerator.h @@ -0,0 +1,118 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TESTS_PARAMGENERATOR_H_ +#define TESTS_PARAMGENERATOR_H_ + +#include +#include + +// ParamStruct is a custom struct which ParamStruct will yield when iterating. +// The types Params... should be the same as the types passed to the constructor +// of ParamStruct. +template +class ParamGenerator { + using ParamTuple = std::tuple...>; + using Index = std::array; + + static constexpr auto s_indexSequence = std::make_index_sequence{}; + + // Using an N-dimensional Index, extract params from ParamTuple and pass + // them to the constructor of ParamStruct. + template + static ParamStruct GetParam(const ParamTuple& params, + const Index& index, + std::index_sequence) { + return ParamStruct(std::get(params)[std::get(index)]...); + } + + // Get the last value index into a ParamTuple. + template + static Index GetLastIndex(const ParamTuple& params, std::index_sequence) { + return Index{std::get(params).size() - 1 ...}; + } + + public: + using value_type = ParamStruct; + + ParamGenerator(std::vector... params) : mParams(params...) { + } + + class Iterator : public std::iterator { + public: + Iterator& operator++() { + // Increment the Index by 1. If the i'th place reaches the maximum, + // reset it to 0 and continue with the i+1'th place. + for (int i = mIndex.size() - 1; i >= 0; --i) { + if (mIndex[i] >= mLastIndex[i]) { + mIndex[i] = 0; + } else { + mIndex[i]++; + return *this; + } + } + + // Set a marker that the iterator has reached the end. + mEnd = true; + return *this; + } + + bool operator==(const Iterator& other) const { + return mEnd == other.mEnd && mIndex == other.mIndex; + } + + bool operator!=(const Iterator& other) const { + return !(*this == other); + } + + ParamStruct operator*() const { + return GetParam(mParams, mIndex, s_indexSequence); + } + + private: + friend class ParamGenerator; + + Iterator(ParamTuple params, Index index) + : mParams(params), mIndex(index), mLastIndex{GetLastIndex(params, s_indexSequence)} { + } + + ParamTuple mParams; + Index mIndex; + Index mLastIndex; + bool mEnd = false; + }; + + Iterator begin() const { + return Iterator(mParams, {}); + } + + Iterator end() const { + Iterator iter(mParams, GetLastIndex(mParams, s_indexSequence)); + ++iter; + return iter; + } + + private: + ParamTuple mParams; +}; + +template +auto MakeParamGenerator(std::vector&& first, + std::initializer_list&&... params) { + return ParamGenerator( + ::detail::GetAvailableAdapterTestParamsForBackends(first.data(), first.size()), + std::forward&&>(params)...); +} + +#endif // TESTS_PARAMGENERATOR_H_ diff --git a/third_party/dawn/src/tests/PerfTestsMain.cpp b/third_party/dawn/src/tests/PerfTestsMain.cpp new file mode 100644 index 00000000000..8852896694a --- /dev/null +++ b/third_party/dawn/src/tests/PerfTestsMain.cpp @@ -0,0 +1,21 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/perf_tests/DawnPerfTest.h" + +int main(int argc, char** argv) { + InitDawnPerfTestEnvironment(argc, argv); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/third_party/dawn/src/tests/end2end/BasicTests.cpp b/third_party/dawn/src/tests/end2end/BasicTests.cpp index 1b650db4ddb..4a0409fd9d9 100644 --- a/third_party/dawn/src/tests/end2end/BasicTests.cpp +++ b/third_party/dawn/src/tests/end2end/BasicTests.cpp @@ -14,35 +14,41 @@ #include "tests/DawnTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" -class BasicTests : public DawnTest { -}; +class BasicTests : public DawnTest {}; -// Test Buffer::SetSubData changes the content of the buffer, but really this is the most +// Test adapter filter by vendor id. +TEST_P(BasicTests, VendorIdFilter) { + DAWN_SKIP_TEST_IF(!HasVendorIdFilter()); + + ASSERT_EQ(GetAdapterProperties().vendorID, GetVendorIdFilter()); +} + +// Test Queue::WriteBuffer changes the content of the buffer, but really this is the most // basic test possible, and tests the test harness -TEST_P(BasicTests, BufferSetSubData) { - dawn::BufferDescriptor descriptor; +TEST_P(BasicTests, QueueWriteBuffer) { + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); uint32_t value = 0x01020304; - buffer.SetSubData(0, sizeof(value), &value); + queue.WriteBuffer(buffer, 0, &value, sizeof(value)); EXPECT_BUFFER_U32_EQ(value, buffer, 0); } -// Test a validation error for buffer setSubData, but really this is the most basic test possible +// Test a validation error for Queue::WriteBuffer but really this is the most basic test possible // for ASSERT_DEVICE_ERROR -TEST_P(BasicTests, BufferSetSubDataError) { - dawn::BufferDescriptor descriptor; +TEST_P(BasicTests, QueueWriteBufferError) { + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); uint8_t value = 187; - ASSERT_DEVICE_ERROR(buffer.SetSubData(1000, sizeof(value), &value)); + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buffer, 1000, &value, sizeof(value))); } -DAWN_INSTANTIATE_TEST(BasicTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(BasicTests, D3D12Backend(), MetalBackend(), OpenGLBackend(), VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/BindGroupTests.cpp b/third_party/dawn/src/tests/end2end/BindGroupTests.cpp index 3a46286e3eb..f1d790f9a70 100644 --- a/third_party/dawn/src/tests/end2end/BindGroupTests.cpp +++ b/third_party/dawn/src/tests/end2end/BindGroupTests.cpp @@ -14,48 +14,115 @@ #include "common/Assert.h" #include "common/Constants.h" +#include "common/Math.h" #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" -constexpr static unsigned int kRTSize = 8; +constexpr static uint32_t kRTSize = 8; class BindGroupTests : public DawnTest { -protected: - dawn::CommandBuffer CreateSimpleComputeCommandBuffer( - const dawn::ComputePipeline& pipeline, const dawn::BindGroup& bindGroup) { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + protected: + wgpu::CommandBuffer CreateSimpleComputeCommandBuffer(const wgpu::ComputePipeline& pipeline, + const wgpu::BindGroup& bindGroup) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.Dispatch(1, 1, 1); + pass.SetBindGroup(0, bindGroup); + pass.Dispatch(1); pass.EndPass(); return encoder.Finish(); } - dawn::PipelineLayout MakeBasicPipelineLayout( - dawn::Device device, - std::vector bindingInitializer) const { - dawn::PipelineLayoutDescriptor descriptor; + wgpu::PipelineLayout MakeBasicPipelineLayout( + std::vector bindingInitializer) const { + wgpu::PipelineLayoutDescriptor descriptor; descriptor.bindGroupLayoutCount = bindingInitializer.size(); descriptor.bindGroupLayouts = bindingInitializer.data(); return device.CreatePipelineLayout(&descriptor); } + + wgpu::ShaderModule MakeSimpleVSModule() const { + return utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); + gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); + })"); + } + + wgpu::ShaderModule MakeFSModule(std::vector bindingTypes) const { + ASSERT(bindingTypes.size() <= kMaxBindGroups); + + std::ostringstream fs; + fs << R"( + #version 450 + layout(location = 0) out vec4 fragColor; + )"; + + for (size_t i = 0; i < bindingTypes.size(); ++i) { + switch (bindingTypes[i]) { + case wgpu::BindingType::UniformBuffer: + fs << "layout (std140, set = " << i << ", binding = 0) uniform UniformBuffer" + << i << R"( { + vec4 color; + } buffer)" + << i << ";\n"; + break; + case wgpu::BindingType::StorageBuffer: + fs << "layout (std430, set = " << i << ", binding = 0) buffer StorageBuffer" + << i << R"( { + vec4 color; + } buffer)" + << i << ";\n"; + break; + default: + UNREACHABLE(); + } + } + + fs << R"( + void main() { + fragColor = vec4(0.0); + )"; + for (size_t i = 0; i < bindingTypes.size(); ++i) { + fs << "fragColor += buffer" << i << ".color;\n"; + } + fs << "}\n"; + + return utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, + fs.str().c_str()); + } + + wgpu::RenderPipeline MakeTestPipeline(const utils::BasicRenderPass& renderPass, + std::vector bindingTypes, + std::vector bindGroupLayouts) { + wgpu::ShaderModule vsModule = MakeSimpleVSModule(); + wgpu::ShaderModule fsModule = MakeFSModule(bindingTypes); + + wgpu::PipelineLayout pipelineLayout = MakeBasicPipelineLayout(bindGroupLayouts); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.layout = pipelineLayout; + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat; + pipelineDescriptor.cColorStates[0].colorBlend.operation = wgpu::BlendOperation::Add; + pipelineDescriptor.cColorStates[0].colorBlend.srcFactor = wgpu::BlendFactor::One; + pipelineDescriptor.cColorStates[0].colorBlend.dstFactor = wgpu::BlendFactor::One; + pipelineDescriptor.cColorStates[0].alphaBlend.operation = wgpu::BlendOperation::Add; + pipelineDescriptor.cColorStates[0].alphaBlend.srcFactor = wgpu::BlendFactor::One; + pipelineDescriptor.cColorStates[0].alphaBlend.dstFactor = wgpu::BlendFactor::One; + + return device.CreateRenderPipeline(&pipelineDescriptor); + } }; // Test a bindgroup reused in two command buffers in the same call to queue.Submit(). // This test passes by not asserting or crashing. TEST_P(BindGroupTests, ReusedBindGroupSingleSubmit) { - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, - { - {0, dawn::ShaderStageBit::Compute, dawn::BindingType::UniformBuffer}, - } - ); - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); - const char* shader = R"( #version 450 layout(std140, set = 0, binding = 0) uniform Contents { @@ -65,26 +132,22 @@ TEST_P(BindGroupTests, ReusedBindGroupSingleSubmit) { } )"; - dawn::ShaderModule module = - utils::CreateShaderModule(device, dawn::ShaderStage::Compute, shader); - dawn::ComputePipelineDescriptor cpDesc; - cpDesc.layout = pl; + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, shader); - dawn::PipelineStageDescriptor computeStage; - computeStage.module = module; - computeStage.entryPoint = "main"; - cpDesc.computeStage = &computeStage; + wgpu::ComputePipelineDescriptor cpDesc; + cpDesc.computeStage.module = module; + cpDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc); - dawn::ComputePipeline cp = device.CreateComputePipeline(&cpDesc); - - dawn::BufferDescriptor bufferDesc; + wgpu::BufferDescriptor bufferDesc; bufferDesc.size = sizeof(float); - bufferDesc.usage = dawn::BufferUsageBit::TransferDst | - dawn::BufferUsageBit::Uniform; - dawn::Buffer buffer = device.CreateBuffer(&bufferDesc); - dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, sizeof(float)}}); + bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, cp.GetBindGroupLayout(0), {{0, buffer}}); - dawn::CommandBuffer cb[2]; + wgpu::CommandBuffer cb[2]; cb[0] = CreateSimpleComputeCommandBuffer(cp, bindGroup); cb[1] = CreateSimpleComputeCommandBuffer(cp, bindGroup); queue.Submit(2, cb); @@ -94,23 +157,21 @@ TEST_P(BindGroupTests, ReusedBindGroupSingleSubmit) { // It contains a transformation matrix for the VS and the fragment color for the FS. // These must result in different register offsets in the native APIs. TEST_P(BindGroupTests, ReusedUBO) { - // TODO(jiawei.shao@intel.com): find out why this test fails on Metal - DAWN_SKIP_TEST_IF(IsMetal()); - utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout (set = 0, binding = 0) uniform vertexUniformBuffer { mat2 transform; }; void main() { - const vec2 pos[3] = vec2[3](vec2(-1.f, -1.f), vec2(1.f, -1.f), vec2(-1.f, 1.f)); + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); gl_Position = vec4(transform * pos[gl_VertexIndex], 0.f, 1.f); - })" - ); + })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout (set = 0, binding = 1) uniform fragmentUniformBuffer { vec4 color; @@ -118,25 +179,14 @@ TEST_P(BindGroupTests, ReusedUBO) { layout(location = 0) out vec4 fragColor; void main() { fragColor = color; - })" - ); - - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, - { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}, - } - ); - dawn::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, &bgl); + })"); utils::ComboRenderPipelineDescriptor textureDescriptor(device); - textureDescriptor.layout = pipelineLayout; - textureDescriptor.cVertexStage.module = vsModule; + textureDescriptor.vertexStage.module = vsModule; textureDescriptor.cFragmentStage.module = fsModule; - textureDescriptor.cColorStates[0]->format = renderPass.colorFormat; + textureDescriptor.cColorStates[0].format = renderPass.colorFormat; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&textureDescriptor); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&textureDescriptor); struct Data { float transform[8]; @@ -145,163 +195,142 @@ TEST_P(BindGroupTests, ReusedUBO) { }; ASSERT(offsetof(Data, color) == 256); constexpr float dummy = 0.0f; - Data data { - { 1.f, 0.f, dummy, dummy, 0.f, 1.0f, dummy, dummy }, - { 0 }, - { 0.f, 1.f, 0.f, 1.f }, + Data data{ + {1.f, 0.f, dummy, dummy, 0.f, 1.0f, dummy, dummy}, + {0}, + {0.f, 1.f, 0.f, 1.f}, }; - dawn::Buffer buffer = utils::CreateBufferFromData(device, &data, sizeof(data), dawn::BufferUsageBit::Uniform); - dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, { - {0, buffer, 0, sizeof(Data::transform)}, - {1, buffer, 256, sizeof(Data::color)} - }); - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::Buffer buffer = + utils::CreateBufferFromData(device, &data, sizeof(data), wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), + {{0, buffer, 0, sizeof(Data::transform)}, {1, buffer, 256, sizeof(Data::color)}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.Draw(3, 1, 0, 0); + pass.SetBindGroup(0, bindGroup); + pass.Draw(3); pass.EndPass(); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); RGBA8 filled(0, 255, 0, 255); RGBA8 notFilled(0, 0, 0, 0); - int min = 1, max = kRTSize - 3; - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); + uint32_t min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max); } -// Test a bindgroup containing a UBO in the vertex shader and a sampler and texture in the fragment shader. -// In D3D12 for example, these different types of bindings end up in different namespaces, but the register -// offsets used must match between the shader module and descriptor range. +// Test a bindgroup containing a UBO in the vertex shader and a sampler and texture in the fragment +// shader. In D3D12 for example, these different types of bindings end up in different namespaces, +// but the register offsets used must match between the shader module and descriptor range. TEST_P(BindGroupTests, UBOSamplerAndTexture) { - // TODO(jiawei.shao@intel.com): find out why this test fails on Metal - DAWN_SKIP_TEST_IF(IsMetal()); - utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout (set = 0, binding = 0) uniform vertexUniformBuffer { mat2 transform; }; void main() { - const vec2 pos[3] = vec2[3](vec2(-1.f, -1.f), vec2(1.f, -1.f), vec2(-1.f, 1.f)); + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); gl_Position = vec4(transform * pos[gl_VertexIndex], 0.f, 1.f); - })" - ); + })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout (set = 0, binding = 1) uniform sampler samp; layout (set = 0, binding = 2) uniform texture2D tex; layout (location = 0) out vec4 fragColor; void main() { fragColor = texture(sampler2D(tex, samp), gl_FragCoord.xy); - })" - ); - - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, - { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}, - {2, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}, - } - ); - dawn::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, &bgl); + })"); utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); - pipelineDescriptor.layout = pipelineLayout; - pipelineDescriptor.cVertexStage.module = vsModule; + pipelineDescriptor.vertexStage.module = vsModule; pipelineDescriptor.cFragmentStage.module = fsModule; - pipelineDescriptor.cColorStates[0]->format = renderPass.colorFormat; + pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); constexpr float dummy = 0.0f; - constexpr float transform[] = { 1.f, 0.f, dummy, dummy, 0.f, 1.f, dummy, dummy }; - dawn::Buffer buffer = utils::CreateBufferFromData(device, &transform, sizeof(transform), dawn::BufferUsageBit::Uniform); - - dawn::SamplerDescriptor samplerDescriptor; - samplerDescriptor.minFilter = dawn::FilterMode::Nearest; - samplerDescriptor.magFilter = dawn::FilterMode::Nearest; - samplerDescriptor.mipmapFilter = dawn::FilterMode::Nearest; - samplerDescriptor.addressModeU = dawn::AddressMode::ClampToEdge; - samplerDescriptor.addressModeV = dawn::AddressMode::ClampToEdge; - samplerDescriptor.addressModeW = dawn::AddressMode::ClampToEdge; - samplerDescriptor.lodMinClamp = kLodMin; - samplerDescriptor.lodMaxClamp = kLodMax; - samplerDescriptor.compareFunction = dawn::CompareFunction::Never; - - dawn::Sampler sampler = device.CreateSampler(&samplerDescriptor); - - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; + constexpr float transform[] = {1.f, 0.f, dummy, dummy, 0.f, 1.f, dummy, dummy}; + wgpu::Buffer buffer = utils::CreateBufferFromData(device, &transform, sizeof(transform), + wgpu::BufferUsage::Uniform); + + wgpu::SamplerDescriptor samplerDescriptor = {}; + samplerDescriptor.minFilter = wgpu::FilterMode::Nearest; + samplerDescriptor.magFilter = wgpu::FilterMode::Nearest; + samplerDescriptor.mipmapFilter = wgpu::FilterMode::Nearest; + samplerDescriptor.addressModeU = wgpu::AddressMode::ClampToEdge; + samplerDescriptor.addressModeV = wgpu::AddressMode::ClampToEdge; + samplerDescriptor.addressModeW = wgpu::AddressMode::ClampToEdge; + + wgpu::Sampler sampler = device.CreateSampler(&samplerDescriptor); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = kRTSize; descriptor.size.height = kRTSize; descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled; - dawn::Texture texture = device.CreateTexture(&descriptor); - dawn::TextureView textureView = texture.CreateDefaultView(); + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + wgpu::Texture texture = device.CreateTexture(&descriptor); + wgpu::TextureView textureView = texture.CreateView(); - int width = kRTSize, height = kRTSize; - int widthInBytes = width * sizeof(RGBA8); + uint32_t width = kRTSize, height = kRTSize; + uint32_t widthInBytes = width * sizeof(RGBA8); widthInBytes = (widthInBytes + 255) & ~255; - int sizeInBytes = widthInBytes * height; - int size = sizeInBytes / sizeof(RGBA8); + uint32_t sizeInBytes = widthInBytes * height; + uint32_t size = sizeInBytes / sizeof(RGBA8); std::vector data = std::vector(size); - for (int i = 0; i < size; i++) { + for (uint32_t i = 0; i < size; i++) { data[i] = RGBA8(0, 255, 0, 255); } - dawn::Buffer stagingBuffer = utils::CreateBufferFromData(device, data.data(), sizeInBytes, dawn::BufferUsageBit::TransferSrc); + wgpu::Buffer stagingBuffer = + utils::CreateBufferFromData(device, data.data(), sizeInBytes, wgpu::BufferUsage::CopySrc); - dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, { - {0, buffer, 0, sizeof(transform)}, - {1, sampler}, - {2, textureView} - }); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, buffer, 0, sizeof(transform)}, {1, sampler}, {2, textureView}}); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::BufferCopyView bufferCopyView = + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, widthInBytes, 0); - dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); - dawn::Extent3D copySize = {width, height, 1}; + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {width, height, 1}; encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.Draw(3, 1, 0, 0); + pass.SetBindGroup(0, bindGroup); + pass.Draw(3); pass.EndPass(); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); RGBA8 filled(0, 255, 0, 255); RGBA8 notFilled(0, 0, 0, 0); - int min = 1, max = kRTSize - 3; - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); + uint32_t min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max); } TEST_P(BindGroupTests, MultipleBindLayouts) { - // Test fails on Metal. - // https://bugs.chromium.org/p/dawn/issues/detail?id=33 - DAWN_SKIP_TEST_IF(IsMetal()); - utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout (set = 0, binding = 0) uniform vertexUniformBuffer1 { mat2 transform1; @@ -310,12 +339,12 @@ TEST_P(BindGroupTests, MultipleBindLayouts) { mat2 transform2; }; void main() { - const vec2 pos[3] = vec2[3](vec2(-1.f, -1.f), vec2(1.f, -1.f), vec2(-1.f, 1.f)); + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); gl_Position = vec4((transform1 + transform2) * pos[gl_VertexIndex], 0.f, 1.f); })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout (set = 0, binding = 1) uniform fragmentUniformBuffer1 { vec4 color1; @@ -328,21 +357,12 @@ TEST_P(BindGroupTests, MultipleBindLayouts) { fragColor = color1 + color2; })"); - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}, - }); - - dawn::PipelineLayout pipelineLayout = MakeBasicPipelineLayout(device, {layout, layout}); - utils::ComboRenderPipelineDescriptor textureDescriptor(device); - textureDescriptor.layout = pipelineLayout; - textureDescriptor.cVertexStage.module = vsModule; + textureDescriptor.vertexStage.module = vsModule; textureDescriptor.cFragmentStage.module = fsModule; - textureDescriptor.cColorStates[0]->format = renderPass.colorFormat; + textureDescriptor.cColorStates[0].format = renderPass.colorFormat; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&textureDescriptor); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&textureDescriptor); struct Data { float transform[8]; @@ -352,8 +372,8 @@ TEST_P(BindGroupTests, MultipleBindLayouts) { ASSERT(offsetof(Data, color) == 256); std::vector data; - std::vector buffers; - std::vector bindGroups; + std::vector buffers; + std::vector bindGroups; data.push_back( {{1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, {0}, {0.0f, 1.0f, 0.0f, 1.0f}}); @@ -362,28 +382,28 @@ TEST_P(BindGroupTests, MultipleBindLayouts) { {{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, {0}, {1.0f, 0.0f, 0.0f, 1.0f}}); for (int i = 0; i < 2; i++) { - dawn::Buffer buffer = utils::CreateBufferFromData(device, &data[i], sizeof(Data), - dawn::BufferUsageBit::Uniform); + wgpu::Buffer buffer = + utils::CreateBufferFromData(device, &data[i], sizeof(Data), wgpu::BufferUsage::Uniform); buffers.push_back(buffer); - bindGroups.push_back(utils::MakeBindGroup(device, layout, + bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buffers[i], 0, sizeof(Data::transform)}, {1, buffers[i], 256, sizeof(Data::color)}})); } - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroups[0], 0, nullptr); - pass.SetBindGroup(1, bindGroups[1], 0, nullptr); - pass.Draw(3, 1, 0, 0); + pass.SetBindGroup(0, bindGroups[0]); + pass.SetBindGroup(1, bindGroups[1]); + pass.Draw(3); pass.EndPass(); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); RGBA8 filled(255, 255, 0, 255); RGBA8 notFilled(0, 0, 0, 0); - int min = 1, max = kRTSize - 3; + uint32_t min = 1, max = kRTSize - 3; EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); @@ -392,82 +412,830 @@ TEST_P(BindGroupTests, MultipleBindLayouts) { // This test reproduces an out-of-bound bug on D3D12 backends when calling draw command twice with // one pipeline that has 4 bind group sets in one render pass. -TEST_P(BindGroupTests, DrawTwiceInSamePipelineWithFourBindGroupSets) -{ +TEST_P(BindGroupTests, DrawTwiceInSamePipelineWithFourBindGroupSets) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); + + wgpu::RenderPipeline pipeline = + MakeTestPipeline(renderPass, + {wgpu::BindingType::UniformBuffer, wgpu::BindingType::UniformBuffer, + wgpu::BindingType::UniformBuffer, wgpu::BindingType::UniformBuffer}, + {layout, layout, layout, layout}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(pipeline); + + // The color will be added 8 times, so the value should be 0.125. But we choose 0.126 + // because of precision issues on some devices (for example NVIDIA bots). + std::array color = {0.126, 0, 0, 0.126}; + wgpu::Buffer uniformBuffer = + utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, sizeof(color)}}); + + pass.SetBindGroup(0, bindGroup); + pass.SetBindGroup(1, bindGroup); + pass.SetBindGroup(2, bindGroup); + pass.SetBindGroup(3, bindGroup); + pass.Draw(3); + + pass.SetPipeline(pipeline); + pass.Draw(3); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + RGBA8 filled(255, 0, 0, 255); + RGBA8 notFilled(0, 0, 0, 0); + uint32_t min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); + EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max); +} + +// Test that bind groups can be set before the pipeline. +TEST_P(BindGroupTests, SetBindGroupBeforePipeline) { utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + // Create a bind group layout which uses a single uniform buffer. + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); + + // Create a pipeline that uses the uniform bind group layout. + wgpu::RenderPipeline pipeline = + MakeTestPipeline(renderPass, {wgpu::BindingType::UniformBuffer}, {layout}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + // Create a bind group with a uniform buffer and fill it with RGBAunorm(1, 0, 0, 1). + std::array color = {1, 0, 0, 1}; + wgpu::Buffer uniformBuffer = + utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, sizeof(color)}}); + + // Set the bind group, then the pipeline, and draw. + pass.SetBindGroup(0, bindGroup); + pass.SetPipeline(pipeline); + pass.Draw(3); + + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // The result should be red. + RGBA8 filled(255, 0, 0, 255); + RGBA8 notFilled(0, 0, 0, 0); + uint32_t min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); + EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max); +} + +// Test that dynamic bind groups can be set before the pipeline. +TEST_P(BindGroupTests, SetDynamicBindGroupBeforePipeline) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + // Create a bind group layout which uses a single dynamic uniform buffer. + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, true}}); + + // Create a pipeline that uses the dynamic uniform bind group layout for two bind groups. + wgpu::RenderPipeline pipeline = MakeTestPipeline( + renderPass, {wgpu::BindingType::UniformBuffer, wgpu::BindingType::UniformBuffer}, + {layout, layout}); + + // Prepare data RGBAunorm(1, 0, 0, 0.5) and RGBAunorm(0, 1, 0, 0.5). They will be added in the + // shader. + std::array color0 = {1, 0, 0, 0.501}; + std::array color1 = {0, 1, 0, 0.501}; + + size_t color1Offset = Align(sizeof(color0), kMinDynamicBufferOffsetAlignment); + + std::vector data(color1Offset + sizeof(color1)); + memcpy(data.data(), color0.data(), sizeof(color0)); + memcpy(data.data() + color1Offset, color1.data(), sizeof(color1)); + + // Create a bind group and uniform buffer with the color data. It will be bound at the offset + // to each color. + wgpu::Buffer uniformBuffer = + utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, 4 * sizeof(float)}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + // Set the first dynamic bind group. + uint32_t dynamicOffset = 0; + pass.SetBindGroup(0, bindGroup, 1, &dynamicOffset); + + // Set the second dynamic bind group. + dynamicOffset = color1Offset; + pass.SetBindGroup(1, bindGroup, 1, &dynamicOffset); + + // Set the pipeline and draw. + pass.SetPipeline(pipeline); + pass.Draw(3); + + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // The result should be RGBAunorm(1, 0, 0, 0.5) + RGBAunorm(0, 1, 0, 0.5) + RGBA8 filled(255, 255, 0, 255); + RGBA8 notFilled(0, 0, 0, 0); + uint32_t min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); + EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max); +} + +// Test that bind groups set for one pipeline are still set when the pipeline changes. +TEST_P(BindGroupTests, BindGroupsPersistAfterPipelineChange) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + // Create a bind group layout which uses a single dynamic uniform buffer. + wgpu::BindGroupLayout uniformLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, true}}); + + // Create a bind group layout which uses a single dynamic storage buffer. + wgpu::BindGroupLayout storageLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer, true}}); + + // Create a pipeline which uses the uniform buffer and storage buffer bind groups. + wgpu::RenderPipeline pipeline0 = MakeTestPipeline( + renderPass, {wgpu::BindingType::UniformBuffer, wgpu::BindingType::StorageBuffer}, + {uniformLayout, storageLayout}); + + // Create a pipeline which uses the uniform buffer bind group twice. + wgpu::RenderPipeline pipeline1 = MakeTestPipeline( + renderPass, {wgpu::BindingType::UniformBuffer, wgpu::BindingType::UniformBuffer}, + {uniformLayout, uniformLayout}); + + // Prepare data RGBAunorm(1, 0, 0, 0.5) and RGBAunorm(0, 1, 0, 0.5). They will be added in the + // shader. + std::array color0 = {1, 0, 0, 0.5}; + std::array color1 = {0, 1, 0, 0.5}; + + size_t color1Offset = Align(sizeof(color0), kMinDynamicBufferOffsetAlignment); + + std::vector data(color1Offset + sizeof(color1)); + memcpy(data.data(), color0.data(), sizeof(color0)); + memcpy(data.data() + color1Offset, color1.data(), sizeof(color1)); + + // Create a bind group and uniform buffer with the color data. It will be bound at the offset + // to each color. + wgpu::Buffer uniformBuffer = + utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, uniformLayout, {{0, uniformBuffer, 0, 4 * sizeof(float)}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + // Set the first pipeline (uniform, storage). + pass.SetPipeline(pipeline0); + + // Set the first bind group at a dynamic offset. + // This bind group matches the slot in the pipeline layout. + uint32_t dynamicOffset = 0; + pass.SetBindGroup(0, bindGroup, 1, &dynamicOffset); + + // Set the second bind group at a dynamic offset. + // This bind group does not match the slot in the pipeline layout. + dynamicOffset = color1Offset; + pass.SetBindGroup(1, bindGroup, 1, &dynamicOffset); + + // Set the second pipeline (uniform, uniform). + // Both bind groups match the pipeline. + // They should persist and not need to be bound again. + pass.SetPipeline(pipeline1); + pass.Draw(3); + + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // The result should be RGBAunorm(1, 0, 0, 0.5) + RGBAunorm(0, 1, 0, 0.5) + RGBA8 filled(255, 255, 0, 255); + RGBA8 notFilled(0, 0, 0, 0); + uint32_t min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); + EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max); +} + +// Do a successful draw. Then, change the pipeline and one bind group. +// Draw to check that the all bind groups are set. +TEST_P(BindGroupTests, DrawThenChangePipelineAndBindGroup) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + // Create a bind group layout which uses a single dynamic uniform buffer. + wgpu::BindGroupLayout uniformLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, true}}); + + // Create a bind group layout which uses a single dynamic storage buffer. + wgpu::BindGroupLayout storageLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer, true}}); + + // Create a pipeline with pipeline layout (uniform, uniform, storage). + wgpu::RenderPipeline pipeline0 = + MakeTestPipeline(renderPass, + {wgpu::BindingType::UniformBuffer, wgpu::BindingType::UniformBuffer, + wgpu::BindingType::StorageBuffer}, + {uniformLayout, uniformLayout, storageLayout}); + + // Create a pipeline with pipeline layout (uniform, storage, storage). + wgpu::RenderPipeline pipeline1 = + MakeTestPipeline(renderPass, + {wgpu::BindingType::UniformBuffer, wgpu::BindingType::StorageBuffer, + wgpu::BindingType::StorageBuffer}, + {uniformLayout, storageLayout, storageLayout}); + + // Prepare color data. + // The first draw will use { color0, color1, color2 }. + // The second draw will use { color0, color3, color2 }. + // The pipeline uses additive color and alpha blending so the result of two draws should be + // { 2 * color0 + color1 + 2 * color2 + color3} = RGBAunorm(1, 1, 1, 1) + std::array color0 = {0.501, 0, 0, 0}; + std::array color1 = {0, 1, 0, 0}; + std::array color2 = {0, 0, 0, 0.501}; + std::array color3 = {0, 0, 1, 0}; + + size_t color1Offset = Align(sizeof(color0), kMinDynamicBufferOffsetAlignment); + size_t color2Offset = Align(color1Offset + sizeof(color1), kMinDynamicBufferOffsetAlignment); + size_t color3Offset = Align(color2Offset + sizeof(color2), kMinDynamicBufferOffsetAlignment); + + std::vector data(color3Offset + sizeof(color3), 0); + memcpy(data.data(), color0.data(), sizeof(color0)); + memcpy(data.data() + color1Offset, color1.data(), sizeof(color1)); + memcpy(data.data() + color2Offset, color2.data(), sizeof(color2)); + memcpy(data.data() + color3Offset, color3.data(), sizeof(color3)); + + // Create a uniform and storage buffer bind groups to bind the color data. + wgpu::Buffer uniformBuffer = + utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Uniform); + + wgpu::Buffer storageBuffer = + utils::CreateBufferFromData(device, data.data(), data.size(), wgpu::BufferUsage::Storage); + + wgpu::BindGroup uniformBindGroup = + utils::MakeBindGroup(device, uniformLayout, {{0, uniformBuffer, 0, 4 * sizeof(float)}}); + wgpu::BindGroup storageBindGroup = + utils::MakeBindGroup(device, storageLayout, {{0, storageBuffer, 0, 4 * sizeof(float)}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + // Set the pipeline to (uniform, uniform, storage) + pass.SetPipeline(pipeline0); + + // Set the first bind group to color0 in the dynamic uniform buffer. + uint32_t dynamicOffset = 0; + pass.SetBindGroup(0, uniformBindGroup, 1, &dynamicOffset); + + // Set the first bind group to color1 in the dynamic uniform buffer. + dynamicOffset = color1Offset; + pass.SetBindGroup(1, uniformBindGroup, 1, &dynamicOffset); + + // Set the first bind group to color2 in the dynamic storage buffer. + dynamicOffset = color2Offset; + pass.SetBindGroup(2, storageBindGroup, 1, &dynamicOffset); + + pass.Draw(3); + + // Set the pipeline to (uniform, storage, storage) + // - The first bind group should persist (inherited on some backends) + // - The second bind group needs to be set again to pass validation. + // It changed from uniform to storage. + // - The third bind group should persist. It should be set again by the backend internally. + pass.SetPipeline(pipeline1); + + // Set the second bind group to color3 in the dynamic storage buffer. + dynamicOffset = color3Offset; + pass.SetBindGroup(1, storageBindGroup, 1, &dynamicOffset); + + pass.Draw(3); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + RGBA8 filled(255, 255, 255, 255); + RGBA8 notFilled(0, 0, 0, 0); + uint32_t min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); + EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max); +} + +// Regression test for crbug.com/dawn/408 where dynamic offsets were applied in the wrong order. +// Dynamic offsets should be applied in increasing order of binding number. +TEST_P(BindGroupTests, DynamicOffsetOrder) { + // We will put the following values and the respective offsets into a buffer. + // The test will ensure that the correct dynamic offset is applied to each buffer by reading the + // value from an offset binding. + std::array offsets = {3 * kMinDynamicBufferOffsetAlignment, + 1 * kMinDynamicBufferOffsetAlignment, + 2 * kMinDynamicBufferOffsetAlignment}; + std::array values = {21, 67, 32}; + + // Create three buffers large enough to by offset by the largest offset. + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = 3 * kMinDynamicBufferOffsetAlignment + sizeof(uint32_t); + bufferDescriptor.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst; + + wgpu::Buffer buffer0 = device.CreateBuffer(&bufferDescriptor); + wgpu::Buffer buffer3 = device.CreateBuffer(&bufferDescriptor); + + // This test uses both storage and uniform buffers to ensure buffer bindings are sorted first by + // binding number before type. + bufferDescriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer2 = device.CreateBuffer(&bufferDescriptor); + + // Populate the values + queue.WriteBuffer(buffer0, offsets[0], &values[0], sizeof(uint32_t)); + queue.WriteBuffer(buffer2, offsets[1], &values[1], sizeof(uint32_t)); + queue.WriteBuffer(buffer3, offsets[2], &values[2], sizeof(uint32_t)); + + wgpu::Buffer outputBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Storage, {0, 0, 0}); + + // Create the bind group and bind group layout. + // Note: The order of the binding numbers are intentionally different and not in increasing + // order. + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, { + {3, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, + {2, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer, true}, + {4, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}, + }); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, + { + {0, buffer0, 0, sizeof(uint32_t)}, + {3, buffer3, 0, sizeof(uint32_t)}, + {2, buffer2, 0, sizeof(uint32_t)}, + {4, outputBuffer, 0, 3 * sizeof(uint32_t)}, + }); + + wgpu::ComputePipelineDescriptor pipelineDescriptor; + pipelineDescriptor.computeStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(std140, set = 0, binding = 2) uniform Buffer2 { + uint value2; + }; + layout(std430, set = 0, binding = 3) readonly buffer Buffer3 { + uint value3; + }; + layout(std430, set = 0, binding = 0) readonly buffer Buffer0 { + uint value0; + }; + layout(std430, set = 0, binding = 4) buffer OutputBuffer { + uvec3 outputBuffer; + }; + void main() { + outputBuffer = uvec3(value0, value2, value3); + } + )"); + pipelineDescriptor.computeStage.entryPoint = "main"; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDescriptor); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetPipeline(pipeline); + computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data()); + computePassEncoder.Dispatch(1); + computePassEncoder.EndPass(); + + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_BUFFER_U32_RANGE_EQ(values.data(), outputBuffer, 0, values.size()); +} + +// Test that visibility of bindings in BindGroupLayout can be none +// This test passes by not asserting or crashing. +TEST_P(BindGroupTests, BindGroupLayoutVisibilityCanBeNone) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::None, + wgpu::BindingType::UniformBuffer}; + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = 1; + descriptor.entries = &entry; + wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&descriptor); + + wgpu::RenderPipeline pipeline = MakeTestPipeline(renderPass, {}, {layout}); + + std::array color = {1, 0, 0, 1}; + wgpu::Buffer uniformBuffer = + utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, sizeof(color)}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(3); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); +} + +// Regression test for crbug.com/dawn/448 that dynamic buffer bindings can have None visibility. +TEST_P(BindGroupTests, DynamicBindingNoneVisibility) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::None, + wgpu::BindingType::UniformBuffer, true}; + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = 1; + descriptor.entries = &entry; + wgpu::BindGroupLayout layout = device.CreateBindGroupLayout(&descriptor); + + wgpu::RenderPipeline pipeline = MakeTestPipeline(renderPass, {}, {layout}); + + std::array color = {1, 0, 0, 1}; + wgpu::Buffer uniformBuffer = + utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, layout, {{0, uniformBuffer, 0, sizeof(color)}}); + + uint32_t dynamicOffset = 0; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup, 1, &dynamicOffset); + pass.Draw(3); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); +} + +// Test that bind group bindings may have unbounded and arbitrary binding numbers +TEST_P(BindGroupTests, ArbitraryBindingNumbers) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { - const vec2 pos[3] = vec2[3](vec2(-1.f, -1.f), vec2(1.f, -1.f), vec2(-1.f, 1.f)); + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 - layout (std140, set = 0, binding = 0) uniform fragmentUniformBuffer1 { + layout (set = 0, binding = 953) uniform ubo1 { vec4 color1; }; - layout (std140, set = 1, binding = 0) uniform fragmentUniformBuffer2 { + layout (set = 0, binding = 47) uniform ubo2 { vec4 color2; }; - layout (std140, set = 2, binding = 0) uniform fragmentUniformBuffer3 { + layout (set = 0, binding = 111) uniform ubo3 { vec4 color3; }; - layout (std140, set = 3, binding = 0) uniform fragmentUniformBuffer4 { - vec4 color4; - }; layout(location = 0) out vec4 fragColor; void main() { - fragColor = color1 + color2 + color3 + color4; + fragColor = color1 + 2 * color2 + 4 * color3; })"); - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout( - device, { - { 0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer } - }); - dawn::PipelineLayout pipelineLayout = MakeBasicPipelineLayout( - device, { layout, layout, layout, layout }); - utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); - pipelineDescriptor.layout = pipelineLayout; - pipelineDescriptor.cVertexStage.module = vsModule; + pipelineDescriptor.vertexStage.module = vsModule; pipelineDescriptor.cFragmentStage.module = fsModule; - pipelineDescriptor.cColorStates[0]->format = renderPass.colorFormat; + pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); + + wgpu::Buffer black = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.f, 0.f, 0.f, 0.f}); + wgpu::Buffer red = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.251f, 0.0f, 0.0f, 0.0f}); + wgpu::Buffer green = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.0f, 0.251f, 0.0f, 0.0f}); + wgpu::Buffer blue = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Uniform, {0.0f, 0.0f, 0.251f, 0.0f}); + + auto DoTest = [&](wgpu::Buffer color1, wgpu::Buffer color2, wgpu::Buffer color3, RGBA8 filled) { + auto DoTestInner = [&](wgpu::BindGroup bindGroup) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(3); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 1); + }; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + utils::BindingInitializationHelper bindings[] = { + {953, color1, 0, 4 * sizeof(float)}, // + {47, color2, 0, 4 * sizeof(float)}, // + {111, color3, 0, 4 * sizeof(float)}, // + }; - pass.SetPipeline(pipeline); + // Should work regardless of what order the bindings are specified in. + DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {bindings[0], bindings[1], bindings[2]})); + DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {bindings[1], bindings[0], bindings[2]})); + DoTestInner(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {bindings[2], bindings[0], bindings[1]})); + }; + + // first color is normal, second is 2x, third is 3x. + DoTest(black, black, black, RGBA8(0, 0, 0, 0)); + + // Check the first binding maps to the first slot. We know this because the colors are + // multiplied 1x. + DoTest(red, black, black, RGBA8(64, 0, 0, 0)); + DoTest(green, black, black, RGBA8(0, 64, 0, 0)); + DoTest(blue, black, black, RGBA8(0, 0, 64, 0)); + + // Use multiple bindings and check the second color maps to the second slot. + // We know this because the second slot is multiplied 2x. + DoTest(green, blue, black, RGBA8(0, 64, 128, 0)); + DoTest(blue, green, black, RGBA8(0, 128, 64, 0)); + DoTest(red, green, black, RGBA8(64, 128, 0, 0)); + + // Use multiple bindings and check the third color maps to the third slot. + // We know this because the third slot is multiplied 4x. + DoTest(black, blue, red, RGBA8(255, 0, 128, 0)); + DoTest(blue, black, green, RGBA8(0, 255, 64, 0)); + DoTest(red, black, blue, RGBA8(64, 0, 255, 0)); +} + +// This is a regression test for crbug.com/dawn/355 which tests that destruction of a bind group +// that holds the last reference to its bind group layout does not result in a use-after-free. In +// the bug, the destructor of BindGroupBase, when destroying member mLayout, +// Ref assigns to Ref::mPointee, AFTER calling Release(). After the BGL is +// destroyed, the storage for |mPointee| has been freed. +TEST_P(BindGroupTests, LastReferenceToBindGroupLayout) { + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = sizeof(float); + bufferDesc.usage = wgpu::BufferUsage::Uniform; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc); + + wgpu::BindGroup bg; + { + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}); + bg = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, sizeof(float)}}); + } +} - std::array color = { 0.25, 0, 0, 0.25 }; - dawn::Buffer uniformBuffer = utils::CreateBufferFromData( - device, &color, sizeof(color), dawn::BufferUsageBit::Uniform); - dawn::BindGroup bindGroup = utils::MakeBindGroup( - device, layout, { { 0, uniformBuffer, 0, sizeof(color) } }); +// Test that bind groups with an empty bind group layout may be created and used. +TEST_P(BindGroupTests, EmptyLayout) { + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout(device, {}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {}); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.SetBindGroup(1, bindGroup, 0, nullptr); - pass.SetBindGroup(2, bindGroup, 0, nullptr); - pass.SetBindGroup(3, bindGroup, 0, nullptr); - pass.Draw(3, 1, 0, 0); + wgpu::ComputePipelineDescriptor pipelineDesc; + pipelineDesc.layout = utils::MakeBasicPipelineLayout(device, &bgl); + pipelineDesc.computeStage.entryPoint = "main"; + pipelineDesc.computeStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + void main() { + })"); + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(pipeline); - pass.Draw(3, 1, 0, 0); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); pass.EndPass(); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); +} - RGBA8 filled(255, 0, 0, 255); - RGBA8 notFilled(0, 0, 0, 0); - int min = 1, max = kRTSize - 3; - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, min); - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, max, min); - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, min, max); - EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, max, max); +// Test creating a BGL with a storage buffer binding but declared readonly in the shader works. +// This is a regression test for crbug.com/dawn/410 which tests that it can successfully compile and +// execute the shader. +TEST_P(BindGroupTests, ReadonlyStorage) { + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + + pipelineDescriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); + gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); + })"); + + pipelineDescriptor.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0) readonly buffer buffer0 { + vec4 color; + }; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = color; + })"); + + constexpr uint32_t kRTSize = 4; + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat; + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); + + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&pipelineDescriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + std::array greenColor = {0, 1, 0, 1}; + wgpu::Buffer storageBuffer = utils::CreateBufferFromData( + device, &greenColor, sizeof(greenColor), wgpu::BufferUsage::Storage); + + pass.SetPipeline(renderPipeline); + pass.SetBindGroup(0, utils::MakeBindGroup(device, bgl, {{0, storageBuffer}})); + pass.Draw(3); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0); +} + +// Test that creating a large bind group, with each binding type at the max count, works and can be +// used correctly. The test loads a different value from each binding, and writes 1 to a storage +// buffer if all values are correct. +TEST_P(BindGroupTests, ReallyLargeBindGroup) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed()); + + std::string interface = "#version 450\n"; + std::string body; + uint32_t binding = 0; + uint32_t expectedValue = 42; + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + + auto CreateTextureWithRedData = [&](uint32_t value, wgpu::TextureUsage usage) { + wgpu::TextureDescriptor textureDesc = {}; + textureDesc.usage = wgpu::TextureUsage::CopyDst | usage; + textureDesc.size = {1, 1, 1}; + textureDesc.format = wgpu::TextureFormat::R32Uint; + wgpu::Texture texture = device.CreateTexture(&textureDesc); + + wgpu::Buffer textureData = + utils::CreateBufferFromData(device, wgpu::BufferUsage::CopySrc, {expectedValue}); + wgpu::BufferCopyView bufferCopyView = {}; + bufferCopyView.buffer = textureData; + bufferCopyView.bytesPerRow = 256; + + wgpu::TextureCopyView textureCopyView = {}; + textureCopyView.texture = texture; + + wgpu::Extent3D copySize = {1, 1, 1}; + + commandEncoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + return texture; + }; + + std::vector bgEntries; + static_assert(kMaxSampledTexturesPerShaderStage == kMaxSamplersPerShaderStage, + "Please update this test"); + body += "result = 0;\n"; + for (uint32_t i = 0; i < kMaxSampledTexturesPerShaderStage; ++i) { + wgpu::Texture texture = + CreateTextureWithRedData(expectedValue, wgpu::TextureUsage::Sampled); + bgEntries.push_back({binding, nullptr, 0, 0, nullptr, texture.CreateView()}); + + interface += "layout(set = 0, binding = " + std::to_string(binding++) + + ") uniform utexture2D tex" + std::to_string(i) + ";\n"; + + wgpu::SamplerDescriptor samplerDesc = {}; + bgEntries.push_back({binding, nullptr, 0, 0, device.CreateSampler(&samplerDesc), nullptr}); + + interface += "layout(set = 0, binding = " + std::to_string(binding++) + + ") uniform sampler samp" + std::to_string(i) + ";\n"; + + body += "if (texelFetch(usampler2D(tex" + std::to_string(i) + ", samp" + std::to_string(i) + + "), ivec2(0, 0), 0).r != " + std::to_string(expectedValue++) + ") {\n"; + body += " return;\n"; + body += "}\n"; + } + for (uint32_t i = 0; i < kMaxStorageTexturesPerShaderStage; ++i) { + wgpu::Texture texture = + CreateTextureWithRedData(expectedValue, wgpu::TextureUsage::Storage); + bgEntries.push_back({binding, nullptr, 0, 0, nullptr, texture.CreateView()}); + + interface += "layout(set = 0, binding = " + std::to_string(binding++) + + ", r32ui) uniform readonly uimage2D image" + std::to_string(i) + ";\n"; + + body += "if (imageLoad(image" + std::to_string(i) + + ", ivec2(0, 0)).r != " + std::to_string(expectedValue++) + ") {\n"; + body += " return;\n"; + body += "}\n"; + } + for (uint32_t i = 0; i < kMaxUniformBuffersPerShaderStage; ++i) { + wgpu::Buffer buffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Uniform, {expectedValue, 0, 0, 0}); + bgEntries.push_back({binding, buffer, 0, 4 * sizeof(uint32_t), nullptr, nullptr}); + + interface += "layout(std140, set = 0, binding = " + std::to_string(binding++) + + ") uniform UBuf" + std::to_string(i) + " {\n"; + interface += " uint ubuf" + std::to_string(i) + ";\n"; + interface += "};\n"; + + body += "if (ubuf" + std::to_string(i) + " != " + std::to_string(expectedValue++) + ") {\n"; + body += " return;\n"; + body += "}\n"; + } + // Save one storage buffer for writing the result + for (uint32_t i = 0; i < kMaxStorageBuffersPerShaderStage - 1; ++i) { + wgpu::Buffer buffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Storage, {expectedValue}); + bgEntries.push_back({binding, buffer, 0, sizeof(uint32_t), nullptr, nullptr}); + + interface += "layout(std430, set = 0, binding = " + std::to_string(binding++) + + ") readonly buffer SBuf" + std::to_string(i) + " {\n"; + interface += " uint sbuf" + std::to_string(i) + ";\n"; + interface += "};\n"; + + body += "if (sbuf" + std::to_string(i) + " != " + std::to_string(expectedValue++) + ") {\n"; + body += " return;\n"; + body += "}\n"; + } + + wgpu::Buffer result = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc, {0}); + bgEntries.push_back({binding, result, 0, sizeof(uint32_t), nullptr, nullptr}); + + interface += "layout(std430, set = 0, binding = " + std::to_string(binding++) + + ") writeonly buffer Result {\n"; + interface += " uint result;\n"; + interface += "};\n"; + + body += "result = 1;\n"; + + std::string shader = interface + "void main() {\n" + body + "}\n"; + + wgpu::ComputePipelineDescriptor cpDesc; + cpDesc.computeStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, shader.c_str()); + cpDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc); + + wgpu::BindGroupDescriptor bgDesc = {}; + bgDesc.layout = cp.GetBindGroupLayout(0); + bgDesc.entryCount = static_cast(bgEntries.size()); + bgDesc.entries = bgEntries.data(); + + wgpu::BindGroup bg = device.CreateBindGroup(&bgDesc); + + wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1, 1, 1); + pass.EndPass(); + + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_BUFFER_U32_EQ(1, result, 0); } -DAWN_INSTANTIATE_TEST(BindGroupTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(BindGroupTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/BufferTests.cpp b/third_party/dawn/src/tests/end2end/BufferTests.cpp index 6b691d9049b..f7cda9591e8 100644 --- a/third_party/dawn/src/tests/end2end/BufferTests.cpp +++ b/third_party/dawn/src/tests/end2end/BufferTests.cpp @@ -17,45 +17,74 @@ #include class BufferMapReadTests : public DawnTest { - protected: - static void MapReadCallback(DawnBufferMapAsyncStatus status, - const void* data, - uint64_t, - void* userdata) { - ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, status); - ASSERT_NE(nullptr, data); + protected: + static void MapReadCallback(WGPUBufferMapAsyncStatus status, + const void* data, + uint64_t, + void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + ASSERT_NE(nullptr, data); + + static_cast(userdata)->mappedData = data; + } - static_cast(userdata)->mappedData = data; - } + const void* MapReadAsyncAndWait(const wgpu::Buffer& buffer) { + buffer.MapReadAsync(MapReadCallback, this); - const void* MapReadAsyncAndWait(const dawn::Buffer& buffer) { - buffer.MapReadAsync(MapReadCallback, this); + while (mappedData == nullptr) { + WaitABit(); + } - while (mappedData == nullptr) { - WaitABit(); - } + return mappedData; + } - return mappedData; - } + void UnmapBuffer(const wgpu::Buffer& buffer) { + buffer.Unmap(); + mappedData = nullptr; + } - private: - const void* mappedData = nullptr; + private: + const void* mappedData = nullptr; }; // Test that the simplest map read works. TEST_P(BufferMapReadTests, SmallReadAtZero) { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); uint32_t myData = 0x01020304; - buffer.SetSubData(0, sizeof(myData), &myData); + queue.WriteBuffer(buffer, 0, &myData, sizeof(myData)); const void* mappedData = MapReadAsyncAndWait(buffer); ASSERT_EQ(myData, *reinterpret_cast(mappedData)); - buffer.Unmap(); + UnmapBuffer(buffer); +} + +// Map, read and unmap twice. Test that both of these two iterations work. +TEST_P(BufferMapReadTests, MapTwice) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + uint32_t myData = 0x01020304; + queue.WriteBuffer(buffer, 0, &myData, sizeof(myData)); + + const void* mappedData = MapReadAsyncAndWait(buffer); + EXPECT_EQ(myData, *reinterpret_cast(mappedData)); + + UnmapBuffer(buffer); + + myData = 0x05060708; + queue.WriteBuffer(buffer, 0, &myData, sizeof(myData)); + + const void* mappedData1 = MapReadAsyncAndWait(buffer); + EXPECT_EQ(myData, *reinterpret_cast(mappedData1)); + + UnmapBuffer(buffer); } // Test mapping a large buffer. @@ -66,58 +95,130 @@ TEST_P(BufferMapReadTests, LargeRead) { myData.push_back(i); } - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = static_cast(kDataSize * sizeof(uint32_t)); - descriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); - buffer.SetSubData(0, kDataSize * sizeof(uint32_t), myData.data()); + queue.WriteBuffer(buffer, 0, myData.data(), kDataSize * sizeof(uint32_t)); const void* mappedData = MapReadAsyncAndWait(buffer); ASSERT_EQ(0, memcmp(mappedData, myData.data(), kDataSize * sizeof(uint32_t))); - buffer.Unmap(); + UnmapBuffer(buffer); +} + +// Test mapping a zero-sized buffer. +TEST_P(BufferMapReadTests, ZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + MapReadAsyncAndWait(buffer); + UnmapBuffer(buffer); +} + +// Test the result of GetMappedRange when mapped for reading. +TEST_P(BufferMapReadTests, GetMappedRange) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + const void* mappedData = MapReadAsyncAndWait(buffer); + ASSERT_EQ(buffer.GetConstMappedRange(), mappedData); + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + UnmapBuffer(buffer); +} + +// Test the result of GetMappedRange when mapped for reading for a zero-sized buffer. +TEST_P(BufferMapReadTests, GetMappedRangeZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + const void* mappedData = MapReadAsyncAndWait(buffer); + ASSERT_EQ(buffer.GetConstMappedRange(), mappedData); + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + UnmapBuffer(buffer); } -DAWN_INSTANTIATE_TEST(BufferMapReadTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(BufferMapReadTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); class BufferMapWriteTests : public DawnTest { - protected: - static void MapWriteCallback(DawnBufferMapAsyncStatus status, - void* data, - uint64_t, - void* userdata) { - ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, status); - ASSERT_NE(nullptr, data); + protected: + static void MapWriteCallback(WGPUBufferMapAsyncStatus status, + void* data, + uint64_t, + void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + ASSERT_NE(nullptr, data); + + static_cast(userdata)->mappedData = data; + } - static_cast(userdata)->mappedData = data; - } + void* MapWriteAsyncAndWait(const wgpu::Buffer& buffer) { + buffer.MapWriteAsync(MapWriteCallback, this); - void* MapWriteAsyncAndWait(const dawn::Buffer& buffer) { - buffer.MapWriteAsync(MapWriteCallback, this); + while (mappedData == nullptr) { + WaitABit(); + } + + // Ensure the prior write's status is updated. + void* resultPointer = mappedData; + mappedData = nullptr; - while (mappedData == nullptr) { - WaitABit(); - } + return resultPointer; + } - return mappedData; - } + void UnmapBuffer(const wgpu::Buffer& buffer) { + buffer.Unmap(); + mappedData = nullptr; + } - private: - void* mappedData = nullptr; + private: + void* mappedData = nullptr; }; // Test that the simplest map write works. TEST_P(BufferMapWriteTests, SmallWriteAtZero) { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); uint32_t myData = 2934875; void* mappedData = MapWriteAsyncAndWait(buffer); memcpy(mappedData, &myData, sizeof(myData)); - buffer.Unmap(); + UnmapBuffer(buffer); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +// Map, write and unmap twice. Test that both of these two iterations work. +TEST_P(BufferMapWriteTests, MapTwice) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + uint32_t myData = 2934875; + void* mappedData = MapWriteAsyncAndWait(buffer); + memcpy(mappedData, &myData, sizeof(myData)); + UnmapBuffer(buffer); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); + + myData = 9999999; + void* mappedData1 = MapWriteAsyncAndWait(buffer); + memcpy(mappedData1, &myData, sizeof(myData)); + UnmapBuffer(buffer); EXPECT_BUFFER_U32_EQ(myData, buffer, 0); } @@ -130,174 +231,551 @@ TEST_P(BufferMapWriteTests, LargeWrite) { myData.push_back(i); } - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = static_cast(kDataSize * sizeof(uint32_t)); - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); void* mappedData = MapWriteAsyncAndWait(buffer); memcpy(mappedData, myData.data(), kDataSize * sizeof(uint32_t)); - buffer.Unmap(); + UnmapBuffer(buffer); EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffer, 0, kDataSize); } -DAWN_INSTANTIATE_TEST(BufferMapWriteTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +// Test mapping a zero-sized buffer. +TEST_P(BufferMapWriteTests, ZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); -class BufferSetSubDataTests : public DawnTest { -}; + MapWriteAsyncAndWait(buffer); + UnmapBuffer(buffer); +} + +// Stress test mapping many buffers. +TEST_P(BufferMapWriteTests, ManyWrites) { + constexpr uint32_t kDataSize = 1000; + std::vector myData; + for (uint32_t i = 0; i < kDataSize; ++i) { + myData.push_back(i); + } + + std::vector buffers; + + constexpr uint32_t kBuffers = 100; + for (uint32_t i = 0; i < kBuffers; ++i) { + wgpu::BufferDescriptor descriptor; + descriptor.size = static_cast(kDataSize * sizeof(uint32_t)); + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + void* mappedData = MapWriteAsyncAndWait(buffer); + memcpy(mappedData, myData.data(), kDataSize * sizeof(uint32_t)); + UnmapBuffer(buffer); + + buffers.push_back(buffer); // Destroy buffers upon return. + } + + for (uint32_t i = 0; i < kBuffers; ++i) { + EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffers[i], 0, kDataSize); + } +} -// Test the simplest set sub data: setting one u32 at offset 0. -TEST_P(BufferSetSubDataTests, SmallDataAtZero) { - dawn::BufferDescriptor descriptor; +// Test the result of GetMappedRange when mapped for writing. +TEST_P(BufferMapWriteTests, GetMappedRange) { + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + void* mappedData = MapWriteAsyncAndWait(buffer); + ASSERT_EQ(buffer.GetMappedRange(), mappedData); + ASSERT_EQ(buffer.GetMappedRange(), buffer.GetConstMappedRange()); + ASSERT_NE(buffer.GetMappedRange(), nullptr); + UnmapBuffer(buffer); +} + +// Test the result of GetMappedRange when mapped for writing for a zero-sized buffer. +TEST_P(BufferMapWriteTests, GetMappedRangeZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + void* mappedData = MapWriteAsyncAndWait(buffer); + ASSERT_EQ(buffer.GetMappedRange(), mappedData); + ASSERT_EQ(buffer.GetMappedRange(), buffer.GetConstMappedRange()); + ASSERT_NE(buffer.GetMappedRange(), nullptr); + UnmapBuffer(buffer); +} + +DAWN_INSTANTIATE_TEST(BufferMapWriteTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +class BufferMappingTests : public DawnTest { + protected: + void MapAsyncAndWait(const wgpu::Buffer& buffer, + wgpu::MapMode mode, + size_t offset, + size_t size) { + bool done = false; + buffer.MapAsync( + mode, offset, size, + [](WGPUBufferMapAsyncStatus status, void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + *static_cast(userdata) = true; + }, + &done); + + while (!done) { + WaitABit(); + } + } + + wgpu::Buffer CreateMapReadBuffer(uint64_t size) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + return device.CreateBuffer(&descriptor); + } - uint32_t value = 0x01020304; - buffer.SetSubData(0, sizeof(value), &value); + wgpu::Buffer CreateMapWriteBuffer(uint64_t size) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + return device.CreateBuffer(&descriptor); + } +}; + +void CheckMapping(const void* actual, const void* expected, size_t size) { + EXPECT_NE(actual, nullptr); + if (actual != nullptr) { + EXPECT_EQ(0, memcmp(actual, expected, size)); + } +} + +// Test that the simplest map read works +TEST_P(BufferMappingTests, MapRead_Basic) { + wgpu::Buffer buffer = CreateMapReadBuffer(4); + + uint32_t myData = 0x01020304; + constexpr size_t kSize = sizeof(myData); + queue.WriteBuffer(buffer, 0, &myData, kSize); - EXPECT_BUFFER_U32_EQ(value, buffer, 0); + MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 4); + CheckMapping(buffer.GetConstMappedRange(), &myData, kSize); + CheckMapping(buffer.GetConstMappedRange(0, kSize), &myData, kSize); + buffer.Unmap(); } -// Test that SetSubData offset works. -TEST_P(BufferSetSubDataTests, SmallDataAtOffset) { - dawn::BufferDescriptor descriptor; - descriptor.size = 4000; - descriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); +// Test map-reading a zero-sized buffer. +TEST_P(BufferMappingTests, MapRead_ZeroSized) { + wgpu::Buffer buffer = CreateMapReadBuffer(0); - constexpr uint64_t kOffset = 2000; - uint32_t value = 0x01020304; - buffer.SetSubData(kOffset, sizeof(value), &value); + MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 0); + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + buffer.Unmap(); +} - EXPECT_BUFFER_U32_EQ(value, buffer, kOffset); +// Test map-reading with a non-zero offset +TEST_P(BufferMappingTests, MapRead_NonZeroOffset) { + wgpu::Buffer buffer = CreateMapReadBuffer(8); + + uint32_t myData[2] = {0x01020304, 0x05060708}; + queue.WriteBuffer(buffer, 0, &myData, sizeof(myData)); + + MapAsyncAndWait(buffer, wgpu::MapMode::Read, 4, 4); + ASSERT_EQ(myData[1], *static_cast(buffer.GetConstMappedRange(4))); + buffer.Unmap(); } -// Stress test for many calls to SetSubData -TEST_P(BufferSetSubDataTests, ManySetSubData) { - // Test failing on Mac Metal Intel, maybe because Metal runs out of space to encode commands. - // See https://bugs.chromium.org/p/dawn/issues/detail?id=108 - DAWN_SKIP_TEST_IF(IsMacOS() && IsMetal() && IsIntel()); +// Map read and unmap twice. Test that both of these two iterations work. +TEST_P(BufferMappingTests, MapRead_Twice) { + wgpu::Buffer buffer = CreateMapReadBuffer(4); - // Note: Increasing the size of the buffer will likely cause timeout issues. - // In D3D12, timeout detection occurs when the GPU scheduler tries but cannot preempt the task - // executing these commands in-flight. If this takes longer than ~2s, a device reset occurs and - // fails the test. Since GPUs may or may not complete by then, this test must be disabled OR - // modified to be well-below the timeout limit. - constexpr uint64_t kSize = 4000 * 1000; - constexpr uint32_t kElements = 500 * 500; - dawn::BufferDescriptor descriptor; - descriptor.size = kSize; - descriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + uint32_t myData = 0x01020304; + queue.WriteBuffer(buffer, 0, &myData, sizeof(myData)); + + MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 4); + ASSERT_EQ(myData, *static_cast(buffer.GetConstMappedRange())); + buffer.Unmap(); + + myData = 0x05060708; + queue.WriteBuffer(buffer, 0, &myData, sizeof(myData)); + + MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 4); + ASSERT_EQ(myData, *static_cast(buffer.GetConstMappedRange())); + buffer.Unmap(); +} - std::vector expectedData; - for (uint32_t i = 0; i < kElements; ++i) { - buffer.SetSubData(i * sizeof(uint32_t), sizeof(i), &i); - expectedData.push_back(i); +// Test map-reading a large buffer. +TEST_P(BufferMappingTests, MapRead_Large) { + constexpr uint32_t kDataSize = 1000 * 1000; + constexpr size_t kByteSize = kDataSize * sizeof(uint32_t); + wgpu::Buffer buffer = CreateMapReadBuffer(kByteSize); + + std::vector myData; + for (uint32_t i = 0; i < kDataSize; ++i) { + myData.push_back(i); } + queue.WriteBuffer(buffer, 0, myData.data(), kByteSize); + + MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, kByteSize); + EXPECT_EQ(nullptr, buffer.GetConstMappedRange(0, kByteSize + 4)); + EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(), myData.data(), kByteSize)); + EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(8), myData.data() + 2, kByteSize - 8)); + EXPECT_EQ( + 0, memcmp(buffer.GetConstMappedRange(8, kByteSize - 8), myData.data() + 2, kByteSize - 8)); + buffer.Unmap(); - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); + MapAsyncAndWait(buffer, wgpu::MapMode::Read, 12, kByteSize - 12); + EXPECT_EQ(nullptr, buffer.GetConstMappedRange(16, kByteSize - 12)); + EXPECT_EQ(nullptr, buffer.GetConstMappedRange()); + EXPECT_EQ(nullptr, buffer.GetConstMappedRange(8)); + EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(12), myData.data() + 3, kByteSize - 12)); + EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(20), myData.data() + 5, kByteSize - 20)); + EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(20, kByteSize - 24), myData.data() + 5, + kByteSize - 44)); + buffer.Unmap(); } -// Test using SetSubData for lots of data -TEST_P(BufferSetSubDataTests, LargeSetSubData) { - constexpr uint64_t kSize = 4000 * 1000; - constexpr uint32_t kElements = 1000 * 1000; - dawn::BufferDescriptor descriptor; - descriptor.size = kSize; - descriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); +// Test that GetConstMappedRange works inside map-read callback +TEST_P(BufferMappingTests, MapRead_InCallback) { + constexpr size_t kBufferSize = 8; + wgpu::Buffer buffer = CreateMapReadBuffer(kBufferSize); + + uint32_t myData[2] = {0x01020304, 0x05060708}; + constexpr size_t kSize = sizeof(myData); + queue.WriteBuffer(buffer, 0, &myData, kSize); + + struct UserData { + bool done; + wgpu::Buffer buffer; + void* expected; + }; + UserData user{false, buffer, &myData}; + + buffer.MapAsync( + wgpu::MapMode::Read, 0, kBufferSize, + [](WGPUBufferMapAsyncStatus status, void* userdata) { + UserData* user = static_cast(userdata); + + EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status); + if (status == WGPUBufferMapAsyncStatus_Success) { + CheckMapping(user->buffer.GetConstMappedRange(), user->expected, kSize); + CheckMapping(user->buffer.GetConstMappedRange(0, kSize), user->expected, kSize); + + CheckMapping(user->buffer.GetConstMappedRange(4, 4), + static_cast(user->expected) + 1, sizeof(uint32_t)); + + user->buffer.Unmap(); + } + user->done = true; + }, + &user); - std::vector expectedData; - for (uint32_t i = 0; i < kElements; ++i) { - expectedData.push_back(i); + while (!user.done) { + WaitABit(); } +} + +// Test that the simplest map write works. +TEST_P(BufferMappingTests, MapWrite_Basic) { + wgpu::Buffer buffer = CreateMapWriteBuffer(4); + + uint32_t myData = 2934875; + MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4); + ASSERT_NE(nullptr, buffer.GetMappedRange()); + ASSERT_NE(nullptr, buffer.GetConstMappedRange()); + memcpy(buffer.GetMappedRange(), &myData, sizeof(myData)); + buffer.Unmap(); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +// Test that the simplest map write works with a range. +TEST_P(BufferMappingTests, MapWrite_BasicRange) { + wgpu::Buffer buffer = CreateMapWriteBuffer(4); + + uint32_t myData = 2934875; + MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4); + ASSERT_NE(nullptr, buffer.GetMappedRange(0, 4)); + ASSERT_NE(nullptr, buffer.GetConstMappedRange(0, 4)); + memcpy(buffer.GetMappedRange(), &myData, sizeof(myData)); + buffer.Unmap(); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +// Test map-writing a zero-sized buffer. +TEST_P(BufferMappingTests, MapWrite_ZeroSized) { + wgpu::Buffer buffer = CreateMapWriteBuffer(0); + + MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 0); + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + ASSERT_NE(buffer.GetMappedRange(), nullptr); + buffer.Unmap(); +} + +// Test map-writing with a non-zero offset. +TEST_P(BufferMappingTests, MapWrite_NonZeroOffset) { + wgpu::Buffer buffer = CreateMapWriteBuffer(8); + + uint32_t myData = 2934875; + MapAsyncAndWait(buffer, wgpu::MapMode::Write, 4, 4); + memcpy(buffer.GetMappedRange(4), &myData, sizeof(myData)); + buffer.Unmap(); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 4); +} - buffer.SetSubData(0, kElements * sizeof(uint32_t), expectedData.data()); +// Map, write and unmap twice. Test that both of these two iterations work. +TEST_P(BufferMappingTests, MapWrite_Twice) { + wgpu::Buffer buffer = CreateMapWriteBuffer(4); - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); + uint32_t myData = 2934875; + MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4); + memcpy(buffer.GetMappedRange(), &myData, sizeof(myData)); + buffer.Unmap(); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); + + myData = 9999999; + MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4); + memcpy(buffer.GetMappedRange(), &myData, sizeof(myData)); + buffer.Unmap(); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +// Test mapping a large buffer. +TEST_P(BufferMappingTests, MapWrite_Large) { + constexpr uint32_t kDataSize = 1000 * 1000; + constexpr size_t kByteSize = kDataSize * sizeof(uint32_t); + wgpu::Buffer buffer = CreateMapWriteBuffer(kDataSize * sizeof(uint32_t)); + + std::vector myData; + for (uint32_t i = 0; i < kDataSize; ++i) { + myData.push_back(i); + } + + MapAsyncAndWait(buffer, wgpu::MapMode::Write, 12, kByteSize - 20); + EXPECT_EQ(nullptr, buffer.GetMappedRange()); + EXPECT_EQ(nullptr, buffer.GetMappedRange(0)); + EXPECT_EQ(nullptr, buffer.GetMappedRange(8)); + EXPECT_EQ(nullptr, buffer.GetMappedRange(16, kByteSize - 8)); + EXPECT_EQ(nullptr, buffer.GetMappedRange(16, kByteSize - 20)); + memcpy(buffer.GetMappedRange(12), myData.data(), kByteSize - 20); + buffer.Unmap(); + EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffer, 12, kDataSize - 5); } -DAWN_INSTANTIATE_TEST(BufferSetSubDataTests, - D3D12Backend, - MetalBackend, - OpenGLBackend, - VulkanBackend); +// Test that the map offset isn't updated when the call is an error. +TEST_P(BufferMappingTests, OffsetNotUpdatedOnError) { + uint32_t data[3] = {0xCA7, 0xB0A7, 0xBA7}; + wgpu::Buffer buffer = CreateMapReadBuffer(sizeof(data)); + queue.WriteBuffer(buffer, 0, data, sizeof(data)); + + // Map the buffer but do not wait on the result yet. + bool done = false; + buffer.MapAsync( + wgpu::MapMode::Read, 4, 4, + [](WGPUBufferMapAsyncStatus status, void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + *static_cast(userdata) = true; + }, + &done); + + // Call MapAsync another time, it is an error because the buffer is already being mapped so + // mMapOffset is not updated. + ASSERT_DEVICE_ERROR(buffer.MapAsync(wgpu::MapMode::Read, 8, 4, nullptr, nullptr)); + + while (!done) { + WaitABit(); + } + + // mMapOffset has not been updated so it should still be 4, which is data[1] + ASSERT_EQ(0, memcmp(buffer.GetConstMappedRange(4), &data[1], sizeof(uint32_t))); +} + +// Test that Get(Const)MappedRange work inside map-write callback. +TEST_P(BufferMappingTests, MapWrite_InCallbackDefault) { + wgpu::Buffer buffer = CreateMapWriteBuffer(4); + + constexpr uint32_t myData = 2934875; + constexpr size_t kSize = sizeof(myData); + + struct UserData { + bool done; + wgpu::Buffer buffer; + }; + UserData user{false, buffer}; + + buffer.MapAsync( + wgpu::MapMode::Write, 0, kSize, + [](WGPUBufferMapAsyncStatus status, void* userdata) { + UserData* user = static_cast(userdata); + + EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status); + if (status == WGPUBufferMapAsyncStatus_Success) { + EXPECT_NE(nullptr, user->buffer.GetConstMappedRange()); + void* ptr = user->buffer.GetMappedRange(); + EXPECT_NE(nullptr, ptr); + if (ptr != nullptr) { + uint32_t data = myData; + memcpy(ptr, &data, kSize); + } + + user->buffer.Unmap(); + } + user->done = true; + }, + &user); + + while (!user.done) { + WaitABit(); + } + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +// Test that Get(Const)MappedRange with range work inside map-write callback. +TEST_P(BufferMappingTests, MapWrite_InCallbackRange) { + wgpu::Buffer buffer = CreateMapWriteBuffer(4); + + constexpr uint32_t myData = 2934875; + constexpr size_t kSize = sizeof(myData); + + struct UserData { + bool done; + wgpu::Buffer buffer; + }; + UserData user{false, buffer}; + + buffer.MapAsync( + wgpu::MapMode::Write, 0, kSize, + [](WGPUBufferMapAsyncStatus status, void* userdata) { + UserData* user = static_cast(userdata); + + EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status); + if (status == WGPUBufferMapAsyncStatus_Success) { + EXPECT_NE(nullptr, user->buffer.GetConstMappedRange(0, kSize)); + void* ptr = user->buffer.GetMappedRange(0, kSize); + EXPECT_NE(nullptr, ptr); + if (ptr != nullptr) { + uint32_t data = myData; + memcpy(ptr, &data, kSize); + } + + user->buffer.Unmap(); + } + user->done = true; + }, + &user); + + while (!user.done) { + WaitABit(); + } + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +DAWN_INSTANTIATE_TEST(BufferMappingTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); class CreateBufferMappedTests : public DawnTest { - protected: - static void MapReadCallback(DawnBufferMapAsyncStatus status, - const void* data, - uint64_t, - void* userdata) { - ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, status); - ASSERT_NE(nullptr, data); + protected: + static void MapReadCallback(WGPUBufferMapAsyncStatus status, + const void* data, + uint64_t, + void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + ASSERT_NE(nullptr, data); + + static_cast(userdata)->mappedData = data; + } - static_cast(userdata)->mappedData = data; - } + const void* MapReadAsyncAndWait(const wgpu::Buffer& buffer) { + buffer.MapReadAsync(MapReadCallback, this); - const void* MapReadAsyncAndWait(const dawn::Buffer& buffer) { - buffer.MapReadAsync(MapReadCallback, this); + while (mappedData == nullptr) { + WaitABit(); + } - while (mappedData == nullptr) { - WaitABit(); - } + return mappedData; + } - return mappedData; - } + void UnmapBuffer(const wgpu::Buffer& buffer) { + buffer.Unmap(); + mappedData = nullptr; + } + + void CheckResultStartsZeroed(const wgpu::CreateBufferMappedResult& result, uint64_t size) { + ASSERT_EQ(result.dataLength, size); + for (uint64_t i = 0; i < result.dataLength; ++i) { + uint8_t value = *(reinterpret_cast(result.data) + i); + ASSERT_EQ(value, 0u); + } + } - private: - const void* mappedData = nullptr; + wgpu::CreateBufferMappedResult CreateBufferMapped(wgpu::BufferUsage usage, uint64_t size) { + wgpu::BufferDescriptor descriptor = {}; + descriptor.size = size; + descriptor.usage = usage; + + wgpu::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); + CheckResultStartsZeroed(result, size); + return result; + } + + wgpu::CreateBufferMappedResult CreateBufferMappedWithData(wgpu::BufferUsage usage, + const std::vector& data) { + size_t byteLength = data.size() * sizeof(uint32_t); + wgpu::CreateBufferMappedResult result = CreateBufferMapped(usage, byteLength); + memcpy(result.data, data.data(), byteLength); + + return result; + } + + private: + const void* mappedData = nullptr; }; // Test that the simplest CreateBufferMapped works for MapWrite buffers. TEST_P(CreateBufferMappedTests, MapWriteUsageSmall) { - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - uint32_t myData = 230502; - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - memcpy(result.data, &myData, sizeof(myData)); - result.buffer.Unmap(); - + wgpu::CreateBufferMappedResult result = CreateBufferMappedWithData( + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(result.buffer); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); } // Test that the simplest CreateBufferMapped works for MapRead buffers. TEST_P(CreateBufferMappedTests, MapReadUsageSmall) { - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapRead; - uint32_t myData = 230502; - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - memcpy(result.data, &myData, sizeof(myData)); - result.buffer.Unmap(); + wgpu::CreateBufferMappedResult result = + CreateBufferMappedWithData(wgpu::BufferUsage::MapRead, {myData}); + UnmapBuffer(result.buffer); const void* mappedData = MapReadAsyncAndWait(result.buffer); ASSERT_EQ(myData, *reinterpret_cast(mappedData)); - result.buffer.Unmap(); + UnmapBuffer(result.buffer); } // Test that the simplest CreateBufferMapped works for non-mappable buffers. TEST_P(CreateBufferMappedTests, NonMappableUsageSmall) { - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::TransferSrc; - uint32_t myData = 4239; - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - memcpy(result.data, &myData, sizeof(myData)); - result.buffer.Unmap(); + wgpu::CreateBufferMappedResult result = + CreateBufferMappedWithData(wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(result.buffer); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); } @@ -310,15 +788,9 @@ TEST_P(CreateBufferMappedTests, MapWriteUsageLarge) { myData.push_back(i); } - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = static_cast(kDataSize * sizeof(uint32_t)); - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - memcpy(result.data, myData.data(), kDataSize * sizeof(uint32_t)); - result.buffer.Unmap(); + wgpu::CreateBufferMappedResult result = CreateBufferMappedWithData( + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(result.buffer); EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize); } @@ -331,19 +803,13 @@ TEST_P(CreateBufferMappedTests, MapReadUsageLarge) { myData.push_back(i); } - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = static_cast(kDataSize * sizeof(uint32_t)); - descriptor.usage = dawn::BufferUsageBit::MapRead; - - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - memcpy(result.data, myData.data(), kDataSize * sizeof(uint32_t)); - result.buffer.Unmap(); + wgpu::CreateBufferMappedResult result = + CreateBufferMappedWithData(wgpu::BufferUsage::MapRead, myData); + UnmapBuffer(result.buffer); const void* mappedData = MapReadAsyncAndWait(result.buffer); ASSERT_EQ(0, memcmp(mappedData, myData.data(), kDataSize * sizeof(uint32_t))); - result.buffer.Unmap(); + UnmapBuffer(result.buffer); } // Test CreateBufferMapped for a large non-mappable buffer @@ -354,67 +820,42 @@ TEST_P(CreateBufferMappedTests, NonMappableUsageLarge) { myData.push_back(i); } - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = static_cast(kDataSize * sizeof(uint32_t)); - descriptor.usage = dawn::BufferUsageBit::TransferSrc; - - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - memcpy(result.data, myData.data(), kDataSize * sizeof(uint32_t)); - result.buffer.Unmap(); + wgpu::CreateBufferMappedResult result = + CreateBufferMappedWithData(wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(result.buffer); EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), result.buffer, 0, kDataSize); } -// Test that CreateBufferMapped returns zero-initialized data -// TODO(enga): This should use the testing toggle to initialize resources to 1. -TEST_P(CreateBufferMappedTests, MappableZeroInitialized) { - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - ASSERT_EQ(*reinterpret_cast(result.data), 0); - result.buffer.Unmap(); +// Test destroying a non-mappable buffer mapped at creation. +// This is a regression test for an issue where the D3D12 backend thought the buffer was actually +// mapped and tried to unlock the heap residency (when actually the buffer was using a staging +// buffer) +TEST_P(CreateBufferMappedTests, DestroyNonMappableWhileMappedForCreation) { + wgpu::CreateBufferMappedResult result = CreateBufferMapped(wgpu::BufferUsage::CopySrc, 4); + result.buffer.Destroy(); } -// Test that CreateBufferMapped returns zero-initialized data -// TODO(enga): This should use the testing toggle to initialize resources to 1. -TEST_P(CreateBufferMappedTests, NonMappableZeroInitialized) { - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::TransferSrc; - - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - ASSERT_EQ(*reinterpret_cast(result.data), 0); - result.buffer.Unmap(); +// Test destroying a mappable buffer mapped at creation. +TEST_P(CreateBufferMappedTests, DestroyMappableWhileMappedForCreation) { + wgpu::CreateBufferMappedResult result = CreateBufferMapped(wgpu::BufferUsage::MapRead, 4); + result.buffer.Destroy(); } // Test that mapping a buffer is valid after CreateBufferMapped and Unmap TEST_P(CreateBufferMappedTests, CreateThenMapSuccess) { - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - static uint32_t myData = 230502; static uint32_t myData2 = 1337; - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - memcpy(result.data, &myData, sizeof(myData)); - result.buffer.Unmap(); + wgpu::CreateBufferMappedResult result = CreateBufferMappedWithData( + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(result.buffer); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); bool done = false; result.buffer.MapWriteAsync( - [](DawnBufferMapAsyncStatus status, void* data, uint64_t, void* userdata) { - ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, status); + [](WGPUBufferMapAsyncStatus status, void* data, uint64_t, void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); ASSERT_NE(nullptr, data); *static_cast(data) = myData2; @@ -426,27 +867,21 @@ TEST_P(CreateBufferMappedTests, CreateThenMapSuccess) { WaitABit(); } - result.buffer.Unmap(); + UnmapBuffer(result.buffer); EXPECT_BUFFER_U32_EQ(myData2, result.buffer, 0); } // Test that is is invalid to map a buffer twice when using CreateBufferMapped TEST_P(CreateBufferMappedTests, CreateThenMapBeforeUnmapFailure) { - dawn::BufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - uint32_t myData = 230502; - dawn::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - ASSERT_EQ(result.dataLength, descriptor.size); - memcpy(result.data, &myData, sizeof(myData)); + wgpu::CreateBufferMappedResult result = CreateBufferMappedWithData( + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData}); ASSERT_DEVICE_ERROR([&]() { bool done = false; result.buffer.MapWriteAsync( - [](DawnBufferMapAsyncStatus status, void* data, uint64_t, void* userdata) { - ASSERT_EQ(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, status); + [](WGPUBufferMapAsyncStatus status, void* data, uint64_t, void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Error, status); ASSERT_EQ(nullptr, data); *static_cast(userdata) = true; @@ -459,12 +894,582 @@ TEST_P(CreateBufferMappedTests, CreateThenMapBeforeUnmapFailure) { }()); // CreateBufferMapped is unaffected by the MapWrite error. - result.buffer.Unmap(); + UnmapBuffer(result.buffer); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); } +// Test that creating a zero-sized buffer mapped is allowed. +TEST_P(CreateBufferMappedTests, ZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::Vertex; + wgpu::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); + + ASSERT_EQ(0u, result.dataLength); + ASSERT_NE(nullptr, result.data); + + // Check that unmapping the buffer works too. + UnmapBuffer(result.buffer); +} + +// Test that creating a zero-sized mapppable buffer mapped. (it is a different code path) +TEST_P(CreateBufferMappedTests, ZeroSizedMappableBuffer) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::MapWrite; + wgpu::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); + + ASSERT_EQ(0u, result.dataLength); + ASSERT_NE(nullptr, result.data); + + // Check that unmapping the buffer works too. + UnmapBuffer(result.buffer); +} + +// Test that creating a zero-sized error buffer mapped. (it is a different code path) +TEST_P(CreateBufferMappedTests, ZeroSizedErrorBuffer) { + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Storage; + wgpu::CreateBufferMappedResult result; + ASSERT_DEVICE_ERROR(result = device.CreateBufferMapped(&descriptor)); + + ASSERT_EQ(0u, result.dataLength); + ASSERT_NE(nullptr, result.data); +} + +// Test the result of GetMappedRange when mapped at creation. +TEST_P(CreateBufferMappedTests, GetMappedRange) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopyDst; + wgpu::CreateBufferMappedResult result; + result = device.CreateBufferMapped(&descriptor); + + ASSERT_EQ(result.buffer.GetMappedRange(), result.data); + ASSERT_EQ(result.buffer.GetMappedRange(), result.buffer.GetConstMappedRange()); + ASSERT_NE(result.buffer.GetMappedRange(), nullptr); + result.buffer.Unmap(); +} + +// Test the result of GetMappedRange when mapped at creation for a zero-sized buffer. +TEST_P(CreateBufferMappedTests, GetMappedRangeZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::CopyDst; + wgpu::CreateBufferMappedResult result; + result = device.CreateBufferMapped(&descriptor); + + ASSERT_EQ(result.buffer.GetMappedRange(), result.data); + ASSERT_EQ(result.buffer.GetMappedRange(), result.buffer.GetConstMappedRange()); + ASSERT_NE(result.buffer.GetMappedRange(), nullptr); + result.buffer.Unmap(); +} + DAWN_INSTANTIATE_TEST(CreateBufferMappedTests, - D3D12Backend, - MetalBackend, - OpenGLBackend, - VulkanBackend); + D3D12Backend(), + D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +class BufferMappedAtCreationTests : public DawnTest { + protected: + static void MapReadCallback(WGPUBufferMapAsyncStatus status, + const void* data, + uint64_t, + void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + ASSERT_NE(nullptr, data); + + static_cast(userdata)->mappedData = data; + } + + const void* MapReadAsyncAndWait(const wgpu::Buffer& buffer) { + buffer.MapReadAsync(MapReadCallback, this); + + while (mappedData == nullptr) { + WaitABit(); + } + + return mappedData; + } + + void UnmapBuffer(const wgpu::Buffer& buffer) { + buffer.Unmap(); + mappedData = nullptr; + } + + wgpu::Buffer BufferMappedAtCreation(wgpu::BufferUsage usage, uint64_t size) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage; + descriptor.mappedAtCreation = true; + return device.CreateBuffer(&descriptor); + } + + wgpu::Buffer BufferMappedAtCreationWithData(wgpu::BufferUsage usage, + const std::vector& data) { + size_t byteLength = data.size() * sizeof(uint32_t); + wgpu::Buffer buffer = BufferMappedAtCreation(usage, byteLength); + memcpy(buffer.GetMappedRange(), data.data(), byteLength); + return buffer; + } + + private: + const void* mappedData = nullptr; +}; + +// Test that the simplest mappedAtCreation works for MapWrite buffers. +TEST_P(BufferMappedAtCreationTests, MapWriteUsageSmall) { + uint32_t myData = 230502; + wgpu::Buffer buffer = BufferMappedAtCreationWithData( + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(buffer); + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +// Test that the simplest mappedAtCreation works for MapRead buffers. +TEST_P(BufferMappedAtCreationTests, MapReadUsageSmall) { + uint32_t myData = 230502; + wgpu::Buffer buffer = BufferMappedAtCreationWithData(wgpu::BufferUsage::MapRead, {myData}); + UnmapBuffer(buffer); + + const void* mappedData = MapReadAsyncAndWait(buffer); + ASSERT_EQ(myData, *reinterpret_cast(mappedData)); + UnmapBuffer(buffer); +} + +// Test that the simplest mappedAtCreation works for non-mappable buffers. +TEST_P(BufferMappedAtCreationTests, NonMappableUsageSmall) { + uint32_t myData = 4239; + wgpu::Buffer buffer = BufferMappedAtCreationWithData(wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(buffer); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +// Test mappedAtCreation for a large MapWrite buffer +TEST_P(BufferMappedAtCreationTests, MapWriteUsageLarge) { + constexpr uint64_t kDataSize = 1000 * 1000; + std::vector myData; + for (uint32_t i = 0; i < kDataSize; ++i) { + myData.push_back(i); + } + + wgpu::Buffer buffer = BufferMappedAtCreationWithData( + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(buffer); + + EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffer, 0, kDataSize); +} + +// Test mappedAtCreation for a large MapRead buffer +TEST_P(BufferMappedAtCreationTests, MapReadUsageLarge) { + constexpr uint64_t kDataSize = 1000 * 1000; + std::vector myData; + for (uint32_t i = 0; i < kDataSize; ++i) { + myData.push_back(i); + } + + wgpu::Buffer buffer = BufferMappedAtCreationWithData(wgpu::BufferUsage::MapRead, myData); + UnmapBuffer(buffer); + + const void* mappedData = MapReadAsyncAndWait(buffer); + ASSERT_EQ(0, memcmp(mappedData, myData.data(), kDataSize * sizeof(uint32_t))); + UnmapBuffer(buffer); +} + +// Test mappedAtCreation for a large non-mappable buffer +TEST_P(BufferMappedAtCreationTests, NonMappableUsageLarge) { + constexpr uint64_t kDataSize = 1000 * 1000; + std::vector myData; + for (uint32_t i = 0; i < kDataSize; ++i) { + myData.push_back(i); + } + + wgpu::Buffer buffer = BufferMappedAtCreationWithData(wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(buffer); + + EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffer, 0, kDataSize); +} + +// Test destroying a non-mappable buffer mapped at creation. +// This is a regression test for an issue where the D3D12 backend thought the buffer was actually +// mapped and tried to unlock the heap residency (when actually the buffer was using a staging +// buffer) +TEST_P(BufferMappedAtCreationTests, DestroyNonMappableWhileMappedForCreation) { + wgpu::Buffer buffer = BufferMappedAtCreation(wgpu::BufferUsage::CopySrc, 4); + buffer.Destroy(); +} + +// Test destroying a mappable buffer mapped at creation. +TEST_P(BufferMappedAtCreationTests, DestroyMappableWhileMappedForCreation) { + wgpu::Buffer buffer = BufferMappedAtCreation(wgpu::BufferUsage::MapRead, 4); + buffer.Destroy(); +} + +// Test that mapping a buffer is valid after mappedAtCreation and Unmap +TEST_P(BufferMappedAtCreationTests, CreateThenMapSuccess) { + static uint32_t myData = 230502; + static uint32_t myData2 = 1337; + wgpu::Buffer buffer = BufferMappedAtCreationWithData( + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData}); + UnmapBuffer(buffer); + + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); + + bool done = false; + buffer.MapWriteAsync( + [](WGPUBufferMapAsyncStatus status, void* data, uint64_t, void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + ASSERT_NE(nullptr, data); + + *static_cast(data) = myData2; + *static_cast(userdata) = true; + }, + &done); + + while (!done) { + WaitABit(); + } + + UnmapBuffer(buffer); + EXPECT_BUFFER_U32_EQ(myData2, buffer, 0); +} + +// Test that is is invalid to map a buffer twice when using mappedAtCreation +TEST_P(BufferMappedAtCreationTests, CreateThenMapBeforeUnmapFailure) { + uint32_t myData = 230502; + wgpu::Buffer buffer = BufferMappedAtCreationWithData( + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData}); + + ASSERT_DEVICE_ERROR([&]() { + bool done = false; + buffer.MapWriteAsync( + [](WGPUBufferMapAsyncStatus status, void* data, uint64_t, void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Error, status); + ASSERT_EQ(nullptr, data); + + *static_cast(userdata) = true; + }, + &done); + + while (!done) { + WaitABit(); + } + }()); + + // mappedAtCreation is unaffected by the MapWrite error. + UnmapBuffer(buffer); + EXPECT_BUFFER_U32_EQ(myData, buffer, 0); +} + +// Test that creating a zero-sized buffer mapped is allowed. +TEST_P(BufferMappedAtCreationTests, ZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::Vertex; + descriptor.mappedAtCreation = true; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + ASSERT_NE(nullptr, buffer.GetMappedRange()); + + // Check that unmapping the buffer works too. + UnmapBuffer(buffer); +} + +// Test that creating a zero-sized mapppable buffer mapped. (it is a different code path) +TEST_P(BufferMappedAtCreationTests, ZeroSizedMappableBuffer) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::MapWrite; + descriptor.mappedAtCreation = true; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + ASSERT_NE(nullptr, buffer.GetMappedRange()); + + // Check that unmapping the buffer works too. + UnmapBuffer(buffer); +} + +// Test that creating a zero-sized error buffer mapped. (it is a different code path) +TEST_P(BufferMappedAtCreationTests, ZeroSizedErrorBuffer) { + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Storage; + descriptor.mappedAtCreation = true; + wgpu::Buffer buffer; + ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor)); + + ASSERT_NE(nullptr, buffer.GetMappedRange()); +} + +// Test the result of GetMappedRange when mapped at creation. +TEST_P(BufferMappedAtCreationTests, GetMappedRange) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopyDst; + descriptor.mappedAtCreation = true; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + ASSERT_EQ(buffer.GetMappedRange(), buffer.GetConstMappedRange()); + ASSERT_NE(buffer.GetMappedRange(), nullptr); + buffer.Unmap(); +} + +// Test the result of GetMappedRange when mapped at creation for a zero-sized buffer. +TEST_P(BufferMappedAtCreationTests, GetMappedRangeZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 0; + descriptor.usage = wgpu::BufferUsage::CopyDst; + descriptor.mappedAtCreation = true; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + ASSERT_EQ(buffer.GetMappedRange(), buffer.GetConstMappedRange()); + ASSERT_NE(buffer.GetMappedRange(), nullptr); + buffer.Unmap(); +} + +DAWN_INSTANTIATE_TEST(BufferMappedAtCreationTests, + D3D12Backend(), + D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +class BufferTests : public DawnTest {}; + +// Test that creating a zero-buffer is allowed. +TEST_P(BufferTests, ZeroSizedBuffer) { + wgpu::BufferDescriptor desc; + desc.size = 0; + desc.usage = wgpu::BufferUsage::CopyDst; + device.CreateBuffer(&desc); +} + +// Test that creating a very large buffers fails gracefully. +TEST_P(BufferTests, CreateBufferOOM) { + // TODO(http://crbug.com/dawn/27): Missing support. + DAWN_SKIP_TEST_IF(IsOpenGL()); + DAWN_SKIP_TEST_IF(IsAsan()); + + wgpu::BufferDescriptor descriptor; + descriptor.usage = wgpu::BufferUsage::CopyDst; + + descriptor.size = std::numeric_limits::max(); + ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); + + // UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails + descriptor.size = 1ull << 50; + ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); +} + +// Test that a very large CreateBufferMapped fails gracefully. +TEST_P(BufferTests, CreateBufferMappedOOM) { + // TODO(http://crbug.com/dawn/27): Missing support. + DAWN_SKIP_TEST_IF(IsOpenGL()); + DAWN_SKIP_TEST_IF(IsAsan()); + + // Test non-mappable buffer + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopyDst; + + // Control: test a small buffer works. + device.CreateBufferMapped(&descriptor); + + // Test an enormous buffer fails + descriptor.size = std::numeric_limits::max(); + ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor)); + + // UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails + descriptor.size = 1ull << 50; + ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor)); + } + + // Test mappable buffer + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; + + // Control: test a small buffer works. + device.CreateBufferMapped(&descriptor); + + // Test an enormous buffer fails + descriptor.size = std::numeric_limits::max(); + ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor)); + + // UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails + descriptor.size = 1ull << 50; + ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor)); + } +} + +// Test that a very large buffer mappedAtCreation fails gracefully. +TEST_P(BufferTests, BufferMappedAtCreationOOM) { + // TODO(http://crbug.com/dawn/27): Missing support. + DAWN_SKIP_TEST_IF(IsOpenGL()); + DAWN_SKIP_TEST_IF(IsAsan()); + + // Test non-mappable buffer + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopyDst; + descriptor.mappedAtCreation = true; + + // Control: test a small buffer works. + device.CreateBuffer(&descriptor); + + // Test an enormous buffer fails + descriptor.size = std::numeric_limits::max(); + ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); + + // UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails + descriptor.size = 1ull << 50; + ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); + } + + // Test mappable buffer + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; + descriptor.mappedAtCreation = true; + + // Control: test a small buffer works. + device.CreateBuffer(&descriptor); + + // Test an enormous buffer fails + descriptor.size = std::numeric_limits::max(); + ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); + + // UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails + descriptor.size = 1ull << 50; + ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); + } +} + +// Test that mapping an OOM buffer for reading fails gracefully +TEST_P(BufferTests, CreateBufferOOMMapReadAsync) { + // TODO(http://crbug.com/dawn/27): Missing support. + DAWN_SKIP_TEST_IF(IsOpenGL()); + DAWN_SKIP_TEST_IF(IsAsan()); + + auto RunTest = [this](const wgpu::BufferDescriptor& descriptor) { + wgpu::Buffer buffer; + ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor)); + + bool done = false; + ASSERT_DEVICE_ERROR(buffer.MapReadAsync( + [](WGPUBufferMapAsyncStatus status, const void* ptr, uint64_t dataLength, + void* userdata) { + EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Error); + EXPECT_EQ(ptr, nullptr); + EXPECT_EQ(dataLength, 0u); + *static_cast(userdata) = true; + }, + &done)); + + while (!done) { + WaitABit(); + } + }; + + // Test an enormous buffer + wgpu::BufferDescriptor descriptor; + descriptor.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead; + + descriptor.size = std::numeric_limits::max(); + RunTest(descriptor); + + // UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails + descriptor.size = 1ull << 50; + RunTest(descriptor); +} + +// Test that mapping an OOM buffer for reading fails gracefully +TEST_P(BufferTests, CreateBufferOOMMapWriteAsync) { + // TODO(http://crbug.com/dawn/27): Missing support. + DAWN_SKIP_TEST_IF(IsOpenGL()); + DAWN_SKIP_TEST_IF(IsAsan()); + + auto RunTest = [this](const wgpu::BufferDescriptor& descriptor) { + wgpu::Buffer buffer; + ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor)); + + bool done = false; + ASSERT_DEVICE_ERROR(buffer.MapWriteAsync( + [](WGPUBufferMapAsyncStatus status, void* ptr, uint64_t dataLength, void* userdata) { + EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Error); + EXPECT_EQ(ptr, nullptr); + EXPECT_EQ(dataLength, 0u); + *static_cast(userdata) = true; + }, + &done)); + + while (!done) { + WaitABit(); + } + }; + + wgpu::BufferDescriptor descriptor; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; + + // Test an enormous buffer + descriptor.size = std::numeric_limits::max(); + RunTest(descriptor); + + // UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails + descriptor.size = 1ull << 50; + RunTest(descriptor); +} + +// Test that mapping an OOM buffer fails gracefully +TEST_P(BufferTests, CreateBufferOOMMapAsync) { + // TODO(http://crbug.com/dawn/27): Missing support. + DAWN_SKIP_TEST_IF(IsOpenGL()); + DAWN_SKIP_TEST_IF(IsAsan()); + + auto RunTest = [this](const wgpu::BufferDescriptor& descriptor) { + wgpu::Buffer buffer; + ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor)); + + bool done = false; + ASSERT_DEVICE_ERROR(buffer.MapAsync( + wgpu::MapMode::Write, 0, 4, + [](WGPUBufferMapAsyncStatus status, void* userdata) { + EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Error); + *static_cast(userdata) = true; + }, + &done)); + + while (!done) { + WaitABit(); + } + }; + + wgpu::BufferDescriptor descriptor; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; + + // Test an enormous buffer + descriptor.size = std::numeric_limits::max(); + RunTest(descriptor); + + // UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails + descriptor.size = 1ull << 50; + RunTest(descriptor); +} + +DAWN_INSTANTIATE_TEST(BufferTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/BufferZeroInitTests.cpp b/third_party/dawn/src/tests/end2end/BufferZeroInitTests.cpp new file mode 100644 index 00000000000..0f685e59edc --- /dev/null +++ b/third_party/dawn/src/tests/end2end/BufferZeroInitTests.cpp @@ -0,0 +1,609 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/WGPUHelpers.h" + +#define EXPECT_LAZY_CLEAR(N, statement) \ + do { \ + if (UsesWire()) { \ + statement; \ + } else { \ + size_t lazyClearsBefore = dawn_native::GetLazyClearCountForTesting(device.Get()); \ + statement; \ + size_t lazyClearsAfter = dawn_native::GetLazyClearCountForTesting(device.Get()); \ + EXPECT_EQ(N, lazyClearsAfter - lazyClearsBefore); \ + } \ + } while (0) + +namespace { + + struct BufferZeroInitInCopyT2BSpec { + wgpu::Extent3D textureSize; + uint64_t bufferOffset; + uint64_t extraBytes; + uint32_t bytesPerRow; + uint32_t rowsPerImage; + uint32_t lazyClearCount; + }; + +} // anonymous namespace + +class BufferZeroInitTest : public DawnTest { + public: + wgpu::Buffer CreateBuffer(uint64_t size, + wgpu::BufferUsage usage, + bool mappedAtCreation = false) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage; + descriptor.mappedAtCreation = mappedAtCreation; + return device.CreateBuffer(&descriptor); + } + + void MapAsyncAndWait(wgpu::Buffer buffer, + wgpu::MapMode mapMode, + uint64_t offset, + uint64_t size) { + ASSERT(mapMode == wgpu::MapMode::Read || mapMode == wgpu::MapMode::Write); + + bool done = false; + buffer.MapAsync( + mapMode, offset, size, + [](WGPUBufferMapAsyncStatus status, void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + *static_cast(userdata) = true; + }, + &done); + + while (!done) { + WaitABit(); + } + } + + wgpu::Texture CreateAndInitializeTexture(const wgpu::Extent3D& size, + wgpu::TextureFormat format, + wgpu::Color color = {0.f, 0.f, 0.f, 0.f}) { + wgpu::TextureDescriptor descriptor; + descriptor.size = size; + descriptor.format = format; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc | + wgpu::TextureUsage::OutputAttachment; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + for (uint32_t arrayLayer = 0; arrayLayer < size.depth; ++arrayLayer) { + wgpu::TextureViewDescriptor viewDescriptor; + viewDescriptor.format = format; + viewDescriptor.dimension = wgpu::TextureViewDimension::e2D; + viewDescriptor.baseArrayLayer = arrayLayer; + viewDescriptor.arrayLayerCount = 1u; + + utils::ComboRenderPassDescriptor renderPassDescriptor( + {texture.CreateView(&viewDescriptor)}); + renderPassDescriptor.cColorAttachments[0].clearColor = color; + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); + renderPass.EndPass(); + } + + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + return texture; + } + + void TestBufferZeroInitInCopyTextureToBuffer(const BufferZeroInitInCopyT2BSpec& spec) { + constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Float; + ASSERT(utils::GetTexelBlockSizeInBytes(kTextureFormat) * spec.textureSize.width % + kTextureBytesPerRowAlignment == + 0); + + constexpr wgpu::Color kClearColor = {0.5f, 0.5f, 0.5f, 0.5f}; + wgpu::Texture texture = + CreateAndInitializeTexture(spec.textureSize, kTextureFormat, kClearColor); + + const wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + + const uint64_t bufferSize = spec.bufferOffset + spec.extraBytes + + utils::RequiredBytesInCopy(spec.bytesPerRow, spec.rowsPerImage, + spec.textureSize, kTextureFormat); + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = bufferSize; + bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + const wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView( + buffer, spec.bufferOffset, spec.bytesPerRow, spec.rowsPerImage); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &spec.textureSize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + EXPECT_LAZY_CLEAR(spec.lazyClearCount, queue.Submit(1, &commandBuffer)); + + const uint64_t expectedValueCount = bufferSize / sizeof(float); + std::vector expectedValues(expectedValueCount, 0.f); + + for (uint32_t slice = 0; slice < spec.textureSize.depth; ++slice) { + const uint64_t baseOffsetBytesPerSlice = + spec.bufferOffset + spec.bytesPerRow * spec.rowsPerImage * slice; + for (uint32_t y = 0; y < spec.textureSize.height; ++y) { + const uint64_t baseOffsetBytesPerRow = + baseOffsetBytesPerSlice + spec.bytesPerRow * y; + const uint64_t baseOffsetFloatCountPerRow = baseOffsetBytesPerRow / sizeof(float); + for (uint32_t x = 0; x < spec.textureSize.width; ++x) { + expectedValues[baseOffsetFloatCountPerRow + x] = 0.5f; + } + } + } + + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedValues.data(), buffer, 0, expectedValues.size()); + } +}; + +// Test that calling writeBuffer to overwrite the entire buffer doesn't need to lazily initialize +// the destination buffer. +TEST_P(BufferZeroInitTest, WriteBufferToEntireBuffer) { + constexpr uint32_t kBufferSize = 8u; + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage); + + constexpr std::array kExpectedData = { + {0x02020202u, 0x02020202u}}; + EXPECT_LAZY_CLEAR(0u, queue.WriteBuffer(buffer, 0, kExpectedData.data(), kBufferSize)); + + EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), buffer, 0, kBufferSize / sizeof(uint32_t)); +} + +// Test that calling writeBuffer to overwrite a part of buffer needs to lazily initialize the +// destination buffer. +TEST_P(BufferZeroInitTest, WriteBufferToSubBuffer) { + constexpr uint32_t kBufferSize = 8u; + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + + constexpr uint32_t kCopyValue = 0x02020202u; + + // offset == 0 + { + wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage); + + constexpr uint32_t kCopyOffset = 0u; + EXPECT_LAZY_CLEAR(1u, + queue.WriteBuffer(buffer, kCopyOffset, &kCopyValue, sizeof(kCopyValue))); + + EXPECT_BUFFER_U32_EQ(kCopyValue, buffer, kCopyOffset); + EXPECT_BUFFER_U32_EQ(0, buffer, kBufferSize - sizeof(kCopyValue)); + } + + // offset > 0 + { + wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage); + + constexpr uint32_t kCopyOffset = 4u; + EXPECT_LAZY_CLEAR(1u, + queue.WriteBuffer(buffer, kCopyOffset, &kCopyValue, sizeof(kCopyValue))); + + EXPECT_BUFFER_U32_EQ(0, buffer, 0); + EXPECT_BUFFER_U32_EQ(kCopyValue, buffer, kCopyOffset); + } +} + +// Test that the code path of CopyBufferToBuffer clears the source buffer correctly when it is the +// first use of the source buffer. +TEST_P(BufferZeroInitTest, CopyBufferToBufferSource) { + constexpr uint64_t kBufferSize = 16u; + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = kBufferSize; + bufferDescriptor.usage = kBufferUsage; + + constexpr std::array kInitialData = { + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}; + + wgpu::Buffer dstBuffer = + utils::CreateBufferFromData(device, kInitialData.data(), kBufferSize, kBufferUsage); + + constexpr std::array kExpectedData = {{0, 0, 0, 0}}; + + // Full copy from the source buffer + { + wgpu::Buffer srcBuffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, kBufferSize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), srcBuffer, 0, + kBufferSize / sizeof(uint32_t)); + } + + // Partial copy from the source buffer + // srcOffset == 0 + { + constexpr uint64_t kSrcOffset = 0; + constexpr uint64_t kCopySize = kBufferSize / 2; + + wgpu::Buffer srcBuffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(srcBuffer, kSrcOffset, dstBuffer, 0, kCopySize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), srcBuffer, 0, + kBufferSize / sizeof(uint32_t)); + } + + // srcOffset > 0 and srcOffset + copySize == srcBufferSize + { + constexpr uint64_t kSrcOffset = kBufferSize / 2; + constexpr uint64_t kCopySize = kBufferSize - kSrcOffset; + + wgpu::Buffer srcBuffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(srcBuffer, kSrcOffset, dstBuffer, 0, kCopySize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), srcBuffer, 0, + kBufferSize / sizeof(uint32_t)); + } + + // srcOffset > 0 and srcOffset + copySize < srcBufferSize + { + constexpr uint64_t kSrcOffset = kBufferSize / 4; + constexpr uint64_t kCopySize = kBufferSize / 2; + + wgpu::Buffer srcBuffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(srcBuffer, kSrcOffset, dstBuffer, 0, kCopySize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + EXPECT_BUFFER_U32_RANGE_EQ(kExpectedData.data(), srcBuffer, 0, + kBufferSize / sizeof(uint32_t)); + } +} + +// Test that the code path of CopyBufferToBuffer clears the destination buffer correctly when it is +// the first use of the destination buffer. +TEST_P(BufferZeroInitTest, CopyBufferToBufferDestination) { + constexpr uint64_t kBufferSize = 16u; + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = kBufferSize; + bufferDescriptor.usage = kBufferUsage; + + const std::array kInitialData = { + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}}; + wgpu::Buffer srcBuffer = + utils::CreateBufferFromData(device, kInitialData.data(), kBufferSize, kBufferUsage); + + // Full copy from the source buffer doesn't need lazy initialization at all. + { + wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, kBufferSize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(kInitialData.data()), + dstBuffer, 0, kBufferSize / sizeof(uint32_t)); + } + + // Partial copy from the source buffer needs lazy initialization. + // offset == 0 + { + constexpr uint32_t kDstOffset = 0; + constexpr uint32_t kCopySize = kBufferSize / 2; + + wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, kDstOffset, kCopySize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + + std::array expectedData; + expectedData.fill(0); + for (uint32_t index = kDstOffset; index < kDstOffset + kCopySize; ++index) { + expectedData[index] = kInitialData[index - kDstOffset]; + } + + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(expectedData.data()), dstBuffer, 0, + kBufferSize / sizeof(uint32_t)); + } + + // offset > 0 and dstOffset + CopySize == kBufferSize + { + constexpr uint32_t kDstOffset = kBufferSize / 2; + constexpr uint32_t kCopySize = kBufferSize - kDstOffset; + + wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, kDstOffset, kCopySize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + + std::array expectedData; + expectedData.fill(0); + for (uint32_t index = kDstOffset; index < kDstOffset + kCopySize; ++index) { + expectedData[index] = kInitialData[index - kDstOffset]; + } + + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(expectedData.data()), dstBuffer, 0, + kBufferSize / sizeof(uint32_t)); + } + + // offset > 0 and dstOffset + CopySize < kBufferSize + { + constexpr uint32_t kDstOffset = kBufferSize / 4; + constexpr uint32_t kCopySize = kBufferSize / 2; + + wgpu::Buffer dstBuffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(srcBuffer, 0, dstBuffer, kDstOffset, kCopySize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + + std::array expectedData; + expectedData.fill(0); + for (uint32_t index = kDstOffset; index < kDstOffset + kCopySize; ++index) { + expectedData[index] = kInitialData[index - kDstOffset]; + } + + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(expectedData.data()), dstBuffer, 0, + kBufferSize / sizeof(uint32_t)); + } +} + +// Test that the code path of readable buffer mapping clears the buffer correctly when it is the +// first use of the buffer. +TEST_P(BufferZeroInitTest, MapReadAsync) { + constexpr uint32_t kBufferSize = 16u; + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + + constexpr wgpu::MapMode kMapMode = wgpu::MapMode::Read; + + // Map the whole buffer + { + wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage); + EXPECT_LAZY_CLEAR(1u, MapAsyncAndWait(buffer, kMapMode, 0, kBufferSize)); + + const uint32_t* mappedDataUint = static_cast(buffer.GetConstMappedRange()); + for (uint32_t i = 0; i < kBufferSize / sizeof(uint32_t); ++i) { + EXPECT_EQ(0u, mappedDataUint[i]); + } + buffer.Unmap(); + } + + // Map a range of a buffer + { + wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage); + + constexpr uint64_t kOffset = 4u; + constexpr uint64_t kSize = 8u; + EXPECT_LAZY_CLEAR(1u, MapAsyncAndWait(buffer, kMapMode, kOffset, kSize)); + + const uint32_t* mappedDataUint = + static_cast(buffer.GetConstMappedRange(kOffset)); + for (uint32_t i = 0; i < kSize / sizeof(uint32_t); ++i) { + EXPECT_EQ(0u, mappedDataUint[i]); + } + buffer.Unmap(); + + EXPECT_LAZY_CLEAR(0u, MapAsyncAndWait(buffer, kMapMode, 0, kBufferSize)); + mappedDataUint = static_cast(buffer.GetConstMappedRange()); + for (uint32_t i = 0; i < kBufferSize / sizeof(uint32_t); ++i) { + EXPECT_EQ(0u, mappedDataUint[i]); + } + buffer.Unmap(); + } +} + +// Test that the code path of writable buffer mapping clears the buffer correctly when it is the +// first use of the buffer. +TEST_P(BufferZeroInitTest, MapWriteAsync) { + constexpr uint32_t kBufferSize = 16u; + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + + constexpr wgpu::MapMode kMapMode = wgpu::MapMode::Write; + + constexpr std::array kExpectedData = {{0, 0, 0, 0}}; + + // Map the whole buffer + { + wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage); + EXPECT_LAZY_CLEAR(1u, MapAsyncAndWait(buffer, kMapMode, 0, kBufferSize)); + buffer.Unmap(); + + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(kExpectedData.data()), buffer, + 0, kExpectedData.size()); + } + + // Map a range of a buffer + { + wgpu::Buffer buffer = CreateBuffer(kBufferSize, kBufferUsage); + + constexpr uint64_t kOffset = 4u; + constexpr uint64_t kSize = 8u; + EXPECT_LAZY_CLEAR(1u, MapAsyncAndWait(buffer, kMapMode, kOffset, kSize)); + buffer.Unmap(); + + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(kExpectedData.data()), buffer, + 0, kExpectedData.size()); + } +} + +// Test that the code path of creating a buffer with BufferDescriptor.mappedAtCreation == true +// clears the buffer correctly at the creation of the buffer. +TEST_P(BufferZeroInitTest, MapAtCreation) { + constexpr uint32_t kBufferSize = 16u; + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + + wgpu::Buffer buffer; + EXPECT_LAZY_CLEAR(1u, buffer = CreateBuffer(kBufferSize, kBufferUsage, true)); + buffer.Unmap(); + + constexpr std::array kExpectedData = {{0, 0, 0, 0}}; + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(kExpectedData.data()), buffer, 0, + kExpectedData.size()); +} + +// Test that the code path of CopyBufferToTexture clears the source buffer correctly when it is the +// first use of the buffer. +TEST_P(BufferZeroInitTest, CopyBufferToTexture) { + constexpr wgpu::Extent3D kTextureSize = {16u, 16u, 1u}; + + constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint; + + wgpu::Texture texture = CreateAndInitializeTexture(kTextureSize, kTextureFormat); + const wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + + const uint32_t requiredBufferSizeForCopy = utils::GetBytesInBufferTextureCopy( + kTextureFormat, kTextureSize.width, kTextureBytesPerRowAlignment, kTextureSize.width, + kTextureSize.depth); + + constexpr wgpu::BufferUsage kBufferUsage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + + // bufferOffset == 0 + { + constexpr uint64_t kOffset = 0; + const uint32_t totalBufferSize = requiredBufferSizeForCopy + kOffset; + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = totalBufferSize; + bufferDescriptor.usage = kBufferUsage; + + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + const wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView( + buffer, kOffset, kTextureBytesPerRowAlignment, kTextureSize.height); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &kTextureSize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + + std::vector expectedValues(totalBufferSize / sizeof(uint32_t), 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedValues.data(), buffer, 0, + totalBufferSize / sizeof(uint32_t)); + } + + // bufferOffset > 0 + { + constexpr uint64_t kOffset = 8u; + const uint32_t totalBufferSize = requiredBufferSizeForCopy + kOffset; + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = totalBufferSize; + bufferDescriptor.usage = kBufferUsage; + + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + const wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView( + buffer, kOffset, kTextureBytesPerRowAlignment, kTextureSize.height); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &kTextureSize); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + + std::vector expectedValues(totalBufferSize / sizeof(uint32_t), 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedValues.data(), buffer, 0, + totalBufferSize / sizeof(uint32_t)); + } +} + +// Test that the code path of CopyTextureToBuffer clears the destination buffer correctly when it is +// the first use of the buffer and the texture is a 2D non-array texture. +TEST_P(BufferZeroInitTest, Copy2DTextureToBuffer) { + constexpr wgpu::Extent3D kTextureSize = {64u, 8u, 1u}; + + // bytesPerRow == texelBlockSizeInBytes * copySize.width && bytesPerRow * copySize.height == + // buffer.size + { + TestBufferZeroInitInCopyTextureToBuffer( + {kTextureSize, 0u, 0u, kTextureBytesPerRowAlignment, kTextureSize.height, 0u}); + } + + // bytesPerRow > texelBlockSizeInBytes * copySize.width + { + constexpr uint64_t kBytesPerRow = kTextureBytesPerRowAlignment * 2; + TestBufferZeroInitInCopyTextureToBuffer( + {kTextureSize, 0u, 0u, kBytesPerRow, kTextureSize.height, 1u}); + } + + // bufferOffset > 0 + { + constexpr uint64_t kBufferOffset = 16u; + TestBufferZeroInitInCopyTextureToBuffer({kTextureSize, kBufferOffset, 0u, + kTextureBytesPerRowAlignment, kTextureSize.height, + 1u}); + } + + // bytesPerRow * copySize.height < buffer.size + { + constexpr uint64_t kExtraBufferSize = 16u; + TestBufferZeroInitInCopyTextureToBuffer({kTextureSize, 0u, kExtraBufferSize, + kTextureBytesPerRowAlignment, kTextureSize.height, + 1u}); + } +} + +// Test that the code path of CopyTextureToBuffer clears the destination buffer correctly when it is +// the first use of the buffer and the texture is a 2D array texture. +TEST_P(BufferZeroInitTest, Copy2DArrayTextureToBuffer) { + constexpr wgpu::Extent3D kTextureSize = {64u, 4u, 3u}; + + // bytesPerRow == texelBlockSizeInBytes * copySize.width && rowsPerImage == copySize.height && + // bytesPerRow * (rowsPerImage * (copySize.depth - 1) + copySize.height) == buffer.size + { + TestBufferZeroInitInCopyTextureToBuffer( + {kTextureSize, 0u, 0u, kTextureBytesPerRowAlignment, kTextureSize.height, 0u}); + } + + // rowsPerImage > copySize.height + { + constexpr uint64_t kRowsPerImage = kTextureSize.height + 1u; + TestBufferZeroInitInCopyTextureToBuffer( + {kTextureSize, 0u, 0u, kTextureBytesPerRowAlignment, kRowsPerImage, 1u}); + } + + // bytesPerRow * rowsPerImage * copySize.depth < buffer.size + { + constexpr uint64_t kExtraBufferSize = 16u; + TestBufferZeroInitInCopyTextureToBuffer({kTextureSize, 0u, kExtraBufferSize, + kTextureBytesPerRowAlignment, kTextureSize.height, + 1u}); + } +} + +DAWN_INSTANTIATE_TEST(BufferZeroInitTest, + D3D12Backend({"nonzero_clear_resources_on_creation_for_testing", + "lazy_clear_buffer_on_first_use"}), + MetalBackend({"nonzero_clear_resources_on_creation_for_testing", + "lazy_clear_buffer_on_first_use"}), + OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing", + "lazy_clear_buffer_on_first_use"}), + VulkanBackend({"nonzero_clear_resources_on_creation_for_testing", + "lazy_clear_buffer_on_first_use"})); diff --git a/third_party/dawn/src/tests/end2end/ClipSpaceTests.cpp b/third_party/dawn/src/tests/end2end/ClipSpaceTests.cpp index ee3663ff36d..33c78e92b6d 100644 --- a/third_party/dawn/src/tests/end2end/ClipSpaceTests.cpp +++ b/third_party/dawn/src/tests/end2end/ClipSpaceTests.cpp @@ -15,11 +15,11 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class ClipSpaceTest : public DawnTest { protected: - dawn::RenderPipeline CreatePipelineForTest() { + wgpu::RenderPipeline CreatePipelineForTest() { utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); // Draw two triangles: @@ -27,40 +27,39 @@ class ClipSpaceTest : public DawnTest { // 2. The depth value of the bottom-right one is <= 0.5 const char* vs = R"(#version 450 - const vec3 pos[6] = vec3[6](vec3(-1.0f, -1.0f, 1.0f), - vec3(-1.0f, 1.0f, 0.5f), - vec3( 1.0f, -1.0f, 0.5f), - vec3( 1.0f, -1.0f, 0.5f), - vec3(-1.0f, 1.0f, 0.5f), - vec3( 1.0f, 1.0f, 0.0f)); - void main() { - gl_Position = vec4(pos[gl_VertexIndex], 1.0); - })"; - pipelineDescriptor.cVertexStage.module = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, vs); + const vec3 pos[6] = vec3[6](vec3(-1.0f, 1.0f, 1.0f), + vec3(-1.0f, -1.0f, 0.5f), + vec3( 1.0f, 1.0f, 0.5f), + vec3( 1.0f, 1.0f, 0.5f), + vec3(-1.0f, -1.0f, 0.5f), + vec3( 1.0f, -1.0f, 0.0f)); + void main() { + gl_Position = vec4(pos[gl_VertexIndex], 1.0); + })"; + pipelineDescriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vs); const char* fs = - "#version 450\n" - "layout(location = 0) out vec4 fragColor;" - "void main() {\n" - " fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" - "}\n"; + R"(#version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(1.0, 0.0, 0.0, 1.0); + })"; pipelineDescriptor.cFragmentStage.module = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, fs); + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fs); - pipelineDescriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::LessEqual; + pipelineDescriptor.cDepthStencilState.depthCompare = wgpu::CompareFunction::LessEqual; pipelineDescriptor.depthStencilState = &pipelineDescriptor.cDepthStencilState; return device.CreateRenderPipeline(&pipelineDescriptor); } - dawn::Texture Create2DTextureForTest(dawn::TextureFormat format) { - dawn::TextureDescriptor textureDescriptor; - textureDescriptor.dimension = dawn::TextureDimension::e2D; + wgpu::Texture Create2DTextureForTest(wgpu::TextureFormat format) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.dimension = wgpu::TextureDimension::e2D; textureDescriptor.format = format; textureDescriptor.usage = - dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; - textureDescriptor.arrayLayerCount = 1; + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; textureDescriptor.mipLevelCount = 1; textureDescriptor.sampleCount = 1; textureDescriptor.size = {kSize, kSize, 1}; @@ -72,30 +71,34 @@ class ClipSpaceTest : public DawnTest { // Test that the clip space is correctly configured. TEST_P(ClipSpaceTest, ClipSpace) { - dawn::Texture colorTexture = Create2DTextureForTest(dawn::TextureFormat::R8G8B8A8Unorm); - dawn::Texture depthStencilTexture = Create2DTextureForTest(dawn::TextureFormat::D32FloatS8Uint); + wgpu::Texture colorTexture = Create2DTextureForTest(wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture depthStencilTexture = + Create2DTextureForTest(wgpu::TextureFormat::Depth24PlusStencil8); - utils::ComboRenderPassDescriptor renderPassDescriptor( - {colorTexture.CreateDefaultView()}, depthStencilTexture.CreateDefaultView()); - renderPassDescriptor.cColorAttachmentsInfoPtr[0]->clearColor = {0.0, 1.0, 0.0, 1.0}; - renderPassDescriptor.cColorAttachmentsInfoPtr[0]->loadOp = dawn::LoadOp::Clear; + utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cColorAttachments[0].clearColor = {0.0, 1.0, 0.0, 1.0}; + renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; // Clear the depth stencil attachment to 0.5f, so only the bottom-right triangle should be // drawn. renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.5f; - renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = dawn::LoadOp::Clear; + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPass = commandEncoder.BeginRenderPass(&renderPassDescriptor); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = commandEncoder.BeginRenderPass(&renderPassDescriptor); renderPass.SetPipeline(CreatePipelineForTest()); - renderPass.Draw(6, 1, 0, 0); + renderPass.Draw(6); renderPass.EndPass(); - dawn::CommandBuffer commandBuffer = commandEncoder.Finish(); - dawn::Queue queue = device.CreateQueue(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); queue.Submit(1, &commandBuffer); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(255, 0, 0, 255), colorTexture, kSize - 1, kSize - 1); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), colorTexture, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, colorTexture, kSize - 1, kSize - 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, colorTexture, 0, 0); } -DAWN_INSTANTIATE_TEST(ClipSpaceTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(ClipSpaceTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/ColorStateTests.cpp b/third_party/dawn/src/tests/end2end/ColorStateTests.cpp index 0ead520fa3e..b4f935dac8f 100644 --- a/third_party/dawn/src/tests/end2end/ColorStateTests.cpp +++ b/third_party/dawn/src/tests/end2end/ColorStateTests.cpp @@ -20,7 +20,7 @@ #include "common/Assert.h" #include "common/Constants.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr static unsigned int kRTSize = 64; @@ -29,7 +29,7 @@ class ColorStateTest : public DawnTest { void SetUp() override { DawnTest::SetUp(); - vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { const vec2 pos[3] = vec2[3](vec2(-1.f, -1.f), vec2(3.f, -1.f), vec2(-1.f, 3.f)); @@ -37,13 +37,6 @@ class ColorStateTest : public DawnTest { } )"); - bindGroupLayout = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}, - }); - - pipelineLayout = utils::MakeBasicPipelineLayout(device, &bindGroupLayout); - renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); } @@ -54,9 +47,9 @@ class ColorStateTest : public DawnTest { // Set up basePipeline and testPipeline. testPipeline has the given blend state on the first // attachment. basePipeline has no blending - void SetupSingleSourcePipelines(dawn::ColorStateDescriptor colorStateDescriptor) { - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + void SetupSingleSourcePipelines(wgpu::ColorStateDescriptor colorStateDescriptor) { + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform myBlock { vec4 color; @@ -70,26 +63,24 @@ class ColorStateTest : public DawnTest { )"); utils::ComboRenderPipelineDescriptor baseDescriptor(device); - baseDescriptor.layout = pipelineLayout; - baseDescriptor.cVertexStage.module = vsModule; + baseDescriptor.vertexStage.module = vsModule; baseDescriptor.cFragmentStage.module = fsModule; - baseDescriptor.cColorStates[0]->format = renderPass.colorFormat; + baseDescriptor.cColorStates[0].format = renderPass.colorFormat; basePipeline = device.CreateRenderPipeline(&baseDescriptor); utils::ComboRenderPipelineDescriptor testDescriptor(device); - testDescriptor.layout = pipelineLayout; - testDescriptor.cVertexStage.module = vsModule; + testDescriptor.vertexStage.module = vsModule; testDescriptor.cFragmentStage.module = fsModule; - testDescriptor.cColorStates[0] = &colorStateDescriptor; - testDescriptor.cColorStates[0]->format = renderPass.colorFormat; + testDescriptor.cColorStates[0] = colorStateDescriptor; + testDescriptor.cColorStates[0].format = renderPass.colorFormat; testPipeline = device.CreateRenderPipeline(&testDescriptor); } // Create a bind group to set the colors as a uniform buffer template - dawn::BindGroup MakeBindGroupForColors(std::array colors) { + wgpu::BindGroup MakeBindGroupForColors(std::array colors) { std::array data; for (unsigned int i = 0; i < N; ++i) { data[4 * i + 0] = static_cast(colors[i].r) / 255.f; @@ -100,34 +91,35 @@ class ColorStateTest : public DawnTest { uint32_t bufferSize = static_cast(4 * N * sizeof(float)); - dawn::Buffer buffer = - utils::CreateBufferFromData(device, &data, bufferSize, dawn::BufferUsageBit::Uniform); - return utils::MakeBindGroup(device, bindGroupLayout, {{0, buffer, 0, bufferSize}}); + wgpu::Buffer buffer = + utils::CreateBufferFromData(device, &data, bufferSize, wgpu::BufferUsage::Uniform); + return utils::MakeBindGroup(device, testPipeline.GetBindGroupLayout(0), + {{0, buffer, 0, bufferSize}}); } // Test that after drawing a triangle with the base color, and then the given triangle spec, the // color is as expected void DoSingleSourceTest(RGBA8 base, const TriangleSpec& triangle, const RGBA8& expected) { - dawn::Color blendColor{triangle.blendFactor[0], triangle.blendFactor[1], + wgpu::Color blendColor{triangle.blendFactor[0], triangle.blendFactor[1], triangle.blendFactor[2], triangle.blendFactor[3]}; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); // First use the base pipeline to draw a triangle with no blending pass.SetPipeline(basePipeline); - pass.SetBindGroup(0, MakeBindGroupForColors(std::array({{base}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + pass.SetBindGroup(0, MakeBindGroupForColors(std::array({{base}}))); + pass.Draw(3); // Then use the test pipeline to draw the test triangle with blending pass.SetPipeline(testPipeline); - pass.SetBindGroup(0, MakeBindGroupForColors(std::array({{triangle.color}})), 0, nullptr); + pass.SetBindGroup(0, MakeBindGroupForColors(std::array({{triangle.color}}))); pass.SetBlendColor(&blendColor); - pass.Draw(3, 1, 0, 0); + pass.Draw(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2); @@ -136,17 +128,17 @@ class ColorStateTest : public DawnTest { // Given a vector of tests where each element is , check that all // expectations are true for the given blend operation void CheckBlendOperation(RGBA8 base, - dawn::BlendOperation operation, + wgpu::BlendOperation operation, std::vector> tests) { - dawn::BlendDescriptor blend; + wgpu::BlendDescriptor blend; blend.operation = operation; - blend.srcFactor = dawn::BlendFactor::One; - blend.dstFactor = dawn::BlendFactor::One; + blend.srcFactor = wgpu::BlendFactor::One; + blend.dstFactor = wgpu::BlendFactor::One; - dawn::ColorStateDescriptor descriptor; + wgpu::ColorStateDescriptor descriptor; descriptor.alphaBlend = blend; descriptor.colorBlend = blend; - descriptor.writeMask = dawn::ColorWriteMask::All; + descriptor.writeMask = wgpu::ColorWriteMask::All; SetupSingleSourcePipelines(descriptor); @@ -158,25 +150,25 @@ class ColorStateTest : public DawnTest { // Given a vector of tests where each element is , check that all // expectations are true for the given blend factors void CheckBlendFactor(RGBA8 base, - dawn::BlendFactor colorSrcFactor, - dawn::BlendFactor colorDstFactor, - dawn::BlendFactor alphaSrcFactor, - dawn::BlendFactor alphaDstFactor, + wgpu::BlendFactor colorSrcFactor, + wgpu::BlendFactor colorDstFactor, + wgpu::BlendFactor alphaSrcFactor, + wgpu::BlendFactor alphaDstFactor, std::vector> tests) { - dawn::BlendDescriptor colorBlend; - colorBlend.operation = dawn::BlendOperation::Add; + wgpu::BlendDescriptor colorBlend; + colorBlend.operation = wgpu::BlendOperation::Add; colorBlend.srcFactor = colorSrcFactor; colorBlend.dstFactor = colorDstFactor; - dawn::BlendDescriptor alphaBlend; - alphaBlend.operation = dawn::BlendOperation::Add; + wgpu::BlendDescriptor alphaBlend; + alphaBlend.operation = wgpu::BlendOperation::Add; alphaBlend.srcFactor = alphaSrcFactor; alphaBlend.dstFactor = alphaDstFactor; - dawn::ColorStateDescriptor descriptor; + wgpu::ColorStateDescriptor descriptor; descriptor.colorBlend = colorBlend; descriptor.alphaBlend = alphaBlend; - descriptor.writeMask = dawn::ColorWriteMask::All; + descriptor.writeMask = wgpu::ColorWriteMask::All; SetupSingleSourcePipelines(descriptor); @@ -186,27 +178,25 @@ class ColorStateTest : public DawnTest { } void CheckSrcBlendFactor(RGBA8 base, - dawn::BlendFactor colorFactor, - dawn::BlendFactor alphaFactor, + wgpu::BlendFactor colorFactor, + wgpu::BlendFactor alphaFactor, std::vector> tests) { - CheckBlendFactor(base, colorFactor, dawn::BlendFactor::One, alphaFactor, - dawn::BlendFactor::One, tests); + CheckBlendFactor(base, colorFactor, wgpu::BlendFactor::One, alphaFactor, + wgpu::BlendFactor::One, tests); } void CheckDstBlendFactor(RGBA8 base, - dawn::BlendFactor colorFactor, - dawn::BlendFactor alphaFactor, + wgpu::BlendFactor colorFactor, + wgpu::BlendFactor alphaFactor, std::vector> tests) { - CheckBlendFactor(base, dawn::BlendFactor::One, colorFactor, dawn::BlendFactor::One, + CheckBlendFactor(base, wgpu::BlendFactor::One, colorFactor, wgpu::BlendFactor::One, alphaFactor, tests); } utils::BasicRenderPass renderPass; - dawn::RenderPipeline basePipeline; - dawn::RenderPipeline testPipeline; - dawn::ShaderModule vsModule; - dawn::BindGroupLayout bindGroupLayout; - dawn::PipelineLayout pipelineLayout; + wgpu::RenderPipeline basePipeline; + wgpu::RenderPipeline testPipeline; + wgpu::ShaderModule vsModule; }; namespace { @@ -291,14 +281,14 @@ namespace { // Test compilation and usage of the fixture TEST_P(ColorStateTest, Basic) { - dawn::BlendDescriptor blend; - blend.operation = dawn::BlendOperation::Add; - blend.srcFactor = dawn::BlendFactor::One; - blend.dstFactor = dawn::BlendFactor::Zero; - dawn::ColorStateDescriptor descriptor; + wgpu::BlendDescriptor blend; + blend.operation = wgpu::BlendOperation::Add; + blend.srcFactor = wgpu::BlendFactor::One; + blend.dstFactor = wgpu::BlendFactor::Zero; + wgpu::ColorStateDescriptor descriptor; descriptor.alphaBlend = blend; descriptor.colorBlend = blend; - descriptor.writeMask = dawn::ColorWriteMask::All; + descriptor.writeMask = wgpu::ColorWriteMask::All; SetupSingleSourcePipelines(descriptor); @@ -311,7 +301,7 @@ TEST_P(ColorStateTest, BlendOperationAdd) { std::vector> tests; std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(color, base + color); }); - CheckBlendOperation(base, dawn::BlendOperation::Add, tests); + CheckBlendOperation(base, wgpu::BlendOperation::Add, tests); } TEST_P(ColorStateTest, BlendOperationSubtract) { @@ -319,7 +309,7 @@ TEST_P(ColorStateTest, BlendOperationSubtract) { std::vector> tests; std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(color, color - base); }); - CheckBlendOperation(base, dawn::BlendOperation::Subtract, tests); + CheckBlendOperation(base, wgpu::BlendOperation::Subtract, tests); } TEST_P(ColorStateTest, BlendOperationReverseSubtract) { @@ -327,7 +317,7 @@ TEST_P(ColorStateTest, BlendOperationReverseSubtract) { std::vector> tests; std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(color, base - color); }); - CheckBlendOperation(base, dawn::BlendOperation::ReverseSubtract, tests); + CheckBlendOperation(base, wgpu::BlendOperation::ReverseSubtract, tests); } TEST_P(ColorStateTest, BlendOperationMin) { @@ -335,7 +325,7 @@ TEST_P(ColorStateTest, BlendOperationMin) { std::vector> tests; std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(color, min(base, color)); }); - CheckBlendOperation(base, dawn::BlendOperation::Min, tests); + CheckBlendOperation(base, wgpu::BlendOperation::Min, tests); } TEST_P(ColorStateTest, BlendOperationMax) { @@ -343,7 +333,7 @@ TEST_P(ColorStateTest, BlendOperationMax) { std::vector> tests; std::transform(kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(color, max(base, color)); }); - CheckBlendOperation(base, dawn::BlendOperation::Max, tests); + CheckBlendOperation(base, wgpu::BlendOperation::Max, tests); } // The following tests check that the Source blend factor works @@ -353,7 +343,7 @@ TEST_P(ColorStateTest, SrcBlendFactorZero) { std::transform( kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::Zero, dawn::BlendFactor::Zero, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::Zero, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, SrcBlendFactorOne) { @@ -362,7 +352,7 @@ TEST_P(ColorStateTest, SrcBlendFactorOne) { std::transform( kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base + color); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::One, dawn::BlendFactor::One, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::One, wgpu::BlendFactor::One, tests); } TEST_P(ColorStateTest, SrcBlendFactorSrcColor) { @@ -375,7 +365,7 @@ TEST_P(ColorStateTest, SrcBlendFactorSrcColor) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::SrcColor, dawn::BlendFactor::Zero, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::SrcColor, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, SrcBlendFactorOneMinusSrcColor) { @@ -388,7 +378,7 @@ TEST_P(ColorStateTest, SrcBlendFactorOneMinusSrcColor) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusSrcColor, dawn::BlendFactor::Zero, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusSrcColor, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, SrcBlendFactorSrcAlpha) { @@ -400,7 +390,7 @@ TEST_P(ColorStateTest, SrcBlendFactorSrcAlpha) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::SrcAlpha, dawn::BlendFactor::SrcAlpha, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::SrcAlpha, wgpu::BlendFactor::SrcAlpha, tests); } TEST_P(ColorStateTest, SrcBlendFactorOneMinusSrcAlpha) { @@ -412,8 +402,8 @@ TEST_P(ColorStateTest, SrcBlendFactorOneMinusSrcAlpha) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusSrcAlpha, - dawn::BlendFactor::OneMinusSrcAlpha, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusSrcAlpha, + wgpu::BlendFactor::OneMinusSrcAlpha, tests); } TEST_P(ColorStateTest, SrcBlendFactorDstColor) { @@ -426,7 +416,7 @@ TEST_P(ColorStateTest, SrcBlendFactorDstColor) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::DstColor, dawn::BlendFactor::Zero, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::DstColor, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, SrcBlendFactorOneMinusDstColor) { @@ -439,7 +429,7 @@ TEST_P(ColorStateTest, SrcBlendFactorOneMinusDstColor) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusDstColor, dawn::BlendFactor::Zero, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusDstColor, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, SrcBlendFactorDstAlpha) { @@ -451,7 +441,7 @@ TEST_P(ColorStateTest, SrcBlendFactorDstAlpha) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::DstAlpha, dawn::BlendFactor::DstAlpha, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::DstAlpha, wgpu::BlendFactor::DstAlpha, tests); } TEST_P(ColorStateTest, SrcBlendFactorOneMinusDstAlpha) { @@ -463,8 +453,8 @@ TEST_P(ColorStateTest, SrcBlendFactorOneMinusDstAlpha) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusDstAlpha, - dawn::BlendFactor::OneMinusDstAlpha, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusDstAlpha, + wgpu::BlendFactor::OneMinusDstAlpha, tests); } TEST_P(ColorStateTest, SrcBlendFactorSrcAlphaSaturated) { @@ -477,8 +467,8 @@ TEST_P(ColorStateTest, SrcBlendFactorSrcAlphaSaturated) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::SrcAlphaSaturated, - dawn::BlendFactor::SrcAlphaSaturated, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::SrcAlphaSaturated, + wgpu::BlendFactor::SrcAlphaSaturated, tests); } TEST_P(ColorStateTest, SrcBlendFactorBlendColor) { @@ -490,7 +480,7 @@ TEST_P(ColorStateTest, SrcBlendFactorBlendColor) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, triangleSpec.blendFactor); return std::make_pair(triangleSpec, expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::BlendColor, dawn::BlendFactor::BlendColor, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::BlendColor, wgpu::BlendFactor::BlendColor, tests); } TEST_P(ColorStateTest, SrcBlendFactorOneMinusBlendColor) { @@ -503,8 +493,8 @@ TEST_P(ColorStateTest, SrcBlendFactorOneMinusBlendColor) { RGBA8 expected = base + mix(RGBA8(0, 0, 0, 0), color, f); return std::make_pair(triangleSpec, expected); }); - CheckSrcBlendFactor(base, dawn::BlendFactor::OneMinusBlendColor, - dawn::BlendFactor::OneMinusBlendColor, tests); + CheckSrcBlendFactor(base, wgpu::BlendFactor::OneMinusBlendColor, + wgpu::BlendFactor::OneMinusBlendColor, tests); } // The following tests check that the Destination blend factor works @@ -514,7 +504,7 @@ TEST_P(ColorStateTest, DstBlendFactorZero) { std::transform( kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), color); }); - CheckDstBlendFactor(base, dawn::BlendFactor::Zero, dawn::BlendFactor::Zero, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::Zero, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, DstBlendFactorOne) { @@ -523,7 +513,7 @@ TEST_P(ColorStateTest, DstBlendFactorOne) { std::transform( kColors.begin(), kColors.end(), std::back_inserter(tests), [&](const RGBA8& color) { return std::make_pair(TriangleSpec({{color}}), base + color); }); - CheckDstBlendFactor(base, dawn::BlendFactor::One, dawn::BlendFactor::One, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::One, wgpu::BlendFactor::One, tests); } TEST_P(ColorStateTest, DstBlendFactorSrcColor) { @@ -536,7 +526,7 @@ TEST_P(ColorStateTest, DstBlendFactorSrcColor) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::SrcColor, dawn::BlendFactor::Zero, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::SrcColor, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, DstBlendFactorOneMinusSrcColor) { @@ -549,7 +539,7 @@ TEST_P(ColorStateTest, DstBlendFactorOneMinusSrcColor) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusSrcColor, dawn::BlendFactor::Zero, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusSrcColor, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, DstBlendFactorSrcAlpha) { @@ -561,7 +551,7 @@ TEST_P(ColorStateTest, DstBlendFactorSrcAlpha) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::SrcAlpha, dawn::BlendFactor::SrcAlpha, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::SrcAlpha, wgpu::BlendFactor::SrcAlpha, tests); } TEST_P(ColorStateTest, DstBlendFactorOneMinusSrcAlpha) { @@ -573,8 +563,8 @@ TEST_P(ColorStateTest, DstBlendFactorOneMinusSrcAlpha) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusSrcAlpha, - dawn::BlendFactor::OneMinusSrcAlpha, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusSrcAlpha, + wgpu::BlendFactor::OneMinusSrcAlpha, tests); } TEST_P(ColorStateTest, DstBlendFactorDstColor) { @@ -587,7 +577,7 @@ TEST_P(ColorStateTest, DstBlendFactorDstColor) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::DstColor, dawn::BlendFactor::Zero, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::DstColor, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, DstBlendFactorOneMinusDstColor) { @@ -600,7 +590,7 @@ TEST_P(ColorStateTest, DstBlendFactorOneMinusDstColor) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusDstColor, dawn::BlendFactor::Zero, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusDstColor, wgpu::BlendFactor::Zero, tests); } TEST_P(ColorStateTest, DstBlendFactorDstAlpha) { @@ -612,7 +602,7 @@ TEST_P(ColorStateTest, DstBlendFactorDstAlpha) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::DstAlpha, dawn::BlendFactor::DstAlpha, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::DstAlpha, wgpu::BlendFactor::DstAlpha, tests); } TEST_P(ColorStateTest, DstBlendFactorOneMinusDstAlpha) { @@ -624,8 +614,8 @@ TEST_P(ColorStateTest, DstBlendFactorOneMinusDstAlpha) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusDstAlpha, - dawn::BlendFactor::OneMinusDstAlpha, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusDstAlpha, + wgpu::BlendFactor::OneMinusDstAlpha, tests); } TEST_P(ColorStateTest, DstBlendFactorSrcAlphaSaturated) { @@ -638,8 +628,8 @@ TEST_P(ColorStateTest, DstBlendFactorSrcAlphaSaturated) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, fac); return std::make_pair(TriangleSpec({{color}}), expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::SrcAlphaSaturated, - dawn::BlendFactor::SrcAlphaSaturated, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::SrcAlphaSaturated, + wgpu::BlendFactor::SrcAlphaSaturated, tests); } TEST_P(ColorStateTest, DstBlendFactorBlendColor) { @@ -651,7 +641,7 @@ TEST_P(ColorStateTest, DstBlendFactorBlendColor) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, triangleSpec.blendFactor); return std::make_pair(triangleSpec, expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::BlendColor, dawn::BlendFactor::BlendColor, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::BlendColor, wgpu::BlendFactor::BlendColor, tests); } TEST_P(ColorStateTest, DstBlendFactorOneMinusBlendColor) { @@ -664,23 +654,23 @@ TEST_P(ColorStateTest, DstBlendFactorOneMinusBlendColor) { RGBA8 expected = color + mix(RGBA8(0, 0, 0, 0), base, f); return std::make_pair(triangleSpec, expected); }); - CheckDstBlendFactor(base, dawn::BlendFactor::OneMinusBlendColor, - dawn::BlendFactor::OneMinusBlendColor, tests); + CheckDstBlendFactor(base, wgpu::BlendFactor::OneMinusBlendColor, + wgpu::BlendFactor::OneMinusBlendColor, tests); } // Check that the color write mask works TEST_P(ColorStateTest, ColorWriteMask) { - dawn::BlendDescriptor blend; - blend.operation = dawn::BlendOperation::Add; - blend.srcFactor = dawn::BlendFactor::One; - blend.dstFactor = dawn::BlendFactor::One; + wgpu::BlendDescriptor blend; + blend.operation = wgpu::BlendOperation::Add; + blend.srcFactor = wgpu::BlendFactor::One; + blend.dstFactor = wgpu::BlendFactor::One; - dawn::ColorStateDescriptor descriptor; + wgpu::ColorStateDescriptor descriptor; descriptor.colorBlend = blend; descriptor.alphaBlend = blend; { // Test single channel color write - descriptor.writeMask = dawn::ColorWriteMask::Red; + descriptor.writeMask = wgpu::ColorWriteMask::Red; SetupSingleSourcePipelines(descriptor); RGBA8 base(32, 64, 128, 192); @@ -692,7 +682,7 @@ TEST_P(ColorStateTest, ColorWriteMask) { { // Test multi channel color write - descriptor.writeMask = dawn::ColorWriteMask::Green | dawn::ColorWriteMask::Alpha; + descriptor.writeMask = wgpu::ColorWriteMask::Green | wgpu::ColorWriteMask::Alpha; SetupSingleSourcePipelines(descriptor); RGBA8 base(32, 64, 128, 192); @@ -704,7 +694,7 @@ TEST_P(ColorStateTest, ColorWriteMask) { { // Test no channel color write - descriptor.writeMask = dawn::ColorWriteMask::None; + descriptor.writeMask = wgpu::ColorWriteMask::None; SetupSingleSourcePipelines(descriptor); RGBA8 base(32, 64, 128, 192); @@ -717,30 +707,30 @@ TEST_P(ColorStateTest, ColorWriteMask) { // Check that the color write mask works when blending is disabled TEST_P(ColorStateTest, ColorWriteMaskBlendingDisabled) { { - dawn::BlendDescriptor blend; - blend.operation = dawn::BlendOperation::Add; - blend.srcFactor = dawn::BlendFactor::One; - blend.dstFactor = dawn::BlendFactor::Zero; - dawn::ColorStateDescriptor descriptor; + wgpu::BlendDescriptor blend; + blend.operation = wgpu::BlendOperation::Add; + blend.srcFactor = wgpu::BlendFactor::One; + blend.dstFactor = wgpu::BlendFactor::Zero; + wgpu::ColorStateDescriptor descriptor; descriptor.alphaBlend = blend; descriptor.colorBlend = blend; - descriptor.writeMask = dawn::ColorWriteMask::Red; + descriptor.writeMask = wgpu::ColorWriteMask::Red; SetupSingleSourcePipelines(descriptor); RGBA8 base(32, 64, 128, 192); RGBA8 expected(32, 0, 0, 0); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(testPipeline); - pass.SetBindGroup(0, MakeBindGroupForColors(std::array({{base}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + pass.SetBindGroup(0, MakeBindGroupForColors(std::array({{base}}))); + pass.Draw(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2); } @@ -750,29 +740,29 @@ TEST_P(ColorStateTest, ColorWriteMaskBlendingDisabled) { TEST_P(ColorStateTest, IndependentColorState) { DAWN_SKIP_TEST_IF(IsWindows() && IsVulkan() && IsIntel()); - std::array renderTargets; - std::array renderTargetViews; + std::array renderTargets; + std::array renderTargetViews; - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = kRTSize; descriptor.size.height = kRTSize; descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; for (uint32_t i = 0; i < 4; ++i) { renderTargets[i] = device.CreateTexture(&descriptor); - renderTargetViews[i] = renderTargets[i].CreateDefaultView(); + renderTargetViews[i] = renderTargets[i].CreateView(); } - utils::ComboRenderPassDescriptor renderPass({renderTargetViews[0], renderTargetViews[1], - renderTargetViews[2], renderTargetViews[3]}); + utils::ComboRenderPassDescriptor renderPass( + {renderTargetViews[0], renderTargetViews[1], renderTargetViews[2], renderTargetViews[3]}); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform myBlock { vec4 color0; @@ -795,43 +785,41 @@ TEST_P(ColorStateTest, IndependentColorState) { )"); utils::ComboRenderPipelineDescriptor baseDescriptor(device); - baseDescriptor.layout = pipelineLayout; - baseDescriptor.cVertexStage.module = vsModule; + baseDescriptor.vertexStage.module = vsModule; baseDescriptor.cFragmentStage.module = fsModule; baseDescriptor.colorStateCount = 4; basePipeline = device.CreateRenderPipeline(&baseDescriptor); utils::ComboRenderPipelineDescriptor testDescriptor(device); - testDescriptor.layout = pipelineLayout; - testDescriptor.cVertexStage.module = vsModule; + testDescriptor.vertexStage.module = vsModule; testDescriptor.cFragmentStage.module = fsModule; testDescriptor.colorStateCount = 4; // set color states - dawn::BlendDescriptor blend1; - blend1.operation = dawn::BlendOperation::Add; - blend1.srcFactor = dawn::BlendFactor::One; - blend1.dstFactor = dawn::BlendFactor::One; + wgpu::BlendDescriptor blend1; + blend1.operation = wgpu::BlendOperation::Add; + blend1.srcFactor = wgpu::BlendFactor::One; + blend1.dstFactor = wgpu::BlendFactor::One; - dawn::BlendDescriptor blend2; - blend2.operation = dawn::BlendOperation::Subtract; - blend2.srcFactor = dawn::BlendFactor::One; - blend2.dstFactor = dawn::BlendFactor::One; + wgpu::BlendDescriptor blend2; + blend2.operation = wgpu::BlendOperation::Subtract; + blend2.srcFactor = wgpu::BlendFactor::One; + blend2.dstFactor = wgpu::BlendFactor::One; - dawn::BlendDescriptor blend3; - blend3.operation = dawn::BlendOperation::Min; - blend3.srcFactor = dawn::BlendFactor::One; - blend3.dstFactor = dawn::BlendFactor::One; + wgpu::BlendDescriptor blend3; + blend3.operation = wgpu::BlendOperation::Min; + blend3.srcFactor = wgpu::BlendFactor::One; + blend3.dstFactor = wgpu::BlendFactor::One; - testDescriptor.cColorStates[0]->colorBlend = blend1; - testDescriptor.cColorStates[0]->alphaBlend = blend1; + testDescriptor.cColorStates[0].colorBlend = blend1; + testDescriptor.cColorStates[0].alphaBlend = blend1; - testDescriptor.cColorStates[1]->colorBlend = blend2; - testDescriptor.cColorStates[1]->alphaBlend = blend2; + testDescriptor.cColorStates[1].colorBlend = blend2; + testDescriptor.cColorStates[1].alphaBlend = blend2; - testDescriptor.cColorStates[3]->colorBlend = blend3; - testDescriptor.cColorStates[3]->alphaBlend = blend3; + testDescriptor.cColorStates[3].colorBlend = blend3; + testDescriptor.cColorStates[3].alphaBlend = blend3; testPipeline = device.CreateRenderPipeline(&testDescriptor); @@ -847,22 +835,22 @@ TEST_P(ColorStateTest, IndependentColorState) { RGBA8 expected2 = color2; RGBA8 expected3 = min(color3, base); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(basePipeline); pass.SetBindGroup( - 0, MakeBindGroupForColors(std::array({{base, base, base, base}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + 0, MakeBindGroupForColors(std::array({{base, base, base, base}}))); + pass.Draw(3); pass.SetPipeline(testPipeline); pass.SetBindGroup(0, MakeBindGroupForColors( - std::array({{color0, color1, color2, color3}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + std::array({{color0, color1, color2, color3}}))); + pass.Draw(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(expected0, renderTargets[0], kRTSize / 2, kRTSize / 2) @@ -882,7 +870,8 @@ TEST_P(ColorStateTest, IndependentColorState) { // Test that the default blend color is correctly set at the beginning of every subpass TEST_P(ColorStateTest, DefaultBlendColor) { - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform myBlock { vec4 color; @@ -896,46 +885,44 @@ TEST_P(ColorStateTest, DefaultBlendColor) { )"); utils::ComboRenderPipelineDescriptor baseDescriptor(device); - baseDescriptor.layout = pipelineLayout; - baseDescriptor.cVertexStage.module = vsModule; + baseDescriptor.vertexStage.module = vsModule; baseDescriptor.cFragmentStage.module = fsModule; - baseDescriptor.cColorStates[0]->format = renderPass.colorFormat; + baseDescriptor.cColorStates[0].format = renderPass.colorFormat; basePipeline = device.CreateRenderPipeline(&baseDescriptor); utils::ComboRenderPipelineDescriptor testDescriptor(device); - testDescriptor.layout = pipelineLayout; - testDescriptor.cVertexStage.module = vsModule; + testDescriptor.vertexStage.module = vsModule; testDescriptor.cFragmentStage.module = fsModule; - testDescriptor.cColorStates[0]->format = renderPass.colorFormat; + testDescriptor.cColorStates[0].format = renderPass.colorFormat; - dawn::BlendDescriptor blend; - blend.operation = dawn::BlendOperation::Add; - blend.srcFactor = dawn::BlendFactor::BlendColor; - blend.dstFactor = dawn::BlendFactor::One; - testDescriptor.cColorStates[0]->colorBlend = blend; - testDescriptor.cColorStates[0]->alphaBlend = blend; + wgpu::BlendDescriptor blend; + blend.operation = wgpu::BlendOperation::Add; + blend.srcFactor = wgpu::BlendFactor::BlendColor; + blend.dstFactor = wgpu::BlendFactor::One; + testDescriptor.cColorStates[0].colorBlend = blend; + testDescriptor.cColorStates[0].alphaBlend = blend; testPipeline = device.CreateRenderPipeline(&testDescriptor); - constexpr dawn::Color kWhite{1.0f, 1.0f, 1.0f, 1.0f}; + constexpr wgpu::Color kWhite{1.0f, 1.0f, 1.0f, 1.0f}; // Check that the initial blend color is (0,0,0,0) { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(basePipeline); pass.SetBindGroup(0, - MakeBindGroupForColors(std::array({{RGBA8(0, 0, 0, 0)}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + MakeBindGroupForColors(std::array({{RGBA8(0, 0, 0, 0)}}))); + pass.Draw(3); pass.SetPipeline(testPipeline); pass.SetBindGroup( - 0, MakeBindGroupForColors(std::array({{RGBA8(255, 255, 255, 255)}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + 0, MakeBindGroupForColors(std::array({{RGBA8(255, 255, 255, 255)}}))); + pass.Draw(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kRTSize / 2, kRTSize / 2); @@ -943,22 +930,22 @@ TEST_P(ColorStateTest, DefaultBlendColor) { // Check that setting the blend color works { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(basePipeline); pass.SetBindGroup(0, - MakeBindGroupForColors(std::array({{RGBA8(0, 0, 0, 0)}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + MakeBindGroupForColors(std::array({{RGBA8(0, 0, 0, 0)}}))); + pass.Draw(3); pass.SetPipeline(testPipeline); pass.SetBlendColor(&kWhite); pass.SetBindGroup( - 0, MakeBindGroupForColors(std::array({{RGBA8(255, 255, 255, 255)}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + 0, MakeBindGroupForColors(std::array({{RGBA8(255, 255, 255, 255)}}))); + pass.Draw(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(RGBA8(255, 255, 255, 255), renderPass.color, kRTSize / 2, @@ -967,34 +954,34 @@ TEST_P(ColorStateTest, DefaultBlendColor) { // Check that the blend color is not inherited between render passes { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(basePipeline); pass.SetBindGroup(0, - MakeBindGroupForColors(std::array({{RGBA8(0, 0, 0, 0)}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + MakeBindGroupForColors(std::array({{RGBA8(0, 0, 0, 0)}}))); + pass.Draw(3); pass.SetPipeline(testPipeline); pass.SetBlendColor(&kWhite); pass.SetBindGroup( - 0, MakeBindGroupForColors(std::array({{RGBA8(255, 255, 255, 255)}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + 0, MakeBindGroupForColors(std::array({{RGBA8(255, 255, 255, 255)}}))); + pass.Draw(3); pass.EndPass(); } { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(basePipeline); pass.SetBindGroup(0, - MakeBindGroupForColors(std::array({{RGBA8(0, 0, 0, 0)}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + MakeBindGroupForColors(std::array({{RGBA8(0, 0, 0, 0)}}))); + pass.Draw(3); pass.SetPipeline(testPipeline); pass.SetBindGroup( - 0, MakeBindGroupForColors(std::array({{RGBA8(255, 255, 255, 255)}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + 0, MakeBindGroupForColors(std::array({{RGBA8(255, 255, 255, 255)}}))); + pass.Draw(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kRTSize / 2, kRTSize / 2); @@ -1005,7 +992,8 @@ TEST_P(ColorStateTest, DefaultBlendColor) { // persisted and prevented a render pass loadOp from fully clearing the output // attachment. TEST_P(ColorStateTest, ColorWriteMaskDoesNotAffectRenderPassLoadOpClear) { - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform myBlock { vec4 color; @@ -1019,32 +1007,30 @@ TEST_P(ColorStateTest, ColorWriteMaskDoesNotAffectRenderPassLoadOpClear) { )"); utils::ComboRenderPipelineDescriptor baseDescriptor(device); - baseDescriptor.layout = pipelineLayout; - baseDescriptor.cVertexStage.module = vsModule; + baseDescriptor.vertexStage.module = vsModule; baseDescriptor.cFragmentStage.module = fsModule; - baseDescriptor.cColorStates[0]->format = renderPass.colorFormat; + baseDescriptor.cColorStates[0].format = renderPass.colorFormat; basePipeline = device.CreateRenderPipeline(&baseDescriptor); utils::ComboRenderPipelineDescriptor testDescriptor(device); - testDescriptor.layout = pipelineLayout; - testDescriptor.cVertexStage.module = vsModule; + testDescriptor.vertexStage.module = vsModule; testDescriptor.cFragmentStage.module = fsModule; - testDescriptor.cColorStates[0]->format = renderPass.colorFormat; - testDescriptor.cColorStates[0]->writeMask = dawn::ColorWriteMask::Red; + testDescriptor.cColorStates[0].format = renderPass.colorFormat; + testDescriptor.cColorStates[0].writeMask = wgpu::ColorWriteMask::Red; testPipeline = device.CreateRenderPipeline(&testDescriptor); RGBA8 base(32, 64, 128, 192); RGBA8 expected(0, 0, 0, 0); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { // Clear the output attachment to |base| - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(basePipeline); - pass.SetBindGroup(0, MakeBindGroupForColors(std::array({{base}})), 0, nullptr); - pass.Draw(3, 1, 0, 0); + pass.SetBindGroup(0, MakeBindGroupForColors(std::array({{base}}))); + pass.Draw(3); // Set a pipeline that will dirty the color write mask pass.SetPipeline(testPipeline); @@ -1052,13 +1038,17 @@ TEST_P(ColorStateTest, ColorWriteMaskDoesNotAffectRenderPassLoadOpClear) { } { // This renderpass' loadOp should clear all channels of the output attachment - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(expected, renderPass.color, kRTSize / 2, kRTSize / 2); } -DAWN_INSTANTIATE_TEST(ColorStateTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(ColorStateTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/CompressedTextureFormatTests.cpp b/third_party/dawn/src/tests/end2end/CompressedTextureFormatTests.cpp new file mode 100644 index 00000000000..2054dfaa90c --- /dev/null +++ b/third_party/dawn/src/tests/end2end/CompressedTextureFormatTests.cpp @@ -0,0 +1,1182 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Assert.h" +#include "common/Constants.h" +#include "common/Math.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" + +// The helper struct to configure the copies between buffers and textures. +struct CopyConfig { + wgpu::TextureDescriptor textureDescriptor; + wgpu::Extent3D copyExtent3D; + wgpu::Origin3D copyOrigin3D = {0, 0, 0}; + uint32_t viewMipmapLevel = 0; + uint32_t bufferOffset = 0; + uint32_t bytesPerRowAlignment = kTextureBytesPerRowAlignment; + uint32_t rowsPerImage = 0; +}; + +class CompressedTextureBCFormatTest : public DawnTest { + protected: + std::vector GetRequiredExtensions() override { + mIsBCFormatSupported = SupportsExtensions({"texture_compression_bc"}); + if (!mIsBCFormatSupported) { + return {}; + } + + return {"texture_compression_bc"}; + } + + bool IsBCFormatSupported() const { + return mIsBCFormatSupported; + } + + // Compute the upload data for the copyConfig. + std::vector UploadData(const CopyConfig& copyConfig) { + uint32_t copyWidthInBlock = copyConfig.copyExtent3D.width / kBCBlockWidthInTexels; + uint32_t copyHeightInBlock = copyConfig.copyExtent3D.height / kBCBlockHeightInTexels; + uint32_t rowPitchInBytes = 0; + if (copyConfig.bytesPerRowAlignment != 0) { + rowPitchInBytes = copyConfig.bytesPerRowAlignment; + } else { + rowPitchInBytes = copyWidthInBlock * + utils::GetTexelBlockSizeInBytes(copyConfig.textureDescriptor.format); + } + uint32_t copyRowsPerImageInBlock = copyConfig.rowsPerImage / kBCBlockHeightInTexels; + if (copyRowsPerImageInBlock == 0) { + copyRowsPerImageInBlock = copyHeightInBlock; + } + uint32_t copyBytesPerImage = rowPitchInBytes * copyRowsPerImageInBlock; + uint32_t uploadBufferSize = + copyConfig.bufferOffset + copyBytesPerImage * copyConfig.copyExtent3D.depth; + + // Fill data with the pre-prepared one-block compressed texture data. + std::vector data(uploadBufferSize, 0); + std::vector oneBlockCompressedTextureData = + GetOneBlockBCFormatTextureData(copyConfig.textureDescriptor.format); + for (uint32_t layer = 0; layer < copyConfig.copyExtent3D.depth; ++layer) { + for (uint32_t h = 0; h < copyHeightInBlock; ++h) { + for (uint32_t w = 0; w < copyWidthInBlock; ++w) { + uint32_t uploadBufferOffset = copyConfig.bufferOffset + + copyBytesPerImage * layer + rowPitchInBytes * h + + oneBlockCompressedTextureData.size() * w; + std::memcpy(&data[uploadBufferOffset], oneBlockCompressedTextureData.data(), + oneBlockCompressedTextureData.size() * sizeof(uint8_t)); + } + } + } + + return data; + } + + // Copy the compressed texture data into the destination texture as is specified in copyConfig. + void InitializeDataInCompressedTexture(wgpu::Texture bcCompressedTexture, + const CopyConfig& copyConfig) { + ASSERT(IsBCFormatSupported()); + + std::vector data = UploadData(copyConfig); + + // Copy texture data from a staging buffer to the destination texture. + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData(device, data.data(), data.size(), + wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, copyConfig.bufferOffset, + copyConfig.bytesPerRowAlignment, copyConfig.rowsPerImage); + + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView( + bcCompressedTexture, copyConfig.viewMipmapLevel, copyConfig.copyOrigin3D); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Config.copyExtent3D); + wgpu::CommandBuffer copy = encoder.Finish(); + queue.Submit(1, ©); + } + + // Create the bind group that includes a BC texture and a sampler. + wgpu::BindGroup CreateBindGroupForTest(wgpu::BindGroupLayout bindGroupLayout, + wgpu::Texture bcCompressedTexture, + wgpu::TextureFormat bcFormat, + uint32_t baseArrayLayer = 0, + uint32_t baseMipLevel = 0) { + ASSERT(IsBCFormatSupported()); + + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + samplerDesc.minFilter = wgpu::FilterMode::Nearest; + samplerDesc.magFilter = wgpu::FilterMode::Nearest; + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + wgpu::TextureViewDescriptor textureViewDescriptor; + textureViewDescriptor.format = bcFormat; + textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; + textureViewDescriptor.baseMipLevel = baseMipLevel; + textureViewDescriptor.baseArrayLayer = baseArrayLayer; + textureViewDescriptor.arrayLayerCount = 1; + textureViewDescriptor.mipLevelCount = 1; + wgpu::TextureView bcTextureView = bcCompressedTexture.CreateView(&textureViewDescriptor); + + return utils::MakeBindGroup(device, bindGroupLayout, {{0, sampler}, {1, bcTextureView}}); + } + + // Create a render pipeline for sampling from a BC texture and rendering into the render target. + wgpu::RenderPipeline CreateRenderPipelineForTest() { + ASSERT(IsBCFormatSupported()); + + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(location=0) out vec2 texCoord; + void main() { + const vec2 pos[3] = vec2[3]( + vec2(-3.0f, 1.0f), + vec2( 3.0f, 1.0f), + vec2( 0.0f, -2.0f) + ); + gl_Position = vec4(pos[gl_VertexIndex], 0.0f, 1.0f); + texCoord = vec2(gl_Position.x / 2.0f, -gl_Position.y / 2.0f) + vec2(0.5f); + })"); + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0) uniform sampler sampler0; + layout(set = 0, binding = 1) uniform texture2D texture0; + layout(location = 0) in vec2 texCoord; + layout(location = 0) out vec4 fragColor; + + void main() { + fragColor = texture(sampler2D(texture0, sampler0), texCoord); + })"); + renderPipelineDescriptor.vertexStage.module = vsModule; + renderPipelineDescriptor.cFragmentStage.module = fsModule; + renderPipelineDescriptor.cColorStates[0].format = + utils::BasicRenderPass::kDefaultColorFormat; + + return device.CreateRenderPipeline(&renderPipelineDescriptor); + } + + // Run the given render pipeline and bind group and verify the pixels in the render target. + void VerifyCompressedTexturePixelValues(wgpu::RenderPipeline renderPipeline, + wgpu::BindGroup bindGroup, + const wgpu::Extent3D& renderTargetSize, + const wgpu::Origin3D& expectedOrigin, + const wgpu::Extent3D& expectedExtent, + const std::vector& expected) { + ASSERT(IsBCFormatSupported()); + + utils::BasicRenderPass renderPass = + utils::CreateBasicRenderPass(device, renderTargetSize.width, renderTargetSize.height); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(renderPipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, expectedOrigin.x, + expectedOrigin.y, expectedExtent.width, expectedExtent.height, 0, + 0); + } + + // Run the tests that copies pre-prepared BC format data into a BC texture and verifies if we + // can render correctly with the pixel values sampled from the BC texture. + void TestCopyRegionIntoBCFormatTextures(const CopyConfig& config) { + ASSERT(IsBCFormatSupported()); + + wgpu::Texture bcTexture = CreateTextureWithCompressedData(config); + + VerifyBCTexture(config, bcTexture); + } + + void VerifyBCTexture(const CopyConfig& config, wgpu::Texture bcTexture) { + wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest(); + + wgpu::Extent3D virtualSizeAtLevel = GetVirtualSizeAtLevel(config); + + // The copy region may exceed the subresource size because of the required paddings for BC + // blocks, so we should limit the size of the expectedData to make it match the real size + // of the render target. + wgpu::Extent3D noPaddingExtent3D = config.copyExtent3D; + if (config.copyOrigin3D.x + config.copyExtent3D.width > virtualSizeAtLevel.width) { + noPaddingExtent3D.width = virtualSizeAtLevel.width - config.copyOrigin3D.x; + } + if (config.copyOrigin3D.y + config.copyExtent3D.height > virtualSizeAtLevel.height) { + noPaddingExtent3D.height = virtualSizeAtLevel.height - config.copyOrigin3D.y; + } + noPaddingExtent3D.depth = 1u; + + std::vector expectedData = + GetExpectedData(config.textureDescriptor.format, noPaddingExtent3D); + + wgpu::Origin3D firstLayerCopyOrigin = {config.copyOrigin3D.x, config.copyOrigin3D.y, 0}; + for (uint32_t layer = config.copyOrigin3D.z; + layer < config.copyOrigin3D.z + config.copyExtent3D.depth; ++layer) { + wgpu::BindGroup bindGroup = CreateBindGroupForTest( + renderPipeline.GetBindGroupLayout(0), bcTexture, config.textureDescriptor.format, + layer, config.viewMipmapLevel); + VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, virtualSizeAtLevel, + firstLayerCopyOrigin, noPaddingExtent3D, + expectedData); + } + } + + // Create a texture and initialize it with the pre-prepared compressed texture data. + wgpu::Texture CreateTextureWithCompressedData(CopyConfig config) { + wgpu::Texture bcTexture = device.CreateTexture(&config.textureDescriptor); + InitializeDataInCompressedTexture(bcTexture, config); + return bcTexture; + } + + // Record a texture-to-texture copy command into command encoder without finishing the encoding. + void RecordTextureToTextureCopy(wgpu::CommandEncoder encoder, + wgpu::Texture srcTexture, + wgpu::Texture dstTexture, + CopyConfig srcConfig, + CopyConfig dstConfig) { + wgpu::TextureCopyView textureCopyViewSrc = utils::CreateTextureCopyView( + srcTexture, srcConfig.viewMipmapLevel, srcConfig.copyOrigin3D); + wgpu::TextureCopyView textureCopyViewDst = utils::CreateTextureCopyView( + dstTexture, dstConfig.viewMipmapLevel, dstConfig.copyOrigin3D); + encoder.CopyTextureToTexture(&textureCopyViewSrc, &textureCopyViewDst, + &dstConfig.copyExtent3D); + } + + wgpu::Texture CreateTextureFromTexture(wgpu::Texture srcTexture, + CopyConfig srcConfig, + CopyConfig dstConfig) { + wgpu::Texture dstTexture = device.CreateTexture(&dstConfig.textureDescriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + RecordTextureToTextureCopy(encoder, srcTexture, dstTexture, srcConfig, dstConfig); + wgpu::CommandBuffer copy = encoder.Finish(); + queue.Submit(1, ©); + + return dstTexture; + } + + // Return the pre-prepared one-block BC texture data. + static std::vector GetOneBlockBCFormatTextureData(wgpu::TextureFormat bcFormat) { + switch (bcFormat) { + // The expected data represents 4x4 pixel images with the left side dark red and the + // right side dark green. We specify the same compressed data in both sRGB and non-sRGB + // tests, but the rendering result should be different because for sRGB formats, the + // red, green, and blue components are converted from an sRGB color space to a linear + // color space as part of filtering. + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + return {0x0, 0xC0, 0x60, 0x6, 0x50, 0x50, 0x50, 0x50}; + case wgpu::TextureFormat::BC7RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return {0x50, 0x18, 0xfc, 0xf, 0x0, 0x30, 0xe3, 0xe1, + 0xe1, 0xe1, 0xc1, 0xf, 0xfc, 0xc0, 0xf, 0xfc}; + + // The expected data represents 4x4 pixel images with the left side dark red and the + // right side dark green. The pixels in the left side of the block all have an alpha + // value equal to 0x88. We specify the same compressed data in both sRGB and non-sRGB + // tests, but the rendering result should be different because for sRGB formats, the + // red, green, and blue components are converted from an sRGB color space to a linear + // color space as part of filtering, and any alpha component is left unchanged. + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + return {0x88, 0xFF, 0x88, 0xFF, 0x88, 0xFF, 0x88, 0xFF, + 0x0, 0xC0, 0x60, 0x6, 0x50, 0x50, 0x50, 0x50}; + case wgpu::TextureFormat::BC3RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + return {0x88, 0xFF, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24, + 0x0, 0xC0, 0x60, 0x6, 0x50, 0x50, 0x50, 0x50}; + + // The expected data represents 4x4 pixel images with the left side red and the + // right side black. + case wgpu::TextureFormat::BC4RSnorm: + return {0x7F, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24}; + case wgpu::TextureFormat::BC4RUnorm: + return {0xFF, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24}; + + // The expected data represents 4x4 pixel images with the left side red and the right + // side green and was encoded with DirectXTex from Microsoft. + case wgpu::TextureFormat::BC5RGSnorm: + return {0x7f, 0x81, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24, + 0x7f, 0x81, 0x9, 0x90, 0x0, 0x9, 0x90, 0x0}; + case wgpu::TextureFormat::BC5RGUnorm: + return {0xff, 0x0, 0x40, 0x2, 0x24, 0x40, 0x2, 0x24, + 0xff, 0x0, 0x9, 0x90, 0x0, 0x9, 0x90, 0x0}; + case wgpu::TextureFormat::BC6HRGBSfloat: + return {0xe3, 0x1f, 0x0, 0x0, 0x0, 0xe0, 0x1f, 0x0, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff}; + case wgpu::TextureFormat::BC6HRGBUfloat: + return {0xe3, 0x3d, 0x0, 0x0, 0x0, 0xe0, 0x3d, 0x0, + 0x0, 0xff, 0x0, 0xff, 0x0, 0xff, 0x0, 0xff}; + + default: + UNREACHABLE(); + return {}; + } + } + + // Return the texture data that is decoded from the result of GetOneBlockBCFormatTextureData in + // RGBA8 formats. + static std::vector GetExpectedData(wgpu::TextureFormat bcFormat, + const wgpu::Extent3D& testRegion) { + constexpr RGBA8 kDarkRed(198, 0, 0, 255); + constexpr RGBA8 kDarkGreen(0, 207, 0, 255); + constexpr RGBA8 kDarkRedSRGB(144, 0, 0, 255); + constexpr RGBA8 kDarkGreenSRGB(0, 159, 0, 255); + + constexpr uint8_t kLeftAlpha = 0x88; + constexpr uint8_t kRightAlpha = 0xFF; + + switch (bcFormat) { + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnorm: + return FillExpectedData(testRegion, kDarkRed, kDarkGreen); + + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnorm: { + constexpr RGBA8 kLeftColor = RGBA8(kDarkRed.r, 0, 0, kLeftAlpha); + constexpr RGBA8 kRightColor = RGBA8(0, kDarkGreen.g, 0, kRightAlpha); + return FillExpectedData(testRegion, kLeftColor, kRightColor); + } + + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return FillExpectedData(testRegion, kDarkRedSRGB, kDarkGreenSRGB); + + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: { + constexpr RGBA8 kLeftColor = RGBA8(kDarkRedSRGB.r, 0, 0, kLeftAlpha); + constexpr RGBA8 kRightColor = RGBA8(0, kDarkGreenSRGB.g, 0, kRightAlpha); + return FillExpectedData(testRegion, kLeftColor, kRightColor); + } + + case wgpu::TextureFormat::BC4RSnorm: + case wgpu::TextureFormat::BC4RUnorm: + return FillExpectedData(testRegion, RGBA8::kRed, RGBA8::kBlack); + + case wgpu::TextureFormat::BC5RGSnorm: + case wgpu::TextureFormat::BC5RGUnorm: + case wgpu::TextureFormat::BC6HRGBSfloat: + case wgpu::TextureFormat::BC6HRGBUfloat: + return FillExpectedData(testRegion, RGBA8::kRed, RGBA8::kGreen); + + default: + UNREACHABLE(); + return {}; + } + } + + static std::vector FillExpectedData(const wgpu::Extent3D& testRegion, + RGBA8 leftColorInBlock, + RGBA8 rightColorInBlock) { + ASSERT(testRegion.depth == 1); + + std::vector expectedData(testRegion.width * testRegion.height, leftColorInBlock); + for (uint32_t y = 0; y < testRegion.height; ++y) { + for (uint32_t x = 0; x < testRegion.width; ++x) { + if (x % kBCBlockWidthInTexels >= kBCBlockWidthInTexels / 2) { + expectedData[testRegion.width * y + x] = rightColorInBlock; + } + } + } + return expectedData; + } + + static wgpu::Extent3D GetVirtualSizeAtLevel(const CopyConfig& config) { + return {config.textureDescriptor.size.width >> config.viewMipmapLevel, + config.textureDescriptor.size.height >> config.viewMipmapLevel, 1}; + } + + static wgpu::Extent3D GetPhysicalSizeAtLevel(const CopyConfig& config) { + wgpu::Extent3D sizeAtLevel = GetVirtualSizeAtLevel(config); + sizeAtLevel.width = (sizeAtLevel.width + kBCBlockWidthInTexels - 1) / + kBCBlockWidthInTexels * kBCBlockWidthInTexels; + sizeAtLevel.height = (sizeAtLevel.height + kBCBlockHeightInTexels - 1) / + kBCBlockHeightInTexels * kBCBlockHeightInTexels; + return sizeAtLevel; + } + + const std::array kBCFormats = { + wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb, + wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb, + wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb, + wgpu::TextureFormat::BC4RSnorm, wgpu::TextureFormat::BC4RUnorm, + wgpu::TextureFormat::BC5RGSnorm, wgpu::TextureFormat::BC5RGUnorm, + wgpu::TextureFormat::BC6HRGBSfloat, wgpu::TextureFormat::BC6HRGBUfloat, + wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb}; + + // Tthe block width and height in texels are 4 for all BC formats. + static constexpr uint32_t kBCBlockWidthInTexels = 4; + static constexpr uint32_t kBCBlockHeightInTexels = 4; + + static constexpr wgpu::TextureUsage kDefaultBCFormatTextureUsage = + wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopyDst; + + bool mIsBCFormatSupported = false; +}; + +// Test copying into the whole BC texture with 2x2 blocks and sampling from it. +TEST_P(CompressedTextureBCFormatTest, Basic) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, 1}; + config.copyExtent3D = config.textureDescriptor.size; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test copying into a sub-region of a texture with BC formats works correctly. +TEST_P(CompressedTextureBCFormatTest, CopyIntoSubRegion) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, 1}; + + const wgpu::Origin3D kOrigin = {4, 4, 0}; + const wgpu::Extent3D kExtent3D = {4, 4, 1}; + config.copyOrigin3D = kOrigin; + config.copyExtent3D = kExtent3D; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test copying into the non-zero layer of a 2D array texture with BC formats works correctly. +TEST_P(CompressedTextureBCFormatTest, CopyIntoNonZeroArrayLayer) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, 1}; + config.copyExtent3D = config.textureDescriptor.size; + + constexpr uint32_t kArrayLayerCount = 3; + config.textureDescriptor.size.depth = kArrayLayerCount; + config.copyOrigin3D.z = kArrayLayerCount - 1; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test copying into a non-zero mipmap level of a texture with BC texture formats. +TEST_P(CompressedTextureBCFormatTest, CopyBufferIntoNonZeroMipmapLevel) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {60, 60, 1}; + + constexpr uint32_t kMipmapLevelCount = 3; + config.textureDescriptor.mipLevelCount = kMipmapLevelCount; + config.viewMipmapLevel = kMipmapLevelCount - 1; + + // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are + // required in the copies. + const wgpu::Extent3D textureSizeLevel0 = config.textureDescriptor.size; + const uint32_t kActualWidthAtLevel = textureSizeLevel0.width >> config.viewMipmapLevel; + const uint32_t kActualHeightAtLevel = textureSizeLevel0.height >> config.viewMipmapLevel; + ASSERT(kActualWidthAtLevel % kBCBlockWidthInTexels != 0); + ASSERT(kActualHeightAtLevel % kBCBlockHeightInTexels != 0); + + const uint32_t kCopyWidthAtLevel = (kActualWidthAtLevel + kBCBlockWidthInTexels - 1) / + kBCBlockWidthInTexels * kBCBlockWidthInTexels; + const uint32_t kCopyHeightAtLevel = (kActualHeightAtLevel + kBCBlockHeightInTexels - 1) / + kBCBlockHeightInTexels * kBCBlockHeightInTexels; + + config.copyExtent3D = {kCopyWidthAtLevel, kCopyHeightAtLevel, 1}; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test texture-to-texture whole-size copies with BC formats. +TEST_P(CompressedTextureBCFormatTest, CopyWholeTextureSubResourceIntoNonZeroMipmapLevel) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + // TODO(cwallez@chromium.org): This consistently fails on with the 12th pixel being opaque black + // instead of opaque red on Win10 FYI Release (NVIDIA GeForce GTX 1660). See + // https://bugs.chromium.org/p/chromium/issues/detail?id=981393 + DAWN_SKIP_TEST_IF(IsWindows() && IsVulkan() && IsNvidia()); + + CopyConfig config; + config.textureDescriptor.size = {60, 60, 1}; + + constexpr uint32_t kMipmapLevelCount = 3; + config.textureDescriptor.mipLevelCount = kMipmapLevelCount; + config.viewMipmapLevel = kMipmapLevelCount - 1; + + // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are + // required in the copies. + const wgpu::Extent3D kVirtualSize = GetVirtualSizeAtLevel(config); + const wgpu::Extent3D kPhysicalSize = GetPhysicalSizeAtLevel(config); + ASSERT_NE(0u, kVirtualSize.width % kBCBlockWidthInTexels); + ASSERT_NE(0u, kVirtualSize.height % kBCBlockHeightInTexels); + + config.copyExtent3D = kPhysicalSize; + for (wgpu::TextureFormat format : kBCFormats) { + // Create bcTextureSrc as the source texture and initialize it with pre-prepared BC + // compressed data. + config.textureDescriptor.format = format; + // Add the usage bit for both source and destination textures so that we don't need to + // create two copy configs. + config.textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + + wgpu::Texture bcTextureSrc = CreateTextureWithCompressedData(config); + + // Create bcTexture and copy from the content in bcTextureSrc into it. + wgpu::Texture bcTextureDst = CreateTextureFromTexture(bcTextureSrc, config, config); + + // Verify if we can use bcTexture as sampled textures correctly. + wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest(); + wgpu::BindGroup bindGroup = + CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), bcTextureDst, format, + config.copyOrigin3D.z, config.viewMipmapLevel); + + std::vector expectedData = GetExpectedData(format, kVirtualSize); + VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kVirtualSize, + config.copyOrigin3D, kVirtualSize, expectedData); + } +} + +// Test BC format texture-to-texture partial copies where the physical size of the destination +// subresource is different from its virtual size. +TEST_P(CompressedTextureBCFormatTest, CopyIntoSubresourceWithPhysicalSizeNotEqualToVirtualSize) { + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + // TODO(jiawei.shao@intel.com): add workaround on the T2T copies where Extent3D fits in one + // subresource and does not fit in another one on OpenGL. + DAWN_SKIP_TEST_IF(IsOpenGL()); + + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + CopyConfig srcConfig; + srcConfig.textureDescriptor.size = {60, 60, 1}; + srcConfig.viewMipmapLevel = srcConfig.textureDescriptor.mipLevelCount - 1; + + const wgpu::Extent3D kSrcVirtualSize = GetVirtualSizeAtLevel(srcConfig); + + CopyConfig dstConfig; + dstConfig.textureDescriptor.size = {60, 60, 1}; + constexpr uint32_t kMipmapLevelCount = 3; + dstConfig.textureDescriptor.mipLevelCount = kMipmapLevelCount; + dstConfig.viewMipmapLevel = kMipmapLevelCount - 1; + + // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are + // required in the copies. + const wgpu::Extent3D kDstVirtualSize = GetVirtualSizeAtLevel(dstConfig); + ASSERT_NE(0u, kDstVirtualSize.width % kBCBlockWidthInTexels); + ASSERT_NE(0u, kDstVirtualSize.height % kBCBlockHeightInTexels); + + const wgpu::Extent3D kDstPhysicalSize = GetPhysicalSizeAtLevel(dstConfig); + + srcConfig.copyExtent3D = dstConfig.copyExtent3D = kDstPhysicalSize; + ASSERT_LT(srcConfig.copyOrigin3D.x + srcConfig.copyExtent3D.width, kSrcVirtualSize.width); + ASSERT_LT(srcConfig.copyOrigin3D.y + srcConfig.copyExtent3D.height, kSrcVirtualSize.height); + + for (wgpu::TextureFormat format : kBCFormats) { + // Create bcTextureSrc as the source texture and initialize it with pre-prepared BC + // compressed data. + srcConfig.textureDescriptor.format = format; + srcConfig.textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + wgpu::Texture bcTextureSrc = CreateTextureWithCompressedData(srcConfig); + wgpu::TextureCopyView textureCopyViewSrc = utils::CreateTextureCopyView( + bcTextureSrc, srcConfig.viewMipmapLevel, srcConfig.copyOrigin3D); + + // Create bcTexture and copy from the content in bcTextureSrc into it. + dstConfig.textureDescriptor.format = format; + dstConfig.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + wgpu::Texture bcTextureDst = CreateTextureFromTexture(bcTextureSrc, srcConfig, dstConfig); + + // Verify if we can use bcTexture as sampled textures correctly. + wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest(); + wgpu::BindGroup bindGroup = + CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), bcTextureDst, format, + dstConfig.copyOrigin3D.z, dstConfig.viewMipmapLevel); + + std::vector expectedData = GetExpectedData(format, kDstVirtualSize); + VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kDstVirtualSize, + dstConfig.copyOrigin3D, kDstVirtualSize, expectedData); + } +} + +// Test BC format texture-to-texture partial copies where the physical size of the source +// subresource is different from its virtual size. +TEST_P(CompressedTextureBCFormatTest, CopyFromSubresourceWithPhysicalSizeNotEqualToVirtualSize) { + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + // TODO(jiawei.shao@intel.com): add workaround on the T2T copies where Extent3D fits in one + // subresource and does not fit in another one on OpenGL. + DAWN_SKIP_TEST_IF(IsOpenGL()); + + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + CopyConfig srcConfig; + srcConfig.textureDescriptor.size = {60, 60, 1}; + constexpr uint32_t kMipmapLevelCount = 3; + srcConfig.textureDescriptor.mipLevelCount = kMipmapLevelCount; + srcConfig.viewMipmapLevel = srcConfig.textureDescriptor.mipLevelCount - 1; + + // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are + // required in the copies. + const wgpu::Extent3D kSrcVirtualSize = GetVirtualSizeAtLevel(srcConfig); + ASSERT_NE(0u, kSrcVirtualSize.width % kBCBlockWidthInTexels); + ASSERT_NE(0u, kSrcVirtualSize.height % kBCBlockHeightInTexels); + + CopyConfig dstConfig; + dstConfig.textureDescriptor.size = {16, 16, 1}; + dstConfig.viewMipmapLevel = dstConfig.textureDescriptor.mipLevelCount - 1; + + const wgpu::Extent3D kDstVirtualSize = GetVirtualSizeAtLevel(dstConfig); + srcConfig.copyExtent3D = dstConfig.copyExtent3D = kDstVirtualSize; + + ASSERT_GT(srcConfig.copyOrigin3D.x + srcConfig.copyExtent3D.width, kSrcVirtualSize.width); + ASSERT_GT(srcConfig.copyOrigin3D.y + srcConfig.copyExtent3D.height, kSrcVirtualSize.height); + + for (wgpu::TextureFormat format : kBCFormats) { + srcConfig.textureDescriptor.format = dstConfig.textureDescriptor.format = format; + srcConfig.textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + dstConfig.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + + // Create bcTextureSrc as the source texture and initialize it with pre-prepared BC + // compressed data. + wgpu::Texture bcTextureSrc = CreateTextureWithCompressedData(srcConfig); + + // Create bcTexture and copy from the content in bcTextureSrc into it. + wgpu::Texture bcTextureDst = CreateTextureFromTexture(bcTextureSrc, srcConfig, dstConfig); + + // Verify if we can use bcTexture as sampled textures correctly. + wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest(); + wgpu::BindGroup bindGroup = + CreateBindGroupForTest(renderPipeline.GetBindGroupLayout(0), bcTextureDst, format, + dstConfig.copyOrigin3D.z, dstConfig.viewMipmapLevel); + + std::vector expectedData = GetExpectedData(format, kDstVirtualSize); + VerifyCompressedTexturePixelValues(renderPipeline, bindGroup, kDstVirtualSize, + dstConfig.copyOrigin3D, kDstVirtualSize, expectedData); + } +} + +// Test recording two BC format texture-to-texture partial copies where the physical size of the +// source subresource is different from its virtual size into one command buffer. +TEST_P(CompressedTextureBCFormatTest, MultipleCopiesWithPhysicalSizeNotEqualToVirtualSize) { + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + // TODO(jiawei.shao@intel.com): add workaround on the T2T copies where Extent3D fits in one + // subresource and does not fit in another one on OpenGL. + DAWN_SKIP_TEST_IF(IsOpenGL()); + + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + constexpr uint32_t kTotalCopyCount = 2; + std::array srcConfigs; + std::array dstConfigs; + + constexpr uint32_t kSrcMipmapLevelCount0 = 3; + srcConfigs[0].textureDescriptor.size = {60, 60, 1}; + srcConfigs[0].textureDescriptor.mipLevelCount = kSrcMipmapLevelCount0; + srcConfigs[0].viewMipmapLevel = srcConfigs[0].textureDescriptor.mipLevelCount - 1; + dstConfigs[0].textureDescriptor.size = {16, 16, 1}; + dstConfigs[0].viewMipmapLevel = dstConfigs[0].textureDescriptor.mipLevelCount - 1; + srcConfigs[0].copyExtent3D = dstConfigs[0].copyExtent3D = GetVirtualSizeAtLevel(dstConfigs[0]); + const wgpu::Extent3D kSrcVirtualSize0 = GetVirtualSizeAtLevel(srcConfigs[0]); + ASSERT_NE(0u, kSrcVirtualSize0.width % kBCBlockWidthInTexels); + ASSERT_NE(0u, kSrcVirtualSize0.height % kBCBlockHeightInTexels); + + constexpr uint32_t kDstMipmapLevelCount1 = 4; + srcConfigs[1].textureDescriptor.size = {8, 8, 1}; + srcConfigs[1].viewMipmapLevel = srcConfigs[1].textureDescriptor.mipLevelCount - 1; + dstConfigs[1].textureDescriptor.size = {56, 56, 1}; + dstConfigs[1].textureDescriptor.mipLevelCount = kDstMipmapLevelCount1; + dstConfigs[1].viewMipmapLevel = dstConfigs[1].textureDescriptor.mipLevelCount - 1; + srcConfigs[1].copyExtent3D = dstConfigs[1].copyExtent3D = GetVirtualSizeAtLevel(srcConfigs[1]); + + std::array dstVirtualSizes; + for (uint32_t i = 0; i < kTotalCopyCount; ++i) { + dstVirtualSizes[i] = GetVirtualSizeAtLevel(dstConfigs[i]); + } + ASSERT_NE(0u, dstVirtualSizes[1].width % kBCBlockWidthInTexels); + ASSERT_NE(0u, dstVirtualSizes[1].height % kBCBlockHeightInTexels); + + for (wgpu::TextureFormat format : kBCFormats) { + std::array bcSrcTextures; + std::array bcDstTextures; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + for (uint32_t i = 0; i < kTotalCopyCount; ++i) { + srcConfigs[i].textureDescriptor.format = dstConfigs[i].textureDescriptor.format = + format; + srcConfigs[i].textureDescriptor.usage = + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + dstConfigs[i].textureDescriptor.usage = kDefaultBCFormatTextureUsage; + + // Create bcSrcTextures as the source textures and initialize them with pre-prepared BC + // compressed data. + bcSrcTextures[i] = CreateTextureWithCompressedData(srcConfigs[i]); + bcDstTextures[i] = device.CreateTexture(&dstConfigs[i].textureDescriptor); + + RecordTextureToTextureCopy(encoder, bcSrcTextures[i], bcDstTextures[i], srcConfigs[i], + dstConfigs[i]); + } + + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + wgpu::RenderPipeline renderPipeline = CreateRenderPipelineForTest(); + + for (uint32_t i = 0; i < kTotalCopyCount; ++i) { + // Verify if we can use bcDstTextures as sampled textures correctly. + wgpu::BindGroup bindGroup0 = CreateBindGroupForTest( + renderPipeline.GetBindGroupLayout(0), bcDstTextures[i], format, + dstConfigs[i].copyOrigin3D.z, dstConfigs[i].viewMipmapLevel); + + std::vector expectedData = GetExpectedData(format, dstVirtualSizes[i]); + VerifyCompressedTexturePixelValues(renderPipeline, bindGroup0, dstVirtualSizes[i], + dstConfigs[i].copyOrigin3D, dstVirtualSizes[i], + expectedData); + } + } +} + +// Test the special case of the B2T copies on the D3D12 backend that the buffer offset and texture +// extent exactly fit the RowPitch. +TEST_P(CompressedTextureBCFormatTest, BufferOffsetAndExtentFitRowPitch) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, 1}; + config.copyExtent3D = config.textureDescriptor.size; + + const uint32_t blockCountPerRow = config.textureDescriptor.size.width / kBCBlockWidthInTexels; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + + const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); + const uint32_t blockCountPerRowPitch = config.bytesPerRowAlignment / blockSizeInBytes; + + config.bufferOffset = (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes; + + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test the special case of the B2T copies on the D3D12 backend that the buffer offset exceeds the +// slice pitch (slicePitch = bytesPerRow * (rowsPerImage / blockHeightInTexels)). On D3D12 +// backend the texelOffset.y will be greater than 0 after calcuting the texelOffset in the function +// ComputeTexelOffsets(). +TEST_P(CompressedTextureBCFormatTest, BufferOffsetExceedsSlicePitch) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, 1}; + config.copyExtent3D = config.textureDescriptor.size; + + const wgpu::Extent3D textureSizeLevel0 = config.textureDescriptor.size; + const uint32_t blockCountPerRow = textureSizeLevel0.width / kBCBlockWidthInTexels; + const uint32_t slicePitchInBytes = + config.bytesPerRowAlignment * (textureSizeLevel0.height / kBCBlockHeightInTexels); + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + + const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); + const uint32_t blockCountPerRowPitch = config.bytesPerRowAlignment / blockSizeInBytes; + + config.bufferOffset = (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes + + config.bytesPerRowAlignment + slicePitchInBytes; + + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test the special case of the B2T copies on the D3D12 backend that the buffer offset and texture +// extent exceed the RowPitch. On D3D12 backend two copies are required for this case. +TEST_P(CompressedTextureBCFormatTest, CopyWithBufferOffsetAndExtentExceedRowPitch) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, 1}; + config.copyExtent3D = config.textureDescriptor.size; + + const uint32_t blockCountPerRow = config.textureDescriptor.size.width / kBCBlockWidthInTexels; + + constexpr uint32_t kExceedRowBlockCount = 1; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + + const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); + const uint32_t blockCountPerRowPitch = config.bytesPerRowAlignment / blockSizeInBytes; + config.bufferOffset = + (blockCountPerRowPitch - blockCountPerRow + kExceedRowBlockCount) * blockSizeInBytes; + + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test the special case of the B2T copies on the D3D12 backend that the slicePitch is equal to the +// bytesPerRow. On D3D12 backend the texelOffset.z will be greater than 0 after calcuting the +// texelOffset in the function ComputeTexelOffsets(). +TEST_P(CompressedTextureBCFormatTest, RowPitchEqualToSlicePitch) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, kBCBlockHeightInTexels, 1}; + config.copyExtent3D = config.textureDescriptor.size; + + const uint32_t blockCountPerRow = config.textureDescriptor.size.width / kBCBlockWidthInTexels; + const uint32_t slicePitchInBytes = config.bytesPerRowAlignment; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + + const uint32_t blockSizeInBytes = utils::GetTexelBlockSizeInBytes(format); + const uint32_t blockCountPerRowPitch = config.bytesPerRowAlignment / blockSizeInBytes; + + config.bufferOffset = + (blockCountPerRowPitch - blockCountPerRow) * blockSizeInBytes + slicePitchInBytes; + + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test the workaround in the B2T copies when (bufferSize - bufferOffset < bytesPerImage * +// copyExtent.depth) on Metal backends. As copyExtent.depth can only be 1 for BC formats, on Metal +// backend we will use two copies to implement such copy. +TEST_P(CompressedTextureBCFormatTest, LargeImageHeight) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, 1}; + config.copyExtent3D = config.textureDescriptor.size; + + config.rowsPerImage = config.textureDescriptor.size.height * 2; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test the workaround in the B2T copies when (bufferSize - bufferOffset < bytesPerImage * +// copyExtent.depth) and copyExtent needs to be clamped. +TEST_P(CompressedTextureBCFormatTest, LargeImageHeightAndClampedCopyExtent) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {56, 56, 1}; + + constexpr uint32_t kMipmapLevelCount = 3; + config.textureDescriptor.mipLevelCount = kMipmapLevelCount; + config.viewMipmapLevel = kMipmapLevelCount - 1; + + // The actual size of the texture at mipmap level == 2 is not a multiple of 4, paddings are + // required in the copies. + const wgpu::Extent3D textureSizeLevel0 = config.textureDescriptor.size; + const uint32_t kActualWidthAtLevel = textureSizeLevel0.width >> config.viewMipmapLevel; + const uint32_t kActualHeightAtLevel = textureSizeLevel0.height >> config.viewMipmapLevel; + ASSERT(kActualWidthAtLevel % kBCBlockWidthInTexels != 0); + ASSERT(kActualHeightAtLevel % kBCBlockHeightInTexels != 0); + + const uint32_t kCopyWidthAtLevel = (kActualWidthAtLevel + kBCBlockWidthInTexels - 1) / + kBCBlockWidthInTexels * kBCBlockWidthInTexels; + const uint32_t kCopyHeightAtLevel = (kActualHeightAtLevel + kBCBlockHeightInTexels - 1) / + kBCBlockHeightInTexels * kBCBlockHeightInTexels; + + config.copyExtent3D = {kCopyWidthAtLevel, kCopyHeightAtLevel, 1}; + + config.rowsPerImage = kCopyHeightAtLevel * 2; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test copying a whole 2D array texture with array layer count > 1 in one copy command works with +// BC formats. +TEST_P(CompressedTextureBCFormatTest, CopyWhole2DArrayTexture) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + constexpr uint32_t kArrayLayerCount = 3; + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, kArrayLayerCount}; + + config.copyExtent3D = config.textureDescriptor.size; + config.copyExtent3D.depth = kArrayLayerCount; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// Test copying a multiple 2D texture array layers in one copy command works with BC formats. +TEST_P(CompressedTextureBCFormatTest, CopyMultiple2DArrayLayers) { + // TODO(jiawei.shao@intel.com): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // TODO(jiawei.shao@intel.com): find out why this test fails on Windows Intel OpenGL drivers. + DAWN_SKIP_TEST_IF(IsIntel() && IsOpenGL() && IsWindows()); + + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + constexpr uint32_t kArrayLayerCount = 3; + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {8, 8, kArrayLayerCount}; + + constexpr uint32_t kCopyBaseArrayLayer = 1; + constexpr uint32_t kCopyLayerCount = 2; + config.copyOrigin3D = {0, 0, kCopyBaseArrayLayer}; + config.copyExtent3D = config.textureDescriptor.size; + config.copyExtent3D.depth = kCopyLayerCount; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestCopyRegionIntoBCFormatTextures(config); + } +} + +// TODO(jiawei.shao@intel.com): support BC formats on OpenGL backend +DAWN_INSTANTIATE_TEST(CompressedTextureBCFormatTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend(), + VulkanBackend({"use_temporary_buffer_in_texture_to_texture_copy"})); + +class CompressedTextureWriteTextureTest : public CompressedTextureBCFormatTest { + protected: + void SetUp() override { + CompressedTextureBCFormatTest::SetUp(); + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + } + + // Write the compressed texture data into the destination texture as is specified in copyConfig. + void WriteToCompressedTexture(wgpu::Texture bcCompressedTexture, const CopyConfig& copyConfig) { + ASSERT(IsBCFormatSupported()); + + std::vector data = UploadData(copyConfig); + + wgpu::TextureDataLayout textureDataLayout = utils::CreateTextureDataLayout( + copyConfig.bufferOffset, copyConfig.bytesPerRowAlignment, copyConfig.rowsPerImage); + + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView( + bcCompressedTexture, copyConfig.viewMipmapLevel, copyConfig.copyOrigin3D); + + queue.WriteTexture(&textureCopyView, data.data(), data.size(), &textureDataLayout, + ©Config.copyExtent3D); + } + + // Run the tests that write pre-prepared BC format data into a BC texture and verifies if we + // can render correctly with the pixel values sampled from the BC texture. + void TestWriteRegionIntoBCFormatTextures(const CopyConfig& config) { + ASSERT(IsBCFormatSupported()); + + wgpu::Texture bcTexture = device.CreateTexture(&config.textureDescriptor); + WriteToCompressedTexture(bcTexture, config); + + VerifyBCTexture(config, bcTexture); + } +}; + +// Test WriteTexture to a 2D texture with all parameters non-default +// with BC formats. +TEST_P(CompressedTextureWriteTextureTest, Basic) { + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {20, 24, 1}; + + config.copyOrigin3D = {4, 8, 0}; + config.copyExtent3D = {12, 16, 1}; + config.bytesPerRowAlignment = 511; + config.rowsPerImage = 20; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestWriteRegionIntoBCFormatTextures(config); + } +} + +// Test writing to multiple 2D texture array layers with BC formats. +TEST_P(CompressedTextureWriteTextureTest, WriteMultiple2DArrayLayers) { + // TODO(dawn:483): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {20, 24, 9}; + + config.copyOrigin3D = {4, 8, 3}; + config.copyExtent3D = {12, 16, 6}; + config.bytesPerRowAlignment = 511; + config.rowsPerImage = 20; + + for (wgpu::TextureFormat format : kBCFormats) { + config.textureDescriptor.format = format; + TestWriteRegionIntoBCFormatTextures(config); + } +} + +// Test BC format write textures where the physical size of the destination +// subresource is different from its virtual size. +TEST_P(CompressedTextureWriteTextureTest, + WriteIntoSubresourceWithPhysicalSizeNotEqualToVirtualSize) { + // TODO(dawn:483): find out why this test is flaky on Windows Intel Vulkan + // bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsWindows()); + + // Texture virtual size at mipLevel 2 will be {15, 15, 1} while the physical + // size will be {16, 16, 1}. + // Setting copyExtent.width or copyExtent.height to 16 fits in + // the texture physical size, but doesn't fit in the virtual size. + for (unsigned int w : {12, 16}) { + for (unsigned int h : {12, 16}) { + for (wgpu::TextureFormat format : kBCFormats) { + CopyConfig config; + config.textureDescriptor.usage = kDefaultBCFormatTextureUsage; + config.textureDescriptor.size = {60, 60, 1}; + config.textureDescriptor.mipLevelCount = 4; + config.viewMipmapLevel = 2; + + config.copyOrigin3D = {0, 0, 0}; + config.copyExtent3D = {w, h, 1}; + config.bytesPerRowAlignment = 256; + config.textureDescriptor.format = format; + TestWriteRegionIntoBCFormatTextures(config); + } + } + } +} + +DAWN_INSTANTIATE_TEST(CompressedTextureWriteTextureTest, MetalBackend(), VulkanBackend()); \ No newline at end of file diff --git a/third_party/dawn/src/tests/end2end/ComputeCopyStorageBufferTests.cpp b/third_party/dawn/src/tests/end2end/ComputeCopyStorageBufferTests.cpp index c024d8d491d..f127acdb360 100644 --- a/third_party/dawn/src/tests/end2end/ComputeCopyStorageBufferTests.cpp +++ b/third_party/dawn/src/tests/end2end/ComputeCopyStorageBufferTests.cpp @@ -14,7 +14,7 @@ #include "tests/DawnTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" #include @@ -28,63 +28,53 @@ class ComputeCopyStorageBufferTests : public DawnTest { }; void ComputeCopyStorageBufferTests::BasicTest(const char* shader) { - auto bgl = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer}, - {1, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer}, - }); - // Set up shader and pipeline - auto module = utils::CreateShaderModule(device, dawn::ShaderStage::Compute, shader); - auto pl = utils::MakeBasicPipelineLayout(device, &bgl); - - dawn::ComputePipelineDescriptor csDesc; - csDesc.layout = pl; + auto module = utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, shader); - dawn::PipelineStageDescriptor computeStage; - computeStage.module = module; - computeStage.entryPoint = "main"; - csDesc.computeStage = &computeStage; + wgpu::ComputePipelineDescriptor csDesc; + csDesc.computeStage.module = module; + csDesc.computeStage.entryPoint = "main"; - dawn::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc); + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc); // Set up src storage buffer - dawn::BufferDescriptor srcDesc; + wgpu::BufferDescriptor srcDesc; srcDesc.size = kNumUints * sizeof(uint32_t); - srcDesc.usage = dawn::BufferUsageBit::Storage | dawn::BufferUsageBit::TransferSrc | - dawn::BufferUsageBit::TransferDst; - dawn::Buffer src = device.CreateBuffer(&srcDesc); + srcDesc.usage = + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer src = device.CreateBuffer(&srcDesc); std::array expected; for (uint32_t i = 0; i < kNumUints; ++i) { expected[i] = (i + 1u) * 0x11111111u; } - src.SetSubData(0, sizeof(expected), expected.data()); + queue.WriteBuffer(src, 0, expected.data(), sizeof(expected)); EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), src, 0, kNumUints); // Set up dst storage buffer - dawn::BufferDescriptor dstDesc; + wgpu::BufferDescriptor dstDesc; dstDesc.size = kNumUints * sizeof(uint32_t); - dstDesc.usage = dawn::BufferUsageBit::Storage | dawn::BufferUsageBit::TransferSrc | - dawn::BufferUsageBit::TransferDst; - dawn::Buffer dst = device.CreateBuffer(&dstDesc); + dstDesc.usage = + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer dst = device.CreateBuffer(&dstDesc); std::array zero{}; - dst.SetSubData(0, sizeof(zero), zero.data()); + queue.WriteBuffer(dst, 0, zero.data(), sizeof(zero)); // Set up bind group and issue dispatch - dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, { - {0, src, 0, kNumUints * sizeof(uint32_t)}, - {1, dst, 0, kNumUints * sizeof(uint32_t)}, - }); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, src, 0, kNumUints * sizeof(uint32_t)}, + {1, dst, 0, kNumUints * sizeof(uint32_t)}, + }); - dawn::CommandBuffer commands; + wgpu::CommandBuffer commands; { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.Dispatch(kInstances, 1, 1); + pass.SetBindGroup(0, bindGroup); + pass.Dispatch(kInstances); pass.EndPass(); commands = encoder.Finish(); @@ -97,9 +87,6 @@ void ComputeCopyStorageBufferTests::BasicTest(const char* shader) { // Test that a trivial compute-shader memcpy implementation works. TEST_P(ComputeCopyStorageBufferTests, SizedArrayOfBasic) { - // TODO(cwallez@chromium.org): Fails on D3D12, could be a spirv-cross issue? - DAWN_SKIP_TEST_IF(IsD3D12()); - BasicTest(R"( #version 450 #define kInstances 4 @@ -114,9 +101,6 @@ TEST_P(ComputeCopyStorageBufferTests, SizedArrayOfBasic) { // Test that a slightly-less-trivial compute-shader memcpy implementation works. TEST_P(ComputeCopyStorageBufferTests, SizedArrayOfStruct) { - // TODO(kainino@chromium.org): Fails on D3D12. Probably due to a limitation in SPIRV-Cross? - DAWN_SKIP_TEST_IF(IsD3D12()); - BasicTest(R"( #version 450 #define kInstances 4 @@ -134,9 +118,6 @@ TEST_P(ComputeCopyStorageBufferTests, SizedArrayOfStruct) { // Test that a trivial compute-shader memcpy implementation works. TEST_P(ComputeCopyStorageBufferTests, UnsizedArrayOfBasic) { - // TODO(cwallez@chromium.org): Fails on D3D12, could be a spirv-cross issue? - DAWN_SKIP_TEST_IF(IsD3D12()); - BasicTest(R"( #version 450 #define kInstances 4 @@ -192,7 +173,7 @@ TEST_P(ComputeCopyStorageBufferTests, DISABLED_UnsizedDescriptorArray) { } DAWN_INSTANTIATE_TEST(ComputeCopyStorageBufferTests, - D3D12Backend, - MetalBackend, - OpenGLBackend, - VulkanBackend); + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/ComputeIndirectTests.cpp b/third_party/dawn/src/tests/end2end/ComputeIndirectTests.cpp index 5dea851a7c8..28647940fb7 100644 --- a/third_party/dawn/src/tests/end2end/ComputeIndirectTests.cpp +++ b/third_party/dawn/src/tests/end2end/ComputeIndirectTests.cpp @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "dawn/dawncpp.h" #include "tests/DawnTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" #include #include @@ -45,57 +44,44 @@ class ComputeIndirectTests : public DawnTest { void ComputeIndirectTests::BasicTest(std::initializer_list bufferList, uint64_t indirectOffset) { - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Compute, dawn::BindingType::UniformBuffer}, - {1, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer}, - }); - // Set up shader and pipeline - dawn::ShaderModule module = - utils::CreateShaderModule(device, dawn::ShaderStage::Compute, shaderSource); - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); - - dawn::ComputePipelineDescriptor csDesc; - csDesc.layout = pl; - - dawn::PipelineStageDescriptor computeStage; - computeStage.module = module; - computeStage.entryPoint = "main"; - csDesc.computeStage = &computeStage; + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, shaderSource); - dawn::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc); + wgpu::ComputePipelineDescriptor csDesc; + csDesc.computeStage.module = module; + csDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc); // Set up dst storage buffer to contain dispatch x, y, z - dawn::Buffer dst = utils::CreateBufferFromData(device, - dawn::BufferUsageBit::Storage | - dawn::BufferUsageBit::TransferSrc | - dawn::BufferUsageBit::TransferDst, - {0, 0, 0}); + wgpu::Buffer dst = utils::CreateBufferFromData( + device, + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst, + {0, 0, 0}); std::vector indirectBufferData = bufferList; - dawn::Buffer indirectBuffer = - utils::CreateBufferFromData(device, dawn::BufferUsageBit::Indirect, bufferList); + wgpu::Buffer indirectBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Indirect, bufferList); - dawn::Buffer expectedBuffer = + wgpu::Buffer expectedBuffer = utils::CreateBufferFromData(device, &indirectBufferData[indirectOffset / sizeof(uint32_t)], - 3 * sizeof(uint32_t), dawn::BufferUsageBit::Uniform); + 3 * sizeof(uint32_t), wgpu::BufferUsage::Uniform); // Set up bind group and issue dispatch - dawn::BindGroup bindGroup = - utils::MakeBindGroup(device, bgl, + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), { {0, expectedBuffer, 0, 3 * sizeof(uint32_t)}, {1, dst, 0, 3 * sizeof(uint32_t)}, }); - dawn::CommandBuffer commands; + wgpu::CommandBuffer commands; { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); + pass.SetBindGroup(0, bindGroup); pass.DispatchIndirect(indirectBuffer, indirectOffset); pass.EndPass(); @@ -110,22 +96,16 @@ void ComputeIndirectTests::BasicTest(std::initializer_list bufferList, // Test basic indirect TEST_P(ComputeIndirectTests, Basic) { - // See https://bugs.chromium.org/p/dawn/issues/detail?id=159 - DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia()); - BasicTest({2, 3, 4}, 0); } // Test indirect with buffer offset TEST_P(ComputeIndirectTests, IndirectOffset) { - // See https://bugs.chromium.org/p/dawn/issues/detail?id=159 - DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia()); - BasicTest({0, 0, 0, 2, 3, 4}, 3 * sizeof(uint32_t)); } DAWN_INSTANTIATE_TEST(ComputeIndirectTests, - D3D12Backend, - MetalBackend, - OpenGLBackend, - VulkanBackend); + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/ComputeSharedMemoryTests.cpp b/third_party/dawn/src/tests/end2end/ComputeSharedMemoryTests.cpp index 6434c1fdc17..a11c01a0a59 100644 --- a/third_party/dawn/src/tests/end2end/ComputeSharedMemoryTests.cpp +++ b/third_party/dawn/src/tests/end2end/ComputeSharedMemoryTests.cpp @@ -14,7 +14,7 @@ #include "tests/DawnTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" #include @@ -26,47 +26,37 @@ class ComputeSharedMemoryTests : public DawnTest { }; void ComputeSharedMemoryTests::BasicTest(const char* shader) { - auto bgl = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer}, - }); - // Set up shader and pipeline - auto module = utils::CreateShaderModule(device, dawn::ShaderStage::Compute, shader); - auto pl = utils::MakeBasicPipelineLayout(device, &bgl); - - dawn::ComputePipelineDescriptor csDesc; - csDesc.layout = pl; + auto module = utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, shader); - dawn::PipelineStageDescriptor computeStage; - computeStage.module = module; - computeStage.entryPoint = "main"; - csDesc.computeStage = &computeStage; - - dawn::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc); + wgpu::ComputePipelineDescriptor csDesc; + csDesc.computeStage.module = module; + csDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc); // Set up dst storage buffer - dawn::BufferDescriptor dstDesc; + wgpu::BufferDescriptor dstDesc; dstDesc.size = sizeof(uint32_t); - dstDesc.usage = dawn::BufferUsageBit::Storage | dawn::BufferUsageBit::TransferSrc | - dawn::BufferUsageBit::TransferDst; - dawn::Buffer dst = device.CreateBuffer(&dstDesc); + dstDesc.usage = + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer dst = device.CreateBuffer(&dstDesc); const uint32_t zero = 0; - dst.SetSubData(0, sizeof(zero), &zero); + queue.WriteBuffer(dst, 0, &zero, sizeof(zero)); // Set up bind group and issue dispatch - dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, { - {0, dst, 0, sizeof(uint32_t)}, - }); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, dst, 0, sizeof(uint32_t)}, + }); - dawn::CommandBuffer commands; + wgpu::CommandBuffer commands; { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.Dispatch(1, 1, 1); + pass.SetBindGroup(0, bindGroup); + pass.Dispatch(1); pass.EndPass(); commands = encoder.Finish(); @@ -80,9 +70,6 @@ void ComputeSharedMemoryTests::BasicTest(const char* shader) { // Basic shared memory test TEST_P(ComputeSharedMemoryTests, Basic) { - // See https://bugs.chromium.org/p/dawn/issues/detail?id=159 - DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia()); - BasicTest(R"( #version 450 const uint kTileSize = 4; @@ -111,7 +98,7 @@ TEST_P(ComputeSharedMemoryTests, Basic) { } DAWN_INSTANTIATE_TEST(ComputeSharedMemoryTests, - D3D12Backend, - MetalBackend, - OpenGLBackend, - VulkanBackend); + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/ComputeStorageBufferBarrierTests.cpp b/third_party/dawn/src/tests/end2end/ComputeStorageBufferBarrierTests.cpp new file mode 100644 index 00000000000..31e66e2d5ce --- /dev/null +++ b/third_party/dawn/src/tests/end2end/ComputeStorageBufferBarrierTests.cpp @@ -0,0 +1,199 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/WGPUHelpers.h" + +class ComputeStorageBufferBarrierTests : public DawnTest { + protected: + static constexpr uint32_t kNumValues = 100; + static constexpr uint32_t kIterations = 100; +}; + +// Test that multiple dispatches to increment values in a storage buffer are synchronized. +TEST_P(ComputeStorageBufferBarrierTests, AddIncrement) { + std::vector data(kNumValues, 0); + std::vector expected(kNumValues, 0x1234 * kIterations); + + uint64_t bufferSize = static_cast(data.size() * sizeof(uint32_t)); + wgpu::Buffer buffer = utils::CreateBufferFromData( + device, data.data(), bufferSize, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc); + + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + #define kNumValues 100 + layout(std430, set = 0, binding = 0) buffer Buf { uint buf[kNumValues]; }; + void main() { + buf[gl_GlobalInvocationID.x] += 0x1234; + } + )"); + + wgpu::ComputePipelineDescriptor pipelineDesc = {}; + pipelineDesc.computeStage.module = module; + pipelineDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc); + + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buffer, 0, bufferSize}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + for (uint32_t i = 0; i < kIterations; ++i) { + pass.Dispatch(kNumValues); + } + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), buffer, 0, kNumValues); +} + +// Test that multiple dispatches to increment values by ping-ponging between two storage buffers +// are synchronized. +TEST_P(ComputeStorageBufferBarrierTests, AddPingPong) { + std::vector data(kNumValues, 0); + std::vector expectedA(kNumValues, 0x1234 * kIterations); + std::vector expectedB(kNumValues, 0x1234 * (kIterations - 1)); + + uint64_t bufferSize = static_cast(data.size() * sizeof(uint32_t)); + + wgpu::Buffer bufferA = utils::CreateBufferFromData( + device, data.data(), bufferSize, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc); + + wgpu::Buffer bufferB = utils::CreateBufferFromData( + device, data.data(), bufferSize, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc); + + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + #define kNumValues 100 + layout(std430, set = 0, binding = 0) buffer Src { uint src[kNumValues]; }; + layout(std430, set = 0, binding = 1) buffer Dst { uint dst[kNumValues]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + dst[index] = src[index] + 0x1234; + } + )"); + + wgpu::ComputePipelineDescriptor pipelineDesc = {}; + pipelineDesc.computeStage.module = module; + pipelineDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc); + + wgpu::BindGroup bindGroupA = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, bufferA, 0, bufferSize}, + {1, bufferB, 0, bufferSize}, + }); + + wgpu::BindGroup bindGroupB = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, bufferB, 0, bufferSize}, + {1, bufferA, 0, bufferSize}, + }); + + wgpu::BindGroup bindGroups[2] = {bindGroupA, bindGroupB}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(pipeline); + + for (uint32_t i = 0; i < kIterations / 2; ++i) { + pass.SetBindGroup(0, bindGroups[0]); + pass.Dispatch(kNumValues); + pass.SetBindGroup(0, bindGroups[1]); + pass.Dispatch(kNumValues); + } + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_BUFFER_U32_RANGE_EQ(expectedA.data(), bufferA, 0, kNumValues); + EXPECT_BUFFER_U32_RANGE_EQ(expectedB.data(), bufferB, 0, kNumValues); +} + +// Test that Storage to Uniform buffer transitions work and synchronize correctly +// by ping-ponging between Storage/Uniform usage in sequential compute passes. +TEST_P(ComputeStorageBufferBarrierTests, UniformToStorageAddPingPong) { + std::vector data(kNumValues, 0); + std::vector expectedA(kNumValues, 0x1234 * kIterations); + std::vector expectedB(kNumValues, 0x1234 * (kIterations - 1)); + + uint64_t bufferSize = static_cast(data.size() * sizeof(uint32_t)); + + wgpu::Buffer bufferA = utils::CreateBufferFromData( + device, data.data(), bufferSize, + wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopySrc); + + wgpu::Buffer bufferB = utils::CreateBufferFromData( + device, data.data(), bufferSize, + wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopySrc); + + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + #define kNumValues 100 + layout(std140, set = 0, binding = 0) uniform Src { uvec4 src[kNumValues / 4]; }; + layout(std430, set = 0, binding = 1) buffer Dst { uvec4 dst[kNumValues / 4]; }; + void main() { + uint index = gl_GlobalInvocationID.x; + dst[index] = src[index] + 0x1234; + } + )"); + + wgpu::ComputePipelineDescriptor pipelineDesc = {}; + pipelineDesc.computeStage.module = module; + pipelineDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc); + + wgpu::BindGroup bindGroupA = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, bufferA, 0, bufferSize}, + {1, bufferB, 0, bufferSize}, + }); + + wgpu::BindGroup bindGroupB = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, bufferB, 0, bufferSize}, + {1, bufferA, 0, bufferSize}, + }); + + wgpu::BindGroup bindGroups[2] = {bindGroupA, bindGroupB}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + for (uint32_t i = 0, b = 0; i < kIterations; ++i, b = 1 - b) { + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroups[b]); + pass.Dispatch(kNumValues / 4); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_BUFFER_U32_RANGE_EQ(expectedA.data(), bufferA, 0, kNumValues); + EXPECT_BUFFER_U32_RANGE_EQ(expectedB.data(), bufferB, 0, kNumValues); +} + +DAWN_INSTANTIATE_TEST(ComputeStorageBufferBarrierTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/CopyTests.cpp b/third_party/dawn/src/tests/end2end/CopyTests.cpp index 1e56160eedc..28728bdfaab 100644 --- a/third_party/dawn/src/tests/end2end/CopyTests.cpp +++ b/third_party/dawn/src/tests/end2end/CopyTests.cpp @@ -17,380 +17,394 @@ #include #include "common/Constants.h" #include "common/Math.h" -#include "utils/DawnHelpers.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" class CopyTests : public DawnTest { - protected: - static constexpr unsigned int kBytesPerTexel = 4; - - struct TextureSpec { - uint32_t width; - uint32_t height; - uint32_t x; - uint32_t y; - uint32_t copyWidth; - uint32_t copyHeight; - uint32_t level; - uint32_t arraySize = 1u; - }; - - struct BufferSpec { - uint64_t size; - uint64_t offset; - uint32_t rowPitch; - }; - - static void FillTextureData(uint32_t width, - uint32_t height, - uint32_t texelsPerRow, - uint32_t layer, - RGBA8* data) { - for (uint32_t y = 0; y < height; ++y) { - for (uint32_t x = 0; x < width; ++x) { - uint32_t i = x + y * texelsPerRow; - data[i] = RGBA8(static_cast((x + layer * x) % 256), - static_cast((y + layer * y) % 256), - static_cast(x / 256), static_cast(y / 256)); + protected: + static constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::RGBA8Unorm; + + struct TextureSpec { + wgpu::Origin3D copyOrigin; + wgpu::Extent3D textureSize; + uint32_t level; + }; + + struct BufferSpec { + uint64_t size; + uint64_t offset; + uint32_t bytesPerRow; + uint32_t rowsPerImage; + }; + + static std::vector GetExpectedTextureData(const utils::TextureDataCopyLayout& layout) { + std::vector textureData(layout.texelBlockCount); + for (uint32_t layer = 0; layer < layout.mipSize.depth; ++layer) { + const uint32_t texelIndexOffsetPerSlice = layout.texelBlocksPerImage * layer; + for (uint32_t y = 0; y < layout.mipSize.height; ++y) { + for (uint32_t x = 0; x < layout.mipSize.width; ++x) { + uint32_t i = x + y * layout.texelBlocksPerRow; + textureData[texelIndexOffsetPerSlice + i] = + RGBA8(static_cast((x + layer * x) % 256), + static_cast((y + layer * y) % 256), + static_cast(x / 256), static_cast(y / 256)); } } } - BufferSpec MinimumBufferSpec(uint32_t width, uint32_t height) { - uint32_t rowPitch = Align(width * kBytesPerTexel, kTextureRowPitchAlignment); - return { rowPitch * (height - 1) + width * kBytesPerTexel, 0, rowPitch }; - } + return textureData; + } - static void PackTextureData(const RGBA8* srcData, uint32_t width, uint32_t height, uint32_t srcTexelsPerRow, RGBA8* dstData, uint32_t dstTexelsPerRow) { - for (unsigned int y = 0; y < height; ++y) { - for (unsigned int x = 0; x < width; ++x) { - unsigned int src = x + y * srcTexelsPerRow; - unsigned int dst = x + y * dstTexelsPerRow; - dstData[dst] = srcData[src]; - } + static BufferSpec MinimumBufferSpec(uint32_t width, + uint32_t rowsPerImage, + uint32_t arrayLayer = 1, + bool testZeroRowsPerImage = true) { + const uint32_t bytesPerRow = utils::GetMinimumBytesPerRow(kTextureFormat, width); + const uint32_t totalBufferSize = utils::GetBytesInBufferTextureCopy( + kTextureFormat, width, bytesPerRow, rowsPerImage, arrayLayer); + uint32_t appliedRowsPerImage = testZeroRowsPerImage ? 0 : rowsPerImage; + return {totalBufferSize, 0, bytesPerRow, appliedRowsPerImage}; + } + + static void PackTextureData(const RGBA8* srcData, + uint32_t width, + uint32_t height, + uint32_t srcTexelsPerRow, + RGBA8* dstData, + uint32_t dstTexelsPerRow) { + for (unsigned int y = 0; y < height; ++y) { + for (unsigned int x = 0; x < width; ++x) { + unsigned int src = x + y * srcTexelsPerRow; + unsigned int dst = x + y * dstTexelsPerRow; + dstData[dst] = srcData[src]; } } + } }; class CopyTests_T2B : public CopyTests { - protected: - - void DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) { - // Create a texture that is `width` x `height` with (`level` + 1) mip levels. - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = textureSpec.width; - descriptor.size.height = textureSpec.height; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = textureSpec.arraySize; - descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.mipLevelCount = textureSpec.level + 1; - descriptor.usage = dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::TransferSrc; - dawn::Texture texture = device.CreateTexture(&descriptor); - - uint32_t width = textureSpec.width >> textureSpec.level; - uint32_t height = textureSpec.height >> textureSpec.level; - uint32_t rowPitch = Align(kBytesPerTexel * width, kTextureRowPitchAlignment); - uint32_t texelsPerRow = rowPitch / kBytesPerTexel; - uint32_t texelCountPerLayer = texelsPerRow * (height - 1) + width; - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - - std::vector> textureArrayData(textureSpec.arraySize); - for (uint32_t slice = 0; slice < textureSpec.arraySize; ++slice) { - textureArrayData[slice].resize(texelCountPerLayer); - FillTextureData(width, height, rowPitch / kBytesPerTexel, slice, - textureArrayData[slice].data()); - - // Create an upload buffer and use it to populate the current slice of the texture in `level` mip level - dawn::Buffer uploadBuffer = utils::CreateBufferFromData(device, textureArrayData[slice].data(), - static_cast(sizeof(RGBA8) * textureArrayData[slice].size()), dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(uploadBuffer, 0, rowPitch, 0); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(texture, textureSpec.level, slice, {0, 0, 0}); - dawn::Extent3D copySize = {width, height, 1}; - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - } + protected: + void DoTest(const TextureSpec& textureSpec, + const BufferSpec& bufferSpec, + const wgpu::Extent3D& copySize) { + // Create a texture that is `width` x `height` with (`level` + 1) mip levels. + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size = textureSpec.textureSize; + descriptor.sampleCount = 1; + descriptor.format = kTextureFormat; + descriptor.mipLevelCount = textureSpec.level + 1; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); - // Create a buffer of size `size * textureSpec.arrayLayer` and populate it with empty data (0,0,0,0) - // Note: Prepopulating the buffer with empty data ensures that there is not random data in the expectation - // and helps ensure that the padding due to the row pitch is not modified by the copy - dawn::BufferDescriptor bufDescriptor; - bufDescriptor.size = bufferSpec.size * textureSpec.arraySize; - bufDescriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&bufDescriptor); - std::vector emptyData(bufferSpec.size / kBytesPerTexel * textureSpec.arraySize); - buffer.SetSubData(0, static_cast(emptyData.size() * sizeof(RGBA8)), - emptyData.data()); - - uint64_t bufferOffset = bufferSpec.offset; - for (uint32_t slice = 0; slice < textureSpec.arraySize; ++slice) { - // Copy the region [(`x`, `y`), (`x + copyWidth, `y + copyWidth`)] from the `level` mip into the buffer at `offset + bufferSpec.size * slice` and `rowPitch` - dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView( - texture, textureSpec.level, slice, {textureSpec.x, textureSpec.y, 0}); - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(buffer, bufferOffset, bufferSpec.rowPitch, 0); - dawn::Extent3D copySize = {textureSpec.copyWidth, textureSpec.copyHeight, 1}; - encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); - bufferOffset += bufferSpec.size; - } + const utils::TextureDataCopyLayout copyLayout = + utils::GetTextureDataCopyLayoutForTexture2DAtLevel( + kTextureFormat, textureSpec.textureSize, textureSpec.level, + bufferSpec.rowsPerImage); - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - bufferOffset = bufferSpec.offset; - std::vector expected(bufferSpec.rowPitch / kBytesPerTexel * (textureSpec.copyHeight - 1) + textureSpec.copyWidth); - for (uint32_t slice = 0; slice < textureSpec.arraySize; ++slice) { - // Pack the data used to create the upload buffer in the specified copy region to have the same format as the expected buffer data. - std::fill(expected.begin(), expected.end(), RGBA8()); - PackTextureData( - &textureArrayData[slice][textureSpec.x + textureSpec.y * (rowPitch / kBytesPerTexel)], - textureSpec.copyWidth, - textureSpec.copyHeight, - rowPitch / kBytesPerTexel, - expected.data(), - bufferSpec.rowPitch / kBytesPerTexel); - - EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(expected.data()), buffer, bufferOffset, static_cast(expected.size())) << - "Texture to Buffer copy failed copying region [(" << textureSpec.x << ", " << textureSpec.y << "), (" << textureSpec.x + textureSpec.copyWidth << ", " << textureSpec.y + textureSpec.copyHeight << - ")) from " << textureSpec.width << " x " << textureSpec.height << " texture at mip level " << textureSpec.level << " layer " << slice << - " to " << bufDescriptor.size << "-byte buffer with offset " << bufferOffset << " and row pitch " << bufferSpec.rowPitch << std::endl; - - bufferOffset += bufferSpec.size; - } + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + // Initialize the source texture + std::vector textureArrayData = GetExpectedTextureData(copyLayout); + { + wgpu::Buffer uploadBuffer = utils::CreateBufferFromData( + device, textureArrayData.data(), copyLayout.byteLength, wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView( + uploadBuffer, 0, copyLayout.bytesPerRow, bufferSpec.rowsPerImage); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, textureSpec.level, {0, 0, 0}); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Layout.mipSize); } + // Create a buffer of `size` and populate it with empty data (0,0,0,0) Note: + // Prepopulating the buffer with empty data ensures that there is not random data in the + // expectation and helps ensure that the padding due to the bytes per row is not modified + // by the copy. + // TODO(jiawei.shao@intel.com): remove the initialization of the buffer after we support + // buffer lazy-initialization. + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); + const std::vector emptyData(bufferSpec.size / bytesPerTexel, RGBA8()); + wgpu::Buffer buffer = + utils::CreateBufferFromData(device, emptyData.data(), bufferSpec.size, + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + { + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, textureSpec.level, textureSpec.copyOrigin); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView( + buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + uint64_t bufferOffset = bufferSpec.offset; + const uint32_t texelCountInCopyRegion = + bufferSpec.bytesPerRow / bytesPerTexel * (copySize.height - 1) + copySize.width; + const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depth; + std::vector expected(texelCountInCopyRegion); + for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) { + // Pack the data used to create the upload buffer in the specified copy region to have + // the same format as the expected buffer data. + std::fill(expected.begin(), expected.end(), RGBA8()); + const uint32_t texelIndexOffset = copyLayout.texelBlocksPerImage * slice; + const uint32_t expectedTexelArrayDataStartIndex = + texelIndexOffset + (textureSpec.copyOrigin.x + + textureSpec.copyOrigin.y * copyLayout.texelBlocksPerRow); + + PackTextureData(&textureArrayData[expectedTexelArrayDataStartIndex], copySize.width, + copySize.height, copyLayout.texelBlocksPerRow, expected.data(), + bufferSpec.bytesPerRow / bytesPerTexel); + + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(expected.data()), buffer, + bufferOffset, static_cast(expected.size())) + << "Texture to Buffer copy failed copying region [(" << textureSpec.copyOrigin.x + << ", " << textureSpec.copyOrigin.y << "), (" + << textureSpec.copyOrigin.x + copySize.width << ", " + << textureSpec.copyOrigin.y + copySize.height << ")) from " + << textureSpec.textureSize.width << " x " << textureSpec.textureSize.height + << " texture at mip level " << textureSpec.level << " layer " << slice << " to " + << bufferSpec.size << "-byte buffer with offset " << bufferOffset + << " and bytes per row " << bufferSpec.bytesPerRow << std::endl; + + bufferOffset += copyLayout.bytesPerImage; + } + } }; class CopyTests_B2T : public CopyTests { -protected: + protected: static void FillBufferData(RGBA8* data, size_t count) { for (size_t i = 0; i < count; ++i) { - data[i] = RGBA8( - static_cast(i % 256), - static_cast((i / 256) % 256), - static_cast((i / 256 / 256) % 256), - 255); + data[i] = RGBA8(static_cast(i % 256), static_cast((i / 256) % 256), + static_cast((i / 256 / 256) % 256), 255); } } - void DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) { + void DoTest(const TextureSpec& textureSpec, + const BufferSpec& bufferSpec, + const wgpu::Extent3D& copySize) { // Create a buffer of size `size` and populate it with data - dawn::BufferDescriptor bufDescriptor; - bufDescriptor.size = bufferSpec.size; - bufDescriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer buffer = device.CreateBuffer(&bufDescriptor); - - std::vector bufferData(bufferSpec.size / kBytesPerTexel); + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); + std::vector bufferData(bufferSpec.size / bytesPerTexel); FillBufferData(bufferData.data(), bufferData.size()); - buffer.SetSubData(0, static_cast(bufferData.size() * sizeof(RGBA8)), - bufferData.data()); + wgpu::Buffer buffer = + utils::CreateBufferFromData(device, bufferData.data(), bufferSpec.size, + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); // Create a texture that is `width` x `height` with (`level` + 1) mip levels. - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = textureSpec.width; - descriptor.size.height = textureSpec.height; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size = textureSpec.textureSize; descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor.format = kTextureFormat; descriptor.mipLevelCount = textureSpec.level + 1; - descriptor.usage = dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::TransferSrc; - dawn::Texture texture = device.CreateTexture(&descriptor); + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - // Create an upload buffer filled with empty data and use it to populate the `level` mip of the texture - // Note: Prepopulating the texture with empty data ensures that there is not random data in the expectation - // and helps ensure that the padding due to the row pitch is not modified by the copy - { - uint32_t width = textureSpec.width >> textureSpec.level; - uint32_t height = textureSpec.height >> textureSpec.level; - uint32_t rowPitch = Align(kBytesPerTexel * width, kTextureRowPitchAlignment); - uint32_t texelsPerRow = rowPitch / kBytesPerTexel; - uint32_t texelCount = texelsPerRow * (height - 1) + width; - - std::vector emptyData(texelCount); - dawn::Buffer uploadBuffer = utils::CreateBufferFromData(device, emptyData.data(), static_cast(sizeof(RGBA8) * emptyData.size()), dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(uploadBuffer, 0, rowPitch, 0); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(texture, textureSpec.level, 0, {0, 0, 0}); - dawn::Extent3D copySize = {width, height, 1}; - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - } + const utils::TextureDataCopyLayout copyLayout = + utils::GetTextureDataCopyLayoutForTexture2DAtLevel( + kTextureFormat, textureSpec.textureSize, textureSpec.level, + bufferSpec.rowsPerImage); - // Copy to the region [(`x`, `y`), (`x + copyWidth, `y + copyWidth`)] at the `level` mip from the buffer at the specified `offset` and `rowPitch` - { - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(buffer, bufferSpec.offset, bufferSpec.rowPitch, 0); - dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView( - texture, textureSpec.level, 0, {textureSpec.x, textureSpec.y, 0}); - dawn::Extent3D copySize = {textureSpec.copyWidth, textureSpec.copyHeight, 1}; - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - } + const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depth; - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView( + buffer, bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, textureSpec.level, textureSpec.copyOrigin); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - // Pack the data used to create the buffer in the specified copy region to have the same format as the expected texture data. - uint32_t rowPitch = Align(kBytesPerTexel * textureSpec.copyWidth, kTextureRowPitchAlignment); - std::vector expected(rowPitch / kBytesPerTexel * (textureSpec.copyHeight - 1) + textureSpec.copyWidth); - PackTextureData(&bufferData[bufferSpec.offset / kBytesPerTexel], textureSpec.copyWidth, textureSpec.copyHeight, bufferSpec.rowPitch / kBytesPerTexel, expected.data(), textureSpec.copyWidth); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, textureSpec.x, textureSpec.y, textureSpec.copyWidth, textureSpec.copyHeight, textureSpec.level, 0) << - "Buffer to Texture copy failed copying " - << bufferSpec.size << "-byte buffer with offset " << bufferSpec.offset << " and row pitch " << bufferSpec.rowPitch << " to [(" - << textureSpec.x << ", " << textureSpec.y << "), (" << textureSpec.x + textureSpec.copyWidth << ", " << textureSpec.y + textureSpec.copyHeight << - ")) region of " << textureSpec.width << " x " << textureSpec.height << " texture at mip level " << textureSpec.level << std::endl; + uint64_t bufferOffset = bufferSpec.offset; + const uint32_t texelCountLastLayer = + copyLayout.texelBlocksPerRow * (copyLayout.mipSize.height - 1) + + copyLayout.mipSize.width; + for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) { + // Pack the data used to create the buffer in the specified copy region to have the same + // format as the expected texture data. + std::vector expected(texelCountLastLayer); + PackTextureData(&bufferData[bufferOffset / bytesPerTexel], copySize.width, + copySize.height, bufferSpec.bytesPerRow / bytesPerTexel, + expected.data(), copySize.width); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, textureSpec.copyOrigin.x, + textureSpec.copyOrigin.y, copySize.width, copySize.height, + textureSpec.level, slice) + << "Buffer to Texture copy failed copying " << bufferSpec.size + << "-byte buffer with offset " << bufferSpec.offset << " and bytes per row " + << bufferSpec.bytesPerRow << " to [(" << textureSpec.copyOrigin.x << ", " + << textureSpec.copyOrigin.y << "), (" << textureSpec.copyOrigin.x + copySize.width + << ", " << textureSpec.copyOrigin.y + copySize.height << ")) region of " + << textureSpec.textureSize.width << " x " << textureSpec.textureSize.height + << " texture at mip level " << textureSpec.level << " layer " << slice << std::endl; + bufferOffset += copyLayout.bytesPerImage; + } } - - }; class CopyTests_T2T : public CopyTests { - struct TextureSpec { - uint32_t width; - uint32_t height; - uint32_t x; - uint32_t y; - uint32_t level; - uint32_t arraySize = 1u; - }; - - struct CopySize { - uint32_t width; - uint32_t height; - }; - protected: - void DoTest(const TextureSpec& srcSpec, const TextureSpec& dstSpec, const CopySize& copy) { - dawn::TextureDescriptor srcDescriptor; - srcDescriptor.dimension = dawn::TextureDimension::e2D; - srcDescriptor.size.width = srcSpec.width; - srcDescriptor.size.height = srcSpec.height; - srcDescriptor.size.depth = 1; - srcDescriptor.arrayLayerCount = srcSpec.arraySize; + void DoTest(const TextureSpec& srcSpec, + const TextureSpec& dstSpec, + const wgpu::Extent3D& copySize, + bool copyWithinSameTexture = false) { + wgpu::TextureDescriptor srcDescriptor; + srcDescriptor.dimension = wgpu::TextureDimension::e2D; + srcDescriptor.size = srcSpec.textureSize; srcDescriptor.sampleCount = 1; - srcDescriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + srcDescriptor.format = kTextureFormat; srcDescriptor.mipLevelCount = srcSpec.level + 1; - srcDescriptor.usage = - dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst; - dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); - - dawn::TextureDescriptor dstDescriptor; - dstDescriptor.dimension = dawn::TextureDimension::e2D; - dstDescriptor.size.width = dstSpec.width; - dstDescriptor.size.height = dstSpec.height; - dstDescriptor.size.depth = 1; - dstDescriptor.arrayLayerCount = dstSpec.arraySize; - dstDescriptor.sampleCount = 1; - dstDescriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - dstDescriptor.mipLevelCount = dstSpec.level + 1; - dstDescriptor.usage = - dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst; - dawn::Texture dstTexture = device.CreateTexture(&dstDescriptor); - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + srcDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + wgpu::Texture dstTexture; + if (copyWithinSameTexture) { + dstTexture = srcTexture; + } else { + wgpu::TextureDescriptor dstDescriptor; + dstDescriptor.dimension = wgpu::TextureDimension::e2D; + dstDescriptor.size = dstSpec.textureSize; + dstDescriptor.sampleCount = 1; + dstDescriptor.format = kTextureFormat; + dstDescriptor.mipLevelCount = dstSpec.level + 1; + dstDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + dstTexture = device.CreateTexture(&dstDescriptor); + } + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); // Create an upload buffer and use it to populate the current slice of the texture in // `level` mip level - uint32_t width = srcSpec.width >> srcSpec.level; - uint32_t height = srcSpec.height >> srcSpec.level; - uint32_t rowPitch = Align(kBytesPerTexel * width, kTextureRowPitchAlignment); - uint32_t texelsPerRow = rowPitch / kBytesPerTexel; - uint32_t texelCountPerLayer = texelsPerRow * (height - 1) + width; - - std::vector> textureArrayData(srcSpec.arraySize); - for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) { - textureArrayData[slice].resize(texelCountPerLayer); - FillTextureData(width, height, rowPitch / kBytesPerTexel, slice, - textureArrayData[slice].data()); - - dawn::Buffer uploadBuffer = utils::CreateBufferFromData( - device, textureArrayData[slice].data(), - static_cast(sizeof(RGBA8) * textureArrayData[slice].size()), - dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(uploadBuffer, 0, rowPitch, 0); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(srcTexture, srcSpec.level, slice, {0, 0, 0}); - dawn::Extent3D bufferCopySize = {width, height, 1}; - - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &bufferCopySize); - } - - // Create an upload buffer filled with empty data and use it to populate the `level` mip of - // the texture. Note: Prepopulating the texture with empty data ensures that there is not - // random data in the expectation and helps ensure that the padding due to the row pitch is - // not modified by the copy - { - uint32_t dstWidth = dstSpec.width >> dstSpec.level; - uint32_t dstHeight = dstSpec.height >> dstSpec.level; - uint32_t dstRowPitch = Align(kBytesPerTexel * dstWidth, kTextureRowPitchAlignment); - uint32_t dstTexelsPerRow = dstRowPitch / kBytesPerTexel; - uint32_t dstTexelCount = dstTexelsPerRow * (dstHeight - 1) + dstWidth; - - std::vector emptyData(dstTexelCount); - dawn::Buffer uploadBuffer = utils::CreateBufferFromData( - device, emptyData.data(), static_cast(sizeof(RGBA8) * emptyData.size()), - dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(uploadBuffer, 0, dstRowPitch, 0); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(dstTexture, dstSpec.level, 0, {0, 0, 0}); - dawn::Extent3D dstCopySize = {dstWidth, dstHeight, 1}; - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &dstCopySize); - } + const utils::TextureDataCopyLayout copyLayout = + utils::GetTextureDataCopyLayoutForTexture2DAtLevel( + kTextureFormat, + {srcSpec.textureSize.width, srcSpec.textureSize.height, copySize.depth}, + srcSpec.level, 0); + + const std::vector textureArrayCopyData = GetExpectedTextureData(copyLayout); + + wgpu::Buffer uploadBuffer = utils::CreateBufferFromData( + device, textureArrayCopyData.data(), copyLayout.byteLength, wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(uploadBuffer, 0, copyLayout.bytesPerRow, 0); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(srcTexture, srcSpec.level, {0, 0, srcSpec.copyOrigin.z}); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Layout.mipSize); // Perform the texture to texture copy - for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) { - dawn::TextureCopyView srcTextureCopyView = utils::CreateTextureCopyView( - srcTexture, srcSpec.level, slice, {srcSpec.x, srcSpec.y, 0}); - dawn::TextureCopyView dstTextureCopyView = utils::CreateTextureCopyView( - dstTexture, dstSpec.level, slice, {dstSpec.x, dstSpec.y, 0}); - dawn::Extent3D copySize = {copy.width, copy.height, 1}; - encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); - } + wgpu::TextureCopyView srcTextureCopyView = + utils::CreateTextureCopyView(srcTexture, srcSpec.level, srcSpec.copyOrigin); + wgpu::TextureCopyView dstTextureCopyView = + utils::CreateTextureCopyView(dstTexture, dstSpec.level, dstSpec.copyOrigin); + encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - std::vector expected(rowPitch / kBytesPerTexel * (copy.height - 1) + copy.width); - for (uint32_t slice = 0; slice < srcSpec.arraySize; ++slice) { + const uint32_t texelCountInCopyRegion = + copyLayout.texelBlocksPerRow * (copySize.height - 1) + copySize.width; + std::vector expected(texelCountInCopyRegion); + for (uint32_t slice = 0; slice < copySize.depth; ++slice) { std::fill(expected.begin(), expected.end(), RGBA8()); - PackTextureData( - &textureArrayData[slice][srcSpec.x + srcSpec.y * (rowPitch / kBytesPerTexel)], - copy.width, copy.height, texelsPerRow, expected.data(), copy.width); - - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, dstSpec.x, dstSpec.y, copy.width, - copy.height, dstSpec.level, slice) - << "Texture to Texture copy failed copying region [(" << srcSpec.x << ", " - << srcSpec.y << "), (" << srcSpec.x + copy.width << ", " << srcSpec.y + copy.height - << ")) from " << srcSpec.width << " x " << srcSpec.height - << " texture at mip level " << srcSpec.level << " layer " << slice << " to [(" - << dstSpec.x << ", " << dstSpec.y << "), (" << dstSpec.x + copy.width << ", " - << dstSpec.y + copy.height << ")) region of " << dstSpec.width << " x " - << dstSpec.height << " texture at mip level " << dstSpec.level << std::endl; + const uint32_t texelIndexOffset = copyLayout.texelBlocksPerImage * slice; + const uint32_t expectedTexelArrayDataStartIndex = + texelIndexOffset + + (srcSpec.copyOrigin.x + srcSpec.copyOrigin.y * copyLayout.texelBlocksPerRow); + PackTextureData(&textureArrayCopyData[expectedTexelArrayDataStartIndex], copySize.width, + copySize.height, copyLayout.texelBlocksPerRow, expected.data(), + copySize.width); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, dstSpec.copyOrigin.x, + dstSpec.copyOrigin.y, copySize.width, copySize.height, + dstSpec.level, dstSpec.copyOrigin.z + slice) + << "Texture to Texture copy failed copying region [(" << srcSpec.copyOrigin.x + << ", " << srcSpec.copyOrigin.y << "), (" << srcSpec.copyOrigin.x + copySize.width + << ", " << srcSpec.copyOrigin.y + copySize.height << ")) from " + << srcSpec.textureSize.width << " x " << srcSpec.textureSize.height + << " texture at mip level " << srcSpec.level << " layer " + << srcSpec.copyOrigin.z + slice << " to [(" << dstSpec.copyOrigin.x << ", " + << dstSpec.copyOrigin.y << "), (" << dstSpec.copyOrigin.x + copySize.width << ", " + << dstSpec.copyOrigin.y + copySize.height << ")) region of " + << dstSpec.textureSize.width << " x " << dstSpec.textureSize.height + << " texture at mip level " << dstSpec.level << " layer " + << dstSpec.copyOrigin.z + slice << std::endl; } } }; +class CopyTests_B2B : public DawnTest { + protected: + // This is the same signature as CopyBufferToBuffer except that the buffers are replaced by + // only their size. + void DoTest(uint64_t sourceSize, + uint64_t sourceOffset, + uint64_t destinationSize, + uint64_t destinationOffset, + uint64_t copySize) { + ASSERT(sourceSize % 4 == 0); + ASSERT(destinationSize % 4 == 0); + + // Create our two test buffers, destination filled with zeros, source filled with non-zeroes + std::vector zeroes(static_cast(destinationSize / sizeof(uint32_t))); + wgpu::Buffer destination = + utils::CreateBufferFromData(device, zeroes.data(), zeroes.size() * sizeof(uint32_t), + wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc); + + std::vector sourceData(static_cast(sourceSize / sizeof(uint32_t))); + for (size_t i = 0; i < sourceData.size(); i++) { + sourceData[i] = i + 1; + } + wgpu::Buffer source = utils::CreateBufferFromData(device, sourceData.data(), + sourceData.size() * sizeof(uint32_t), + wgpu::BufferUsage::CopySrc); + + // Submit the copy + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(source, sourceOffset, destination, destinationOffset, copySize); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Check destination is exactly the expected content. + EXPECT_BUFFER_U32_RANGE_EQ(zeroes.data(), destination, 0, + destinationOffset / sizeof(uint32_t)); + EXPECT_BUFFER_U32_RANGE_EQ(sourceData.data() + sourceOffset / sizeof(uint32_t), destination, + destinationOffset, copySize / sizeof(uint32_t)); + uint64_t copyEnd = destinationOffset + copySize; + EXPECT_BUFFER_U32_RANGE_EQ(zeroes.data(), destination, copyEnd, + (destinationSize - copyEnd) / sizeof(uint32_t)); + } +}; + // Test that copying an entire texture with 256-byte aligned dimensions works TEST_P(CopyTests_T2B, FullTextureAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, MinimumBufferSpec(kWidth, kHeight)); + + TextureSpec textureSpec; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1}); } // Test that copying an entire texture without 256-byte aligned dimensions works TEST_P(CopyTests_T2B, FullTextureUnaligned) { constexpr uint32_t kWidth = 259; constexpr uint32_t kHeight = 127; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, MinimumBufferSpec(kWidth, kHeight)); + + TextureSpec textureSpec; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1}); } // Test that reading pixels from a 256-byte aligned texture works @@ -398,12 +412,48 @@ TEST_P(CopyTests_T2B, PixelReadAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; BufferSpec pixelBuffer = MinimumBufferSpec(1, 1); - DoTest({ kWidth, kHeight, 0, 0, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth - 1, 0, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, 0, kHeight - 1, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth - 1, kHeight - 1, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth / 3, kHeight / 7, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth / 7, kHeight / 3, 1, 1, 0 }, pixelBuffer); + + constexpr wgpu::Extent3D kCopySize = {1, 1, 1}; + constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1}; + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = kTextureSize; + defaultTextureSpec.level = 0; + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth - 1, 0, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {0, kHeight - 1, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth - 1, kHeight - 1, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth / 3, kHeight / 7, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth / 7, kHeight / 3, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } } // Test that copying pixels from a texture that is not 256-byte aligned works @@ -411,12 +461,48 @@ TEST_P(CopyTests_T2B, PixelReadUnaligned) { constexpr uint32_t kWidth = 259; constexpr uint32_t kHeight = 127; BufferSpec pixelBuffer = MinimumBufferSpec(1, 1); - DoTest({ kWidth, kHeight, 0, 0, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth - 1, 0, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, 0, kHeight - 1, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth - 1, kHeight - 1, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth / 3, kHeight / 7, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth / 7, kHeight / 3, 1, 1, 0 }, pixelBuffer); + + constexpr wgpu::Extent3D kCopySize = {1, 1, 1}; + constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1}; + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = kTextureSize; + defaultTextureSpec.level = 0; + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth - 1, 0, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {0, kHeight - 1, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth - 1, kHeight - 1, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth / 3, kHeight / 7, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth / 7, kHeight / 3, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } } // Test that copying regions with 256-byte aligned sizes works @@ -424,8 +510,12 @@ TEST_P(CopyTests_T2B, TextureRegionAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; for (unsigned int w : {64, 128, 256}) { - for (unsigned int h : { 16, 32, 48 }) { - DoTest({ kWidth, kHeight, 0, 0, w, h, 0 }, MinimumBufferSpec(w, h)); + for (unsigned int h : {16, 32, 48}) { + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + textureSpec.textureSize = {kWidth, kHeight, 1}; + DoTest(textureSpec, MinimumBufferSpec(w, h), {w, h, 1}); } } } @@ -434,9 +524,16 @@ TEST_P(CopyTests_T2B, TextureRegionAligned) { TEST_P(CopyTests_T2B, TextureRegionUnaligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.level = 0; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + for (unsigned int w : {13, 63, 65}) { - for (unsigned int h : { 17, 19, 63 }) { - DoTest({ kWidth, kHeight, 0, 0, w, h, 0 }, MinimumBufferSpec(w, h)); + for (unsigned int h : {17, 19, 63}) { + TextureSpec textureSpec = defaultTextureSpec; + DoTest(textureSpec, MinimumBufferSpec(w, h), {w, h, 1}); } } } @@ -445,8 +542,16 @@ TEST_P(CopyTests_T2B, TextureRegionUnaligned) { TEST_P(CopyTests_T2B, TextureMipAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + for (unsigned int i = 1; i < 4; ++i) { - DoTest({ kWidth, kHeight, 0, 0, kWidth >> i, kHeight >> i, i }, MinimumBufferSpec(kWidth >> i, kHeight >> i)); + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.level = i; + DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i), + {kWidth >> i, kHeight >> i, 1}); } } @@ -454,8 +559,16 @@ TEST_P(CopyTests_T2B, TextureMipAligned) { TEST_P(CopyTests_T2B, TextureMipUnaligned) { constexpr uint32_t kWidth = 259; constexpr uint32_t kHeight = 127; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + for (unsigned int i = 1; i < 4; ++i) { - DoTest({ kWidth, kHeight, 0, 0, kWidth >> i, kHeight >> i, i }, MinimumBufferSpec(kWidth >> i, kHeight >> i)); + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.level = i; + DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i), + {kWidth >> i, kHeight >> i, 1}); } } @@ -463,12 +576,18 @@ TEST_P(CopyTests_T2B, TextureMipUnaligned) { TEST_P(CopyTests_T2B, OffsetBufferAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + for (unsigned int i = 0; i < 3; ++i) { BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); uint64_t offset = 512 * i; bufferSpec.size += offset; bufferSpec.offset += offset; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } @@ -476,85 +595,225 @@ TEST_P(CopyTests_T2B, OffsetBufferAligned) { TEST_P(CopyTests_T2B, OffsetBufferUnaligned) { constexpr uint32_t kWidth = 128; constexpr uint32_t kHeight = 128; - for (uint32_t i = kBytesPerTexel; i < 512; i += kBytesPerTexel * 9) { + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); + for (uint32_t i = bytesPerTexel; i < 512; i += bytesPerTexel * 9) { BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); bufferSpec.size += i; bufferSpec.offset += i; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } -// Test that copying without a 512-byte aligned buffer offset that is greater than the row pitch works +// Test that copying without a 512-byte aligned buffer offset that is greater than the bytes per row +// works TEST_P(CopyTests_T2B, OffsetBufferUnalignedSmallRowPitch) { constexpr uint32_t kWidth = 32; constexpr uint32_t kHeight = 128; - for (uint32_t i = 256 + kBytesPerTexel; i < 512; i += kBytesPerTexel * 9) { + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); + for (uint32_t i = 256 + bytesPerTexel; i < 512; i += bytesPerTexel * 9) { BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); bufferSpec.size += i; bufferSpec.offset += i; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } -// Test that copying with a greater row pitch than needed on a 256-byte aligned texture works +// Test that copying with a greater bytes per row than needed on a 256-byte aligned texture works TEST_P(CopyTests_T2B, RowPitchAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); for (unsigned int i = 1; i < 4; ++i) { - bufferSpec.rowPitch += 256; + bufferSpec.bytesPerRow += 256; bufferSpec.size += 256 * kHeight; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } -// Test that copying with a greater row pitch than needed on a texture that is not 256-byte aligned works +// Test that copying with a greater bytes per row than needed on a texture that is not 256-byte +// aligned works TEST_P(CopyTests_T2B, RowPitchUnaligned) { constexpr uint32_t kWidth = 259; constexpr uint32_t kHeight = 127; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); for (unsigned int i = 1; i < 4; ++i) { - bufferSpec.rowPitch += 256; + bufferSpec.bytesPerRow += 256; bufferSpec.size += 256 * kHeight; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } -// Test that copying regions of each texture 2D array layer works +// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works. TEST_P(CopyTests_T2B, Texture2DArrayRegion) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; constexpr uint32_t kLayers = 6u; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0, kLayers }, MinimumBufferSpec(kWidth, kHeight)); + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kLayers), {kWidth, kHeight, kLayers}); +} + +// Test that copying a range of texture 2D array layers in one texture-to-buffer-copy works. +TEST_P(CopyTests_T2B, Texture2DArraySubRegion) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 6u; + constexpr uint32_t kBaseLayer = 2u; + constexpr uint32_t kCopyLayers = 3u; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, kBaseLayer}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kCopyLayers), + {kWidth, kHeight, kCopyLayers}); } // Test that copying texture 2D array mips with 256-byte aligned sizes works TEST_P(CopyTests_T2B, Texture2DArrayMip) { - // TODO(bryan.bernhart@intel.com): Figure out why this test fails on Intel Linux. - // See https://bugs.chromium.org/p/dawn/issues/detail?id=101 - DAWN_SKIP_TEST_IF(IsLinux() && IsVulkan() && IsIntel()); constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; constexpr uint32_t kLayers = 6u; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers}; + for (unsigned int i = 1; i < 4; ++i) { - DoTest({ kWidth, kHeight, 0, 0, kWidth >> i, kHeight >> i, i, kLayers }, MinimumBufferSpec(kWidth >> i, kHeight >> i)); + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.level = i; + + DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i, kLayers), + {kWidth >> i, kHeight >> i, kLayers}); } } -DAWN_INSTANTIATE_TEST(CopyTests_T2B, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +// Test that copying from a range of texture 2D array layers in one texture-to-buffer-copy when +// RowsPerImage is not equal to the height of the texture works. +TEST_P(CopyTests_T2B, Texture2DArrayRegionNonzeroRowsPerImage) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 6u; + constexpr uint32_t kBaseLayer = 2u; + constexpr uint32_t kCopyLayers = 3u; + + constexpr uint32_t kRowsPerImage = kHeight * 2; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, kBaseLayer}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers, false); + bufferSpec.rowsPerImage = kRowsPerImage; + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers}); +} + +// Test a special code path in the D3D12 backends when (BytesPerRow * RowsPerImage) is not a +// multiple of 512. +TEST_P(CopyTests_T2B, Texture2DArrayRegionWithOffsetOddRowsPerImage) { + constexpr uint32_t kWidth = 64; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 8u; + constexpr uint32_t kBaseLayer = 2u; + constexpr uint32_t kCopyLayers = 5u; + + constexpr uint32_t kRowsPerImage = kHeight + 1; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, kBaseLayer}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers, false); + bufferSpec.offset += 128u; + bufferSpec.size += 128u; + bufferSpec.rowsPerImage = kRowsPerImage; + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers}); +} + +// Test a special code path in the D3D12 backends when (BytesPerRow * RowsPerImage) is a multiple +// of 512. +TEST_P(CopyTests_T2B, Texture2DArrayRegionWithOffsetEvenRowsPerImage) { + constexpr uint32_t kWidth = 64; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 8u; + constexpr uint32_t kBaseLayer = 2u; + constexpr uint32_t kCopyLayers = 4u; + + constexpr uint32_t kRowsPerImage = kHeight + 2; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, kBaseLayer}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers, false); + bufferSpec.offset += 128u; + bufferSpec.size += 128u; + bufferSpec.rowsPerImage = kRowsPerImage; + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers}); +} + +DAWN_INSTANTIATE_TEST(CopyTests_T2B, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); // Test that copying an entire texture with 256-byte aligned dimensions works TEST_P(CopyTests_B2T, FullTextureAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, MinimumBufferSpec(kWidth, kHeight)); + + TextureSpec textureSpec; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1}); } // Test that copying an entire texture without 256-byte aligned dimensions works TEST_P(CopyTests_B2T, FullTextureUnaligned) { constexpr uint32_t kWidth = 259; constexpr uint32_t kHeight = 127; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, MinimumBufferSpec(kWidth, kHeight)); + + TextureSpec textureSpec; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight), {kWidth, kHeight, 1}); } // Test that reading pixels from a 256-byte aligned texture works @@ -562,12 +821,48 @@ TEST_P(CopyTests_B2T, PixelReadAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; BufferSpec pixelBuffer = MinimumBufferSpec(1, 1); - DoTest({ kWidth, kHeight, 0, 0, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth - 1, 0, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, 0, kHeight - 1, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth - 1, kHeight - 1, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth / 3, kHeight / 7, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth / 7, kHeight / 3, 1, 1, 0 }, pixelBuffer); + + constexpr wgpu::Extent3D kCopySize = {1, 1, 1}; + constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1}; + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = kTextureSize; + defaultTextureSpec.level = 0; + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth - 1, 0, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {0, kHeight - 1, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth - 1, kHeight - 1, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth / 3, kHeight / 7, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth / 7, kHeight / 3, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } } // Test that copying pixels from a texture that is not 256-byte aligned works @@ -575,12 +870,48 @@ TEST_P(CopyTests_B2T, PixelReadUnaligned) { constexpr uint32_t kWidth = 259; constexpr uint32_t kHeight = 127; BufferSpec pixelBuffer = MinimumBufferSpec(1, 1); - DoTest({ kWidth, kHeight, 0, 0, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth - 1, 0, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, 0, kHeight - 1, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth - 1, kHeight - 1, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth / 3, kHeight / 7, 1, 1, 0 }, pixelBuffer); - DoTest({ kWidth, kHeight, kWidth / 7, kHeight / 3, 1, 1, 0 }, pixelBuffer); + + constexpr wgpu::Extent3D kCopySize = {1, 1, 1}; + constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1}; + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = kTextureSize; + defaultTextureSpec.level = 0; + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth - 1, 0, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {0, kHeight - 1, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth - 1, kHeight - 1, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth / 3, kHeight / 7, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } + + { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {kWidth / 7, kHeight / 3, 0}; + DoTest(textureSpec, pixelBuffer, kCopySize); + } } // Test that copying regions with 256-byte aligned sizes works @@ -588,8 +919,12 @@ TEST_P(CopyTests_B2T, TextureRegionAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; for (unsigned int w : {64, 128, 256}) { - for (unsigned int h : { 16, 32, 48 }) { - DoTest({ kWidth, kHeight, 0, 0, w, h, 0 }, MinimumBufferSpec(w, h)); + for (unsigned int h : {16, 32, 48}) { + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + textureSpec.textureSize = {kWidth, kHeight, 1}; + DoTest(textureSpec, MinimumBufferSpec(w, h), {w, h, 1}); } } } @@ -598,9 +933,16 @@ TEST_P(CopyTests_B2T, TextureRegionAligned) { TEST_P(CopyTests_B2T, TextureRegionUnaligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.level = 0; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + for (unsigned int w : {13, 63, 65}) { - for (unsigned int h : { 17, 19, 63 }) { - DoTest({ kWidth, kHeight, 0, 0, w, h, 0 }, MinimumBufferSpec(w, h)); + for (unsigned int h : {17, 19, 63}) { + TextureSpec textureSpec = defaultTextureSpec; + DoTest(textureSpec, MinimumBufferSpec(w, h), {w, h, 1}); } } } @@ -609,8 +951,16 @@ TEST_P(CopyTests_B2T, TextureRegionUnaligned) { TEST_P(CopyTests_B2T, TextureMipAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + for (unsigned int i = 1; i < 4; ++i) { - DoTest({ kWidth, kHeight, 0, 0, kWidth >> i, kHeight >> i, i }, MinimumBufferSpec(kWidth >> i, kHeight >> i)); + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.level = i; + DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i), + {kWidth >> i, kHeight >> i, 1}); } } @@ -618,8 +968,16 @@ TEST_P(CopyTests_B2T, TextureMipAligned) { TEST_P(CopyTests_B2T, TextureMipUnaligned) { constexpr uint32_t kWidth = 259; constexpr uint32_t kHeight = 127; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + for (unsigned int i = 1; i < 4; ++i) { - DoTest({ kWidth, kHeight, 0, 0, kWidth >> i, kHeight >> i, i }, MinimumBufferSpec(kWidth >> i, kHeight >> i)); + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.level = i; + DoTest(textureSpec, MinimumBufferSpec(kWidth >> i, kHeight >> i), + {kWidth >> i, kHeight >> i, 1}); } } @@ -627,12 +985,18 @@ TEST_P(CopyTests_B2T, TextureMipUnaligned) { TEST_P(CopyTests_B2T, OffsetBufferAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + for (unsigned int i = 0; i < 3; ++i) { BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); uint64_t offset = 512 * i; bufferSpec.size += offset; bufferSpec.offset += offset; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } @@ -640,96 +1004,422 @@ TEST_P(CopyTests_B2T, OffsetBufferAligned) { TEST_P(CopyTests_B2T, OffsetBufferUnaligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; - for (uint32_t i = kBytesPerTexel; i < 512; i += kBytesPerTexel * 9) { + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); + for (uint32_t i = bytesPerTexel; i < 512; i += bytesPerTexel * 9) { BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); bufferSpec.size += i; bufferSpec.offset += i; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } -// Test that copying without a 512-byte aligned buffer offset that is greater than the row pitch works +// Test that copying without a 512-byte aligned buffer offset that is greater than the bytes per row +// works TEST_P(CopyTests_B2T, OffsetBufferUnalignedSmallRowPitch) { constexpr uint32_t kWidth = 32; constexpr uint32_t kHeight = 128; - for (uint32_t i = 256 + kBytesPerTexel; i < 512; i += kBytesPerTexel * 9) { + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); + for (uint32_t i = 256 + bytesPerTexel; i < 512; i += bytesPerTexel * 9) { BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); bufferSpec.size += i; bufferSpec.offset += i; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } -// Test that copying with a greater row pitch than needed on a 256-byte aligned texture works +// Test that copying with a greater bytes per row than needed on a 256-byte aligned texture works TEST_P(CopyTests_B2T, RowPitchAligned) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); for (unsigned int i = 1; i < 4; ++i) { - bufferSpec.rowPitch += 256; + bufferSpec.bytesPerRow += 256; bufferSpec.size += 256 * kHeight; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } -// Test that copying with a greater row pitch than needed on a texture that is not 256-byte aligned works +// Test that copying with a greater bytes per row than needed on a texture that is not 256-byte +// aligned works TEST_P(CopyTests_B2T, RowPitchUnaligned) { constexpr uint32_t kWidth = 259; constexpr uint32_t kHeight = 127; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kHeight); for (unsigned int i = 1; i < 4; ++i) { - bufferSpec.rowPitch += 256; + bufferSpec.bytesPerRow += 256; bufferSpec.size += 256 * kHeight; - DoTest({ kWidth, kHeight, 0, 0, kWidth, kHeight, 0 }, bufferSpec); + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, 1}); } } -DAWN_INSTANTIATE_TEST(CopyTests_B2T, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +// Test that copying whole texture 2D array layers in one texture-to-buffer-copy works. +TEST_P(CopyTests_B2T, Texture2DArrayRegion) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 6u; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kLayers), {kWidth, kHeight, kLayers}); +} + +// Test that copying a range of texture 2D array layers in one texture-to-buffer-copy works. +TEST_P(CopyTests_B2T, Texture2DArraySubRegion) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 6u; + constexpr uint32_t kBaseLayer = 2u; + constexpr uint32_t kCopyLayers = 3u; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, kBaseLayer}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumBufferSpec(kWidth, kHeight, kCopyLayers), + {kWidth, kHeight, kCopyLayers}); +} + +// Test that copying into a range of texture 2D array layers in one texture-to-buffer-copy when +// RowsPerImage is not equal to the height of the texture works. +TEST_P(CopyTests_B2T, Texture2DArrayRegionNonzeroRowsPerImage) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 6u; + constexpr uint32_t kBaseLayer = 2u; + constexpr uint32_t kCopyLayers = 3u; + + constexpr uint32_t kRowsPerImage = kHeight * 2; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, kBaseLayer}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers, false); + bufferSpec.rowsPerImage = kRowsPerImage; + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers}); +} + +// Test a special code path in the D3D12 backends when (BytesPerRow * RowsPerImage) is not a +// multiple of 512. +TEST_P(CopyTests_B2T, Texture2DArrayRegionWithOffsetOddRowsPerImage) { + constexpr uint32_t kWidth = 64; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 8u; + constexpr uint32_t kBaseLayer = 2u; + constexpr uint32_t kCopyLayers = 5u; + + constexpr uint32_t kRowsPerImage = kHeight + 1; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, kBaseLayer}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers, false); + bufferSpec.offset += 128u; + bufferSpec.size += 128u; + bufferSpec.rowsPerImage = kRowsPerImage; + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers}); +} + +// Test a special code path in the D3D12 backends when (BytesPerRow * RowsPerImage) is a multiple +// of 512. +TEST_P(CopyTests_B2T, Texture2DArrayRegionWithOffsetEvenRowsPerImage) { + constexpr uint32_t kWidth = 64; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 8u; + constexpr uint32_t kBaseLayer = 2u; + constexpr uint32_t kCopyLayers = 5u; + + constexpr uint32_t kRowsPerImage = kHeight + 2; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, kBaseLayer}; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.level = 0; + + BufferSpec bufferSpec = MinimumBufferSpec(kWidth, kRowsPerImage, kCopyLayers, false); + bufferSpec.offset += 128u; + bufferSpec.size += 128u; + bufferSpec.rowsPerImage = kRowsPerImage; + DoTest(textureSpec, bufferSpec, {kWidth, kHeight, kCopyLayers}); +} + +DAWN_INSTANTIATE_TEST(CopyTests_B2T, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); TEST_P(CopyTests_T2T, Texture) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; - DoTest({kWidth, kHeight, 0, 0, 0}, {kWidth, kHeight, 0, 0, 0}, {kWidth, kHeight}); + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + textureSpec.textureSize = {kWidth, kHeight, 1}; + DoTest(textureSpec, textureSpec, {kWidth, kHeight, 1}); } TEST_P(CopyTests_T2T, TextureRegion) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.level = 0; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + for (unsigned int w : {64, 128, 256}) { for (unsigned int h : {16, 32, 48}) { - DoTest({kWidth, kHeight, 0, 0, 0, 1}, {kWidth, kHeight, 0, 0, 0, 1}, {w, h}); + TextureSpec textureSpec = defaultTextureSpec; + DoTest(textureSpec, textureSpec, {w, h, 1}); } } } +// Test copying the whole 2D array texture. TEST_P(CopyTests_T2T, Texture2DArray) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; constexpr uint32_t kLayers = 6u; - DoTest({kWidth, kHeight, 0, 0, 0, kLayers}, {kWidth, kHeight, 0, 0, 0, kLayers}, - {kWidth, kHeight}); + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + + DoTest(textureSpec, textureSpec, {kWidth, kHeight, kLayers}); } +// Test copying a subresource region of the 2D array texture. TEST_P(CopyTests_T2T, Texture2DArrayRegion) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; constexpr uint32_t kLayers = 6u; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.level = 0; + defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers}; + for (unsigned int w : {64, 128, 256}) { for (unsigned int h : {16, 32, 48}) { - DoTest({kWidth, kHeight, 0, 0, 0, kLayers}, {kWidth, kHeight, 0, 0, 0, kLayers}, - {w, h}); + TextureSpec textureSpec = defaultTextureSpec; + DoTest(textureSpec, textureSpec, {w, h, kLayers}); } } } +// Test copying one slice of a 2D array texture. +TEST_P(CopyTests_T2T, Texture2DArrayCopyOneSlice) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 6u; + constexpr uint32_t kSrcBaseLayer = 1u; + constexpr uint32_t kDstBaseLayer = 3u; + constexpr uint32_t kCopyArrayLayerCount = 1u; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers}; + defaultTextureSpec.level = 0; + + TextureSpec srcTextureSpec = defaultTextureSpec; + srcTextureSpec.copyOrigin = {0, 0, kSrcBaseLayer}; + + TextureSpec dstTextureSpec = defaultTextureSpec; + dstTextureSpec.copyOrigin = {0, 0, kDstBaseLayer}; + + DoTest(srcTextureSpec, dstTextureSpec, {kWidth, kHeight, kCopyArrayLayerCount}); +} + +// Test copying multiple contiguous slices of a 2D array texture. +TEST_P(CopyTests_T2T, Texture2DArrayCopyMultipleSlices) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + constexpr uint32_t kLayers = 6u; + constexpr uint32_t kSrcBaseLayer = 0u; + constexpr uint32_t kDstBaseLayer = 3u; + constexpr uint32_t kCopyArrayLayerCount = 3u; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers}; + defaultTextureSpec.level = 0; + + TextureSpec srcTextureSpec = defaultTextureSpec; + srcTextureSpec.copyOrigin = {0, 0, kSrcBaseLayer}; + + TextureSpec dstTextureSpec = defaultTextureSpec; + dstTextureSpec.copyOrigin = {0, 0, kDstBaseLayer}; + + DoTest(srcTextureSpec, dstTextureSpec, {kWidth, kHeight, kCopyArrayLayerCount}); +} + +// Test copying one texture slice within the same texture. +TEST_P(CopyTests_T2T, CopyWithinSameTextureOneSlice) { + constexpr uint32_t kWidth = 256u; + constexpr uint32_t kHeight = 128u; + constexpr uint32_t kLayers = 6u; + constexpr uint32_t kSrcBaseLayer = 0u; + constexpr uint32_t kDstBaseLayer = 3u; + constexpr uint32_t kCopyArrayLayerCount = 1u; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers}; + defaultTextureSpec.level = 0; + + TextureSpec srcTextureSpec = defaultTextureSpec; + srcTextureSpec.copyOrigin = {0, 0, kSrcBaseLayer}; + + TextureSpec dstTextureSpec = defaultTextureSpec; + dstTextureSpec.copyOrigin = {0, 0, kDstBaseLayer}; + + DoTest(srcTextureSpec, dstTextureSpec, {kWidth, kHeight, kCopyArrayLayerCount}, true); +} + +// Test copying multiple contiguous texture slices within the same texture with non-overlapped +// slices. +TEST_P(CopyTests_T2T, CopyWithinSameTextureNonOverlappedSlices) { + constexpr uint32_t kWidth = 256u; + constexpr uint32_t kHeight = 128u; + constexpr uint32_t kLayers = 6u; + constexpr uint32_t kSrcBaseLayer = 0u; + constexpr uint32_t kDstBaseLayer = 3u; + constexpr uint32_t kCopyArrayLayerCount = 3u; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = {kWidth, kHeight, kLayers}; + defaultTextureSpec.level = 0; + + TextureSpec srcTextureSpec = defaultTextureSpec; + srcTextureSpec.copyOrigin = {0, 0, kSrcBaseLayer}; + + TextureSpec dstTextureSpec = defaultTextureSpec; + dstTextureSpec.copyOrigin = {0, 0, kDstBaseLayer}; + + DoTest(srcTextureSpec, dstTextureSpec, {kWidth, kHeight, kCopyArrayLayerCount}, true); +} + TEST_P(CopyTests_T2T, TextureMip) { constexpr uint32_t kWidth = 256; constexpr uint32_t kHeight = 128; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + + for (unsigned int i = 1; i < 4; ++i) { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.level = i; + + DoTest(textureSpec, textureSpec, {kWidth >> i, kHeight >> i, 1}); + } +} + +TEST_P(CopyTests_T2T, SingleMipSrcMultipleMipDst) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + + for (unsigned int i = 1; i < 4; ++i) { + TextureSpec srcTextureSpec = defaultTextureSpec; + srcTextureSpec.textureSize = {kWidth >> i, kHeight >> i, 1}; + srcTextureSpec.level = 0; + + TextureSpec dstTextureSpec = defaultTextureSpec; + dstTextureSpec.textureSize = {kWidth, kHeight, 1}; + dstTextureSpec.level = i; + + DoTest(srcTextureSpec, dstTextureSpec, {kWidth >> i, kHeight >> i, 1}); + } +} + +TEST_P(CopyTests_T2T, MultipleMipSrcSingleMipDst) { + constexpr uint32_t kWidth = 256; + constexpr uint32_t kHeight = 128; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + for (unsigned int i = 1; i < 4; ++i) { - DoTest({kWidth, kHeight, 0, 0, i}, {kWidth, kHeight, 0, 0, i}, {kWidth >> i, kHeight >> i}); + TextureSpec srcTextureSpec = defaultTextureSpec; + srcTextureSpec.textureSize = {kWidth, kHeight, 1}; + srcTextureSpec.level = i; + + TextureSpec dstTextureSpec = defaultTextureSpec; + dstTextureSpec.textureSize = {kWidth >> i, kHeight >> i, 1}; + dstTextureSpec.level = 0; + + DoTest(srcTextureSpec, dstTextureSpec, {kWidth >> i, kHeight >> i, 1}); } } -// TODO(brandon1.jones@intel.com) Add test for ensuring blitCommandEncoder on Metal. +DAWN_INSTANTIATE_TEST(CopyTests_T2T, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +static constexpr uint64_t kSmallBufferSize = 4; +static constexpr uint64_t kLargeBufferSize = 1 << 16; + +// Test copying full buffers +TEST_P(CopyTests_B2B, FullCopy) { + DoTest(kSmallBufferSize, 0, kSmallBufferSize, 0, kSmallBufferSize); + DoTest(kLargeBufferSize, 0, kLargeBufferSize, 0, kLargeBufferSize); +} + +// Test copying small pieces of a buffer at different corner case offsets +TEST_P(CopyTests_B2B, SmallCopyInBigBuffer) { + constexpr uint64_t kEndOffset = kLargeBufferSize - kSmallBufferSize; + DoTest(kLargeBufferSize, 0, kLargeBufferSize, 0, kSmallBufferSize); + DoTest(kLargeBufferSize, kEndOffset, kLargeBufferSize, 0, kSmallBufferSize); + DoTest(kLargeBufferSize, 0, kLargeBufferSize, kEndOffset, kSmallBufferSize); + DoTest(kLargeBufferSize, kEndOffset, kLargeBufferSize, kEndOffset, kSmallBufferSize); +} + +// Test zero-size copies +TEST_P(CopyTests_B2B, ZeroSizedCopy) { + DoTest(kLargeBufferSize, 0, kLargeBufferSize, 0, 0); + DoTest(kLargeBufferSize, 0, kLargeBufferSize, kLargeBufferSize, 0); + DoTest(kLargeBufferSize, kLargeBufferSize, kLargeBufferSize, 0, 0); + DoTest(kLargeBufferSize, kLargeBufferSize, kLargeBufferSize, kLargeBufferSize, 0); +} -DAWN_INSTANTIATE_TEST(CopyTests_T2T, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(CopyTests_B2B, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/CullingTests.cpp b/third_party/dawn/src/tests/end2end/CullingTests.cpp new file mode 100644 index 00000000000..726df138db8 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/CullingTests.cpp @@ -0,0 +1,134 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class CullingTest : public DawnTest { + protected: + wgpu::RenderPipeline CreatePipelineForTest(wgpu::FrontFace frontFace, wgpu::CullMode cullMode) { + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + + // Draw two triangles with different winding orders: + // 1. The top-left one is counterclockwise (CCW) + // 2. The bottom-right one is clockwise (CW) + const char* vs = + R"(#version 450 + const vec2 pos[6] = vec2[6](vec2(-1.0f, 1.0f), + vec2(-1.0f, 0.0f), + vec2( 0.0f, 1.0f), + vec2( 0.0f, -1.0f), + vec2( 1.0f, 0.0f), + vec2( 1.0f, -1.0f)); + void main() { + gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); + })"; + pipelineDescriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vs); + + // gl_FragCoord of pixel(x, y) in framebuffer coordinate is (x + 0.5, y + 0.5). And we use + // RGBA8 format for the back buffer. So (gl_FragCoord.xy - vec2(0.5)) / 255 in shader code + // will make the pixel's R and G channels exactly equal to the pixel's x and y coordinates. + const char* fs = + R"(#version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4((gl_FragCoord.xy - vec2(0.5)) / 255, 0.0, 1.0); + })"; + pipelineDescriptor.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fs); + + // Set culling mode and front face according to the parameters + pipelineDescriptor.cRasterizationState.frontFace = frontFace; + pipelineDescriptor.cRasterizationState.cullMode = cullMode; + + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + wgpu::Texture Create2DTextureForTest(wgpu::TextureFormat format) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.dimension = wgpu::TextureDimension::e2D; + textureDescriptor.format = format; + textureDescriptor.usage = + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + textureDescriptor.mipLevelCount = 1; + textureDescriptor.sampleCount = 1; + textureDescriptor.size = {kSize, kSize, 1}; + return device.CreateTexture(&textureDescriptor); + } + + void DoTest(wgpu::FrontFace frontFace, + wgpu::CullMode cullMode, + bool isCCWTriangleCulled, + bool isCWTriangleCulled) { + wgpu::Texture colorTexture = Create2DTextureForTest(wgpu::TextureFormat::RGBA8Unorm); + + utils::ComboRenderPassDescriptor renderPassDescriptor({colorTexture.CreateView()}); + renderPassDescriptor.cColorAttachments[0].clearColor = {0.0, 0.0, 1.0, 1.0}; + renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = commandEncoder.BeginRenderPass(&renderPassDescriptor); + renderPass.SetPipeline(CreatePipelineForTest(frontFace, cullMode)); + renderPass.Draw(6); + renderPass.EndPass(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + const RGBA8 kBackgroundColor = RGBA8::kBlue; + const RGBA8 kTopLeftColor = RGBA8::kBlack; + constexpr RGBA8 kBottomRightColor = RGBA8(3, 3, 0, 255); + + RGBA8 kCCWTriangleTopLeftColor = isCCWTriangleCulled ? kBackgroundColor : kTopLeftColor; + EXPECT_PIXEL_RGBA8_EQ(kCCWTriangleTopLeftColor, colorTexture, 0, 0); + + RGBA8 kCWTriangleBottomRightColor = + isCWTriangleCulled ? kBackgroundColor : kBottomRightColor; + EXPECT_PIXEL_RGBA8_EQ(kCWTriangleBottomRightColor, colorTexture, kSize - 1, kSize - 1); + } + + static constexpr uint32_t kSize = 4; +}; + +TEST_P(CullingTest, CullNoneWhenCCWIsFrontFace) { + DoTest(wgpu::FrontFace::CCW, wgpu::CullMode::None, false, false); +} + +TEST_P(CullingTest, CullFrontFaceWhenCCWIsFrontFace) { + DoTest(wgpu::FrontFace::CCW, wgpu::CullMode::Front, true, false); +} + +TEST_P(CullingTest, CullBackFaceWhenCCWIsFrontFace) { + DoTest(wgpu::FrontFace::CCW, wgpu::CullMode::Back, false, true); +} + +TEST_P(CullingTest, CullNoneWhenCWIsFrontFace) { + DoTest(wgpu::FrontFace::CW, wgpu::CullMode::None, false, false); +} + +TEST_P(CullingTest, CullFrontFaceWhenCWIsFrontFace) { + DoTest(wgpu::FrontFace::CW, wgpu::CullMode::Front, false, true); +} + +TEST_P(CullingTest, CullBackFaceWhenCWIsFrontFace) { + DoTest(wgpu::FrontFace::CW, wgpu::CullMode::Back, true, false); +} + +DAWN_INSTANTIATE_TEST(CullingTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/D3D12ResourceWrappingTests.cpp b/third_party/dawn/src/tests/end2end/D3D12ResourceWrappingTests.cpp new file mode 100644 index 00000000000..abc86a35225 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/D3D12ResourceWrappingTests.cpp @@ -0,0 +1,521 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include +#include +#include +#include + +#include "dawn_native/D3D12Backend.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +using Microsoft::WRL::ComPtr; + +namespace { + + class D3D12ResourceTestBase : public DawnTest { + public: + void SetUp() override { + DawnTest::SetUp(); + if (UsesWire()) { + return; + } + + // Create the D3D11 device/contexts that will be used in subsequent tests + ComPtr d3d12Device = dawn_native::d3d12::GetD3D12Device(device.Get()); + + const LUID adapterLuid = d3d12Device->GetAdapterLuid(); + + ComPtr dxgiFactory; + HRESULT hr = ::CreateDXGIFactory2(0, IID_PPV_ARGS(&dxgiFactory)); + ASSERT_EQ(hr, S_OK); + + ComPtr dxgiAdapter; + hr = dxgiFactory->EnumAdapterByLuid(adapterLuid, IID_PPV_ARGS(&dxgiAdapter)); + ASSERT_EQ(hr, S_OK); + + ComPtr d3d11Device; + D3D_FEATURE_LEVEL d3dFeatureLevel; + ComPtr d3d11DeviceContext; + hr = ::D3D11CreateDevice(dxgiAdapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, 0, + nullptr, 0, D3D11_SDK_VERSION, &d3d11Device, &d3dFeatureLevel, + &d3d11DeviceContext); + ASSERT_EQ(hr, S_OK); + + mD3d11Device = std::move(d3d11Device); + mD3d11DeviceContext = std::move(d3d11DeviceContext); + + baseDawnDescriptor.dimension = wgpu::TextureDimension::e2D; + baseDawnDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + baseDawnDescriptor.size = {kTestWidth, kTestHeight, 1}; + baseDawnDescriptor.sampleCount = 1; + baseDawnDescriptor.mipLevelCount = 1; + baseDawnDescriptor.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc | + wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopyDst; + + baseD3dDescriptor.Width = kTestWidth; + baseD3dDescriptor.Height = kTestHeight; + baseD3dDescriptor.MipLevels = 1; + baseD3dDescriptor.ArraySize = 1; + baseD3dDescriptor.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + baseD3dDescriptor.SampleDesc.Count = 1; + baseD3dDescriptor.SampleDesc.Quality = 0; + baseD3dDescriptor.Usage = D3D11_USAGE_DEFAULT; + baseD3dDescriptor.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + baseD3dDescriptor.CPUAccessFlags = 0; + baseD3dDescriptor.MiscFlags = + D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX; + } + + protected: + void WrapSharedHandle(const wgpu::TextureDescriptor* dawnDesc, + const D3D11_TEXTURE2D_DESC* baseD3dDescriptor, + wgpu::Texture* dawnTexture, + ID3D11Texture2D** d3d11TextureOut) const { + ComPtr d3d11Texture; + HRESULT hr = mD3d11Device->CreateTexture2D(baseD3dDescriptor, nullptr, &d3d11Texture); + ASSERT_EQ(hr, S_OK); + + ComPtr dxgiResource; + hr = d3d11Texture.As(&dxgiResource); + ASSERT_EQ(hr, S_OK); + + HANDLE sharedHandle; + hr = dxgiResource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &sharedHandle); + ASSERT_EQ(hr, S_OK); + + dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externDesc; + externDesc.cTextureDescriptor = + reinterpret_cast(dawnDesc); + externDesc.sharedHandle = sharedHandle; + externDesc.acquireMutexKey = 0; + WGPUTexture texture = dawn_native::d3d12::WrapSharedHandle(device.Get(), &externDesc); + + // Now that we've created all of our resources, we can close the handle + // since we no longer need it. + ::CloseHandle(sharedHandle); + + *dawnTexture = wgpu::Texture::Acquire(texture); + *d3d11TextureOut = d3d11Texture.Detach(); + } + + static constexpr size_t kTestWidth = 10; + static constexpr size_t kTestHeight = 10; + + ComPtr mD3d11Device; + ComPtr mD3d11DeviceContext; + + D3D11_TEXTURE2D_DESC baseD3dDescriptor; + wgpu::TextureDescriptor baseDawnDescriptor; + }; + +} // anonymous namespace + +// A small fixture used to initialize default data for the D3D12Resource validation tests. +// These tests are skipped if the harness is using the wire. +class D3D12SharedHandleValidation : public D3D12ResourceTestBase {}; + +// Test a successful wrapping of an D3D12Resource in a texture +TEST_P(D3D12SharedHandleValidation, Success) { + DAWN_SKIP_TEST_IF(UsesWire()); + + wgpu::Texture texture; + ComPtr d3d11Texture; + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture); + + ASSERT_NE(texture.Get(), nullptr); +} + +// Test an error occurs if the texture descriptor is invalid +TEST_P(D3D12SharedHandleValidation, InvalidTextureDescriptor) { + DAWN_SKIP_TEST_IF(UsesWire()); + + wgpu::ChainedStruct chainedDescriptor; + baseDawnDescriptor.nextInChain = &chainedDescriptor; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor mip level count isn't 1 +TEST_P(D3D12SharedHandleValidation, InvalidMipLevelCount) { + DAWN_SKIP_TEST_IF(UsesWire()); + baseDawnDescriptor.mipLevelCount = 2; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor depth isn't 1 +TEST_P(D3D12SharedHandleValidation, InvalidDepth) { + DAWN_SKIP_TEST_IF(UsesWire()); + baseDawnDescriptor.size.depth = 2; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor sample count isn't 1 +TEST_P(D3D12SharedHandleValidation, InvalidSampleCount) { + DAWN_SKIP_TEST_IF(UsesWire()); + baseDawnDescriptor.sampleCount = 4; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor width doesn't match the texture's +TEST_P(D3D12SharedHandleValidation, InvalidWidth) { + DAWN_SKIP_TEST_IF(UsesWire()); + baseDawnDescriptor.size.width = kTestWidth + 1; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor height doesn't match the texture's +TEST_P(D3D12SharedHandleValidation, InvalidHeight) { + DAWN_SKIP_TEST_IF(UsesWire()); + baseDawnDescriptor.size.height = kTestHeight + 1; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the descriptor format isn't compatible with the D3D12 Resource +TEST_P(D3D12SharedHandleValidation, InvalidFormat) { + DAWN_SKIP_TEST_IF(UsesWire()); + baseDawnDescriptor.format = wgpu::TextureFormat::R8Unorm; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the number of D3D mip levels is greater than 1. +TEST_P(D3D12SharedHandleValidation, InvalidNumD3DMipLevels) { + DAWN_SKIP_TEST_IF(UsesWire()); + baseD3dDescriptor.MipLevels = 2; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +// Test an error occurs if the number of array levels is greater than 1. +TEST_P(D3D12SharedHandleValidation, InvalidD3DArraySize) { + DAWN_SKIP_TEST_IF(UsesWire()); + baseD3dDescriptor.ArraySize = 2; + + wgpu::Texture texture; + ComPtr d3d11Texture; + ASSERT_DEVICE_ERROR( + WrapSharedHandle(&baseDawnDescriptor, &baseD3dDescriptor, &texture, &d3d11Texture)); + + ASSERT_EQ(texture.Get(), nullptr); +} + +class D3D12SharedHandleUsageTests : public D3D12ResourceTestBase { + protected: + // Submits a 1x1x1 copy from source to destination + void SimpleCopyTextureToTexture(wgpu::Texture source, wgpu::Texture destination) { + wgpu::TextureCopyView copySrc = utils::CreateTextureCopyView(source, 0, {0, 0, 0}); + wgpu::TextureCopyView copyDst = utils::CreateTextureCopyView(destination, 0, {0, 0, 0}); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + + queue.Submit(1, &commands); + } + + // Clear a texture on a given device + void ClearImage(wgpu::Texture wrappedTexture, const wgpu::Color& clearColor) { + wgpu::TextureView wrappedView = wrappedTexture.CreateView(); + + // Submit a clear operation + utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {}); + renderPassDescriptor.cColorAttachments[0].clearColor = clearColor; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + void WrapAndClearD3D11Texture(const wgpu::TextureDescriptor* dawnDescriptor, + const D3D11_TEXTURE2D_DESC* d3dDescriptor, + wgpu::Texture* dawnTextureOut, + const wgpu::Color& clearColor, + ID3D11Texture2D** d3d11TextureOut, + IDXGIKeyedMutex** dxgiKeyedMutexOut, + bool isCleared = true) const { + ComPtr d3d11Texture; + HRESULT hr = mD3d11Device->CreateTexture2D(d3dDescriptor, nullptr, &d3d11Texture); + ASSERT_EQ(hr, S_OK); + + ComPtr dxgiResource; + hr = d3d11Texture.As(&dxgiResource); + ASSERT_EQ(hr, S_OK); + + HANDLE sharedHandle; + hr = dxgiResource->CreateSharedHandle( + nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr, + &sharedHandle); + ASSERT_EQ(hr, S_OK); + + ComPtr dxgiKeyedMutex; + hr = d3d11Texture.As(&dxgiKeyedMutex); + ASSERT_EQ(hr, S_OK); + + ComPtr d3d11RTV; + hr = mD3d11Device->CreateRenderTargetView(d3d11Texture.Get(), nullptr, &d3d11RTV); + ASSERT_EQ(hr, S_OK); + + hr = dxgiKeyedMutex->AcquireSync(0, INFINITE); + ASSERT_EQ(hr, S_OK); + + const float colorRGBA[] = {clearColor.r, clearColor.g, clearColor.b, clearColor.a}; + mD3d11DeviceContext->ClearRenderTargetView(d3d11RTV.Get(), colorRGBA); + + hr = dxgiKeyedMutex->ReleaseSync(1); + ASSERT_EQ(hr, S_OK); + + dawn_native::d3d12::ExternalImageDescriptorDXGISharedHandle externDesc; + externDesc.cTextureDescriptor = + reinterpret_cast(dawnDescriptor); + externDesc.sharedHandle = sharedHandle; + externDesc.acquireMutexKey = 1; + externDesc.isCleared = isCleared; + WGPUTexture dawnTexture = dawn_native::d3d12::WrapSharedHandle(device.Get(), &externDesc); + + *dawnTextureOut = wgpu::Texture::Acquire(dawnTexture); + *d3d11TextureOut = d3d11Texture.Detach(); + *dxgiKeyedMutexOut = dxgiKeyedMutex.Detach(); + } + + void ExpectPixelRGBA8EQ(UINT64 acquireKey, + ID3D11Texture2D* d3d11Texture, + IDXGIKeyedMutex* dxgiKeyedMutex, + const wgpu::Color& color) { + HRESULT hr = dxgiKeyedMutex->AcquireSync(acquireKey, INFINITE); + ASSERT_EQ(hr, S_OK); + + D3D11_TEXTURE2D_DESC texture2DDesc; + d3d11Texture->GetDesc(&texture2DDesc); + + const CD3D11_TEXTURE2D_DESC texture2DStagingDesc( + texture2DDesc.Format, // Format + texture2DDesc.Width, // Width + texture2DDesc.Height, // Height + 1, // ArraySize + 1, // MipLevels + 0, // BindFlags + D3D11_USAGE_STAGING, // Usage + D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE); // CPUAccessFlags + + ComPtr spD3DTextureStaging; + hr = mD3d11Device->CreateTexture2D(&texture2DStagingDesc, nullptr, &spD3DTextureStaging); + ASSERT_EQ(hr, S_OK); + + D3D11_BOX d3dRc; + d3dRc.back = 1; + d3dRc.front = 0; + d3dRc.top = 0; + d3dRc.left = 0; + d3dRc.bottom = texture2DDesc.Height; + d3dRc.right = texture2DDesc.Width; + + mD3d11DeviceContext->CopySubresourceRegion(spD3DTextureStaging.Get(), // pDstResource + 0, // DstSubresource + 0, // DstX + 0, // DstY + 0, // DstZ + d3d11Texture, // pSrcResource + 0, // SrcSubresource + &d3dRc); // pSrcBox + + D3D11_MAPPED_SUBRESOURCE mappedResource; + hr = mD3d11DeviceContext->Map(spD3DTextureStaging.Get(), 0, D3D11_MAP_READ_WRITE, 0, + &mappedResource); + ASSERT_EQ(hr, S_OK); + + const uint8_t* colorData = static_cast(mappedResource.pData); + EXPECT_EQ(colorData[0], color.r * 255u); + EXPECT_EQ(colorData[1], color.g * 255u); + EXPECT_EQ(colorData[2], color.b * 255u); + EXPECT_EQ(colorData[3], color.a * 255u); + + mD3d11DeviceContext->Unmap(spD3DTextureStaging.Get(), 0); + + hr = dxgiKeyedMutex->ReleaseSync(acquireKey + 1); + ASSERT_EQ(hr, S_OK); + } +}; + +// 1. Create and clear a D3D11 texture +// 2. Copy the wrapped texture to another dawn texture +// 3. Readback the copied texture and ensure the color matches the original clear color. +TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11CopyAndReadbackInD3D12) { + DAWN_SKIP_TEST_IF(UsesWire()); + + const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f}; + wgpu::Texture dawnSrcTexture; + ComPtr d3d11Texture; + ComPtr dxgiKeyedMutex; + WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnSrcTexture, clearColor, + &d3d11Texture, &dxgiKeyedMutex); + + // Create a texture on the device and copy the source texture to it. + wgpu::Texture dawnCopyDestTexture = device.CreateTexture(&baseDawnDescriptor); + SimpleCopyTextureToTexture(dawnSrcTexture, dawnCopyDestTexture); + + // Readback the destination texture and ensure it contains the colors we used + // to clear the source texture on the D3D device. + EXPECT_PIXEL_RGBA8_EQ( + RGBA8(clearColor.r * 255u, clearColor.g * 255u, clearColor.b * 255u, clearColor.a * 255u), + dawnCopyDestTexture, 0, 0); +} + +// 1. Create and clear a D3D11 texture +// 2. Readback the wrapped texture and ensure the color matches the original clear color. +TEST_P(D3D12SharedHandleUsageTests, ClearInD3D11ReadbackInD3D12) { + DAWN_SKIP_TEST_IF(UsesWire()); + + const wgpu::Color clearColor{1.0f, 1.0f, 0.0f, 1.0f}; + wgpu::Texture dawnTexture; + ComPtr d3d11Texture; + ComPtr dxgiKeyedMutex; + WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, clearColor, + &d3d11Texture, &dxgiKeyedMutex); + + // Readback the destination texture and ensure it contains the colors we used + // to clear the source texture on the D3D device. + EXPECT_PIXEL_RGBA8_EQ( + RGBA8(clearColor.r * 255, clearColor.g * 255, clearColor.b * 255, clearColor.a * 255), + dawnTexture, 0, 0); +} + +// 1. Create and clear a D3D11 texture +// 2. Wrap it in a Dawn texture and clear it to a different color +// 3. Readback the texture with D3D11 and ensure we receive the color we cleared with Dawn. +TEST_P(D3D12SharedHandleUsageTests, ClearInD3D12ReadbackInD3D11) { + DAWN_SKIP_TEST_IF(UsesWire()); + + const wgpu::Color d3d11ClearColor{1.0f, 1.0f, 0.0f, 1.0f}; + wgpu::Texture dawnTexture; + ComPtr d3d11Texture; + ComPtr dxgiKeyedMutex; + WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, d3d11ClearColor, + &d3d11Texture, &dxgiKeyedMutex); + + const wgpu::Color d3d12ClearColor{0.0f, 0.0f, 1.0f, 1.0f}; + ClearImage(dawnTexture, d3d12ClearColor); + + dawnTexture.Destroy(); + + // Now that Dawn (via D3D12) has finished writing to the texture, we should be + // able to read it back by copying it to a staging texture and verifying the + // color matches the D3D12 clear color. + ExpectPixelRGBA8EQ(2, d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d12ClearColor); +} + +// 1. Create and clear a D3D11 texture +// 2. Wrap it in a Dawn texture and clear the texture to two different colors. +// 3. Readback the texture with D3D11. +// 4. Verify the readback color was the final color cleared. +TEST_P(D3D12SharedHandleUsageTests, ClearTwiceInD3D12ReadbackInD3D11) { + DAWN_SKIP_TEST_IF(UsesWire()); + + const wgpu::Color d3d11ClearColor{1.0f, 1.0f, 0.0f, 1.0f}; + wgpu::Texture dawnTexture; + ComPtr d3d11Texture; + ComPtr dxgiKeyedMutex; + WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, d3d11ClearColor, + &d3d11Texture, &dxgiKeyedMutex); + + const wgpu::Color d3d12ClearColor1{0.0f, 0.0f, 1.0f, 1.0f}; + ClearImage(dawnTexture, d3d12ClearColor1); + + const wgpu::Color d3d12ClearColor2{0.0f, 1.0f, 1.0f, 1.0f}; + ClearImage(dawnTexture, d3d12ClearColor2); + + dawnTexture.Destroy(); + + // Now that Dawn (via D3D12) has finished writing to the texture, we should be + // able to read it back by copying it to a staging texture and verifying the + // color matches the last D3D12 clear color. + ExpectPixelRGBA8EQ(2, d3d11Texture.Get(), dxgiKeyedMutex.Get(), d3d12ClearColor2); +} + +// 1. Create and clear a D3D11 texture with clearColor +// 2. Import the texture with isCleared = false +// 3. Verify clearColor is not visible in wrapped texture +TEST_P(D3D12SharedHandleUsageTests, UnclearedTextureIsCleared) { + DAWN_SKIP_TEST_IF(UsesWire()); + + const wgpu::Color clearColor{1.0f, 0.0f, 0.0f, 1.0f}; + wgpu::Texture dawnTexture; + ComPtr d3d11Texture; + ComPtr dxgiKeyedMutex; + WrapAndClearD3D11Texture(&baseDawnDescriptor, &baseD3dDescriptor, &dawnTexture, clearColor, + &d3d11Texture, &dxgiKeyedMutex, false); + + // Readback the destination texture and ensure it contains the colors we used + // to clear the source texture on the D3D device. + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), dawnTexture, 0, 0); +} + +DAWN_INSTANTIATE_TEST(D3D12SharedHandleValidation, D3D12Backend()); +DAWN_INSTANTIATE_TEST(D3D12SharedHandleUsageTests, D3D12Backend()); diff --git a/third_party/dawn/src/tests/end2end/DebugMarkerTests.cpp b/third_party/dawn/src/tests/end2end/DebugMarkerTests.cpp index 81c3a7d2a24..d368a175f6b 100644 --- a/third_party/dawn/src/tests/end2end/DebugMarkerTests.cpp +++ b/third_party/dawn/src/tests/end2end/DebugMarkerTests.cpp @@ -14,7 +14,7 @@ #include "tests/DawnTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class DebugMarkerTests : public DawnTest {}; @@ -22,17 +22,28 @@ class DebugMarkerTests : public DawnTest {}; TEST_P(DebugMarkerTests, NoFailureWithoutDebugToolAttached) { utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 4, 4); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.PushDebugGroup("Event Start"); + pass.InsertDebugMarker("Marker"); + pass.PopDebugGroup(); + pass.EndPass(); + } + { + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.PushDebugGroup("Event Start"); pass.InsertDebugMarker("Marker"); pass.PopDebugGroup(); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); } -DAWN_INSTANTIATE_TEST(DebugMarkerTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(DebugMarkerTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DeprecatedAPITests.cpp b/third_party/dawn/src/tests/end2end/DeprecatedAPITests.cpp new file mode 100644 index 00000000000..312063a5c68 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/DeprecatedAPITests.cpp @@ -0,0 +1,365 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains test for deprecated parts of Dawn's API while following WebGPU's evolution. +// It contains test for the "old" behavior that will be deleted once users are migrated, tests that +// a deprecation warning is emitted when the "old" behavior is used, and tests that an error is +// emitted when both the old and the new behavior are used (when applicable). + +#include "tests/DawnTest.h" + +#include "common/Constants.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class DeprecationTests : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + // Skip when validation is off because warnings might be emitted during validation calls + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + } + + void TearDown() override { + if (!UsesWire()) { + EXPECT_EQ(mLastWarningCount, + dawn_native::GetDeprecationWarningCountForTesting(device.Get())); + } + DawnTest::TearDown(); + } + + size_t mLastWarningCount = 0; +}; + +#define EXPECT_DEPRECATION_WARNING(statement) \ + do { \ + if (UsesWire()) { \ + statement; \ + } else { \ + size_t warningsBefore = \ + dawn_native::GetDeprecationWarningCountForTesting(device.Get()); \ + statement; \ + size_t warningsAfter = \ + dawn_native::GetDeprecationWarningCountForTesting(device.Get()); \ + EXPECT_EQ(mLastWarningCount, warningsBefore); \ + EXPECT_EQ(warningsAfter, warningsBefore + 1); \ + mLastWarningCount = warningsAfter; \ + } \ + } while (0) + +// Test that using SetSubData emits a deprecation warning. +TEST_P(DeprecationTests, SetSubDataDeprecated) { + wgpu::BufferDescriptor descriptor; + descriptor.usage = wgpu::BufferUsage::CopyDst; + descriptor.size = 4; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + EXPECT_DEPRECATION_WARNING(buffer.SetSubData(0, 0, nullptr)); +} + +// Test that using SetSubData works +TEST_P(DeprecationTests, SetSubDataStillWorks) { + DAWN_SKIP_TEST_IF(IsNull()); + + wgpu::BufferDescriptor descriptor; + descriptor.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; + descriptor.size = 4; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + uint32_t data = 2020; + EXPECT_DEPRECATION_WARNING(buffer.SetSubData(0, 4, &data)); + EXPECT_BUFFER_U32_EQ(data, buffer, 0); +} + +// Test that using TextureDescriptor::arrayLayerCount emits a warning. +TEST_P(DeprecationTests, TextureDescriptorArrayLayerCountDeprecated) { + wgpu::TextureDescriptor desc; + desc.usage = wgpu::TextureUsage::Sampled; + desc.dimension = wgpu::TextureDimension::e2D; + desc.size = {1, 1, 1}; + desc.arrayLayerCount = 2; + desc.format = wgpu::TextureFormat::RGBA8Unorm; + desc.mipLevelCount = 1; + desc.sampleCount = 1; + + EXPECT_DEPRECATION_WARNING(device.CreateTexture(&desc)); +} + +// Test that using both TextureDescriptor::arrayLayerCount and size.depth triggers an error. +TEST_P(DeprecationTests, TextureDescriptorArrayLayerCountAndDepthSizeIsError) { + wgpu::TextureDescriptor desc; + desc.usage = wgpu::TextureUsage::Sampled; + desc.dimension = wgpu::TextureDimension::e2D; + desc.size = {1, 1, 2}; + desc.arrayLayerCount = 2; + desc.format = wgpu::TextureFormat::RGBA8Unorm; + desc.mipLevelCount = 1; + desc.sampleCount = 1; + + ASSERT_DEVICE_ERROR(device.CreateTexture(&desc)); +} + +// Test that TextureDescriptor::arrayLayerCount does correct state tracking. +TEST_P(DeprecationTests, TextureDescriptorArrayLayerCountStateTracking) { + wgpu::TextureDescriptor desc; + desc.usage = wgpu::TextureUsage::Sampled; + desc.dimension = wgpu::TextureDimension::e2D; + desc.size = {1, 1, 1}; + desc.arrayLayerCount = 2; + desc.format = wgpu::TextureFormat::RGBA8Unorm; + desc.mipLevelCount = 1; + desc.sampleCount = 1; + + wgpu::Texture texture; + EXPECT_DEPRECATION_WARNING(texture = device.CreateTexture(&desc)); + + wgpu::TextureViewDescriptor viewDesc; + viewDesc.dimension = wgpu::TextureViewDimension::e2DArray; + viewDesc.arrayLayerCount = 2; + texture.CreateView(&viewDesc); + viewDesc.arrayLayerCount = 3; + ASSERT_DEVICE_ERROR(texture.CreateView(&viewDesc)); +} + +DAWN_INSTANTIATE_TEST(DeprecationTests, + D3D12Backend(), + MetalBackend(), + NullBackend(), + OpenGLBackend(), + VulkanBackend()); + +class TextureCopyViewArrayLayerDeprecationTests : public DeprecationTests { + protected: + wgpu::TextureCopyView MakeOldTextureCopyView() { + wgpu::TextureDescriptor desc; + desc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + desc.dimension = wgpu::TextureDimension::e2D; + desc.size = {1, 1, 2}; + desc.format = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::TextureCopyView copy; + copy.texture = device.CreateTexture(&desc); + copy.arrayLayer = 1; + copy.origin = {0, 0, 0}; + return copy; + } + + wgpu::TextureCopyView MakeNewTextureCopyView() { + wgpu::TextureCopyView copy = MakeOldTextureCopyView(); + copy.arrayLayer = 0; + copy.origin.z = 1; + return copy; + } + + wgpu::TextureCopyView MakeErrorTextureCopyView() { + wgpu::TextureCopyView copy = MakeOldTextureCopyView(); + copy.origin.z = 1; + return copy; + } + + wgpu::BufferCopyView MakeBufferCopyView() const { + wgpu::BufferDescriptor desc; + desc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + desc.size = 4; + + wgpu::BufferCopyView copy = {}; + copy.buffer = device.CreateBuffer(&desc); + copy.layout.bytesPerRow = kTextureBytesPerRowAlignment; + return copy; + } + + wgpu::Extent3D copySize = {1, 1, 1}; +}; + +// Test that using TextureCopyView::arrayLayer emits a warning. +TEST_P(TextureCopyViewArrayLayerDeprecationTests, DeprecationWarning) { + wgpu::TextureCopyView texOldCopy = MakeOldTextureCopyView(); + wgpu::TextureCopyView texNewCopy = MakeNewTextureCopyView(); + wgpu::BufferCopyView bufCopy = MakeBufferCopyView(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + EXPECT_DEPRECATION_WARNING(encoder.CopyBufferToTexture(&bufCopy, &texOldCopy, ©Size)); + EXPECT_DEPRECATION_WARNING(encoder.CopyTextureToTexture(&texNewCopy, &texOldCopy, ©Size)); + EXPECT_DEPRECATION_WARNING(encoder.CopyTextureToBuffer(&texOldCopy, &bufCopy, ©Size)); + EXPECT_DEPRECATION_WARNING(encoder.CopyTextureToTexture(&texOldCopy, &texNewCopy, ©Size)); + wgpu::CommandBuffer command = encoder.Finish(); + + queue.Submit(1, &command); +} + +// Test that using both TextureCopyView::arrayLayer and origin.z is an error. +TEST_P(TextureCopyViewArrayLayerDeprecationTests, BothArrayLayerAndOriginZIsError) { + wgpu::TextureCopyView texErrorCopy = MakeErrorTextureCopyView(); + wgpu::TextureCopyView texNewCopy = MakeNewTextureCopyView(); + wgpu::BufferCopyView bufCopy = MakeBufferCopyView(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufCopy, &texErrorCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); + + encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&texNewCopy, &texErrorCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); + + encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&texErrorCopy, &bufCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); + + encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&texErrorCopy, &texNewCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); +} + +// Test that using TextureCopyView::arrayLayer is correctly taken into account +TEST_P(TextureCopyViewArrayLayerDeprecationTests, StateTracking) { + wgpu::TextureCopyView texOOBCopy = MakeErrorTextureCopyView(); + texOOBCopy.arrayLayer = 2; // Oh no, it is OOB! + wgpu::TextureCopyView texNewCopy = MakeNewTextureCopyView(); + wgpu::BufferCopyView bufCopy = MakeBufferCopyView(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufCopy, &texOOBCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); + + encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&texNewCopy, &texOOBCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); + + encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&texOOBCopy, &bufCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); + + encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&texOOBCopy, &texNewCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); +} + +DAWN_INSTANTIATE_TEST(TextureCopyViewArrayLayerDeprecationTests, + D3D12Backend(), + MetalBackend(), + NullBackend(), + OpenGLBackend(), + VulkanBackend()); + +class BufferCopyViewDeprecationTests : public DeprecationTests { + protected: + wgpu::TextureCopyView MakeTextureCopyView() { + wgpu::TextureDescriptor desc = {}; + desc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + desc.dimension = wgpu::TextureDimension::e2D; + desc.size = {1, 1, 2}; + desc.format = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::TextureCopyView copy; + copy.texture = device.CreateTexture(&desc); + copy.arrayLayer = 0; + copy.origin = {0, 0, 1}; + return copy; + } + + wgpu::Extent3D copySize = {1, 1, 1}; +}; + +// Test that using BufferCopyView::{offset,bytesPerRow,rowsPerImage} emits a warning. +TEST_P(BufferCopyViewDeprecationTests, DeprecationWarning) { + wgpu::BufferDescriptor desc; + desc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + desc.size = 8; + wgpu::Buffer buffer = device.CreateBuffer(&desc); + + wgpu::TextureCopyView texCopy = MakeTextureCopyView(); + + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::BufferCopyView bufCopy = {}; + bufCopy.buffer = buffer; + bufCopy.offset = 4; + EXPECT_DEPRECATION_WARNING(encoder.CopyBufferToTexture(&bufCopy, &texCopy, ©Size)); + EXPECT_DEPRECATION_WARNING(encoder.CopyTextureToBuffer(&texCopy, &bufCopy, ©Size)); + // Since bytesPerRow is 0 + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::BufferCopyView bufCopy = {}; + bufCopy.buffer = buffer; + bufCopy.bytesPerRow = kTextureBytesPerRowAlignment; + EXPECT_DEPRECATION_WARNING(encoder.CopyBufferToTexture(&bufCopy, &texCopy, ©Size)); + EXPECT_DEPRECATION_WARNING(encoder.CopyTextureToBuffer(&texCopy, &bufCopy, ©Size)); + wgpu::CommandBuffer command = encoder.Finish(); + queue.Submit(1, &command); + } + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::BufferCopyView bufCopy = {}; + bufCopy.buffer = buffer; + bufCopy.rowsPerImage = 1; + EXPECT_DEPRECATION_WARNING(encoder.CopyBufferToTexture(&bufCopy, &texCopy, ©Size)); + EXPECT_DEPRECATION_WARNING(encoder.CopyTextureToBuffer(&texCopy, &bufCopy, ©Size)); + // Since bytesPerRow is 0 + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Test that using both any old field and any new field is an error +TEST_P(BufferCopyViewDeprecationTests, BothOldAndNew) { + wgpu::BufferDescriptor desc; + desc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + desc.size = 8; + wgpu::Buffer buffer = device.CreateBuffer(&desc); + + wgpu::TextureCopyView texCopy = MakeTextureCopyView(); + + auto testOne = [=](const wgpu::BufferCopyView& bufCopy) { + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufCopy, &texCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&texCopy, &bufCopy, ©Size); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + }; + + { + wgpu::BufferCopyView bufCopy = {}; + bufCopy.buffer = buffer; + bufCopy.layout.bytesPerRow = kTextureBytesPerRowAlignment; + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufCopy, &texCopy, ©Size); + encoder.CopyTextureToBuffer(&texCopy, &bufCopy, ©Size); + wgpu::CommandBuffer command = encoder.Finish(); + queue.Submit(1, &command); + } + + bufCopy.offset = 4; + testOne(bufCopy); + bufCopy.offset = 0; + bufCopy.bytesPerRow = kTextureBytesPerRowAlignment; + testOne(bufCopy); + bufCopy.bytesPerRow = 0; + bufCopy.rowsPerImage = 1; + testOne(bufCopy); + } +} + +DAWN_INSTANTIATE_TEST(BufferCopyViewDeprecationTests, + D3D12Backend(), + MetalBackend(), + NullBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DepthSamplingTests.cpp b/third_party/dawn/src/tests/end2end/DepthSamplingTests.cpp new file mode 100644 index 00000000000..29ec5e4b198 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/DepthSamplingTests.cpp @@ -0,0 +1,434 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "tests/DawnTest.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +namespace { + + constexpr wgpu::CompareFunction kCompareFunctions[] = { + wgpu::CompareFunction::Never, wgpu::CompareFunction::Less, + wgpu::CompareFunction::LessEqual, wgpu::CompareFunction::Greater, + wgpu::CompareFunction::GreaterEqual, wgpu::CompareFunction::Equal, + wgpu::CompareFunction::NotEqual, wgpu::CompareFunction::Always, + }; + + // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs. + constexpr float kCompareRefs[] = {-0.1, 0.4, 1.2}; + + // Test 0, below the ref, equal to, above the ref, and 1. + const std::vector kNormalizedTextureValues = {0.0, 0.3, 0.4, 0.5, 1.0}; + +} // anonymous namespace + +class DepthSamplingTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + + wgpu::BufferDescriptor uniformBufferDesc; + uniformBufferDesc.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst; + uniformBufferDesc.size = sizeof(float); + mUniformBuffer = device.CreateBuffer(&uniformBufferDesc); + + wgpu::BufferDescriptor textureUploadDesc; + textureUploadDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + textureUploadDesc.size = sizeof(float); + mTextureUploadBuffer = device.CreateBuffer(&textureUploadDesc); + + wgpu::TextureDescriptor inputTextureDesc; + inputTextureDesc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment; + inputTextureDesc.size = {1, 1, 1}; + inputTextureDesc.format = wgpu::TextureFormat::Depth32Float; + mInputTexture = device.CreateTexture(&inputTextureDesc); + + wgpu::TextureDescriptor outputTextureDesc; + outputTextureDesc.usage = + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + outputTextureDesc.size = {1, 1, 1}; + outputTextureDesc.format = wgpu::TextureFormat::R32Float; + mOutputTexture = device.CreateTexture(&outputTextureDesc); + + wgpu::BufferDescriptor outputBufferDesc; + outputBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc; + outputBufferDesc.size = sizeof(float); + mOutputBuffer = device.CreateBuffer(&outputBufferDesc); + } + + wgpu::RenderPipeline CreateSamplingRenderPipeline() { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + gl_PointSize = 1.0; + } + )"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0) uniform sampler samp; + layout(set = 0, binding = 1) uniform texture2D tex; + + layout(location = 0) out float samplerResult; + + void main() { + samplerResult = texture(sampler2D(tex, samp), vec2(0.5, 0.5)).r; + } + )"); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + pipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList; + pipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::R32Float; + + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + wgpu::ComputePipeline CreateSamplingComputePipeline() { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(set = 0, binding = 0) uniform sampler samp; + layout(set = 0, binding = 1) uniform texture2D tex; + layout(set = 0, binding = 2) writeonly buffer SamplerResult { + float samplerResult; + }; + + void main() { + samplerResult = texture(sampler2D(tex, samp), vec2(0.5, 0.5)).r; + } + )"); + + wgpu::ComputePipelineDescriptor pipelineDescriptor; + pipelineDescriptor.computeStage.module = csModule; + pipelineDescriptor.computeStage.entryPoint = "main"; + + return device.CreateComputePipeline(&pipelineDescriptor); + } + + wgpu::RenderPipeline CreateComparisonRenderPipeline() { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + gl_PointSize = 1.0; + } + )"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0) uniform samplerShadow samp; + layout(set = 0, binding = 1) uniform texture2D tex; + layout(set = 0, binding = 2) uniform Uniforms { + float compareRef; + }; + + layout(location = 0) out float samplerResult; + + void main() { + samplerResult = texture(sampler2DShadow(tex, samp), vec3(0.5, 0.5, compareRef)); + } + )"); + + // TODO(dawn:367): Cannot use GetBindGroupLayout for comparison samplers without shader + // reflection data. + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ComparisonSampler}, + {1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}, + {2, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); + pipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList; + pipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::R32Float; + + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + wgpu::ComputePipeline CreateComparisonComputePipeline() { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(set = 0, binding = 0) uniform samplerShadow samp; + layout(set = 0, binding = 1) uniform texture2D tex; + layout(set = 0, binding = 2) uniform Uniforms { + float compareRef; + }; + layout(set = 0, binding = 3) writeonly buffer SamplerResult { + float samplerResult; + }; + + void main() { + samplerResult = texture(sampler2DShadow(tex, samp), vec3(0.5, 0.5, compareRef)); + } + )"); + + // TODO(dawn:367): Cannot use GetBindGroupLayout without shader reflection data. + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ComparisonSampler}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture}, + {2, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer}, + {3, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + + wgpu::ComputePipelineDescriptor pipelineDescriptor; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); + pipelineDescriptor.computeStage.module = csModule; + pipelineDescriptor.computeStage.entryPoint = "main"; + + return device.CreateComputePipeline(&pipelineDescriptor); + } + + void UpdateInputTexture(wgpu::CommandEncoder commandEncoder, float textureValue) { + utils::ComboRenderPassDescriptor passDescriptor({}, mInputTexture.CreateView()); + passDescriptor.cDepthStencilAttachmentInfo.clearDepth = textureValue; + + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); + pass.EndPass(); + } + + void DoSamplingTest(wgpu::RenderPipeline pipeline, std::vector textureValues) { + wgpu::SamplerDescriptor samplerDesc; + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, sampler}, + {1, mInputTexture.CreateView()}, + }); + + for (float textureValue : textureValues) { + // Set the input depth texture to the provided texture value + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + UpdateInputTexture(commandEncoder, textureValue); + + // Render into the output texture + { + utils::ComboRenderPassDescriptor passDescriptor({mOutputTexture.CreateView()}); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_FLOAT_EQ(textureValue, mOutputTexture, 0, 0); + } + } + + void DoSamplingTest(wgpu::ComputePipeline pipeline, std::vector textureValues) { + wgpu::SamplerDescriptor samplerDesc; + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, mInputTexture.CreateView()}, {2, mOutputBuffer}}); + + for (float textureValue : textureValues) { + // Set the input depth texture to the provided texture value + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + UpdateInputTexture(commandEncoder, textureValue); + + // Sample into the output buffer + { + wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass(); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Dispatch(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_BUFFER_U32_EQ(*reinterpret_cast(&textureValue), mOutputBuffer, 0); + } + } + + static bool CompareFunctionPasses(float compareRef, + wgpu::CompareFunction compare, + float textureValue) { + switch (compare) { + case wgpu::CompareFunction::Never: + return false; + case wgpu::CompareFunction::Less: + return compareRef < textureValue; + case wgpu::CompareFunction::LessEqual: + return compareRef <= textureValue; + case wgpu::CompareFunction::Greater: + return compareRef > textureValue; + case wgpu::CompareFunction::GreaterEqual: + return compareRef >= textureValue; + case wgpu::CompareFunction::Equal: + return compareRef == textureValue; + case wgpu::CompareFunction::NotEqual: + return compareRef != textureValue; + case wgpu::CompareFunction::Always: + return true; + default: + return false; + } + } + + void DoCompareRefTest(wgpu::RenderPipeline pipeline, + float compareRef, + wgpu::CompareFunction compare, + std::vector textureValues) { + queue.WriteBuffer(mUniformBuffer, 0, &compareRef, sizeof(float)); + + wgpu::SamplerDescriptor samplerDesc; + samplerDesc.compare = compare; + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, sampler}, + {1, mInputTexture.CreateView()}, + {2, mUniformBuffer}, + }); + + for (float textureValue : textureValues) { + // Set the input depth texture to the provided texture value + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + UpdateInputTexture(commandEncoder, textureValue); + + // Render into the output texture + { + utils::ComboRenderPassDescriptor passDescriptor({mOutputTexture.CreateView()}); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&passDescriptor); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_FLOAT_EQ( + CompareFunctionPasses(compareRef, compare, textureValue) ? 1.f : 0.f, + mOutputTexture, 0, 0); + } + } + + void DoCompareRefTest(wgpu::ComputePipeline pipeline, + float compareRef, + wgpu::CompareFunction compare, + std::vector textureValues) { + queue.WriteBuffer(mUniformBuffer, 0, &compareRef, sizeof(float)); + + wgpu::SamplerDescriptor samplerDesc; + samplerDesc.compare = compare; + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, sampler}, + {1, mInputTexture.CreateView()}, + {2, mUniformBuffer}, + {3, mOutputBuffer}}); + + for (float textureValue : textureValues) { + // Set the input depth texture to the provided texture value + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + UpdateInputTexture(commandEncoder, textureValue); + + // Sample into the output buffer + { + wgpu::ComputePassEncoder pass = commandEncoder.BeginComputePass(); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Dispatch(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + float float0 = 0.f; + float float1 = 1.f; + float* expected = + CompareFunctionPasses(compareRef, compare, textureValue) ? &float1 : &float0; + + EXPECT_BUFFER_U32_EQ(*reinterpret_cast(expected), mOutputBuffer, 0); + } + } + + private: + wgpu::Buffer mUniformBuffer; + wgpu::Buffer mTextureUploadBuffer; + wgpu::Texture mInputTexture; + wgpu::Texture mOutputTexture; + wgpu::Buffer mOutputBuffer; +}; + +// Test that sampling a depth texture with a render pipeline works +TEST_P(DepthSamplingTest, SampleRender) { + // Test 0, between [0, 1], and 1. + DoSamplingTest(CreateSamplingRenderPipeline(), kNormalizedTextureValues); +} + +// Test that sampling a depth texture with a compute pipeline works +TEST_P(DepthSamplingTest, SampleCompute) { + // Test 0, between [0, 1], and 1. + DoSamplingTest(CreateSamplingComputePipeline(), kNormalizedTextureValues); +} + +// Test that sampling in a render pipeline with all of the compare functions works. +TEST_P(DepthSamplingTest, CompareFunctionsRender) { + // Initialization via renderPass loadOp doesn't work on Mac Intel. + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + + wgpu::RenderPipeline pipeline = CreateComparisonRenderPipeline(); + + // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs. + for (float compareRef : kCompareRefs) { + // Test 0, below the ref, equal to, above the ref, and 1. + for (wgpu::CompareFunction f : kCompareFunctions) { + DoCompareRefTest(pipeline, compareRef, f, kNormalizedTextureValues); + } + } +} + +// Test that sampling in a render pipeline with all of the compare functions works. +TEST_P(DepthSamplingTest, CompareFunctionsCompute) { + // Initialization via renderPass loadOp doesn't work on Mac Intel. + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + + wgpu::ComputePipeline pipeline = CreateComparisonComputePipeline(); + + // Test a "normal" ref value between 0 and 1; as well as negative and > 1 refs. + for (float compareRef : kCompareRefs) { + // Test 0, below the ref, equal to, above the ref, and 1. + for (wgpu::CompareFunction f : kCompareFunctions) { + DoCompareRefTest(pipeline, compareRef, f, kNormalizedTextureValues); + } + } +} + +DAWN_INSTANTIATE_TEST(DepthSamplingTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DepthStencilStateTests.cpp b/third_party/dawn/src/tests/end2end/DepthStencilStateTests.cpp index e700ceab4a6..4788094e59b 100644 --- a/third_party/dawn/src/tests/end2end/DepthStencilStateTests.cpp +++ b/third_party/dawn/src/tests/end2end/DepthStencilStateTests.cpp @@ -16,44 +16,43 @@ #include "common/Assert.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr static unsigned int kRTSize = 64; class DepthStencilStateTest : public DawnTest { - protected: - void SetUp() override { - DawnTest::SetUp(); - - dawn::TextureDescriptor renderTargetDescriptor; - renderTargetDescriptor.dimension = dawn::TextureDimension::e2D; - renderTargetDescriptor.size.width = kRTSize; - renderTargetDescriptor.size.height = kRTSize; - renderTargetDescriptor.size.depth = 1; - renderTargetDescriptor.arrayLayerCount = 1; - renderTargetDescriptor.sampleCount = 1; - renderTargetDescriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - renderTargetDescriptor.mipLevelCount = 1; - renderTargetDescriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; - renderTarget = device.CreateTexture(&renderTargetDescriptor); - - renderTargetView = renderTarget.CreateDefaultView(); - - dawn::TextureDescriptor depthDescriptor; - depthDescriptor.dimension = dawn::TextureDimension::e2D; - depthDescriptor.size.width = kRTSize; - depthDescriptor.size.height = kRTSize; - depthDescriptor.size.depth = 1; - depthDescriptor.arrayLayerCount = 1; - depthDescriptor.sampleCount = 1; - depthDescriptor.format = dawn::TextureFormat::D32FloatS8Uint; - depthDescriptor.mipLevelCount = 1; - depthDescriptor.usage = dawn::TextureUsageBit::OutputAttachment; - depthTexture = device.CreateTexture(&depthDescriptor); - - depthTextureView = depthTexture.CreateDefaultView(); - - vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + protected: + void SetUp() override { + DawnTest::SetUp(); + + wgpu::TextureDescriptor renderTargetDescriptor; + renderTargetDescriptor.dimension = wgpu::TextureDimension::e2D; + renderTargetDescriptor.size.width = kRTSize; + renderTargetDescriptor.size.height = kRTSize; + renderTargetDescriptor.size.depth = 1; + renderTargetDescriptor.sampleCount = 1; + renderTargetDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + renderTargetDescriptor.mipLevelCount = 1; + renderTargetDescriptor.usage = + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + renderTarget = device.CreateTexture(&renderTargetDescriptor); + + renderTargetView = renderTarget.CreateView(); + + wgpu::TextureDescriptor depthDescriptor; + depthDescriptor.dimension = wgpu::TextureDimension::e2D; + depthDescriptor.size.width = kRTSize; + depthDescriptor.size.height = kRTSize; + depthDescriptor.size.depth = 1; + depthDescriptor.sampleCount = 1; + depthDescriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; + depthDescriptor.mipLevelCount = 1; + depthDescriptor.usage = wgpu::TextureUsage::OutputAttachment; + depthTexture = device.CreateTexture(&depthDescriptor); + + depthTextureView = depthTexture.CreateView(); + + vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(set = 0, binding = 0) uniform myBlock { vec3 color; @@ -68,7 +67,7 @@ class DepthStencilStateTest : public DawnTest { } )"); - fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + fsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform myBlock { vec3 color; @@ -79,277 +78,296 @@ class DepthStencilStateTest : public DawnTest { fragColor = vec4(myUbo.color, 1.f); } )"); + } - bindGroupLayout = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Vertex | dawn::ShaderStageBit::Fragment, - dawn::BindingType::UniformBuffer}, - }); - - pipelineLayout = utils::MakeBasicPipelineLayout(device, &bindGroupLayout); - } - - struct TestSpec { - const dawn::DepthStencilStateDescriptor& depthStencilState; - RGBA8 color; - float depth; - uint32_t stencil; - }; + struct TestSpec { + const wgpu::DepthStencilStateDescriptor& depthStencilState; + RGBA8 color; + float depth; + uint32_t stencil; + }; - // Check whether a depth comparison function works as expected - // The less, equal, greater booleans denote wether the respective triangle should be visible based on the comparison function - void CheckDepthCompareFunction(dawn::CompareFunction compareFunction, bool less, bool equal, bool greater) { - dawn::StencilStateFaceDescriptor stencilFace; - stencilFace.compare = dawn::CompareFunction::Always; - stencilFace.failOp = dawn::StencilOperation::Keep; - stencilFace.depthFailOp = dawn::StencilOperation::Keep; - stencilFace.passOp = dawn::StencilOperation::Keep; - - dawn::DepthStencilStateDescriptor baseState; - baseState.depthWriteEnabled = true; - baseState.depthCompare = dawn::CompareFunction::Always; - baseState.stencilBack = stencilFace; - baseState.stencilFront = stencilFace; - baseState.stencilReadMask = 0xff; - baseState.stencilWriteMask = 0xff; - - dawn::DepthStencilStateDescriptor state; - state.depthWriteEnabled = true; - state.depthCompare = compareFunction; - state.stencilBack = stencilFace; - state.stencilFront = stencilFace; - state.stencilReadMask = 0xff; - state.stencilWriteMask = 0xff; - - RGBA8 baseColor = RGBA8(255, 255, 255, 255); - RGBA8 lessColor = RGBA8(255, 0, 0, 255); - RGBA8 equalColor = RGBA8(0, 255, 0, 255); - RGBA8 greaterColor = RGBA8(0, 0, 255, 255); - - // Base triangle at depth 0.5, depth always, depth write enabled - TestSpec base = { baseState, baseColor, 0.5f, 0u }; - - // Draw the base triangle, then a triangle in stencilFront of the base triangle with the - // given depth comparison function - DoTest({ base, { state, lessColor, 0.f, 0u } }, less ? lessColor : baseColor); - - // Draw the base triangle, then a triangle in at the same depth as the base triangle with the given depth comparison function - DoTest({ base, { state, equalColor, 0.5f, 0u } }, equal ? equalColor : baseColor); - - // Draw the base triangle, then a triangle behind the base triangle with the given depth comparison function - DoTest({ base, { state, greaterColor, 1.0f, 0u } }, greater ? greaterColor : baseColor); - } + // Check whether a depth comparison function works as expected + // The less, equal, greater booleans denote wether the respective triangle should be visible + // based on the comparison function + void CheckDepthCompareFunction(wgpu::CompareFunction compareFunction, + bool less, + bool equal, + bool greater) { + wgpu::StencilStateFaceDescriptor stencilFace; + stencilFace.compare = wgpu::CompareFunction::Always; + stencilFace.failOp = wgpu::StencilOperation::Keep; + stencilFace.depthFailOp = wgpu::StencilOperation::Keep; + stencilFace.passOp = wgpu::StencilOperation::Keep; + + wgpu::DepthStencilStateDescriptor baseState; + baseState.depthWriteEnabled = true; + baseState.depthCompare = wgpu::CompareFunction::Always; + baseState.stencilBack = stencilFace; + baseState.stencilFront = stencilFace; + baseState.stencilReadMask = 0xff; + baseState.stencilWriteMask = 0xff; + + wgpu::DepthStencilStateDescriptor state; + state.depthWriteEnabled = true; + state.depthCompare = compareFunction; + state.stencilBack = stencilFace; + state.stencilFront = stencilFace; + state.stencilReadMask = 0xff; + state.stencilWriteMask = 0xff; + + RGBA8 baseColor = RGBA8(255, 255, 255, 255); + RGBA8 lessColor = RGBA8(255, 0, 0, 255); + RGBA8 equalColor = RGBA8(0, 255, 0, 255); + RGBA8 greaterColor = RGBA8(0, 0, 255, 255); + + // Base triangle at depth 0.5, depth always, depth write enabled + TestSpec base = {baseState, baseColor, 0.5f, 0u}; + + // Draw the base triangle, then a triangle in stencilFront of the base triangle with the + // given depth comparison function + DoTest({base, {state, lessColor, 0.f, 0u}}, less ? lessColor : baseColor); + + // Draw the base triangle, then a triangle in at the same depth as the base triangle with + // the given depth comparison function + DoTest({base, {state, equalColor, 0.5f, 0u}}, equal ? equalColor : baseColor); + + // Draw the base triangle, then a triangle behind the base triangle with the given depth + // comparison function + DoTest({base, {state, greaterColor, 1.0f, 0u}}, greater ? greaterColor : baseColor); + } - // Check whether a stencil comparison function works as expected - // The less, equal, greater booleans denote wether the respective triangle should be visible based on the comparison function - void CheckStencilCompareFunction(dawn::CompareFunction compareFunction, bool less, bool equal, bool greater) { - dawn::StencilStateFaceDescriptor baseStencilFaceDescriptor; - baseStencilFaceDescriptor.compare = dawn::CompareFunction::Always; - baseStencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.passOp = dawn::StencilOperation::Replace; - dawn::DepthStencilStateDescriptor baseState; - baseState.depthWriteEnabled = false; - baseState.depthCompare = dawn::CompareFunction::Always; - baseState.stencilBack = baseStencilFaceDescriptor; - baseState.stencilFront = baseStencilFaceDescriptor; - baseState.stencilReadMask = 0xff; - baseState.stencilWriteMask = 0xff; - - dawn::StencilStateFaceDescriptor stencilFaceDescriptor; - stencilFaceDescriptor.compare = compareFunction; - stencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.passOp = dawn::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor state; - state.depthWriteEnabled = false; - state.depthCompare = dawn::CompareFunction::Always; - state.stencilBack = stencilFaceDescriptor; - state.stencilFront = stencilFaceDescriptor; - state.stencilReadMask = 0xff; - state.stencilWriteMask = 0xff; - - RGBA8 baseColor = RGBA8(255, 255, 255, 255); - RGBA8 lessColor = RGBA8(255, 0, 0, 255); - RGBA8 equalColor = RGBA8(0, 255, 0, 255); - RGBA8 greaterColor = RGBA8(0, 0, 255, 255); - - // Base triangle with stencil reference 1 - TestSpec base = { baseState, baseColor, 0.0f, 1u }; - - // Draw the base triangle, then a triangle with stencil reference 0 with the given stencil comparison function - DoTest({ base, { state, lessColor, 0.f, 0u } }, less ? lessColor : baseColor); - - // Draw the base triangle, then a triangle with stencil reference 1 with the given stencil comparison function - DoTest({ base, { state, equalColor, 0.f, 1u } }, equal ? equalColor : baseColor); - - // Draw the base triangle, then a triangle with stencil reference 2 with the given stencil comparison function - DoTest({ base, { state, greaterColor, 0.f, 2u } }, greater ? greaterColor : baseColor); - } + // Check whether a stencil comparison function works as expected + // The less, equal, greater booleans denote wether the respective triangle should be visible + // based on the comparison function + void CheckStencilCompareFunction(wgpu::CompareFunction compareFunction, + bool less, + bool equal, + bool greater) { + wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; + baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; + baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; + wgpu::DepthStencilStateDescriptor baseState; + baseState.depthWriteEnabled = false; + baseState.depthCompare = wgpu::CompareFunction::Always; + baseState.stencilBack = baseStencilFaceDescriptor; + baseState.stencilFront = baseStencilFaceDescriptor; + baseState.stencilReadMask = 0xff; + baseState.stencilWriteMask = 0xff; + + wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; + stencilFaceDescriptor.compare = compareFunction; + stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; + wgpu::DepthStencilStateDescriptor state; + state.depthWriteEnabled = false; + state.depthCompare = wgpu::CompareFunction::Always; + state.stencilBack = stencilFaceDescriptor; + state.stencilFront = stencilFaceDescriptor; + state.stencilReadMask = 0xff; + state.stencilWriteMask = 0xff; + + RGBA8 baseColor = RGBA8(255, 255, 255, 255); + RGBA8 lessColor = RGBA8(255, 0, 0, 255); + RGBA8 equalColor = RGBA8(0, 255, 0, 255); + RGBA8 greaterColor = RGBA8(0, 0, 255, 255); + + // Base triangle with stencil reference 1 + TestSpec base = {baseState, baseColor, 0.0f, 1u}; + + // Draw the base triangle, then a triangle with stencil reference 0 with the given stencil + // comparison function + DoTest({base, {state, lessColor, 0.f, 0u}}, less ? lessColor : baseColor); + + // Draw the base triangle, then a triangle with stencil reference 1 with the given stencil + // comparison function + DoTest({base, {state, equalColor, 0.f, 1u}}, equal ? equalColor : baseColor); + + // Draw the base triangle, then a triangle with stencil reference 2 with the given stencil + // comparison function + DoTest({base, {state, greaterColor, 0.f, 2u}}, greater ? greaterColor : baseColor); + } - // Given the provided `initialStencil` and `reference`, check that applying the `stencilOperation` produces the `expectedStencil` - void CheckStencilOperation(dawn::StencilOperation stencilOperation, uint32_t initialStencil, uint32_t reference, uint32_t expectedStencil) { - dawn::StencilStateFaceDescriptor baseStencilFaceDescriptor; - baseStencilFaceDescriptor.compare = dawn::CompareFunction::Always; - baseStencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.passOp = dawn::StencilOperation::Replace; - dawn::DepthStencilStateDescriptor baseState; - baseState.depthWriteEnabled = false; - baseState.depthCompare = dawn::CompareFunction::Always; - baseState.stencilBack = baseStencilFaceDescriptor; - baseState.stencilFront = baseStencilFaceDescriptor; - baseState.stencilReadMask = 0xff; - baseState.stencilWriteMask = 0xff; - - dawn::StencilStateFaceDescriptor stencilFaceDescriptor; - stencilFaceDescriptor.compare = dawn::CompareFunction::Always; - stencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.passOp = stencilOperation; - dawn::DepthStencilStateDescriptor state; - state.depthWriteEnabled = false; - state.depthCompare = dawn::CompareFunction::Always; - state.stencilBack = stencilFaceDescriptor; - state.stencilFront = stencilFaceDescriptor; - state.stencilReadMask = 0xff; - state.stencilWriteMask = 0xff; - - CheckStencil({ + // Given the provided `initialStencil` and `reference`, check that applying the + // `stencilOperation` produces the `expectedStencil` + void CheckStencilOperation(wgpu::StencilOperation stencilOperation, + uint32_t initialStencil, + uint32_t reference, + uint32_t expectedStencil) { + wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; + baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; + baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; + wgpu::DepthStencilStateDescriptor baseState; + baseState.depthWriteEnabled = false; + baseState.depthCompare = wgpu::CompareFunction::Always; + baseState.stencilBack = baseStencilFaceDescriptor; + baseState.stencilFront = baseStencilFaceDescriptor; + baseState.stencilReadMask = 0xff; + baseState.stencilWriteMask = 0xff; + + wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; + stencilFaceDescriptor.compare = wgpu::CompareFunction::Always; + stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.passOp = stencilOperation; + wgpu::DepthStencilStateDescriptor state; + state.depthWriteEnabled = false; + state.depthCompare = wgpu::CompareFunction::Always; + state.stencilBack = stencilFaceDescriptor; + state.stencilFront = stencilFaceDescriptor; + state.stencilReadMask = 0xff; + state.stencilWriteMask = 0xff; + + CheckStencil( + { // Wipe the stencil buffer with the initialStencil value - { baseState, RGBA8(255, 255, 255, 255), 0.f, initialStencil }, + {baseState, RGBA8(255, 255, 255, 255), 0.f, initialStencil}, // Draw a triangle with the provided stencil operation and reference - { state, RGBA8(255, 0, 0, 255), 0.f, reference }, - }, expectedStencil); - } - - // Draw a list of test specs, and check if the stencil value is equal to the expected value - void CheckStencil(std::vector testParams, uint32_t expectedStencil) { - dawn::StencilStateFaceDescriptor stencilFaceDescriptor; - stencilFaceDescriptor.compare = dawn::CompareFunction::Equal; - stencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.passOp = dawn::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor state; - state.depthWriteEnabled = false; - state.depthCompare = dawn::CompareFunction::Always; - state.stencilBack = stencilFaceDescriptor; - state.stencilFront = stencilFaceDescriptor; - state.stencilReadMask = 0xff; - state.stencilWriteMask = 0xff; - - testParams.push_back({ state, RGBA8(0, 255, 0, 255), 0, expectedStencil }); - DoTest(testParams, RGBA8(0, 255, 0, 255)); - } - - // Each test param represents a pair of triangles with a color, depth, stencil value, and depthStencil state, one frontfacing, one backfacing - // Draw the triangles in order and check the expected colors for the frontfaces and backfaces - void DoTest(const std::vector &testParams, const RGBA8& expectedFront, const RGBA8& expectedBack) { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - - struct TriangleData { - float color[3]; - float depth; - }; - - utils::ComboRenderPassDescriptor renderPass({renderTargetView}, depthTextureView); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); - - for (size_t i = 0; i < testParams.size(); ++i) { - const TestSpec& test = testParams[i]; + {state, RGBA8(255, 0, 0, 255), 0.f, reference}, + }, + expectedStencil); + } - TriangleData data = { - { static_cast(test.color.r) / 255.f, static_cast(test.color.g) / 255.f, static_cast(test.color.b) / 255.f }, - test.depth, - }; - // Upload a buffer for each triangle's depth and color data - dawn::Buffer buffer = utils::CreateBufferFromData(device, &data, sizeof(TriangleData), dawn::BufferUsageBit::Uniform); + // Draw a list of test specs, and check if the stencil value is equal to the expected value + void CheckStencil(std::vector testParams, uint32_t expectedStencil) { + wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; + stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal; + stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; + wgpu::DepthStencilStateDescriptor state; + state.depthWriteEnabled = false; + state.depthCompare = wgpu::CompareFunction::Always; + state.stencilBack = stencilFaceDescriptor; + state.stencilFront = stencilFaceDescriptor; + state.stencilReadMask = 0xff; + state.stencilWriteMask = 0xff; + + testParams.push_back({state, RGBA8(0, 255, 0, 255), 0, expectedStencil}); + DoTest(testParams, RGBA8(0, 255, 0, 255)); + } - // Create a bind group for the data - dawn::BindGroup bindGroup = utils::MakeBindGroup(device, bindGroupLayout, {{0, buffer, 0, sizeof(TriangleData)}}); + // Each test param represents a pair of triangles with a color, depth, stencil value, and + // depthStencil state, one frontfacing, one backfacing Draw the triangles in order and check the + // expected colors for the frontfaces and backfaces + void DoTest(const std::vector& testParams, + const RGBA8& expectedFront, + const RGBA8& expectedBack) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); - // Create a pipeline for the triangles with the test spec's depth stencil state + struct TriangleData { + float color[3]; + float depth; + }; - utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.layout = pipelineLayout; - descriptor.cVertexStage.module = vsModule; - descriptor.cFragmentStage.module = fsModule; - descriptor.cDepthStencilState = test.depthStencilState; - descriptor.cDepthStencilState.format = dawn::TextureFormat::D32FloatS8Uint; - descriptor.depthStencilState = &descriptor.cDepthStencilState; + utils::ComboRenderPassDescriptor renderPass({renderTargetView}, depthTextureView); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + for (size_t i = 0; i < testParams.size(); ++i) { + const TestSpec& test = testParams[i]; - pass.SetPipeline(pipeline); - pass.SetStencilReference(test.stencil); // Set the stencil reference - pass.SetBindGroup(0, bindGroup, 0, nullptr); // Set the bind group which contains color and depth data - pass.Draw(6, 1, 0, 0); - } - pass.EndPass(); + TriangleData data = { + {static_cast(test.color.r) / 255.f, static_cast(test.color.g) / 255.f, + static_cast(test.color.b) / 255.f}, + test.depth, + }; + // Upload a buffer for each triangle's depth and color data + wgpu::Buffer buffer = utils::CreateBufferFromData(device, &data, sizeof(TriangleData), + wgpu::BufferUsage::Uniform); + + // Create a pipeline for the triangles with the test spec's depth stencil state + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cDepthStencilState = test.depthStencilState; + descriptor.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; + descriptor.depthStencilState = &descriptor.cDepthStencilState; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + + // Create a bind group for the data + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), {{0, buffer, 0, sizeof(TriangleData)}}); + + pass.SetPipeline(pipeline); + pass.SetStencilReference(test.stencil); // Set the stencil reference + pass.SetBindGroup(0, + bindGroup); // Set the bind group which contains color and depth data + pass.Draw(6); + } + pass.EndPass(); - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(expectedFront, renderTarget, kRTSize / 4, kRTSize / 2) << "Front face check failed"; - EXPECT_PIXEL_RGBA8_EQ(expectedBack, renderTarget, 3 * kRTSize / 4, kRTSize / 2) << "Back face check failed"; - } + EXPECT_PIXEL_RGBA8_EQ(expectedFront, renderTarget, kRTSize / 4, kRTSize / 2) + << "Front face check failed"; + EXPECT_PIXEL_RGBA8_EQ(expectedBack, renderTarget, 3 * kRTSize / 4, kRTSize / 2) + << "Back face check failed"; + } - void DoTest(const std::vector &testParams, const RGBA8& expected) { - DoTest(testParams, expected, expected); - } + void DoTest(const std::vector& testParams, const RGBA8& expected) { + DoTest(testParams, expected, expected); + } - dawn::Texture renderTarget; - dawn::Texture depthTexture; - dawn::TextureView renderTargetView; - dawn::TextureView depthTextureView; - dawn::ShaderModule vsModule; - dawn::ShaderModule fsModule; - dawn::BindGroupLayout bindGroupLayout; - dawn::PipelineLayout pipelineLayout; + wgpu::Texture renderTarget; + wgpu::Texture depthTexture; + wgpu::TextureView renderTargetView; + wgpu::TextureView depthTextureView; + wgpu::ShaderModule vsModule; + wgpu::ShaderModule fsModule; }; // Test compilation and usage of the fixture TEST_P(DepthStencilStateTest, Basic) { - dawn::StencilStateFaceDescriptor stencilFace; - stencilFace.compare = dawn::CompareFunction::Always; - stencilFace.failOp = dawn::StencilOperation::Keep; - stencilFace.depthFailOp = dawn::StencilOperation::Keep; - stencilFace.passOp = dawn::StencilOperation::Keep; + wgpu::StencilStateFaceDescriptor stencilFace; + stencilFace.compare = wgpu::CompareFunction::Always; + stencilFace.failOp = wgpu::StencilOperation::Keep; + stencilFace.depthFailOp = wgpu::StencilOperation::Keep; + stencilFace.passOp = wgpu::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor state; + wgpu::DepthStencilStateDescriptor state; state.depthWriteEnabled = false; - state.depthCompare = dawn::CompareFunction::Always; + state.depthCompare = wgpu::CompareFunction::Always; state.stencilBack = stencilFace; state.stencilFront = stencilFace; state.stencilReadMask = 0xff; state.stencilWriteMask = 0xff; - DoTest({ - { state, RGBA8(0, 255, 0, 255), 0.5f, 0u }, - }, RGBA8(0, 255, 0, 255)); + DoTest( + { + {state, RGBA8(0, 255, 0, 255), 0.5f, 0u}, + }, + RGBA8(0, 255, 0, 255)); } // Test defaults: depth and stencil tests disabled TEST_P(DepthStencilStateTest, DepthStencilDisabled) { - dawn::StencilStateFaceDescriptor stencilFace; - stencilFace.compare = dawn::CompareFunction::Always; - stencilFace.failOp = dawn::StencilOperation::Keep; - stencilFace.depthFailOp = dawn::StencilOperation::Keep; - stencilFace.passOp = dawn::StencilOperation::Keep; + wgpu::StencilStateFaceDescriptor stencilFace; + stencilFace.compare = wgpu::CompareFunction::Always; + stencilFace.failOp = wgpu::StencilOperation::Keep; + stencilFace.depthFailOp = wgpu::StencilOperation::Keep; + stencilFace.passOp = wgpu::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor state; + wgpu::DepthStencilStateDescriptor state; state.depthWriteEnabled = false; - state.depthCompare = dawn::CompareFunction::Always; + state.depthCompare = wgpu::CompareFunction::Always; state.stencilBack = stencilFace; state.stencilFront = stencilFace; state.stencilReadMask = 0xff; state.stencilWriteMask = 0xff; TestSpec specs[3] = { - { state, RGBA8(255, 0, 0, 255), 0.0f, 0u }, - { state, RGBA8(0, 255, 0, 255), 0.5f, 0u }, - { state, RGBA8(0, 0, 255, 255), 1.0f, 0u }, + {state, RGBA8(255, 0, 0, 255), 0.0f, 0u}, + {state, RGBA8(0, 255, 0, 255), 0.5f, 0u}, + {state, RGBA8(0, 0, 255, 255), 1.0f, 0u}, }; // Test that for all combinations, the last triangle drawn is the one visible @@ -357,176 +375,182 @@ TEST_P(DepthStencilStateTest, DepthStencilDisabled) { for (uint32_t last = 0; last < 3; ++last) { uint32_t i = (last + 1) % 3; uint32_t j = (last + 2) % 3; - DoTest({ specs[i], specs[j], specs[last] }, specs[last].color); - DoTest({ specs[j], specs[i], specs[last] }, specs[last].color); + DoTest({specs[i], specs[j], specs[last]}, specs[last].color); + DoTest({specs[j], specs[i], specs[last]}, specs[last].color); } } // The following tests check that each depth comparison function works TEST_P(DepthStencilStateTest, DepthAlways) { - CheckDepthCompareFunction(dawn::CompareFunction::Always , true, true, true); + CheckDepthCompareFunction(wgpu::CompareFunction::Always, true, true, true); } TEST_P(DepthStencilStateTest, DepthEqual) { - CheckDepthCompareFunction(dawn::CompareFunction::Equal, false, true, false); + CheckDepthCompareFunction(wgpu::CompareFunction::Equal, false, true, false); } TEST_P(DepthStencilStateTest, DepthGreater) { - CheckDepthCompareFunction(dawn::CompareFunction::Greater, false, false, true); + CheckDepthCompareFunction(wgpu::CompareFunction::Greater, false, false, true); } TEST_P(DepthStencilStateTest, DepthGreaterEqual) { - CheckDepthCompareFunction(dawn::CompareFunction::GreaterEqual, false, true, true); + CheckDepthCompareFunction(wgpu::CompareFunction::GreaterEqual, false, true, true); } TEST_P(DepthStencilStateTest, DepthLess) { - CheckDepthCompareFunction(dawn::CompareFunction::Less, true, false, false); + CheckDepthCompareFunction(wgpu::CompareFunction::Less, true, false, false); } TEST_P(DepthStencilStateTest, DepthLessEqual) { - CheckDepthCompareFunction(dawn::CompareFunction::LessEqual, true, true, false); + CheckDepthCompareFunction(wgpu::CompareFunction::LessEqual, true, true, false); } TEST_P(DepthStencilStateTest, DepthNever) { - CheckDepthCompareFunction(dawn::CompareFunction::Never, false, false, false); + CheckDepthCompareFunction(wgpu::CompareFunction::Never, false, false, false); } TEST_P(DepthStencilStateTest, DepthNotEqual) { - CheckDepthCompareFunction(dawn::CompareFunction::NotEqual, true, false, true); + CheckDepthCompareFunction(wgpu::CompareFunction::NotEqual, true, false, true); } // Test that disabling depth writes works and leaves the depth buffer unchanged TEST_P(DepthStencilStateTest, DepthWriteDisabled) { - dawn::StencilStateFaceDescriptor stencilFace; - stencilFace.compare = dawn::CompareFunction::Always; - stencilFace.failOp = dawn::StencilOperation::Keep; - stencilFace.depthFailOp = dawn::StencilOperation::Keep; - stencilFace.passOp = dawn::StencilOperation::Keep; + wgpu::StencilStateFaceDescriptor stencilFace; + stencilFace.compare = wgpu::CompareFunction::Always; + stencilFace.failOp = wgpu::StencilOperation::Keep; + stencilFace.depthFailOp = wgpu::StencilOperation::Keep; + stencilFace.passOp = wgpu::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor baseState; + wgpu::DepthStencilStateDescriptor baseState; baseState.depthWriteEnabled = true; - baseState.depthCompare = dawn::CompareFunction::Always; + baseState.depthCompare = wgpu::CompareFunction::Always; baseState.stencilBack = stencilFace; baseState.stencilFront = stencilFace; baseState.stencilReadMask = 0xff; baseState.stencilWriteMask = 0xff; - dawn::DepthStencilStateDescriptor noDepthWrite; + wgpu::DepthStencilStateDescriptor noDepthWrite; noDepthWrite.depthWriteEnabled = false; - noDepthWrite.depthCompare = dawn::CompareFunction::Always; + noDepthWrite.depthCompare = wgpu::CompareFunction::Always; noDepthWrite.stencilBack = stencilFace; noDepthWrite.stencilFront = stencilFace; noDepthWrite.stencilReadMask = 0xff; noDepthWrite.stencilWriteMask = 0xff; - dawn::DepthStencilStateDescriptor checkState; + wgpu::DepthStencilStateDescriptor checkState; checkState.depthWriteEnabled = false; - checkState.depthCompare = dawn::CompareFunction::Equal; + checkState.depthCompare = wgpu::CompareFunction::Equal; checkState.stencilBack = stencilFace; checkState.stencilFront = stencilFace; checkState.stencilReadMask = 0xff; checkState.stencilWriteMask = 0xff; - DoTest({ - { baseState, RGBA8(255, 255, 255, 255), 1.f, 0u }, // Draw a base triangle with depth enabled - { noDepthWrite, RGBA8(0, 0, 0, 255), 0.f, 0u }, // Draw a second triangle without depth enabled - { checkState, RGBA8(0, 255, 0, 255), 1.f, 0u }, // Draw a third triangle which should occlude the second even though it is behind it - }, RGBA8(0, 255, 0, 255)); + DoTest( + { + {baseState, RGBA8(255, 255, 255, 255), 1.f, + 0u}, // Draw a base triangle with depth enabled + {noDepthWrite, RGBA8(0, 0, 0, 255), 0.f, + 0u}, // Draw a second triangle without depth enabled + {checkState, RGBA8(0, 255, 0, 255), 1.f, + 0u}, // Draw a third triangle which should occlude the second even though it is behind + // it + }, + RGBA8(0, 255, 0, 255)); } // The following tests check that each stencil comparison function works TEST_P(DepthStencilStateTest, StencilAlways) { - CheckStencilCompareFunction(dawn::CompareFunction::Always, true, true, true); + CheckStencilCompareFunction(wgpu::CompareFunction::Always, true, true, true); } TEST_P(DepthStencilStateTest, StencilEqual) { - CheckStencilCompareFunction(dawn::CompareFunction::Equal, false, true, false); + CheckStencilCompareFunction(wgpu::CompareFunction::Equal, false, true, false); } TEST_P(DepthStencilStateTest, StencilGreater) { - CheckStencilCompareFunction(dawn::CompareFunction::Greater, false, false, true); + CheckStencilCompareFunction(wgpu::CompareFunction::Greater, false, false, true); } TEST_P(DepthStencilStateTest, StencilGreaterEqual) { - CheckStencilCompareFunction(dawn::CompareFunction::GreaterEqual, false, true, true); + CheckStencilCompareFunction(wgpu::CompareFunction::GreaterEqual, false, true, true); } TEST_P(DepthStencilStateTest, StencilLess) { - CheckStencilCompareFunction(dawn::CompareFunction::Less, true, false, false); + CheckStencilCompareFunction(wgpu::CompareFunction::Less, true, false, false); } TEST_P(DepthStencilStateTest, StencilLessEqual) { - CheckStencilCompareFunction(dawn::CompareFunction::LessEqual, true, true, false); + CheckStencilCompareFunction(wgpu::CompareFunction::LessEqual, true, true, false); } TEST_P(DepthStencilStateTest, StencilNever) { - CheckStencilCompareFunction(dawn::CompareFunction::Never, false, false, false); + CheckStencilCompareFunction(wgpu::CompareFunction::Never, false, false, false); } TEST_P(DepthStencilStateTest, StencilNotEqual) { - CheckStencilCompareFunction(dawn::CompareFunction::NotEqual, true, false, true); + CheckStencilCompareFunction(wgpu::CompareFunction::NotEqual, true, false, true); } // The following tests check that each stencil operation works TEST_P(DepthStencilStateTest, StencilKeep) { - CheckStencilOperation(dawn::StencilOperation::Keep, 1, 3, 1); + CheckStencilOperation(wgpu::StencilOperation::Keep, 1, 3, 1); } TEST_P(DepthStencilStateTest, StencilZero) { - CheckStencilOperation(dawn::StencilOperation::Zero, 1, 3, 0); + CheckStencilOperation(wgpu::StencilOperation::Zero, 1, 3, 0); } TEST_P(DepthStencilStateTest, StencilReplace) { - CheckStencilOperation(dawn::StencilOperation::Replace, 1, 3, 3); + CheckStencilOperation(wgpu::StencilOperation::Replace, 1, 3, 3); } TEST_P(DepthStencilStateTest, StencilInvert) { - CheckStencilOperation(dawn::StencilOperation::Invert, 0xf0, 3, 0x0f); + CheckStencilOperation(wgpu::StencilOperation::Invert, 0xf0, 3, 0x0f); } TEST_P(DepthStencilStateTest, StencilIncrementClamp) { - CheckStencilOperation(dawn::StencilOperation::IncrementClamp, 1, 3, 2); - CheckStencilOperation(dawn::StencilOperation::IncrementClamp, 0xff, 3, 0xff); + CheckStencilOperation(wgpu::StencilOperation::IncrementClamp, 1, 3, 2); + CheckStencilOperation(wgpu::StencilOperation::IncrementClamp, 0xff, 3, 0xff); } TEST_P(DepthStencilStateTest, StencilIncrementWrap) { - CheckStencilOperation(dawn::StencilOperation::IncrementWrap, 1, 3, 2); - CheckStencilOperation(dawn::StencilOperation::IncrementWrap, 0xff, 3, 0); + CheckStencilOperation(wgpu::StencilOperation::IncrementWrap, 1, 3, 2); + CheckStencilOperation(wgpu::StencilOperation::IncrementWrap, 0xff, 3, 0); } TEST_P(DepthStencilStateTest, StencilDecrementClamp) { - CheckStencilOperation(dawn::StencilOperation::DecrementClamp, 1, 3, 0); - CheckStencilOperation(dawn::StencilOperation::DecrementClamp, 0, 3, 0); + CheckStencilOperation(wgpu::StencilOperation::DecrementClamp, 1, 3, 0); + CheckStencilOperation(wgpu::StencilOperation::DecrementClamp, 0, 3, 0); } TEST_P(DepthStencilStateTest, StencilDecrementWrap) { - CheckStencilOperation(dawn::StencilOperation::DecrementWrap, 1, 3, 0); - CheckStencilOperation(dawn::StencilOperation::DecrementWrap, 0, 3, 0xff); + CheckStencilOperation(wgpu::StencilOperation::DecrementWrap, 1, 3, 0); + CheckStencilOperation(wgpu::StencilOperation::DecrementWrap, 0, 3, 0xff); } // Check that the setting a stencil read mask works TEST_P(DepthStencilStateTest, StencilReadMask) { - dawn::StencilStateFaceDescriptor baseStencilFaceDescriptor; - baseStencilFaceDescriptor.compare = dawn::CompareFunction::Always; - baseStencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.passOp = dawn::StencilOperation::Replace; - dawn::DepthStencilStateDescriptor baseState; + wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; + baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; + baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; + wgpu::DepthStencilStateDescriptor baseState; baseState.depthWriteEnabled = false; - baseState.depthCompare = dawn::CompareFunction::Always; + baseState.depthCompare = wgpu::CompareFunction::Always; baseState.stencilBack = baseStencilFaceDescriptor; baseState.stencilFront = baseStencilFaceDescriptor; baseState.stencilReadMask = 0xff; baseState.stencilWriteMask = 0xff; - dawn::StencilStateFaceDescriptor stencilFaceDescriptor; - stencilFaceDescriptor.compare = dawn::CompareFunction::Equal; - stencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.passOp = dawn::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor state; + wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; + stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal; + stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; + wgpu::DepthStencilStateDescriptor state; state.depthWriteEnabled = false; - state.depthCompare = dawn::CompareFunction::Always; + state.depthCompare = wgpu::CompareFunction::Always; state.stencilBack = stencilFaceDescriptor; state.stencilFront = stencilFaceDescriptor; state.stencilReadMask = 0x2; @@ -536,34 +560,36 @@ TEST_P(DepthStencilStateTest, StencilReadMask) { RGBA8 red = RGBA8(255, 0, 0, 255); RGBA8 green = RGBA8(0, 255, 0, 255); - TestSpec base = { baseState, baseColor, 0.5f, 3u }; // Base triangle to set the stencil to 3 - DoTest({ base, { state, red, 0.f, 1u } }, baseColor); // Triangle with stencil reference 1 and read mask 2 does not draw because (3 & 2 != 1) - DoTest({ base, { state, green, 0.f, 2u } }, green); // Triangle with stencil reference 2 and read mask 2 draws because (3 & 2 == 2) + TestSpec base = {baseState, baseColor, 0.5f, 3u}; // Base triangle to set the stencil to 3 + DoTest({base, {state, red, 0.f, 1u}}, baseColor); // Triangle with stencil reference 1 and read + // mask 2 does not draw because (3 & 2 != 1) + DoTest({base, {state, green, 0.f, 2u}}, + green); // Triangle with stencil reference 2 and read mask 2 draws because (3 & 2 == 2) } // Check that setting a stencil write mask works TEST_P(DepthStencilStateTest, StencilWriteMask) { - dawn::StencilStateFaceDescriptor baseStencilFaceDescriptor; - baseStencilFaceDescriptor.compare = dawn::CompareFunction::Always; - baseStencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.passOp = dawn::StencilOperation::Replace; - dawn::DepthStencilStateDescriptor baseState; + wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; + baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; + baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; + wgpu::DepthStencilStateDescriptor baseState; baseState.depthWriteEnabled = false; - baseState.depthCompare = dawn::CompareFunction::Always; + baseState.depthCompare = wgpu::CompareFunction::Always; baseState.stencilBack = baseStencilFaceDescriptor; baseState.stencilFront = baseStencilFaceDescriptor; baseState.stencilReadMask = 0xff; baseState.stencilWriteMask = 0x1; - dawn::StencilStateFaceDescriptor stencilFaceDescriptor; - stencilFaceDescriptor.compare = dawn::CompareFunction::Equal; - stencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.passOp = dawn::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor state; + wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; + stencilFaceDescriptor.compare = wgpu::CompareFunction::Equal; + stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; + wgpu::DepthStencilStateDescriptor state; state.depthWriteEnabled = false; - state.depthCompare = dawn::CompareFunction::Always; + state.depthCompare = wgpu::CompareFunction::Always; state.stencilBack = stencilFaceDescriptor; state.stencilFront = stencilFaceDescriptor; state.stencilReadMask = 0xff; @@ -572,115 +598,145 @@ TEST_P(DepthStencilStateTest, StencilWriteMask) { RGBA8 baseColor = RGBA8(255, 255, 255, 255); RGBA8 green = RGBA8(0, 255, 0, 255); - TestSpec base = { baseState, baseColor, 0.5f, 3u }; // Base triangle with stencil reference 3 and mask 1 to set the stencil 1 - DoTest({ base, { state, green, 0.f, 2u } }, baseColor); // Triangle with stencil reference 2 does not draw because 2 != (3 & 1) - DoTest({ base, { state, green, 0.f, 1u } }, green); // Triangle with stencil reference 1 draws because 1 == (3 & 1) + TestSpec base = {baseState, baseColor, 0.5f, + 3u}; // Base triangle with stencil reference 3 and mask 1 to set the stencil 1 + DoTest({base, {state, green, 0.f, 2u}}, + baseColor); // Triangle with stencil reference 2 does not draw because 2 != (3 & 1) + DoTest({base, {state, green, 0.f, 1u}}, + green); // Triangle with stencil reference 1 draws because 1 == (3 & 1) } // Test that the stencil operation is executed on stencil fail TEST_P(DepthStencilStateTest, StencilFail) { - dawn::StencilStateFaceDescriptor baseStencilFaceDescriptor; - baseStencilFaceDescriptor.compare = dawn::CompareFunction::Always; - baseStencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.passOp = dawn::StencilOperation::Replace; - dawn::DepthStencilStateDescriptor baseState; + wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; + baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; + baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; + wgpu::DepthStencilStateDescriptor baseState; baseState.depthWriteEnabled = false; - baseState.depthCompare = dawn::CompareFunction::Always; + baseState.depthCompare = wgpu::CompareFunction::Always; baseState.stencilBack = baseStencilFaceDescriptor; baseState.stencilFront = baseStencilFaceDescriptor; baseState.stencilReadMask = 0xff; baseState.stencilWriteMask = 0xff; - dawn::StencilStateFaceDescriptor stencilFaceDescriptor; - stencilFaceDescriptor.compare = dawn::CompareFunction::Less; - stencilFaceDescriptor.failOp = dawn::StencilOperation::Replace; - stencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.passOp = dawn::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor state; + wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; + stencilFaceDescriptor.compare = wgpu::CompareFunction::Less; + stencilFaceDescriptor.failOp = wgpu::StencilOperation::Replace; + stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; + wgpu::DepthStencilStateDescriptor state; state.depthWriteEnabled = false; - state.depthCompare = dawn::CompareFunction::Always; + state.depthCompare = wgpu::CompareFunction::Always; state.stencilBack = stencilFaceDescriptor; state.stencilFront = stencilFaceDescriptor; state.stencilReadMask = 0xff; state.stencilWriteMask = 0xff; - CheckStencil({ - { baseState, RGBA8(255, 255, 255, 255), 1.f, 1 }, // Triangle to set stencil value to 1 - { state, RGBA8(0, 0, 0, 255), 0.f, 2 } // Triangle with stencil reference 2 fails the Less comparison function - }, 2); // Replace the stencil on failure, so it should be 2 + CheckStencil( + { + {baseState, RGBA8(255, 255, 255, 255), 1.f, 1}, // Triangle to set stencil value to 1 + {state, RGBA8(0, 0, 0, 255), 0.f, + 2} // Triangle with stencil reference 2 fails the Less comparison function + }, + 2); // Replace the stencil on failure, so it should be 2 } // Test that the stencil operation is executed on stencil pass, depth fail TEST_P(DepthStencilStateTest, StencilDepthFail) { - dawn::StencilStateFaceDescriptor baseStencilFaceDescriptor; - baseStencilFaceDescriptor.compare = dawn::CompareFunction::Always; - baseStencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.passOp = dawn::StencilOperation::Replace; - dawn::DepthStencilStateDescriptor baseState; + wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; + baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; + baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; + wgpu::DepthStencilStateDescriptor baseState; baseState.depthWriteEnabled = true; - baseState.depthCompare = dawn::CompareFunction::Always; + baseState.depthCompare = wgpu::CompareFunction::Always; baseState.stencilBack = baseStencilFaceDescriptor; baseState.stencilFront = baseStencilFaceDescriptor; baseState.stencilReadMask = 0xff; baseState.stencilWriteMask = 0xff; - dawn::StencilStateFaceDescriptor stencilFaceDescriptor; - stencilFaceDescriptor.compare = dawn::CompareFunction::Greater; - stencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Replace; - stencilFaceDescriptor.passOp = dawn::StencilOperation::Keep; - dawn::DepthStencilStateDescriptor state; + wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; + stencilFaceDescriptor.compare = wgpu::CompareFunction::Greater; + stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Replace; + stencilFaceDescriptor.passOp = wgpu::StencilOperation::Keep; + wgpu::DepthStencilStateDescriptor state; state.depthWriteEnabled = true; - state.depthCompare = dawn::CompareFunction::Less; + state.depthCompare = wgpu::CompareFunction::Less; state.stencilBack = stencilFaceDescriptor; state.stencilFront = stencilFaceDescriptor; state.stencilReadMask = 0xff; state.stencilWriteMask = 0xff; - CheckStencil({ - { baseState, RGBA8(255, 255, 255, 255), 0.f, 1 }, // Triangle to set stencil value to 1. Depth is 0 - { state, RGBA8(0, 0, 0, 255), 1.f, 2 } }, // Triangle with stencil reference 2 passes the Greater comparison function. At depth 1, it fails the Less depth test - 2); // Replace the stencil on stencil pass, depth failure, so it should be 2 + CheckStencil({{baseState, RGBA8(255, 255, 255, 255), 0.f, + 1}, // Triangle to set stencil value to 1. Depth is 0 + {state, RGBA8(0, 0, 0, 255), 1.f, + 2}}, // Triangle with stencil reference 2 passes the Greater comparison + // function. At depth 1, it fails the Less depth test + 2); // Replace the stencil on stencil pass, depth failure, so it should be 2 } // Test that the stencil operation is executed on stencil pass, depth pass TEST_P(DepthStencilStateTest, StencilDepthPass) { - dawn::StencilStateFaceDescriptor baseStencilFaceDescriptor; - baseStencilFaceDescriptor.compare = dawn::CompareFunction::Always; - baseStencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - baseStencilFaceDescriptor.passOp = dawn::StencilOperation::Replace; - dawn::DepthStencilStateDescriptor baseState; + wgpu::StencilStateFaceDescriptor baseStencilFaceDescriptor; + baseStencilFaceDescriptor.compare = wgpu::CompareFunction::Always; + baseStencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + baseStencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; + wgpu::DepthStencilStateDescriptor baseState; baseState.depthWriteEnabled = true; - baseState.depthCompare = dawn::CompareFunction::Always; + baseState.depthCompare = wgpu::CompareFunction::Always; baseState.stencilBack = baseStencilFaceDescriptor; baseState.stencilFront = baseStencilFaceDescriptor; baseState.stencilReadMask = 0xff; baseState.stencilWriteMask = 0xff; - dawn::StencilStateFaceDescriptor stencilFaceDescriptor; - stencilFaceDescriptor.compare = dawn::CompareFunction::Greater; - stencilFaceDescriptor.failOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.depthFailOp = dawn::StencilOperation::Keep; - stencilFaceDescriptor.passOp = dawn::StencilOperation::Replace; - dawn::DepthStencilStateDescriptor state; + wgpu::StencilStateFaceDescriptor stencilFaceDescriptor; + stencilFaceDescriptor.compare = wgpu::CompareFunction::Greater; + stencilFaceDescriptor.failOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.depthFailOp = wgpu::StencilOperation::Keep; + stencilFaceDescriptor.passOp = wgpu::StencilOperation::Replace; + wgpu::DepthStencilStateDescriptor state; state.depthWriteEnabled = true; - state.depthCompare = dawn::CompareFunction::Less; + state.depthCompare = wgpu::CompareFunction::Less; state.stencilBack = stencilFaceDescriptor; state.stencilFront = stencilFaceDescriptor; state.stencilReadMask = 0xff; state.stencilWriteMask = 0xff; - CheckStencil({ - { baseState, RGBA8(255, 255, 255, 255), 1.f, 1 }, // Triangle to set stencil value to 1. Depth is 0 - { state, RGBA8(0, 0, 0, 255), 0.f, 2 } }, // Triangle with stencil reference 2 passes the Greater comparison function. At depth 0, it pass the Less depth test -2); // Replace the stencil on stencil pass, depth pass, so it should be 2 + CheckStencil({{baseState, RGBA8(255, 255, 255, 255), 1.f, + 1}, // Triangle to set stencil value to 1. Depth is 0 + {state, RGBA8(0, 0, 0, 255), 0.f, + 2}}, // Triangle with stencil reference 2 passes the Greater comparison + // function. At depth 0, it pass the Less depth test + 2); // Replace the stencil on stencil pass, depth pass, so it should be 2 +} + +// Test that creating a render pipeline works with for all depth and combined formats +TEST_P(DepthStencilStateTest, CreatePipelineWithAllFormats) { + constexpr wgpu::TextureFormat kDepthStencilFormats[] = { + wgpu::TextureFormat::Depth32Float, + wgpu::TextureFormat::Depth24PlusStencil8, + wgpu::TextureFormat::Depth24Plus, + }; + + for (wgpu::TextureFormat depthStencilFormat : kDepthStencilFormats) { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cDepthStencilState.format = depthStencilFormat; + descriptor.depthStencilState = &descriptor.cDepthStencilState; + + device.CreateRenderPipeline(&descriptor); + } } DAWN_INSTANTIATE_TEST(DepthStencilStateTest, - D3D12Backend, - MetalBackend, - OpenGLBackend, - VulkanBackend); + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend({"vulkan_use_d32s8"}, {}), + VulkanBackend({}, {"vulkan_use_d32s8"})); diff --git a/third_party/dawn/src/tests/end2end/DestroyTests.cpp b/third_party/dawn/src/tests/end2end/DestroyTests.cpp index d61f5ef7ad2..4122001189d 100644 --- a/third_party/dawn/src/tests/end2end/DestroyTests.cpp +++ b/third_party/dawn/src/tests/end2end/DestroyTests.cpp @@ -1,160 +1,164 @@ -// Copyright 2019 The Dawn Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "tests/DawnTest.h" - -#include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" - -constexpr uint32_t kRTSize = 4; - -class DestroyTest : public DawnTest { - protected: - void SetUp() override { - DawnTest::SetUp(); - - renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( - #version 450 - layout(location = 0) in vec4 pos; - void main() { - gl_Position = pos; - })"); - - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( - #version 450 - layout(location = 0) out vec4 fragColor; - void main() { - fragColor = vec4(0.0, 1.0, 0.0, 1.0); - })"); - - utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; - descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; - descriptor.cColorStates[0]->format = renderPass.colorFormat; - - pipeline = device.CreateRenderPipeline(&descriptor); - - vertexBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Vertex, - {// The bottom left triangle - -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f}); - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.BeginRenderPass(&renderPass.renderPassInfo).EndPass(); - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - } - - utils::BasicRenderPass renderPass; - dawn::RenderPipeline pipeline; - dawn::Buffer vertexBuffer; - - dawn::CommandBuffer CreateTriangleCommandBuffer() { - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); - pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.Draw(3, 1, 0, 0); - pass.EndPass(); - } - dawn::CommandBuffer commands = encoder.Finish(); - return commands; - } -}; - -// Destroy before submit will result in error, and nothing drawn -TEST_P(DestroyTest, BufferDestroyBeforeSubmit) { - RGBA8 notFilled(0, 0, 0, 0); - - dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); - vertexBuffer.Destroy(); - ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); - - EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 1, 3); -} - -// Destroy after submit will draw successfully -TEST_P(DestroyTest, BufferDestroyAfterSubmit) { - RGBA8 filled(0, 255, 0, 255); - - dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); - queue.Submit(1, &commands); - - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); - vertexBuffer.Destroy(); -} - -// First submit succeeds, draws triangle, second submit fails -// after destroy is called on the buffer, pixel does not change -TEST_P(DestroyTest, BufferSubmitDestroySubmit) { - RGBA8 filled(0, 255, 0, 255); - - dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); - queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); - - vertexBuffer.Destroy(); - - // Submit fails because vertex buffer was destroyed - ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); - - // Pixel stays the same - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); -} - -// Destroy texture before submit should fail submit -TEST_P(DestroyTest, TextureDestroyBeforeSubmit) { - dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); - renderPass.color.Destroy(); - ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); -} - -// Destroy after submit will draw successfully -TEST_P(DestroyTest, TextureDestroyAfterSubmit) { - RGBA8 filled(0, 255, 0, 255); - - dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); - queue.Submit(1, &commands); - - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); - renderPass.color.Destroy(); -} - -// First submit succeeds, draws triangle, second submit fails -// after destroy is called on the texture -TEST_P(DestroyTest, TextureSubmitDestroySubmit) { - RGBA8 filled(0, 255, 0, 255); - - dawn::CommandBuffer commands = CreateTriangleCommandBuffer(); - queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); - - renderPass.color.Destroy(); - - // Submit fails because texture was destroyed - ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); -} - -DAWN_INSTANTIATE_TEST(DestroyTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +constexpr uint32_t kRTSize = 4; + +class DestroyTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + + renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(location = 0) in vec4 pos; + void main() { + gl_Position = pos; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + descriptor.cColorStates[0].format = renderPass.colorFormat; + + pipeline = device.CreateRenderPipeline(&descriptor); + + vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + {// The bottom left triangle + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.BeginRenderPass(&renderPass.renderPassInfo).EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + utils::BasicRenderPass renderPass; + wgpu::RenderPipeline pipeline; + wgpu::Buffer vertexBuffer; + + wgpu::CommandBuffer CreateTriangleCommandBuffer() { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); + pass.EndPass(); + } + wgpu::CommandBuffer commands = encoder.Finish(); + return commands; + } +}; + +// Destroy before submit will result in error, and nothing drawn +TEST_P(DestroyTest, BufferDestroyBeforeSubmit) { + RGBA8 notFilled(0, 0, 0, 0); + + wgpu::CommandBuffer commands = CreateTriangleCommandBuffer(); + vertexBuffer.Destroy(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + + EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 1, 3); +} + +// Destroy after submit will draw successfully +TEST_P(DestroyTest, BufferDestroyAfterSubmit) { + RGBA8 filled(0, 255, 0, 255); + + wgpu::CommandBuffer commands = CreateTriangleCommandBuffer(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); + vertexBuffer.Destroy(); +} + +// First submit succeeds, draws triangle, second submit fails +// after destroy is called on the buffer, pixel does not change +TEST_P(DestroyTest, BufferSubmitDestroySubmit) { + RGBA8 filled(0, 255, 0, 255); + + wgpu::CommandBuffer commands = CreateTriangleCommandBuffer(); + queue.Submit(1, &commands); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); + + vertexBuffer.Destroy(); + + // Submit fails because vertex buffer was destroyed + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + + // Pixel stays the same + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); +} + +// Destroy texture before submit should fail submit +TEST_P(DestroyTest, TextureDestroyBeforeSubmit) { + wgpu::CommandBuffer commands = CreateTriangleCommandBuffer(); + renderPass.color.Destroy(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); +} + +// Destroy after submit will draw successfully +TEST_P(DestroyTest, TextureDestroyAfterSubmit) { + RGBA8 filled(0, 255, 0, 255); + + wgpu::CommandBuffer commands = CreateTriangleCommandBuffer(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); + renderPass.color.Destroy(); +} + +// First submit succeeds, draws triangle, second submit fails +// after destroy is called on the texture +TEST_P(DestroyTest, TextureSubmitDestroySubmit) { + RGBA8 filled(0, 255, 0, 255); + + wgpu::CommandBuffer commands = CreateTriangleCommandBuffer(); + queue.Submit(1, &commands); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3); + + renderPass.color.Destroy(); + + // Submit fails because texture was destroyed + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); +} + +DAWN_INSTANTIATE_TEST(DestroyTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DeviceLostTests.cpp b/third_party/dawn/src/tests/end2end/DeviceLostTests.cpp new file mode 100644 index 00000000000..651252d157a --- /dev/null +++ b/third_party/dawn/src/tests/end2end/DeviceLostTests.cpp @@ -0,0 +1,563 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +#include + +using namespace testing; + +class MockDeviceLostCallback { + public: + MOCK_METHOD(void, Call, (const char* message, void* userdata)); +}; + +static std::unique_ptr mockDeviceLostCallback; +static void ToMockDeviceLostCallback(const char* message, void* userdata) { + mockDeviceLostCallback->Call(message, userdata); + DawnTestBase* self = static_cast(userdata); + self->StartExpectDeviceError(); +} + +class MockFenceOnCompletionCallback { + public: + MOCK_METHOD(void, Call, (WGPUFenceCompletionStatus status, void* userdata)); +}; + +static std::unique_ptr mockFenceOnCompletionCallback; +static void ToMockFenceOnCompletionCallbackFails(WGPUFenceCompletionStatus status, void* userdata) { + EXPECT_EQ(WGPUFenceCompletionStatus_DeviceLost, status); + mockFenceOnCompletionCallback->Call(status, userdata); + mockFenceOnCompletionCallback = nullptr; +} +static void ToMockFenceOnCompletionCallbackSucceeds(WGPUFenceCompletionStatus status, + void* userdata) { + EXPECT_EQ(WGPUFenceCompletionStatus_Success, status); + mockFenceOnCompletionCallback->Call(status, userdata); + mockFenceOnCompletionCallback = nullptr; +} + +static const int fakeUserData = 0; + +class DeviceLostTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + mockDeviceLostCallback = std::make_unique(); + mockFenceOnCompletionCallback = std::make_unique(); + } + + void TearDown() override { + mockDeviceLostCallback = nullptr; + DawnTest::TearDown(); + } + + void SetCallbackAndLoseForTesting() { + device.SetDeviceLostCallback(ToMockDeviceLostCallback, this); + EXPECT_CALL(*mockDeviceLostCallback, Call(_, this)).Times(1); + device.LoseForTesting(); + } + + template + static void MapFailCallback(WGPUBufferMapAsyncStatus status, + T* data, + uint64_t datalength, + void* userdata) { + EXPECT_EQ(WGPUBufferMapAsyncStatus_DeviceLost, status); + EXPECT_EQ(nullptr, data); + EXPECT_EQ(0u, datalength); + EXPECT_EQ(&fakeUserData, userdata); + } +}; + +// Test that DeviceLostCallback is invoked when LostForTestimg is called +TEST_P(DeviceLostTest, DeviceLostCallbackIsCalled) { + SetCallbackAndLoseForTesting(); +} + +// Test that submit fails when device is lost +TEST_P(DeviceLostTest, SubmitFails) { + wgpu::CommandBuffer commands; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + commands = encoder.Finish(); + + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(queue.Submit(0, &commands)); +} + +// Test that CreateBindGroupLayout fails when device is lost +TEST_P(DeviceLostTest, CreateBindGroupLayoutFails) { + SetCallbackAndLoseForTesting(); + + wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::None, + wgpu::BindingType::UniformBuffer}; + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = 1; + descriptor.entries = &entry; + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor)); +} + +// Test that GetBindGroupLayout fails when device is lost +TEST_P(DeviceLostTest, GetBindGroupLayoutFails) { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(set = 0, binding = 0) uniform UniformBuffer { + vec4 pos; + }; + void main() { + })"); + + wgpu::ComputePipelineDescriptor descriptor; + descriptor.layout = nullptr; + descriptor.computeStage.module = csModule; + descriptor.computeStage.entryPoint = "main"; + + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&descriptor); + + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(pipeline.GetBindGroupLayout(0).Get()); +} + +// Test that CreateBindGroup fails when device is lost +TEST_P(DeviceLostTest, CreateBindGroupFails) { + SetCallbackAndLoseForTesting(); + + wgpu::BindGroupEntry entry; + entry.binding = 0; + entry.sampler = nullptr; + entry.textureView = nullptr; + entry.buffer = nullptr; + entry.offset = 0; + entry.size = 0; + + wgpu::BindGroupDescriptor descriptor; + descriptor.layout = nullptr; + descriptor.entryCount = 1; + descriptor.entries = &entry; + ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor)); +} + +// Test that CreatePipelineLayout fails when device is lost +TEST_P(DeviceLostTest, CreatePipelineLayoutFails) { + SetCallbackAndLoseForTesting(); + + wgpu::PipelineLayoutDescriptor descriptor; + descriptor.bindGroupLayoutCount = 0; + descriptor.bindGroupLayouts = nullptr; + ASSERT_DEVICE_ERROR(device.CreatePipelineLayout(&descriptor)); +} + +// Tests that CreateRenderBundleEncoder fails when device is lost +TEST_P(DeviceLostTest, CreateRenderBundleEncoderFails) { + SetCallbackAndLoseForTesting(); + + wgpu::RenderBundleEncoderDescriptor descriptor; + descriptor.colorFormatsCount = 0; + descriptor.colorFormats = nullptr; + ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&descriptor)); +} + +// Tests that CreateComputePipeline fails when device is lost +TEST_P(DeviceLostTest, CreateComputePipelineFails) { + SetCallbackAndLoseForTesting(); + + wgpu::ComputePipelineDescriptor descriptor = {}; + descriptor.layout = nullptr; + descriptor.computeStage.module = nullptr; + ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&descriptor)); +} + +// Tests that CreateRenderPipeline fails when device is lost +TEST_P(DeviceLostTest, CreateRenderPipelineFails) { + SetCallbackAndLoseForTesting(); + + utils::ComboRenderPipelineDescriptor descriptor(device); + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); +} + +// Tests that CreateSampler fails when device is lost +TEST_P(DeviceLostTest, CreateSamplerFails) { + SetCallbackAndLoseForTesting(); + + wgpu::SamplerDescriptor descriptor = utils::GetDefaultSamplerDescriptor(); + ASSERT_DEVICE_ERROR(device.CreateSampler(&descriptor)); +} + +// Tests that CreateShaderModule fails when device is lost +TEST_P(DeviceLostTest, CreateShaderModuleFails) { + SetCallbackAndLoseForTesting(); + + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) in vec4 color; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = color; + })")); +} + +// Tests that CreateSwapChain fails when device is lost +TEST_P(DeviceLostTest, CreateSwapChainFails) { + SetCallbackAndLoseForTesting(); + + wgpu::SwapChainDescriptor descriptor = {}; + ASSERT_DEVICE_ERROR(device.CreateSwapChain(nullptr, &descriptor)); +} + +// Tests that CreateTexture fails when device is lost +TEST_P(DeviceLostTest, CreateTextureFails) { + SetCallbackAndLoseForTesting(); + + wgpu::TextureDescriptor descriptor; + descriptor.size.width = 4; + descriptor.size.height = 4; + descriptor.size.depth = 1; + descriptor.mipLevelCount = 1; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.usage = wgpu::TextureUsage::OutputAttachment; + + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); +} + +TEST_P(DeviceLostTest, TickFails) { + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(device.Tick()); +} + +// Test that CreateBuffer fails when device is lost +TEST_P(DeviceLostTest, CreateBufferFails) { + SetCallbackAndLoseForTesting(); + + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::CopySrc; + ASSERT_DEVICE_ERROR(device.CreateBuffer(&bufferDescriptor)); +} + +// Test that buffer.MapWriteAsync fails after device is lost +TEST_P(DeviceLostTest, BufferMapWriteAsyncFails) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::MapWrite; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(buffer.MapWriteAsync(MapFailCallback, const_cast(&fakeUserData))); +} + +// Test that buffer.MapWriteAsync calls back with device loss status +TEST_P(DeviceLostTest, BufferMapWriteAsyncBeforeLossFails) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::MapWrite; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + + buffer.MapWriteAsync(MapFailCallback, const_cast(&fakeUserData)); + SetCallbackAndLoseForTesting(); +} + +// Test that buffer.Unmap fails after device is lost +TEST_P(DeviceLostTest, BufferUnmapFails) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::MapWrite; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + wgpu::CreateBufferMappedResult result = device.CreateBufferMapped(&bufferDescriptor); + + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(result.buffer.Unmap()); +} + +// Test that CreateBufferMapped fails after device is lost +TEST_P(DeviceLostTest, CreateBufferMappedFails) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::MapWrite; + + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&bufferDescriptor)); +} + +// Test that mappedAtCreation fails after device is lost +TEST_P(DeviceLostTest, CreateBufferMappedAtCreationFails) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::MapWrite; + bufferDescriptor.mappedAtCreation = true; + + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(device.CreateBuffer(&bufferDescriptor)); +} + +// Test that BufferMapReadAsync fails after device is lost +TEST_P(DeviceLostTest, BufferMapReadAsyncFails) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(buffer.MapReadAsync(MapFailCallback, const_cast(&fakeUserData))); +} + +// Test that BufferMapReadAsync calls back with device lost status when device lost after map read +TEST_P(DeviceLostTest, BufferMapReadAsyncBeforeLossFails) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + + buffer.MapReadAsync(MapFailCallback, const_cast(&fakeUserData)); + SetCallbackAndLoseForTesting(); +} + +// Test that WriteBuffer fails after device is lost +TEST_P(DeviceLostTest, WriteBufferFails) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = sizeof(float); + bufferDescriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + + SetCallbackAndLoseForTesting(); + float data = 12.0f; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buffer, 0, &data, sizeof(data))); +} + +// Test it's possible to GetMappedRange on a buffer created mapped after device loss +TEST_P(DeviceLostTest, GetMappedRange_CreateBufferMappedAfterLoss) { + SetCallbackAndLoseForTesting(); + + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::CopySrc; + ASSERT_DEVICE_ERROR(wgpu::CreateBufferMappedResult result = device.CreateBufferMapped(&desc)); + + ASSERT_NE(result.buffer.GetMappedRange(), nullptr); + ASSERT_EQ(result.buffer.GetMappedRange(), result.data); +} + +// Test that device loss doesn't change the result of GetMappedRange, createBufferMapped version. +TEST_P(DeviceLostTest, GetMappedRange_CreateBufferMappedBeforeLoss) { + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::CopySrc; + wgpu::CreateBufferMappedResult result = device.CreateBufferMapped(&desc); + + void* rangeBeforeLoss = result.buffer.GetMappedRange(); + SetCallbackAndLoseForTesting(); + + ASSERT_NE(result.buffer.GetMappedRange(), nullptr); + ASSERT_EQ(result.buffer.GetMappedRange(), rangeBeforeLoss); + ASSERT_EQ(result.buffer.GetMappedRange(), result.data); +} + +// Test it's possible to GetMappedRange on a buffer created mapped after device loss +TEST_P(DeviceLostTest, GetMappedRange_CreateBufferMappedAtCreationAfterLoss) { + SetCallbackAndLoseForTesting(); + + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::CopySrc; + desc.mappedAtCreation = true; + ASSERT_DEVICE_ERROR(wgpu::Buffer buffer = device.CreateBuffer(&desc)); + + ASSERT_NE(buffer.GetMappedRange(), nullptr); +} + +// Test that device loss doesn't change the result of GetMappedRange, mappedAtCreation version. +TEST_P(DeviceLostTest, GetMappedRange_CreateBufferMappedAtCreationBeforeLoss) { + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::CopySrc; + desc.mappedAtCreation = true; + wgpu::Buffer buffer = device.CreateBuffer(&desc); + + void* rangeBeforeLoss = buffer.GetMappedRange(); + SetCallbackAndLoseForTesting(); + + ASSERT_NE(buffer.GetMappedRange(), nullptr); + ASSERT_EQ(buffer.GetMappedRange(), rangeBeforeLoss); +} + +// Test that device loss doesn't change the result of GetMappedRange, mapReadAsync version. +TEST_P(DeviceLostTest, GetMappedRange_MapReadAsync) { + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&desc); + + buffer.MapReadAsync(nullptr, nullptr); + queue.Submit(0, nullptr); + + const void* rangeBeforeLoss = buffer.GetConstMappedRange(); + SetCallbackAndLoseForTesting(); + + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(buffer.GetConstMappedRange(), rangeBeforeLoss); +} + +// Test that device loss doesn't change the result of GetMappedRange, mapReadAsync version. +TEST_P(DeviceLostTest, GetMappedRange_MapWriteAsync) { + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + wgpu::Buffer buffer = device.CreateBuffer(&desc); + + buffer.MapWriteAsync(nullptr, nullptr); + queue.Submit(0, nullptr); + + const void* rangeBeforeLoss = buffer.GetConstMappedRange(); + SetCallbackAndLoseForTesting(); + + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(buffer.GetConstMappedRange(), rangeBeforeLoss); +} + +// mapreadasync + resolve + loss getmappedrange != nullptr. +// mapwriteasync + resolve + loss getmappedrange != nullptr. + +// Test that Command Encoder Finish fails when device lost +TEST_P(DeviceLostTest, CommandEncoderFinishFails) { + wgpu::CommandBuffer commands; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(encoder.Finish()); +} + +// Test that CreateFenceFails when device is lost +TEST_P(DeviceLostTest, CreateFenceFails) { + SetCallbackAndLoseForTesting(); + + wgpu::FenceDescriptor descriptor; + descriptor.initialValue = 0; + + ASSERT_DEVICE_ERROR(queue.CreateFence(&descriptor)); +} + +// Test that queue signal fails when device is lost +TEST_P(DeviceLostTest, QueueSignalFenceFails) { + wgpu::FenceDescriptor descriptor; + descriptor.initialValue = 0; + wgpu::Fence fence = queue.CreateFence(&descriptor); + + SetCallbackAndLoseForTesting(); + + ASSERT_DEVICE_ERROR(queue.Signal(fence, 3)); + + // callback should have device lost status + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_DeviceLost, nullptr)) + .Times(1); + ASSERT_DEVICE_ERROR(fence.OnCompletion(2u, ToMockFenceOnCompletionCallbackFails, nullptr)); + + // completed value should not have changed from initial value + EXPECT_EQ(fence.GetCompletedValue(), descriptor.initialValue); +} + +// Test that Fence On Completion fails after device is lost +TEST_P(DeviceLostTest, FenceOnCompletionFails) { + wgpu::FenceDescriptor descriptor; + descriptor.initialValue = 0; + wgpu::Fence fence = queue.CreateFence(&descriptor); + + queue.Signal(fence, 2); + + SetCallbackAndLoseForTesting(); + // callback should have device lost status + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_DeviceLost, nullptr)) + .Times(1); + ASSERT_DEVICE_ERROR(fence.OnCompletion(2u, ToMockFenceOnCompletionCallbackFails, nullptr)); + ASSERT_DEVICE_ERROR(device.Tick()); + + // completed value should not have changed from initial value + EXPECT_EQ(fence.GetCompletedValue(), 0u); +} + +// Test that Fence::OnCompletion callbacks with device lost status when device is lost after calling +// OnCompletion +TEST_P(DeviceLostTest, FenceOnCompletionBeforeLossFails) { + wgpu::FenceDescriptor descriptor; + descriptor.initialValue = 0; + wgpu::Fence fence = queue.CreateFence(&descriptor); + + queue.Signal(fence, 2); + + // callback should have device lost status + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_DeviceLost, nullptr)) + .Times(1); + fence.OnCompletion(2u, ToMockFenceOnCompletionCallbackFails, nullptr); + SetCallbackAndLoseForTesting(); + ASSERT_DEVICE_ERROR(device.Tick()); + + EXPECT_EQ(fence.GetCompletedValue(), 0u); +} + +// Regression test for the Null backend not properly setting the completedSerial when +// WaitForIdleForDestruction is called, causing the fence signaling to not be retired and an +// ASSERT to fire. +TEST_P(DeviceLostTest, AfterSubmitAndSerial) { + queue.Submit(0, nullptr); + + wgpu::FenceDescriptor descriptor; + descriptor.initialValue = 0; + wgpu::Fence fence = queue.CreateFence(&descriptor); + + queue.Signal(fence, 1); + SetCallbackAndLoseForTesting(); +} + +// Test that when you Signal, then Tick, then device lost, the fence completed value would be 2 +TEST_P(DeviceLostTest, FenceSignalTickOnCompletion) { + wgpu::FenceDescriptor descriptor; + descriptor.initialValue = 0; + wgpu::Fence fence = queue.CreateFence(&descriptor); + + queue.Signal(fence, 2); + device.Tick(); + + // callback should have device lost status + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr)) + .Times(1); + fence.OnCompletion(2u, ToMockFenceOnCompletionCallbackSucceeds, nullptr); + SetCallbackAndLoseForTesting(); + + EXPECT_EQ(fence.GetCompletedValue(), 2u); +} + +// Test that LostForTesting can only be called on one time +TEST_P(DeviceLostTest, LoseForTestingOnce) { + // First LoseForTesting call should occur normally + SetCallbackAndLoseForTesting(); + + // Second LoseForTesting call should result in no callbacks. The LoseForTesting will return + // without doing anything when it sees that device has already been lost. + device.SetDeviceLostCallback(ToMockDeviceLostCallback, this); + EXPECT_CALL(*mockDeviceLostCallback, Call(_, this)).Times(0); + device.LoseForTesting(); +} + +DAWN_INSTANTIATE_TEST(DeviceLostTest, + D3D12Backend(), + MetalBackend(), + NullBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DrawIndexedIndirectTests.cpp b/third_party/dawn/src/tests/end2end/DrawIndexedIndirectTests.cpp index a02bd052f22..a8e8c126e4c 100644 --- a/third_party/dawn/src/tests/end2end/DrawIndexedIndirectTests.cpp +++ b/third_party/dawn/src/tests/end2end/DrawIndexedIndirectTests.cpp @@ -15,7 +15,7 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr uint32_t kRTSize = 4; @@ -26,16 +26,16 @@ class DrawIndexedIndirectTest : public DawnTest { renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(location = 0) in vec4 pos; void main() { gl_Position = pos; })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -43,58 +43,57 @@ class DrawIndexedIndirectTest : public DawnTest { })"); utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; - descriptor.cColorStates[0]->format = renderPass.colorFormat; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + descriptor.cColorStates[0].format = renderPass.colorFormat; pipeline = device.CreateRenderPipeline(&descriptor); vertexBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Vertex, + device, wgpu::BufferUsage::Vertex, {// First quad: the first 3 vertices represent the bottom left triangle - -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, // Second quad: the first 3 vertices represent the top right triangle - -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f}); indexBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Index, + device, wgpu::BufferUsage::Index, {0, 1, 2, 0, 3, 1, // The indices below are added to test negatve baseVertex 0 + 4, 1 + 4, 2 + 4, 0 + 4, 3 + 4, 1 + 4}); } utils::BasicRenderPass renderPass; - dawn::RenderPipeline pipeline; - dawn::Buffer vertexBuffer; - dawn::Buffer indexBuffer; + wgpu::RenderPipeline pipeline; + wgpu::Buffer vertexBuffer; + wgpu::Buffer indexBuffer; void Test(std::initializer_list bufferList, uint64_t indexOffset, uint64_t indirectOffset, RGBA8 bottomLeftExpected, RGBA8 topRightExpected) { - dawn::Buffer indirectBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Indirect, bufferList); + wgpu::Buffer indirectBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Indirect, bufferList); - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + pass.SetVertexBuffer(0, vertexBuffer); pass.SetIndexBuffer(indexBuffer, indexOffset); pass.DrawIndexedIndirect(indirectBuffer, indirectOffset); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3); @@ -163,7 +162,7 @@ TEST_P(DrawIndexedIndirectTest, IndirectOffset) { } DAWN_INSTANTIATE_TEST(DrawIndexedIndirectTest, - D3D12Backend, - MetalBackend, - OpenGLBackend, - VulkanBackend); + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DrawIndexedTests.cpp b/third_party/dawn/src/tests/end2end/DrawIndexedTests.cpp index 186d5af4edc..f980af3cebb 100644 --- a/third_party/dawn/src/tests/end2end/DrawIndexedTests.cpp +++ b/third_party/dawn/src/tests/end2end/DrawIndexedTests.cpp @@ -15,101 +15,94 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr uint32_t kRTSize = 4; class DrawIndexedTest : public DawnTest { - protected: - void SetUp() override { - DawnTest::SetUp(); + protected: + void SetUp() override { + DawnTest::SetUp(); - renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(location = 0) in vec4 pos; void main() { gl_Position = pos; })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); - })" - ); - - utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; - descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; - descriptor.cColorStates[0]->format = renderPass.colorFormat; - - pipeline = device.CreateRenderPipeline(&descriptor); - - vertexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Vertex, { - // First quad: the first 3 vertices represent the bottom left triangle - -1.0f, -1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, - - // Second quad: the first 3 vertices represent the top right triangle - -1.0f, -1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, -1.0f, 0.0f, 1.0f, - -1.0f, 1.0f, 0.0f, 1.0f - }); - indexBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Index, - {0, 1, 2, 0, 3, 1, - // The indices below are added to test negatve baseVertex - 0 + 4, 1 + 4, 2 + 4, 0 + 4, 3 + 4, 1 + 4}); - } + })"); - utils::BasicRenderPass renderPass; - dawn::RenderPipeline pipeline; - dawn::Buffer vertexBuffer; - dawn::Buffer indexBuffer; - - void Test(uint32_t indexCount, - uint32_t instanceCount, - uint32_t firstIndex, - int32_t baseVertex, - uint32_t firstInstance, - uint64_t bufferOffset, - RGBA8 bottomLeftExpected, - RGBA8 topRightExpected) { - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass( - &renderPass.renderPassInfo); - pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.SetIndexBuffer(indexBuffer, bufferOffset); - pass.DrawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance); - pass.EndPass(); - } - - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3); - EXPECT_PIXEL_RGBA8_EQ(topRightExpected, renderPass.color, 3, 1); + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + descriptor.cColorStates[0].format = renderPass.colorFormat; + + pipeline = device.CreateRenderPipeline(&descriptor); + + vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + {// First quad: the first 3 vertices represent the bottom left triangle + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, + 0.0f, 1.0f, + + // Second quad: the first 3 vertices represent the top right triangle + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, + 0.0f, 1.0f}); + indexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Index, + {0, 1, 2, 0, 3, 1, + // The indices below are added to test negatve baseVertex + 0 + 4, 1 + 4, 2 + 4, 0 + 4, 3 + 4, 1 + 4}); + } + + utils::BasicRenderPass renderPass; + wgpu::RenderPipeline pipeline; + wgpu::Buffer vertexBuffer; + wgpu::Buffer indexBuffer; + + void Test(uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t baseVertex, + uint32_t firstInstance, + uint64_t bufferOffset, + RGBA8 bottomLeftExpected, + RGBA8 topRightExpected) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer, bufferOffset); + pass.DrawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance); + pass.EndPass(); } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3); + EXPECT_PIXEL_RGBA8_EQ(topRightExpected, renderPass.color, 3, 1); + } }; // The most basic DrawIndexed triangle draw. TEST_P(DrawIndexedTest, Uint32) { - RGBA8 filled(0, 255, 0, 255); RGBA8 notFilled(0, 0, 0, 0); @@ -140,4 +133,8 @@ TEST_P(DrawIndexedTest, BaseVertex) { Test(3, 1, 3, -4, 0, 6 * sizeof(uint32_t), notFilled, filled); } -DAWN_INSTANTIATE_TEST(DrawIndexedTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(DrawIndexedTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DrawIndirectTests.cpp b/third_party/dawn/src/tests/end2end/DrawIndirectTests.cpp index a27aac9095d..56b55ec89ee 100644 --- a/third_party/dawn/src/tests/end2end/DrawIndirectTests.cpp +++ b/third_party/dawn/src/tests/end2end/DrawIndirectTests.cpp @@ -15,7 +15,7 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr uint32_t kRTSize = 4; @@ -26,16 +26,16 @@ class DrawIndirectTest : public DawnTest { renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(location = 0) in vec4 pos; void main() { gl_Position = pos; })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -43,48 +43,47 @@ class DrawIndirectTest : public DawnTest { })"); utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; - descriptor.cColorStates[0]->format = renderPass.colorFormat; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + descriptor.cColorStates[0].format = renderPass.colorFormat; pipeline = device.CreateRenderPipeline(&descriptor); vertexBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Vertex, + device, wgpu::BufferUsage::Vertex, {// The bottom left triangle - -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, // The top right triangle - -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f}); + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}); } utils::BasicRenderPass renderPass; - dawn::RenderPipeline pipeline; - dawn::Buffer vertexBuffer; + wgpu::RenderPipeline pipeline; + wgpu::Buffer vertexBuffer; void Test(std::initializer_list bufferList, uint64_t indirectOffset, RGBA8 bottomLeftExpected, RGBA8 topRightExpected) { - dawn::Buffer indirectBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Indirect, bufferList); + wgpu::Buffer indirectBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Indirect, bufferList); - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + pass.SetVertexBuffer(0, vertexBuffer); pass.DrawIndirect(indirectBuffer, indirectOffset); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3); @@ -125,4 +124,8 @@ TEST_P(DrawIndirectTest, IndirectOffset) { Test({3, 1, 0, 0, 3, 1, 3, 0}, 4 * sizeof(uint32_t), notFilled, filled); } -DAWN_INSTANTIATE_TEST(DrawIndirectTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(DrawIndirectTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DrawTests.cpp b/third_party/dawn/src/tests/end2end/DrawTests.cpp index 53c268fa146..b4673bfedf8 100644 --- a/third_party/dawn/src/tests/end2end/DrawTests.cpp +++ b/third_party/dawn/src/tests/end2end/DrawTests.cpp @@ -15,7 +15,7 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr uint32_t kRTSize = 4; @@ -26,16 +26,16 @@ class DrawTest : public DawnTest { renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(location = 0) in vec4 pos; void main() { gl_Position = pos; })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -43,29 +43,29 @@ class DrawTest : public DawnTest { })"); utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; - descriptor.cColorStates[0]->format = renderPass.colorFormat; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + descriptor.cColorStates[0].format = renderPass.colorFormat; pipeline = device.CreateRenderPipeline(&descriptor); vertexBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Vertex, + device, wgpu::BufferUsage::Vertex, {// The bottom left triangle - -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, // The top right triangle - -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f}); + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}); } utils::BasicRenderPass renderPass; - dawn::RenderPipeline pipeline; - dawn::Buffer vertexBuffer; + wgpu::RenderPipeline pipeline; + wgpu::Buffer vertexBuffer; void Test(uint32_t vertexCount, uint32_t instanceCount, @@ -73,17 +73,16 @@ class DrawTest : public DawnTest { uint32_t firstInstance, RGBA8 bottomLeftExpected, RGBA8 topRightExpected) { - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); + pass.SetVertexBuffer(0, vertexBuffer); pass.Draw(vertexCount, instanceCount, firstIndex, firstInstance); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3); @@ -106,4 +105,4 @@ TEST_P(DrawTest, Uint32) { Test(6, 1, 0, 0, filled, filled); } -DAWN_INSTANTIATE_TEST(DrawTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(DrawTest, D3D12Backend(), MetalBackend(), OpenGLBackend(), VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/DynamicBufferOffsetTests.cpp b/third_party/dawn/src/tests/end2end/DynamicBufferOffsetTests.cpp index 5916d04f61f..d60f493f327 100644 --- a/third_party/dawn/src/tests/end2end/DynamicBufferOffsetTests.cpp +++ b/third_party/dawn/src/tests/end2end/DynamicBufferOffsetTests.cpp @@ -15,113 +15,196 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr uint32_t kRTSize = 400; constexpr uint32_t kBufferElementsCount = kMinDynamicBufferOffsetAlignment / sizeof(uint32_t) + 2; constexpr uint32_t kBufferSize = kBufferElementsCount * sizeof(uint32_t); +constexpr uint32_t kBindingSize = 8; class DynamicBufferOffsetTests : public DawnTest { protected: void SetUp() override { DawnTest::SetUp(); + // Mix up dynamic and non dynamic resources in one bind group and using not continuous + // binding number to cover more cases. std::array uniformData = {0}; - uniformData[0] = 1; uniformData[1] = 2; + + mUniformBuffers[0] = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, + wgpu::BufferUsage::Uniform); + uniformData[uniformData.size() - 2] = 5; uniformData[uniformData.size() - 1] = 6; - mUniformBuffer = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, - dawn::BufferUsageBit::Uniform); + // Dynamic uniform buffer + mUniformBuffers[1] = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, + wgpu::BufferUsage::Uniform); - dawn::BufferDescriptor storageBufferDescriptor; + wgpu::BufferDescriptor storageBufferDescriptor; storageBufferDescriptor.size = kBufferSize; - storageBufferDescriptor.usage = dawn::BufferUsageBit::Storage | - dawn::BufferUsageBit::TransferDst | - dawn::BufferUsageBit::TransferSrc; - - mStorageBuffer = device.CreateBuffer(&storageBufferDescriptor); - - mBindGroupLayout = utils::MakeBindGroupLayout( - device, {{0, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, - dawn::BindingType::DynamicUniformBuffer}, - {1, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, - dawn::BindingType::DynamicStorageBuffer}}); - - mBindGroup = utils::MakeBindGroup( - device, mBindGroupLayout, - {{0, mUniformBuffer, 0, kBufferSize}, {1, mStorageBuffer, 0, kBufferSize}}); + storageBufferDescriptor.usage = + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; + + mStorageBuffers[0] = device.CreateBuffer(&storageBufferDescriptor); + + // Dynamic storage buffer + mStorageBuffers[1] = device.CreateBuffer(&storageBufferDescriptor); + + // Default bind group layout + mBindGroupLayouts[0] = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer}, + {1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}, + {3, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer, true}, + {4, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer, true}}); + + // Default bind group + mBindGroups[0] = utils::MakeBindGroup(device, mBindGroupLayouts[0], + {{0, mUniformBuffers[0], 0, kBindingSize}, + {1, mStorageBuffers[0], 0, kBindingSize}, + {3, mUniformBuffers[1], 0, kBindingSize}, + {4, mStorageBuffers[1], 0, kBindingSize}}); + + // Extra uniform buffer for inheriting test + mUniformBuffers[2] = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, + wgpu::BufferUsage::Uniform); + + // Bind group layout for inheriting test + mBindGroupLayouts[1] = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer}}); + + // Bind group for inheriting test + mBindGroups[1] = utils::MakeBindGroup(device, mBindGroupLayouts[1], + {{0, mUniformBuffers[2], 0, kBindingSize}}); } // Create objects to use as resources inside test bind groups. - dawn::BindGroup mBindGroup; - dawn::BindGroupLayout mBindGroupLayout; - dawn::Buffer mUniformBuffer; - dawn::Buffer mStorageBuffer; - dawn::Texture mColorAttachment; + wgpu::BindGroup mBindGroups[2]; + wgpu::BindGroupLayout mBindGroupLayouts[2]; + wgpu::Buffer mUniformBuffers[3]; + wgpu::Buffer mStorageBuffers[2]; + wgpu::Texture mColorAttachment; - dawn::RenderPipeline CreateRenderPipeline() { - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::RenderPipeline CreateRenderPipeline(bool isInheritedPipeline = false) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { - const vec2 pos[3] = vec2[3](vec2(-1.0f, 0.0f), vec2(-1.0f, -1.0f), vec2(0.0f, -1.0f)); + const vec2 pos[3] = vec2[3](vec2(-1.0f, 0.0f), vec2(-1.0f, 1.0f), vec2(0.0f, 1.0f)); gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( - #version 450 - layout(std140, set = 0, binding = 0) uniform uBuffer { - uvec2 value; + // Construct fragment shader source + std::ostringstream fs; + std::string multipleNumber = isInheritedPipeline ? "2" : "1"; + fs << R"( + #version 450 + layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { + uvec2 notDynamicValue; + }; + layout(std140, set = 0, binding = 1) buffer sBufferNotDynamic { + uvec2 notDynamicResult; + } mid; + layout(std140, set = 0, binding = 3) uniform uBuffer { + uvec2 value; + }; + layout(std140, set = 0, binding = 4) buffer SBuffer { + uvec2 result; + } sBuffer; + )"; + + if (isInheritedPipeline) { + fs << R"( + layout(std140, set = 1, binding = 0) uniform paddingBlock { + uvec2 padding; }; - layout(std140, set = 0, binding = 1) buffer SBuffer { - uvec2 result; - } sBuffer; - layout(location = 0) out vec4 fragColor; - void main() { - sBuffer.result.xy = value.xy; - fragColor = vec4(value.x / 255.0f, value.y / 255.0f, 1.0f, 1.0f); - })"); + )"; + } + + fs << " layout(location = 0) out vec4 fragColor;\n"; + fs << " void main() {\n"; + fs << " mid.notDynamicResult.xy = notDynamicValue.xy;\n"; + fs << " sBuffer.result.xy = " << multipleNumber + << " * (value.xy + mid.notDynamicResult.xy);\n"; + fs << " fragColor = vec4(value.x / 255.0f, value.y / 255.0f, 1.0f, 1.0f);\n"; + fs << " }\n"; + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fs.str().c_str()); utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); - pipelineDescriptor.cVertexStage.module = vsModule; + pipelineDescriptor.vertexStage.module = vsModule; pipelineDescriptor.cFragmentStage.module = fsModule; - pipelineDescriptor.cColorStates[0]->format = dawn::TextureFormat::R8G8B8A8Unorm; - dawn::PipelineLayout pipelineLayout = - utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - pipelineDescriptor.layout = pipelineLayout; + pipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::PipelineLayoutDescriptor pipelineLayoutDescriptor; + if (isInheritedPipeline) { + pipelineLayoutDescriptor.bindGroupLayoutCount = 2; + } else { + pipelineLayoutDescriptor.bindGroupLayoutCount = 1; + } + pipelineLayoutDescriptor.bindGroupLayouts = mBindGroupLayouts; + pipelineDescriptor.layout = device.CreatePipelineLayout(&pipelineLayoutDescriptor); return device.CreateRenderPipeline(&pipelineDescriptor); } - dawn::ComputePipeline CreateComputePipeline() { - dawn::ShaderModule csModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"( - #version 450 - layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; - layout(std140, set = 0, binding = 0) uniform UniformBuffer { - uvec2 value; + wgpu::ComputePipeline CreateComputePipeline(bool isInheritedPipeline = false) { + // Construct compute shader source + std::ostringstream cs; + std::string multipleNumber = isInheritedPipeline ? "2" : "1"; + cs << R"( + #version 450 + layout(std140, set = 0, binding = 0) uniform uBufferNotDynamic { + uvec2 notDynamicValue; + }; + layout(std140, set = 0, binding = 1) buffer sBufferNotDynamic { + uvec2 notDynamicResult; + } mid; + layout(std140, set = 0, binding = 3) uniform uBuffer { + uvec2 value; + }; + layout(std140, set = 0, binding = 4) buffer SBuffer { + uvec2 result; + } sBuffer; + )"; + + if (isInheritedPipeline) { + cs << R"( + layout(std140, set = 1, binding = 0) uniform paddingBlock { + uvec2 padding; }; - layout(std140, set = 0, binding = 1) buffer SBuffer { - uvec2 result; - } sBuffer; - - void main() { - sBuffer.result.xy = value.xy; - })"); - - dawn::ComputePipelineDescriptor csDesc; - dawn::PipelineLayout pipelineLayout = - utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - csDesc.layout = pipelineLayout; - - dawn::PipelineStageDescriptor computeStage; - computeStage.module = csModule; - computeStage.entryPoint = "main"; - csDesc.computeStage = &computeStage; + )"; + } + + cs << " void main() {\n"; + cs << " mid.notDynamicResult.xy = notDynamicValue.xy;\n"; + cs << " sBuffer.result.xy = " << multipleNumber + << " * (value.xy + mid.notDynamicResult.xy);\n"; + cs << " }\n"; + + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, cs.str().c_str()); + + wgpu::ComputePipelineDescriptor csDesc; + csDesc.computeStage.module = csModule; + csDesc.computeStage.entryPoint = "main"; + + wgpu::PipelineLayoutDescriptor pipelineLayoutDescriptor; + if (isInheritedPipeline) { + pipelineLayoutDescriptor.bindGroupLayoutCount = 2; + } else { + pipelineLayoutDescriptor.bindGroupLayoutCount = 1; + } + pipelineLayoutDescriptor.bindGroupLayouts = mBindGroupLayouts; + csDesc.layout = device.CreatePipelineLayout(&pipelineLayoutDescriptor); return device.CreateComputePipeline(&csDesc); } @@ -129,86 +212,199 @@ class DynamicBufferOffsetTests : public DawnTest { // Dynamic offsets are all zero and no effect to result. TEST_P(DynamicBufferOffsetTests, BasicRenderPipeline) { - dawn::RenderPipeline pipeline = CreateRenderPipeline(); + wgpu::RenderPipeline pipeline = CreateRenderPipeline(); utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - std::array offsets = {0, 0}; - dawn::RenderPassEncoder renderPassEncoder = + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + std::array offsets = {0, 0}; + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass.renderPassInfo); renderPassEncoder.SetPipeline(pipeline); - renderPassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data()); - renderPassEncoder.Draw(3, 1, 0, 0); + renderPassEncoder.SetBindGroup(0, mBindGroups[0], offsets.size(), offsets.data()); + renderPassEncoder.Draw(3); renderPassEncoder.EndPass(); - dawn::CommandBuffer commands = commandEncoder.Finish(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); - std::vector expectedData = {1, 2}; + std::vector expectedData = {2, 4}; EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 255, 255), renderPass.color, 0, 0); - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, 0, expectedData.size()); + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], 0, expectedData.size()); } // Have non-zero dynamic offsets. TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsRenderPipeline) { - dawn::RenderPipeline pipeline = CreateRenderPipeline(); + wgpu::RenderPipeline pipeline = CreateRenderPipeline(); utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - std::array offsets = {kMinDynamicBufferOffsetAlignment, + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + std::array offsets = {kMinDynamicBufferOffsetAlignment, kMinDynamicBufferOffsetAlignment}; - dawn::RenderPassEncoder renderPassEncoder = + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass.renderPassInfo); renderPassEncoder.SetPipeline(pipeline); - renderPassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data()); - renderPassEncoder.Draw(3, 1, 0, 0); + renderPassEncoder.SetBindGroup(0, mBindGroups[0], offsets.size(), offsets.data()); + renderPassEncoder.Draw(3); renderPassEncoder.EndPass(); - dawn::CommandBuffer commands = commandEncoder.Finish(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); - std::vector expectedData = {5, 6}; + std::vector expectedData = {6, 8}; EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0); - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], kMinDynamicBufferOffsetAlignment, expectedData.size()); } // Dynamic offsets are all zero and no effect to result. TEST_P(DynamicBufferOffsetTests, BasicComputePipeline) { - dawn::ComputePipeline pipeline = CreateComputePipeline(); + wgpu::ComputePipeline pipeline = CreateComputePipeline(); - std::array offsets = {0, 0}; + std::array offsets = {0, 0}; - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); computePassEncoder.SetPipeline(pipeline); - computePassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data()); - computePassEncoder.Dispatch(1, 1, 1); + computePassEncoder.SetBindGroup(0, mBindGroups[0], offsets.size(), offsets.data()); + computePassEncoder.Dispatch(1); computePassEncoder.EndPass(); - dawn::CommandBuffer commands = commandEncoder.Finish(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); - std::vector expectedData = {1, 2}; - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, 0, expectedData.size()); + std::vector expectedData = {2, 4}; + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], 0, expectedData.size()); } // Have non-zero dynamic offsets. TEST_P(DynamicBufferOffsetTests, SetDynamicOffestsComputePipeline) { - dawn::ComputePipeline pipeline = CreateComputePipeline(); + wgpu::ComputePipeline pipeline = CreateComputePipeline(); - std::array offsets = {kMinDynamicBufferOffsetAlignment, + std::array offsets = {kMinDynamicBufferOffsetAlignment, kMinDynamicBufferOffsetAlignment}; - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); computePassEncoder.SetPipeline(pipeline); - computePassEncoder.SetBindGroup(0, mBindGroup, offsets.size(), offsets.data()); - computePassEncoder.Dispatch(1, 1, 1); + computePassEncoder.SetBindGroup(0, mBindGroups[0], offsets.size(), offsets.data()); + computePassEncoder.Dispatch(1); computePassEncoder.EndPass(); - dawn::CommandBuffer commands = commandEncoder.Finish(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = {6, 8}; + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], + kMinDynamicBufferOffsetAlignment, expectedData.size()); +} + +// Test inherit dynamic offsets on render pipeline +TEST_P(DynamicBufferOffsetTests, InheritDynamicOffestsRenderPipeline) { + // Using default pipeline and setting dynamic offsets + wgpu::RenderPipeline pipeline = CreateRenderPipeline(); + wgpu::RenderPipeline testPipeline = CreateRenderPipeline(true); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + std::array offsets = {kMinDynamicBufferOffsetAlignment, + kMinDynamicBufferOffsetAlignment}; + wgpu::RenderPassEncoder renderPassEncoder = + commandEncoder.BeginRenderPass(&renderPass.renderPassInfo); + renderPassEncoder.SetPipeline(pipeline); + renderPassEncoder.SetBindGroup(0, mBindGroups[0], offsets.size(), offsets.data()); + renderPassEncoder.Draw(3); + renderPassEncoder.SetPipeline(testPipeline); + renderPassEncoder.SetBindGroup(1, mBindGroups[1]); + renderPassEncoder.Draw(3); + renderPassEncoder.EndPass(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); queue.Submit(1, &commands); - std::vector expectedData = {5, 6}; - EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffer, + std::vector expectedData = {12, 16}; + EXPECT_PIXEL_RGBA8_EQ(RGBA8(5, 6, 255, 255), renderPass.color, 0, 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], kMinDynamicBufferOffsetAlignment, expectedData.size()); } -DAWN_INSTANTIATE_TEST(DynamicBufferOffsetTests, MetalBackend, VulkanBackend); +// Test inherit dynamic offsets on compute pipeline +// TODO(shaobo.yan@intel.com) : Try this test on GTX1080 and cannot reproduce the failure. +// Suspect it is due to dawn doesn't handle sync between two dispatch and disable this case. +// Will double check root cause after got GTX1660. +TEST_P(DynamicBufferOffsetTests, InheritDynamicOffestsComputePipeline) { + DAWN_SKIP_TEST_IF(IsWindows()); + wgpu::ComputePipeline pipeline = CreateComputePipeline(); + wgpu::ComputePipeline testPipeline = CreateComputePipeline(true); + + std::array offsets = {kMinDynamicBufferOffsetAlignment, + kMinDynamicBufferOffsetAlignment}; + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetPipeline(pipeline); + computePassEncoder.SetBindGroup(0, mBindGroups[0], offsets.size(), offsets.data()); + computePassEncoder.Dispatch(1); + computePassEncoder.SetPipeline(testPipeline); + computePassEncoder.SetBindGroup(1, mBindGroups[1]); + computePassEncoder.Dispatch(1); + computePassEncoder.EndPass(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = {12, 16}; + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], + kMinDynamicBufferOffsetAlignment, expectedData.size()); +} + +// Setting multiple dynamic offsets for the same bindgroup in one render pass. +TEST_P(DynamicBufferOffsetTests, UpdateDynamicOffestsMultipleTimesRenderPipeline) { + // Using default pipeline and setting dynamic offsets + wgpu::RenderPipeline pipeline = CreateRenderPipeline(); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + std::array offsets = {kMinDynamicBufferOffsetAlignment, + kMinDynamicBufferOffsetAlignment}; + std::array testOffsets = {0, 0}; + + wgpu::RenderPassEncoder renderPassEncoder = + commandEncoder.BeginRenderPass(&renderPass.renderPassInfo); + renderPassEncoder.SetPipeline(pipeline); + renderPassEncoder.SetBindGroup(0, mBindGroups[0], offsets.size(), offsets.data()); + renderPassEncoder.Draw(3); + renderPassEncoder.SetBindGroup(0, mBindGroups[0], testOffsets.size(), testOffsets.data()); + renderPassEncoder.Draw(3); + renderPassEncoder.EndPass(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = {2, 4}; + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 255, 255), renderPass.color, 0, 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], 0, expectedData.size()); +} + +// Setting multiple dynamic offsets for the same bindgroup in one compute pass. +TEST_P(DynamicBufferOffsetTests, UpdateDynamicOffsetsMultipleTimesComputePipeline) { + wgpu::ComputePipeline pipeline = CreateComputePipeline(); + + std::array offsets = {kMinDynamicBufferOffsetAlignment, + kMinDynamicBufferOffsetAlignment}; + std::array testOffsets = {0, 0}; + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetPipeline(pipeline); + computePassEncoder.SetBindGroup(0, mBindGroups[0], offsets.size(), offsets.data()); + computePassEncoder.Dispatch(1); + computePassEncoder.SetBindGroup(0, mBindGroups[0], testOffsets.size(), testOffsets.data()); + computePassEncoder.Dispatch(1); + computePassEncoder.EndPass(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + std::vector expectedData = {2, 4}; + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), mStorageBuffers[1], 0, expectedData.size()); +} + +DAWN_INSTANTIATE_TEST(DynamicBufferOffsetTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/FenceTests.cpp b/third_party/dawn/src/tests/end2end/FenceTests.cpp index b88c953e375..34d419846d3 100644 --- a/third_party/dawn/src/tests/end2end/FenceTests.cpp +++ b/third_party/dawn/src/tests/end2end/FenceTests.cpp @@ -18,24 +18,37 @@ #include #include +using namespace testing; + class MockFenceOnCompletionCallback { public: - MOCK_METHOD2(Call, void(DawnFenceCompletionStatus status, void* userdata)); + MOCK_METHOD(void, Call, (WGPUFenceCompletionStatus status, void* userdata)); }; static std::unique_ptr mockFenceOnCompletionCallback; -static void ToMockFenceOnCompletionCallback(DawnFenceCompletionStatus status, void* userdata) { +static void ToMockFenceOnCompletionCallback(WGPUFenceCompletionStatus status, void* userdata) { mockFenceOnCompletionCallback->Call(status, userdata); } + +class MockPopErrorScopeCallback { + public: + MOCK_METHOD(void, Call, (WGPUErrorType type, const char* message, void* userdata)); +}; + +static std::unique_ptr mockPopErrorScopeCallback; +static void ToMockPopErrorScopeCallback(WGPUErrorType type, const char* message, void* userdata) { + mockPopErrorScopeCallback->Call(type, message, userdata); +} + class FenceTests : public DawnTest { private: struct CallbackInfo { FenceTests* test; uint64_t value; - DawnFenceCompletionStatus status; + WGPUFenceCompletionStatus status; int32_t callIndex = -1; // If this is -1, the callback was not called - void Update(DawnFenceCompletionStatus status) { + void Update(WGPUFenceCompletionStatus status) { this->callIndex = test->mCallIndex++; this->status = status; } @@ -50,14 +63,16 @@ class FenceTests : public DawnTest { void SetUp() override { DawnTest::SetUp(); mockFenceOnCompletionCallback = std::make_unique(); + mockPopErrorScopeCallback = std::make_unique(); } void TearDown() override { mockFenceOnCompletionCallback = nullptr; + mockPopErrorScopeCallback = nullptr; DawnTest::TearDown(); } - void WaitForCompletedValue(dawn::Fence fence, uint64_t completedValue) { + void WaitForCompletedValue(wgpu::Fence fence, uint64_t completedValue) { while (fence.GetCompletedValue() < completedValue) { WaitABit(); } @@ -66,9 +81,9 @@ class FenceTests : public DawnTest { // Test that signaling a fence updates the completed value TEST_P(FenceTests, SimpleSignal) { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1u; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); // Completed value starts at initial value EXPECT_EQ(fence.GetCompletedValue(), 1u); @@ -82,9 +97,7 @@ TEST_P(FenceTests, SimpleSignal) { // Test callbacks are called in increasing order of fence completion value TEST_P(FenceTests, OnCompletionOrdering) { - dawn::FenceDescriptor descriptor; - descriptor.initialValue = 0u; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(); queue.Signal(fence, 4); @@ -92,19 +105,19 @@ TEST_P(FenceTests, OnCompletionOrdering) { testing::InSequence s; EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 0)) + Call(WGPUFenceCompletionStatus_Success, this + 0)) .Times(1); EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 1)) + Call(WGPUFenceCompletionStatus_Success, this + 1)) .Times(1); EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 2)) + Call(WGPUFenceCompletionStatus_Success, this + 2)) .Times(1); EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 3)) + Call(WGPUFenceCompletionStatus_Success, this + 3)) .Times(1); } @@ -118,42 +131,71 @@ TEST_P(FenceTests, OnCompletionOrdering) { // Test callbacks still occur if Queue::Signal happens multiple times TEST_P(FenceTests, MultipleSignalOnCompletion) { - dawn::FenceDescriptor descriptor; - descriptor.initialValue = 0u; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(); queue.Signal(fence, 2); queue.Signal(fence, 4); - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, nullptr)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr)) .Times(1); fence.OnCompletion(3u, ToMockFenceOnCompletionCallback, nullptr); WaitForCompletedValue(fence, 4); } +// Test callbacks still occur if Queue::Signal and fence::OnCompletion happens multiple times +TEST_P(FenceTests, SignalOnCompletionWait) { + wgpu::Fence fence = queue.CreateFence(); + + queue.Signal(fence, 2); + queue.Signal(fence, 6); + + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 2)) + .Times(1); + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 6)) + .Times(1); + + fence.OnCompletion(1u, ToMockFenceOnCompletionCallback, this + 2); + fence.OnCompletion(5u, ToMockFenceOnCompletionCallback, this + 6); + + WaitForCompletedValue(fence, 6); +} + +// Test callbacks still occur if Queue::Signal and fence::OnCompletion happens multiple times +TEST_P(FenceTests, SignalOnCompletionWaitStaggered) { + wgpu::Fence fence = queue.CreateFence(); + + queue.Signal(fence, 2); + + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 2)) + .Times(1); + fence.OnCompletion(1u, ToMockFenceOnCompletionCallback, this + 2); + + queue.Signal(fence, 4); + + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 4)) + .Times(1); + fence.OnCompletion(3u, ToMockFenceOnCompletionCallback, this + 4); + + WaitForCompletedValue(fence, 4); +} + // Test all callbacks are called if they are added for the same fence value TEST_P(FenceTests, OnCompletionMultipleCallbacks) { - dawn::FenceDescriptor descriptor; - descriptor.initialValue = 0u; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(); queue.Signal(fence, 4); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 0)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0)) .Times(1); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 1)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 1)) .Times(1); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 2)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 2)) .Times(1); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 3)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 3)) .Times(1); fence.OnCompletion(4u, ToMockFenceOnCompletionCallback, this + 0); @@ -167,32 +209,28 @@ TEST_P(FenceTests, OnCompletionMultipleCallbacks) { // TODO(enga): Enable when fence is removed from fence signal tracker // Currently it holds a reference and is not destructed TEST_P(FenceTests, DISABLED_DestroyBeforeOnCompletionEnd) { - dawn::FenceDescriptor descriptor; - descriptor.initialValue = 0u; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(); // The fence in this block will be deleted when it goes out of scope { - dawn::FenceDescriptor descriptor; - descriptor.initialValue = 0u; - dawn::Fence testFence = queue.CreateFence(&descriptor); + wgpu::Fence testFence = queue.CreateFence(); queue.Signal(testFence, 4); EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, this + 0)) + Call(WGPUFenceCompletionStatus_Unknown, this + 0)) .Times(1); EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, this + 1)) + Call(WGPUFenceCompletionStatus_Unknown, this + 1)) .Times(1); EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, this + 2)) + Call(WGPUFenceCompletionStatus_Unknown, this + 2)) .Times(1); EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, this + 3)) + Call(WGPUFenceCompletionStatus_Unknown, this + 3)) .Times(1); testFence.OnCompletion(1u, ToMockFenceOnCompletionCallback, this + 0); @@ -206,4 +244,20 @@ TEST_P(FenceTests, DISABLED_DestroyBeforeOnCompletionEnd) { WaitForCompletedValue(fence, 1); } -DAWN_INSTANTIATE_TEST(FenceTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +// Regression test that validation errors that are tracked client-side are captured +// in error scopes. +TEST_P(FenceTests, ClientValidationErrorInErrorScope) { + wgpu::Fence fence = queue.CreateFence(); + + queue.Signal(fence, 4); + + device.PushErrorScope(wgpu::ErrorFilter::Validation); + queue.Signal(fence, 2); + + EXPECT_CALL(*mockPopErrorScopeCallback, Call(WGPUErrorType_Validation, _, this)).Times(1); + device.PopErrorScope(ToMockPopErrorScopeCallback, this); + + WaitForCompletedValue(fence, 4); +} + +DAWN_INSTANTIATE_TEST(FenceTests, D3D12Backend(), MetalBackend(), OpenGLBackend(), VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/GpuMemorySynchronizationTests.cpp b/third_party/dawn/src/tests/end2end/GpuMemorySynchronizationTests.cpp new file mode 100644 index 00000000000..4cf64d0929d --- /dev/null +++ b/third_party/dawn/src/tests/end2end/GpuMemorySynchronizationTests.cpp @@ -0,0 +1,699 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "common/Constants.h" +#include "common/Math.h" +#include "tests/DawnTest.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class GpuMemorySyncTests : public DawnTest { + protected: + wgpu::Buffer CreateBuffer() { + wgpu::BufferDescriptor srcDesc; + srcDesc.size = 4; + srcDesc.usage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Storage; + wgpu::Buffer buffer = device.CreateBuffer(&srcDesc); + + int myData = 0; + queue.WriteBuffer(buffer, 0, &myData, sizeof(myData)); + return buffer; + } + + std::tuple CreatePipelineAndBindGroupForCompute( + const wgpu::Buffer& buffer) { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(std140, set = 0, binding = 0) buffer Data { + int a; + } data; + void main() { + data.a += 1; + })"); + + wgpu::ComputePipelineDescriptor cpDesc; + cpDesc.computeStage.module = csModule; + cpDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&cpDesc); + + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buffer}}); + return std::make_tuple(pipeline, bindGroup); + } + + std::tuple CreatePipelineAndBindGroupForRender( + const wgpu::Buffer& buffer, + wgpu::TextureFormat colorFormat) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + gl_PointSize = 1.0; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (set = 0, binding = 0) buffer Data { + int i; + } data; + layout(location = 0) out vec4 fragColor; + void main() { + data.i += 1; + fragColor = vec4(data.i / 255.f, 0.f, 0.f, 1.f); + })"); + + utils::ComboRenderPipelineDescriptor rpDesc(device); + rpDesc.vertexStage.module = vsModule; + rpDesc.cFragmentStage.module = fsModule; + rpDesc.primitiveTopology = wgpu::PrimitiveTopology::PointList; + rpDesc.cColorStates[0].format = colorFormat; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&rpDesc); + + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, buffer}}); + return std::make_tuple(pipeline, bindGroup); + } +}; + +// Clear storage buffer with zero. Then read data, add one, and write the result to storage buffer +// in compute pass. Iterate this read-add-write steps per compute pass a few time. The successive +// iteration reads the result in buffer from last iteration, which makes the iterations a data +// dependency chain. The test verifies that data in buffer among iterations in compute passes is +// correctly synchronized. +TEST_P(GpuMemorySyncTests, ComputePass) { + // Create pipeline, bind group, and buffer for compute pass. + wgpu::Buffer buffer = CreateBuffer(); + wgpu::ComputePipeline compute; + wgpu::BindGroup bindGroup; + std::tie(compute, bindGroup) = CreatePipelineAndBindGroupForCompute(buffer); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + // Iterate the read-add-write operations in compute pass a few times. + int iteration = 3; + for (int i = 0; i < iteration; ++i) { + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(compute); + pass.SetBindGroup(0, bindGroup); + pass.Dispatch(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Verify the result. + EXPECT_BUFFER_U32_EQ(iteration, buffer, 0); +} + +// Clear storage buffer with zero. Then read data, add one, and write the result to storage buffer +// in render pass. Iterate this read-add-write steps per render pass a few time. The successive +// iteration reads the result in buffer from last iteration, which makes the iterations a data +// dependency chain. In addition, color output by fragment shader depends on the data in storage +// buffer, so we can check color in render target to verify that data in buffer among iterations in +// render passes is correctly synchronized. +TEST_P(GpuMemorySyncTests, RenderPass) { + // Create pipeline, bind group, and buffer for render pass. + wgpu::Buffer buffer = CreateBuffer(); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::RenderPipeline render; + wgpu::BindGroup bindGroup; + std::tie(render, bindGroup) = + CreatePipelineAndBindGroupForRender(buffer, renderPass.colorFormat); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + // Iterate the read-add-write operations in render pass a few times. + int iteration = 3; + for (int i = 0; i < iteration; ++i) { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(render); + pass.SetBindGroup(0, bindGroup); + pass.Draw(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Verify the result. + EXPECT_PIXEL_RGBA8_EQ(RGBA8(iteration, 0, 0, 255), renderPass.color, 0, 0); +} + +// Write into a storage buffer in a render pass. Then read that data in a compute +// pass. And verify the data flow is correctly synchronized. +TEST_P(GpuMemorySyncTests, RenderPassToComputePass) { + // Create pipeline, bind group, and buffer for render pass and compute pass. + wgpu::Buffer buffer = CreateBuffer(); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::RenderPipeline render; + wgpu::BindGroup bindGroup0; + std::tie(render, bindGroup0) = + CreatePipelineAndBindGroupForRender(buffer, renderPass.colorFormat); + + wgpu::ComputePipeline compute; + wgpu::BindGroup bindGroup1; + std::tie(compute, bindGroup1) = CreatePipelineAndBindGroupForCompute(buffer); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + // Write data into a storage buffer in render pass. + wgpu::RenderPassEncoder pass0 = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass0.SetPipeline(render); + pass0.SetBindGroup(0, bindGroup0); + pass0.Draw(1); + pass0.EndPass(); + + // Read that data in compute pass. + wgpu::ComputePassEncoder pass1 = encoder.BeginComputePass(); + pass1.SetPipeline(compute); + pass1.SetBindGroup(0, bindGroup1); + pass1.Dispatch(1); + pass1.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Verify the result. + EXPECT_BUFFER_U32_EQ(2, buffer, 0); +} + +// Write into a storage buffer in a compute pass. Then read that data in a render +// pass. And verify the data flow is correctly synchronized. +TEST_P(GpuMemorySyncTests, ComputePassToRenderPass) { + // Create pipeline, bind group, and buffer for compute pass and render pass. + wgpu::Buffer buffer = CreateBuffer(); + wgpu::ComputePipeline compute; + wgpu::BindGroup bindGroup1; + std::tie(compute, bindGroup1) = CreatePipelineAndBindGroupForCompute(buffer); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::RenderPipeline render; + wgpu::BindGroup bindGroup0; + std::tie(render, bindGroup0) = + CreatePipelineAndBindGroupForRender(buffer, renderPass.colorFormat); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + // Write data into a storage buffer in compute pass. + wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass(); + pass0.SetPipeline(compute); + pass0.SetBindGroup(0, bindGroup1); + pass0.Dispatch(1); + pass0.EndPass(); + + // Read that data in render pass. + wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(render); + pass1.SetBindGroup(0, bindGroup0); + pass1.Draw(1); + pass1.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Verify the result. + EXPECT_PIXEL_RGBA8_EQ(RGBA8(2, 0, 0, 255), renderPass.color, 0, 0); +} + +DAWN_INSTANTIATE_TEST(GpuMemorySyncTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +class StorageToUniformSyncTests : public DawnTest { + protected: + void CreateBuffer() { + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = sizeof(float); + bufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform; + mBuffer = device.CreateBuffer(&bufferDesc); + } + + std::tuple CreatePipelineAndBindGroupForCompute() { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(std140, set = 0, binding = 0) buffer Data { + float a; + } data; + void main() { + data.a = 1.0; + })"); + + wgpu::ComputePipelineDescriptor cpDesc; + cpDesc.computeStage.module = csModule; + cpDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&cpDesc); + + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, mBuffer}}); + return std::make_tuple(pipeline, bindGroup); + } + + std::tuple CreatePipelineAndBindGroupForRender( + wgpu::TextureFormat colorFormat) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + gl_PointSize = 1.0; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (set = 0, binding = 0) uniform Contents{ + float color; + }; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(color, 0.f, 0.f, 1.f); + })"); + + utils::ComboRenderPipelineDescriptor rpDesc(device); + rpDesc.vertexStage.module = vsModule; + rpDesc.cFragmentStage.module = fsModule; + rpDesc.primitiveTopology = wgpu::PrimitiveTopology::PointList; + rpDesc.cColorStates[0].format = colorFormat; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&rpDesc); + + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, mBuffer}}); + return std::make_tuple(pipeline, bindGroup); + } + + wgpu::Buffer mBuffer; +}; + +// Write into a storage buffer in compute pass in a command buffer. Then read that data in a render +// pass. The two passes use the same command buffer. +TEST_P(StorageToUniformSyncTests, ReadAfterWriteWithSameCommandBuffer) { + // Create pipeline, bind group, and buffer for compute pass and render pass. + CreateBuffer(); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::ComputePipeline compute; + wgpu::BindGroup computeBindGroup; + std::tie(compute, computeBindGroup) = CreatePipelineAndBindGroupForCompute(); + wgpu::RenderPipeline render; + wgpu::BindGroup renderBindGroup; + std::tie(render, renderBindGroup) = CreatePipelineAndBindGroupForRender(renderPass.colorFormat); + + // Write data into a storage buffer in compute pass. + wgpu::CommandEncoder encoder0 = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass0 = encoder0.BeginComputePass(); + pass0.SetPipeline(compute); + pass0.SetBindGroup(0, computeBindGroup); + pass0.Dispatch(1); + pass0.EndPass(); + + // Read that data in render pass. + wgpu::RenderPassEncoder pass1 = encoder0.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(render); + pass1.SetBindGroup(0, renderBindGroup); + pass1.Draw(1); + pass1.EndPass(); + + wgpu::CommandBuffer commands = encoder0.Finish(); + queue.Submit(1, &commands); + + // Verify the rendering result. + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0); +} + +// Write into a storage buffer in compute pass in a command buffer. Then read that data in a render +// pass. The two passes use the different command buffers. The command buffers are submitted to the +// queue in one shot. +TEST_P(StorageToUniformSyncTests, ReadAfterWriteWithDifferentCommandBuffers) { + // Create pipeline, bind group, and buffer for compute pass and render pass. + CreateBuffer(); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::ComputePipeline compute; + wgpu::BindGroup computeBindGroup; + std::tie(compute, computeBindGroup) = CreatePipelineAndBindGroupForCompute(); + wgpu::RenderPipeline render; + wgpu::BindGroup renderBindGroup; + std::tie(render, renderBindGroup) = CreatePipelineAndBindGroupForRender(renderPass.colorFormat); + + // Write data into a storage buffer in compute pass. + wgpu::CommandBuffer cb[2]; + wgpu::CommandEncoder encoder0 = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass0 = encoder0.BeginComputePass(); + pass0.SetPipeline(compute); + pass0.SetBindGroup(0, computeBindGroup); + pass0.Dispatch(1); + pass0.EndPass(); + cb[0] = encoder0.Finish(); + + // Read that data in render pass. + wgpu::CommandEncoder encoder1 = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass1 = encoder1.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(render); + pass1.SetBindGroup(0, renderBindGroup); + pass1.Draw(1); + pass1.EndPass(); + + cb[1] = encoder1.Finish(); + queue.Submit(2, cb); + + // Verify the rendering result. + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0); +} + +// Write into a storage buffer in compute pass in a command buffer. Then read that data in a render +// pass. The two passes use the different command buffers. The command buffers are submitted to the +// queue separately. +TEST_P(StorageToUniformSyncTests, ReadAfterWriteWithDifferentQueueSubmits) { + // Create pipeline, bind group, and buffer for compute pass and render pass. + CreateBuffer(); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + wgpu::ComputePipeline compute; + wgpu::BindGroup computeBindGroup; + std::tie(compute, computeBindGroup) = CreatePipelineAndBindGroupForCompute(); + wgpu::RenderPipeline render; + wgpu::BindGroup renderBindGroup; + std::tie(render, renderBindGroup) = CreatePipelineAndBindGroupForRender(renderPass.colorFormat); + + // Write data into a storage buffer in compute pass. + wgpu::CommandBuffer cb[2]; + wgpu::CommandEncoder encoder0 = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass0 = encoder0.BeginComputePass(); + pass0.SetPipeline(compute); + pass0.SetBindGroup(0, computeBindGroup); + pass0.Dispatch(1); + pass0.EndPass(); + cb[0] = encoder0.Finish(); + queue.Submit(1, &cb[0]); + + // Read that data in render pass. + wgpu::CommandEncoder encoder1 = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass1 = encoder1.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(render); + pass1.SetBindGroup(0, renderBindGroup); + pass1.Draw(1); + pass1.EndPass(); + + cb[1] = encoder1.Finish(); + queue.Submit(1, &cb[1]); + + // Verify the rendering result. + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0); +} + +DAWN_INSTANTIATE_TEST(StorageToUniformSyncTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +constexpr int kRTSize = 8; +constexpr int kVertexBufferStride = 4 * sizeof(float); + +class MultipleWriteThenMultipleReadTests : public DawnTest { + protected: + wgpu::Buffer CreateZeroedBuffer(uint64_t size, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor srcDesc; + srcDesc.size = size; + srcDesc.usage = usage; + wgpu::Buffer buffer = device.CreateBuffer(&srcDesc); + + std::vector zeros(size, 0); + queue.WriteBuffer(buffer, 0, zeros.data(), size); + + return buffer; + } +}; + +// Write into a few storage buffers in compute pass. Then read that data in a render pass. The +// readonly buffers in render pass include vertex buffer, index buffer, uniform buffer, and readonly +// storage buffer. Data to be read in all of these buffers in render pass depend on the write +// operation in compute pass. +TEST_P(MultipleWriteThenMultipleReadTests, SeparateBuffers) { + // Create pipeline, bind group, and different buffers for compute pass. + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(std140, set = 0, binding = 0) buffer VBContents { + vec4 pos[4]; + }; + + layout(std140, set = 0, binding = 1) buffer IBContents { + ivec4 indices[2]; + }; + + layout(std140, set = 0, binding = 2) buffer UniformContents { + float color0; + }; + + layout(std140, set = 0, binding = 3) buffer ReadonlyStorageContents { + float color1; + }; + + void main() { + pos[0] = vec4(-1.f, 1.f, 0.f, 1.f); + pos[1] = vec4(1.f, 1.f, 0.f, 1.f); + pos[2] = vec4(1.f, -1.f, 0.f, 1.f); + pos[3] = vec4(-1.f, -1.f, 0.f, 1.f); + int dummy = 0; + indices[0] = ivec4(0, 1, 2, 0); + indices[1] = ivec4(2, 3, dummy, dummy); + color0 = 1.0; + color1 = 1.0; + })"); + + wgpu::ComputePipelineDescriptor cpDesc; + cpDesc.computeStage.module = csModule; + cpDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc); + wgpu::Buffer vertexBuffer = CreateZeroedBuffer( + kVertexBufferStride * 4, + wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst); + wgpu::Buffer indexBuffer = CreateZeroedBuffer( + sizeof(int) * 4 * 2, + wgpu::BufferUsage::Index | wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst); + wgpu::Buffer uniformBuffer = + CreateZeroedBuffer(sizeof(float), wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage | + wgpu::BufferUsage::CopyDst); + wgpu::Buffer readonlyStorageBuffer = + CreateZeroedBuffer(sizeof(float), wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst); + + wgpu::BindGroup bindGroup0 = utils::MakeBindGroup( + device, cp.GetBindGroupLayout(0), + {{0, vertexBuffer}, {1, indexBuffer}, {2, uniformBuffer}, {3, readonlyStorageBuffer}}); + // Write data into storage buffers in compute pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass(); + pass0.SetPipeline(cp); + pass0.SetBindGroup(0, bindGroup0); + pass0.Dispatch(1); + pass0.EndPass(); + + // Create pipeline, bind group, and reuse buffers in render pass. + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(location = 0) in vec4 pos; + void main() { + gl_Position = pos; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (set = 0, binding = 0) uniform UniformBuffer { + float color0; + }; + + layout (set = 0, binding = 1) readonly buffer ReadonlyStorageBuffer { + float color1; + }; + + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(color0, color1, 0.f, 1.f); + })"); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + utils::ComboRenderPipelineDescriptor rpDesc(device); + rpDesc.vertexStage.module = vsModule; + rpDesc.cFragmentStage.module = fsModule; + rpDesc.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + rpDesc.cVertexState.vertexBufferCount = 1; + rpDesc.cVertexState.cVertexBuffers[0].arrayStride = kVertexBufferStride; + rpDesc.cVertexState.cVertexBuffers[0].attributeCount = 1; + rpDesc.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + rpDesc.cColorStates[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline rp = device.CreateRenderPipeline(&rpDesc); + + wgpu::BindGroup bindGroup1 = utils::MakeBindGroup( + device, rp.GetBindGroupLayout(0), {{0, uniformBuffer}, {1, readonlyStorageBuffer}}); + + // Read data in buffers in render pass. + wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(rp); + pass1.SetVertexBuffer(0, vertexBuffer); + pass1.SetIndexBuffer(indexBuffer, 0); + pass1.SetBindGroup(0, bindGroup1); + pass1.DrawIndexed(6); + pass1.EndPass(); + + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + // Verify the rendering result. + int min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kYellow, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kYellow, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kYellow, renderPass.color, min, max); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kYellow, renderPass.color, max, max); +} + +// Write into a storage buffer in compute pass. Then read that data in buffer in a render pass. The +// buffer is composed of vertices, indices, uniforms and readonly storage. Data to be read in the +// buffer in render pass depend on the write operation in compute pass. +TEST_P(MultipleWriteThenMultipleReadTests, OneBuffer) { + // Create pipeline, bind group, and a complex buffer for compute pass. + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(std140, set = 0, binding = 0) buffer Contents { + // Every single float (and every float in an array, and every single vec2, vec3, and + // every column in mat2/mat3, etc) uses the same amount of memory as vec4 (float4). + vec4 pos[4]; + vec4 padding0[12]; + ivec4 indices[2]; + ivec4 padding1[14]; + float color0; + float padding2[15]; + float color1; + }; + + void main() { + pos[0] = vec4(-1.f, 1.f, 0.f, 1.f); + pos[1] = vec4(1.f, 1.f, 0.f, 1.f); + pos[2] = vec4(1.f, -1.f, 0.f, 1.f); + pos[3] = vec4(-1.f, -1.f, 0.f, 1.f); + int dummy = 0; + indices[0] = ivec4(0, 1, 2, 0); + indices[1] = ivec4(2, 3, dummy, dummy); + color0 = 1.0; + color1 = 1.0; + })"); + + wgpu::ComputePipelineDescriptor cpDesc; + cpDesc.computeStage.module = csModule; + cpDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline cp = device.CreateComputePipeline(&cpDesc); + struct Data { + float pos[4][4]; + char padding0[256 - sizeof(float) * 16]; + int indices[2][4]; + char padding1[256 - sizeof(int) * 8]; + float color0[4]; + char padding2[256 - sizeof(float) * 4]; + float color1[4]; + }; + wgpu::Buffer buffer = CreateZeroedBuffer( + sizeof(Data), wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Index | + wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage | + wgpu::BufferUsage::CopyDst); + wgpu::BindGroup bindGroup0 = + utils::MakeBindGroup(device, cp.GetBindGroupLayout(0), {{0, buffer}}); + + // Write various data (vertices, indices, and uniforms) into the buffer in compute pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass(); + pass0.SetPipeline(cp); + pass0.SetBindGroup(0, bindGroup0); + pass0.Dispatch(1); + pass0.EndPass(); + + // Create pipeline, bind group, and reuse the buffer in render pass. + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(location = 0) in vec4 pos; + void main() { + gl_Position = pos; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (set = 0, binding = 0) uniform UniformBuffer { + float color0; + }; + + layout (set = 0, binding = 1) readonly buffer ReadonlyStorageBuffer { + float color1; + }; + + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(color0, color1, 0.f, 1.f); + })"); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + utils::ComboRenderPipelineDescriptor rpDesc(device); + rpDesc.vertexStage.module = vsModule; + rpDesc.cFragmentStage.module = fsModule; + rpDesc.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + rpDesc.cVertexState.vertexBufferCount = 1; + rpDesc.cVertexState.cVertexBuffers[0].arrayStride = kVertexBufferStride; + rpDesc.cVertexState.cVertexBuffers[0].attributeCount = 1; + rpDesc.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + rpDesc.cColorStates[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline rp = device.CreateRenderPipeline(&rpDesc); + + wgpu::BindGroup bindGroup1 = + utils::MakeBindGroup(device, rp.GetBindGroupLayout(0), + {{0, buffer, offsetof(Data, color0), sizeof(float)}, + {1, buffer, offsetof(Data, color1), sizeof(float)}}); + + // Read various data in the buffer in render pass. + wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass1.SetPipeline(rp); + pass1.SetVertexBuffer(0, buffer); + pass1.SetIndexBuffer(buffer, offsetof(Data, indices)); + pass1.SetBindGroup(0, bindGroup1); + pass1.DrawIndexed(6); + pass1.EndPass(); + + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + // Verify the rendering result. + int min = 1, max = kRTSize - 3; + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kYellow, renderPass.color, min, min); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kYellow, renderPass.color, max, min); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kYellow, renderPass.color, min, max); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kYellow, renderPass.color, max, max); +} + +DAWN_INSTANTIATE_TEST(MultipleWriteThenMultipleReadTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/IOSurfaceWrappingTests.cpp b/third_party/dawn/src/tests/end2end/IOSurfaceWrappingTests.cpp index 4dd2002734e..9e44cb7a7c7 100644 --- a/third_party/dawn/src/tests/end2end/IOSurfaceWrappingTests.cpp +++ b/third_party/dawn/src/tests/end2end/IOSurfaceWrappingTests.cpp @@ -16,9 +16,10 @@ #include "dawn_native/MetalBackend.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" #include +#include #include namespace { @@ -93,13 +94,18 @@ namespace { class IOSurfaceTestBase : public DawnTest { public: - dawn::Texture WrapIOSurface(const dawn::TextureDescriptor* descriptor, + wgpu::Texture WrapIOSurface(const wgpu::TextureDescriptor* descriptor, IOSurfaceRef ioSurface, - uint32_t plane) { - DawnTexture texture = dawn_native::metal::WrapIOSurface( - device.Get(), reinterpret_cast(descriptor), ioSurface, - plane); - return dawn::Texture::Acquire(texture); + uint32_t plane, + bool isCleared = true) { + dawn_native::metal::ExternalImageDescriptorIOSurface externDesc; + externDesc.cTextureDescriptor = + reinterpret_cast(descriptor); + externDesc.ioSurface = ioSurface; + externDesc.plane = plane; + externDesc.isCleared = isCleared; + WGPUTexture texture = dawn_native::metal::WrapIOSurface(device.Get(), &externDesc); + return wgpu::Texture::Acquire(texture); } }; @@ -110,35 +116,36 @@ namespace { class IOSurfaceValidationTests : public IOSurfaceTestBase { public: IOSurfaceValidationTests() { - defaultIOSurface = CreateSinglePlaneIOSurface(10, 10, 'BGRA', 4); + defaultIOSurface = CreateSinglePlaneIOSurface(10, 10, kCVPixelFormatType_32BGRA, 4); - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.format = dawn::TextureFormat::B8G8R8A8Unorm; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.format = wgpu::TextureFormat::BGRA8Unorm; descriptor.size = {10, 10, 1}; descriptor.sampleCount = 1; - descriptor.arrayLayerCount = 1; descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment; + descriptor.usage = wgpu::TextureUsage::OutputAttachment; } protected: - dawn::TextureDescriptor descriptor; + wgpu::TextureDescriptor descriptor; ScopedIOSurfaceRef defaultIOSurface; }; // Test a successful wrapping of an IOSurface in a texture TEST_P(IOSurfaceValidationTests, Success) { DAWN_SKIP_TEST_IF(UsesWire()); - dawn::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0); + wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0); ASSERT_NE(texture.Get(), nullptr); } // Test an error occurs if the texture descriptor is invalid TEST_P(IOSurfaceValidationTests, InvalidTextureDescriptor) { DAWN_SKIP_TEST_IF(UsesWire()); - descriptor.nextInChain = this; - ASSERT_DEVICE_ERROR(dawn::Texture texture = + wgpu::ChainedStruct chainedDescriptor; + descriptor.nextInChain = &chainedDescriptor; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0)); ASSERT_EQ(texture.Get(), nullptr); } @@ -146,7 +153,7 @@ TEST_P(IOSurfaceValidationTests, InvalidTextureDescriptor) { // Test an error occurs if the plane is too large TEST_P(IOSurfaceValidationTests, PlaneTooLarge) { DAWN_SKIP_TEST_IF(UsesWire()); - ASSERT_DEVICE_ERROR(dawn::Texture texture = + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 1)); ASSERT_EQ(texture.Get(), nullptr); } @@ -155,9 +162,9 @@ TEST_P(IOSurfaceValidationTests, PlaneTooLarge) { // TODO(cwallez@chromium.org): Reenable when 1D or 3D textures are implemented TEST_P(IOSurfaceValidationTests, DISABLED_InvalidTextureDimension) { DAWN_SKIP_TEST_IF(UsesWire()); - descriptor.dimension = dawn::TextureDimension::e2D; + descriptor.dimension = wgpu::TextureDimension::e2D; - ASSERT_DEVICE_ERROR(dawn::Texture texture = + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0)); ASSERT_EQ(texture.Get(), nullptr); } @@ -167,17 +174,17 @@ TEST_P(IOSurfaceValidationTests, InvalidMipLevelCount) { DAWN_SKIP_TEST_IF(UsesWire()); descriptor.mipLevelCount = 2; - ASSERT_DEVICE_ERROR(dawn::Texture texture = + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0)); ASSERT_EQ(texture.Get(), nullptr); } -// Test an error occurs if the descriptor array layer count isn't 1 -TEST_P(IOSurfaceValidationTests, InvalidArrayLayerCount) { +// Test an error occurs if the descriptor depth isn't 1 +TEST_P(IOSurfaceValidationTests, InvalidDepth) { DAWN_SKIP_TEST_IF(UsesWire()); - descriptor.arrayLayerCount = 2; + descriptor.size.depth = 2; - ASSERT_DEVICE_ERROR(dawn::Texture texture = + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0)); ASSERT_EQ(texture.Get(), nullptr); } @@ -187,7 +194,7 @@ TEST_P(IOSurfaceValidationTests, InvalidSampleCount) { DAWN_SKIP_TEST_IF(UsesWire()); descriptor.sampleCount = 4; - ASSERT_DEVICE_ERROR(dawn::Texture texture = + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0)); ASSERT_EQ(texture.Get(), nullptr); } @@ -197,7 +204,7 @@ TEST_P(IOSurfaceValidationTests, InvalidWidth) { DAWN_SKIP_TEST_IF(UsesWire()); descriptor.size.width = 11; - ASSERT_DEVICE_ERROR(dawn::Texture texture = + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0)); ASSERT_EQ(texture.Get(), nullptr); } @@ -207,7 +214,7 @@ TEST_P(IOSurfaceValidationTests, InvalidHeight) { DAWN_SKIP_TEST_IF(UsesWire()); descriptor.size.height = 11; - ASSERT_DEVICE_ERROR(dawn::Texture texture = + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0)); ASSERT_EQ(texture.Get(), nullptr); } @@ -215,9 +222,9 @@ TEST_P(IOSurfaceValidationTests, InvalidHeight) { // Test an error occurs if the descriptor format isn't compatible with the IOSurface's TEST_P(IOSurfaceValidationTests, InvalidFormat) { DAWN_SKIP_TEST_IF(UsesWire()); - descriptor.format = dawn::TextureFormat::R8Unorm; + descriptor.format = wgpu::TextureFormat::R8Unorm; - ASSERT_DEVICE_ERROR(dawn::Texture texture = + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapIOSurface(&descriptor, defaultIOSurface.get(), 0)); ASSERT_EQ(texture.Get(), nullptr); } @@ -228,7 +235,7 @@ class IOSurfaceUsageTests : public IOSurfaceTestBase { public: // Test that sampling a 1x1 works. void DoSampleTest(IOSurfaceRef ioSurface, - dawn::TextureFormat format, + wgpu::TextureFormat format, void* data, size_t dataSize, RGBA8 expectedColor) { @@ -237,38 +244,11 @@ class IOSurfaceUsageTests : public IOSurfaceTestBase { memcpy(IOSurfaceGetBaseAddress(ioSurface), data, dataSize); IOSurfaceUnlock(ioSurface, 0, nullptr); - // The bindgroup containing the texture view for the ioSurface as well as the sampler. - dawn::BindGroupLayout bgl; - dawn::BindGroup bindGroup; - { - dawn::TextureDescriptor textureDescriptor; - textureDescriptor.dimension = dawn::TextureDimension::e2D; - textureDescriptor.format = format; - textureDescriptor.size = {1, 1, 1}; - textureDescriptor.sampleCount = 1; - textureDescriptor.arrayLayerCount = 1; - textureDescriptor.mipLevelCount = 1; - textureDescriptor.usage = dawn::TextureUsageBit::Sampled; - dawn::Texture wrappingTexture = WrapIOSurface(&textureDescriptor, ioSurface, 0); - - dawn::TextureView textureView = wrappingTexture.CreateDefaultView(); - - dawn::SamplerDescriptor samplerDescriptor = utils::GetDefaultSamplerDescriptor(); - dawn::Sampler sampler = device.CreateSampler(&samplerDescriptor); - - bgl = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}, - {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}, - }); - - bindGroup = utils::MakeBindGroup(device, bgl, {{0, sampler}, {1, textureView}}); - } - // The simplest texture sampling pipeline. - dawn::RenderPipeline pipeline; + wgpu::RenderPipeline pipeline; { - dawn::ShaderModule vs = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vs = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout (location = 0) out vec2 o_texCoord; void main() { @@ -288,8 +268,8 @@ class IOSurfaceUsageTests : public IOSurfaceTestBase { o_texCoord = texCoord[gl_VertexIndex]; } )"); - dawn::ShaderModule fs = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fs = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform sampler sampler0; layout(set = 0, binding = 1) uniform texture2D texture0; @@ -302,26 +282,46 @@ class IOSurfaceUsageTests : public IOSurfaceTestBase { )"); utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vs; + descriptor.vertexStage.module = vs; descriptor.cFragmentStage.module = fs; - descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); - descriptor.cColorStates[0]->format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; pipeline = device.CreateRenderPipeline(&descriptor); } + // The bindgroup containing the texture view for the ioSurface as well as the sampler. + wgpu::BindGroup bindGroup; + { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.dimension = wgpu::TextureDimension::e2D; + textureDescriptor.format = format; + textureDescriptor.size = {1, 1, 1}; + textureDescriptor.sampleCount = 1; + textureDescriptor.mipLevelCount = 1; + textureDescriptor.usage = wgpu::TextureUsage::Sampled; + wgpu::Texture wrappingTexture = WrapIOSurface(&textureDescriptor, ioSurface, 0); + + wgpu::TextureView textureView = wrappingTexture.CreateView(); + + wgpu::SamplerDescriptor samplerDescriptor = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDescriptor); + + bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, textureView}}); + } + // Submit commands samping from the ioSurface and writing the result to renderPass.color utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.Draw(6, 1, 0, 0); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(expectedColor, renderPass.color, 0, 0); @@ -329,32 +329,31 @@ class IOSurfaceUsageTests : public IOSurfaceTestBase { // Test that clearing using BeginRenderPass writes correct data in the ioSurface. void DoClearTest(IOSurfaceRef ioSurface, - dawn::TextureFormat format, + wgpu::TextureFormat format, void* data, size_t dataSize) { // Get a texture view for the ioSurface - dawn::TextureDescriptor textureDescriptor; - textureDescriptor.dimension = dawn::TextureDimension::e2D; + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.dimension = wgpu::TextureDimension::e2D; textureDescriptor.format = format; textureDescriptor.size = {1, 1, 1}; textureDescriptor.sampleCount = 1; - textureDescriptor.arrayLayerCount = 1; textureDescriptor.mipLevelCount = 1; - textureDescriptor.usage = dawn::TextureUsageBit::OutputAttachment; - dawn::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface, 0); + textureDescriptor.usage = wgpu::TextureUsage::OutputAttachment; + wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface, 0); - dawn::TextureView ioSurfaceView = ioSurfaceTexture.CreateDefaultView(); + wgpu::TextureView ioSurfaceView = ioSurfaceTexture.CreateView(); utils::ComboRenderPassDescriptor renderPassDescriptor({ioSurfaceView}, {}); - renderPassDescriptor.cColorAttachmentsInfoPtr[0]->clearColor = {1 / 255.0f, 2 / 255.0f, - 3 / 255.0f, 4 / 255.0f}; + renderPassDescriptor.cColorAttachments[0].clearColor = {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, + 4 / 255.0f}; // Execute commands to clear the ioSurface - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); pass.EndPass(); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); // Wait for the commands touching the IOSurface to be scheduled @@ -370,59 +369,106 @@ class IOSurfaceUsageTests : public IOSurfaceTestBase { // Test sampling from a R8 IOSurface TEST_P(IOSurfaceUsageTests, SampleFromR8IOSurface) { DAWN_SKIP_TEST_IF(UsesWire()); - ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'L008', 1); + ScopedIOSurfaceRef ioSurface = + CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_OneComponent8, 1); uint8_t data = 0x01; - DoSampleTest(ioSurface.get(), dawn::TextureFormat::R8Unorm, &data, sizeof(data), + DoSampleTest(ioSurface.get(), wgpu::TextureFormat::R8Unorm, &data, sizeof(data), RGBA8(1, 0, 0, 255)); } // Test clearing a R8 IOSurface TEST_P(IOSurfaceUsageTests, ClearR8IOSurface) { DAWN_SKIP_TEST_IF(UsesWire()); - ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'L008', 1); + ScopedIOSurfaceRef ioSurface = + CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_OneComponent8, 1); uint8_t data = 0x01; - DoClearTest(ioSurface.get(), dawn::TextureFormat::R8Unorm, &data, sizeof(data)); + DoClearTest(ioSurface.get(), wgpu::TextureFormat::R8Unorm, &data, sizeof(data)); } // Test sampling from a RG8 IOSurface TEST_P(IOSurfaceUsageTests, SampleFromRG8IOSurface) { DAWN_SKIP_TEST_IF(UsesWire()); - ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, '2C08', 2); + ScopedIOSurfaceRef ioSurface = + CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_TwoComponent8, 2); uint16_t data = 0x0102; // Stored as (G, R) - DoSampleTest(ioSurface.get(), dawn::TextureFormat::R8G8Unorm, &data, sizeof(data), + DoSampleTest(ioSurface.get(), wgpu::TextureFormat::RG8Unorm, &data, sizeof(data), RGBA8(2, 1, 0, 255)); } // Test clearing a RG8 IOSurface TEST_P(IOSurfaceUsageTests, ClearRG8IOSurface) { DAWN_SKIP_TEST_IF(UsesWire()); - ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, '2C08', 2); + ScopedIOSurfaceRef ioSurface = + CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_TwoComponent8, 2); uint16_t data = 0x0201; - DoClearTest(ioSurface.get(), dawn::TextureFormat::R8G8Unorm, &data, sizeof(data)); + DoClearTest(ioSurface.get(), wgpu::TextureFormat::RG8Unorm, &data, sizeof(data)); } // Test sampling from a BGRA8 IOSurface -TEST_P(IOSurfaceUsageTests, SampleFromBGRA8888IOSurface) { +TEST_P(IOSurfaceUsageTests, SampleFromBGRA8IOSurface) { DAWN_SKIP_TEST_IF(UsesWire()); - ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4); + ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32BGRA, 4); uint32_t data = 0x01020304; // Stored as (A, R, G, B) - DoSampleTest(ioSurface.get(), dawn::TextureFormat::B8G8R8A8Unorm, &data, sizeof(data), + DoSampleTest(ioSurface.get(), wgpu::TextureFormat::BGRA8Unorm, &data, sizeof(data), RGBA8(2, 3, 4, 1)); } // Test clearing a BGRA8 IOSurface TEST_P(IOSurfaceUsageTests, ClearBGRA8IOSurface) { DAWN_SKIP_TEST_IF(UsesWire()); - ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, 'BGRA', 4); + ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32BGRA, 4); uint32_t data = 0x04010203; - DoClearTest(ioSurface.get(), dawn::TextureFormat::B8G8R8A8Unorm, &data, sizeof(data)); + DoClearTest(ioSurface.get(), wgpu::TextureFormat::BGRA8Unorm, &data, sizeof(data)); +} + +// Test sampling from an RGBA8 IOSurface +TEST_P(IOSurfaceUsageTests, SampleFromRGBA8IOSurface) { + DAWN_SKIP_TEST_IF(UsesWire()); + ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32RGBA, 4); + + uint32_t data = 0x01020304; // Stored as (A, B, G, R) + DoSampleTest(ioSurface.get(), wgpu::TextureFormat::RGBA8Unorm, &data, sizeof(data), + RGBA8(4, 3, 2, 1)); +} + +// Test clearing an RGBA8 IOSurface +TEST_P(IOSurfaceUsageTests, ClearRGBA8IOSurface) { + DAWN_SKIP_TEST_IF(UsesWire()); + ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32RGBA, 4); + + uint32_t data = 0x04030201; + DoClearTest(ioSurface.get(), wgpu::TextureFormat::RGBA8Unorm, &data, sizeof(data)); +} + +// Test that texture with color is cleared when isCleared = false +TEST_P(IOSurfaceUsageTests, UnclearedTextureIsCleared) { + DAWN_SKIP_TEST_IF(UsesWire()); + + ScopedIOSurfaceRef ioSurface = CreateSinglePlaneIOSurface(1, 1, kCVPixelFormatType_32RGBA, 4); + uint32_t data = 0x04030201; + + IOSurfaceLock(ioSurface.get(), 0, nullptr); + memcpy(IOSurfaceGetBaseAddress(ioSurface.get()), &data, sizeof(data)); + IOSurfaceUnlock(ioSurface.get(), 0, nullptr); + + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.dimension = wgpu::TextureDimension::e2D; + textureDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + textureDescriptor.size = {1, 1, 1}; + textureDescriptor.sampleCount = 1; + textureDescriptor.mipLevelCount = 1; + textureDescriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + + // wrap ioSurface and ensure color is not visible when isCleared set to false + wgpu::Texture ioSurfaceTexture = WrapIOSurface(&textureDescriptor, ioSurface.get(), 0, false); + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), ioSurfaceTexture, 0, 0); } -DAWN_INSTANTIATE_TEST(IOSurfaceValidationTests, MetalBackend); -DAWN_INSTANTIATE_TEST(IOSurfaceUsageTests, MetalBackend); +DAWN_INSTANTIATE_TEST(IOSurfaceValidationTests, MetalBackend()); +DAWN_INSTANTIATE_TEST(IOSurfaceUsageTests, MetalBackend()); diff --git a/third_party/dawn/src/tests/end2end/IndexFormatTests.cpp b/third_party/dawn/src/tests/end2end/IndexFormatTests.cpp index dcabee81c1b..554c674d00c 100644 --- a/third_party/dawn/src/tests/end2end/IndexFormatTests.cpp +++ b/third_party/dawn/src/tests/end2end/IndexFormatTests.cpp @@ -16,114 +16,105 @@ #include "common/Assert.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr uint32_t kRTSize = 400; class IndexFormatTest : public DawnTest { - protected: - void SetUp() override { - DawnTest::SetUp(); + protected: + void SetUp() override { + DawnTest::SetUp(); - renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - } - - utils::BasicRenderPass renderPass; + renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + } - dawn::RenderPipeline MakeTestPipeline(dawn::IndexFormat format) { + utils::BasicRenderPass renderPass; - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::RenderPipeline MakeTestPipeline(wgpu::IndexFormat format) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(location = 0) in vec4 pos; void main() { gl_Position = pos; - })" - ); + })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); - })" - ); - - utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; - descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; - descriptor.cVertexInput.indexFormat = format; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; - descriptor.cColorStates[0]->format = renderPass.colorFormat; - - return device.CreateRenderPipeline(&descriptor); - } + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cVertexState.indexFormat = format; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + descriptor.cColorStates[0].format = renderPass.colorFormat; + + return device.CreateRenderPipeline(&descriptor); + } }; // Test that the Uint32 index format is correctly interpreted TEST_P(IndexFormatTest, Uint32) { - dawn::RenderPipeline pipeline = MakeTestPipeline(dawn::IndexFormat::Uint32); - - dawn::Buffer vertexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Vertex, { - -1.0f, 1.0f, 0.0f, 1.0f, // Note Vertices[0] = Vertices[1] - -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 1.0f - }); + wgpu::RenderPipeline pipeline = MakeTestPipeline(wgpu::IndexFormat::Uint32); + + wgpu::Buffer vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + {-1.0f, -1.0f, 0.0f, 1.0f, // Note Vertices[0] = Vertices[1] + -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f}); // If this is interpreted as Uint16, then it would be 0, 1, 0, ... and would draw nothing. - dawn::Buffer indexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Index, { - 1, 2, 3 - }); + wgpu::Buffer indexBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Index, {1, 2, 3}); - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.SetIndexBuffer(indexBuffer, 0); - pass.DrawIndexed(3, 1, 0, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer); + pass.DrawIndexed(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 100, 300); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 100, 300); } // Test that the Uint16 index format is correctly interpreted TEST_P(IndexFormatTest, Uint16) { - dawn::RenderPipeline pipeline = MakeTestPipeline(dawn::IndexFormat::Uint16); + wgpu::RenderPipeline pipeline = MakeTestPipeline(wgpu::IndexFormat::Uint16); - dawn::Buffer vertexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Vertex, { - -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 1.0f - }); + wgpu::Buffer vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + {-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f}); // If this is interpreted as uint32, it will have index 1 and 2 be both 0 and render nothing - dawn::Buffer indexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Index, { - 1, 2, 0, 0, 0, 0 - }); + wgpu::Buffer indexBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Index, {1, 2, 0, 0, 0, 0}); - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.SetIndexBuffer(indexBuffer, 0); - pass.DrawIndexed(3, 1, 0, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer); + pass.DrawIndexed(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 100, 300); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 100, 300); } // Test for primitive restart use vertices like in the drawing and draw the following @@ -140,72 +131,84 @@ TEST_P(IndexFormatTest, Uint16) { // Test use of primitive restart with an Uint32 index format TEST_P(IndexFormatTest, Uint32PrimitiveRestart) { - dawn::RenderPipeline pipeline = MakeTestPipeline(dawn::IndexFormat::Uint32); - - dawn::Buffer vertexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Vertex, { - 0.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 0.0f, 0.0f, 1.0f, - 0.0f, 0.0f, 0.0f, 1.0f, - 0.0f, -1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 1.0f, - }); - dawn::Buffer indexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Index, { - 0, 1, 2, 0xFFFFFFFFu, 3, 4, 2, - }); - - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = MakeTestPipeline(wgpu::IndexFormat::Uint32); + + wgpu::Buffer vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + { + 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, + }); + wgpu::Buffer indexBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Index, + { + 0, + 1, + 2, + 0xFFFFFFFFu, + 3, + 4, + 2, + }); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.SetIndexBuffer(indexBuffer, 0); - pass.DrawIndexed(7, 1, 0, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer); + pass.DrawIndexed(7); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 190, 190); // A - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 210, 210); // B - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, 210, 190); // C + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 190, 190); // A + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 210, 210); // B + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, 210, 190); // C } // Test use of primitive restart with an Uint16 index format TEST_P(IndexFormatTest, Uint16PrimitiveRestart) { - dawn::RenderPipeline pipeline = MakeTestPipeline(dawn::IndexFormat::Uint16); - - dawn::Buffer vertexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Vertex, { - 0.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 0.0f, 0.0f, 1.0f, - 0.0f, 0.0f, 0.0f, 1.0f, - 0.0f, -1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 1.0f, - }); - dawn::Buffer indexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Index, { - 0, 1, 2, 0xFFFFu, 3, 4, 2, - // This value is for padding. - 0xFFFFu, - }); - - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = MakeTestPipeline(wgpu::IndexFormat::Uint16); + + wgpu::Buffer vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + { + 0.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, + }); + wgpu::Buffer indexBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Index, + { + 0, + 1, + 2, + 0xFFFFu, + 3, + 4, + 2, + // This value is for padding. + 0xFFFFu, + }); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.SetIndexBuffer(indexBuffer, 0); - pass.DrawIndexed(7, 1, 0, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer); + pass.DrawIndexed(7); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 190, 190); // A - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 210, 210); // B - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, 210, 190); // C + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 190, 190); // A + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 210, 210); // B + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, 210, 190); // C } // Test that the index format used is the format of the last set pipeline. This is to @@ -214,36 +217,32 @@ TEST_P(IndexFormatTest, Uint16PrimitiveRestart) { TEST_P(IndexFormatTest, ChangePipelineAfterSetIndexBuffer) { DAWN_SKIP_TEST_IF(IsD3D12() || IsVulkan()); - dawn::RenderPipeline pipeline32 = MakeTestPipeline(dawn::IndexFormat::Uint32); - dawn::RenderPipeline pipeline16 = MakeTestPipeline(dawn::IndexFormat::Uint16); + wgpu::RenderPipeline pipeline32 = MakeTestPipeline(wgpu::IndexFormat::Uint32); + wgpu::RenderPipeline pipeline16 = MakeTestPipeline(wgpu::IndexFormat::Uint16); - dawn::Buffer vertexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Vertex, { - -1.0f, 1.0f, 0.0f, 1.0f, // Note Vertices[0] = Vertices[1] - -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 1.0f - }); + wgpu::Buffer vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + {-1.0f, -1.0f, 0.0f, 1.0f, // Note Vertices[0] = Vertices[1] + -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f}); // If this is interpreted as Uint16, then it would be 0, 1, 0, ... and would draw nothing. - dawn::Buffer indexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Index, { - 1, 2, 3 - }); + wgpu::Buffer indexBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Index, {1, 2, 3}); - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline16); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.SetIndexBuffer(indexBuffer, 0); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetIndexBuffer(indexBuffer); pass.SetPipeline(pipeline32); - pass.DrawIndexed(3, 1, 0, 0, 0); + pass.DrawIndexed(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 100, 300); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 100, 300); } // Test that setting the index buffer before the pipeline works, this is important @@ -252,32 +251,32 @@ TEST_P(IndexFormatTest, ChangePipelineAfterSetIndexBuffer) { // TODO(cwallez@chromium.org): This is currently disallowed by the validation but // we want to support eventually. TEST_P(IndexFormatTest, DISABLED_SetIndexBufferBeforeSetPipeline) { - dawn::RenderPipeline pipeline = MakeTestPipeline(dawn::IndexFormat::Uint32); - - dawn::Buffer vertexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Vertex, { - -1.0f, 1.0f, 0.0f, 1.0f, - 1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -1.0f, 0.0f, 1.0f - }); - dawn::Buffer indexBuffer = utils::CreateBufferFromData(device, dawn::BufferUsageBit::Index, { - 0, 1, 2 - }); - - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = MakeTestPipeline(wgpu::IndexFormat::Uint32); + + wgpu::Buffer vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + {-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f}); + wgpu::Buffer indexBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Index, {0, 1, 2}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); - pass.SetIndexBuffer(indexBuffer, 0); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetIndexBuffer(indexBuffer); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.DrawIndexed(3, 1, 0, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer); + pass.DrawIndexed(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 100, 300); } -DAWN_INSTANTIATE_TEST(IndexFormatTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(IndexFormatTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/MultisampledRenderingTests.cpp b/third_party/dawn/src/tests/end2end/MultisampledRenderingTests.cpp index 82d5cf540d7..0bd94a09a92 100644 --- a/third_party/dawn/src/tests/end2end/MultisampledRenderingTests.cpp +++ b/third_party/dawn/src/tests/end2end/MultisampledRenderingTests.cpp @@ -16,7 +16,7 @@ #include "common/Assert.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class MultisampledRenderingTest : public DawnTest { protected: @@ -27,16 +27,18 @@ class MultisampledRenderingTest : public DawnTest { } void InitTexturesForTest() { - mMultisampledColorView = - CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateDefaultView(); + mMultisampledColorTexture = CreateTextureForOutputAttachment(kColorFormat, kSampleCount); + mMultisampledColorView = mMultisampledColorTexture.CreateView(); mResolveTexture = CreateTextureForOutputAttachment(kColorFormat, 1); - mResolveView = mResolveTexture.CreateDefaultView(); + mResolveView = mResolveTexture.CreateView(); mDepthStencilTexture = CreateTextureForOutputAttachment(kDepthStencilFormat, kSampleCount); - mDepthStencilView = mDepthStencilTexture.CreateDefaultView(); + mDepthStencilView = mDepthStencilTexture.CreateView(); } - dawn::RenderPipeline CreateRenderPipelineWithOneOutputForTest(bool testDepth) { + wgpu::RenderPipeline CreateRenderPipelineWithOneOutputForTest( + bool testDepth, + uint32_t sampleMask = 0xFFFFFFFF) { const char* kFsOneOutputWithDepth = R"(#version 450 layout(location = 0) out vec4 fragColor; @@ -61,11 +63,11 @@ class MultisampledRenderingTest : public DawnTest { const char* fs = testDepth ? kFsOneOutputWithDepth : kFsOneOutputWithoutDepth; - - return CreateRenderPipelineForTest(fs, 1, testDepth); + return CreateRenderPipelineForTest(fs, 1, testDepth, sampleMask); } - dawn::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest() { + wgpu::RenderPipeline CreateRenderPipelineWithTwoOutputsForTest( + uint32_t sampleMask = 0xFFFFFFFF) { const char* kFsTwoOutputs = R"(#version 450 layout(location = 0) out vec4 fragColor1; @@ -79,63 +81,59 @@ class MultisampledRenderingTest : public DawnTest { fragColor2 = color2; })"; - return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false); + return CreateRenderPipelineForTest(kFsTwoOutputs, 2, false, sampleMask); } - dawn::Texture CreateTextureForOutputAttachment(dawn::TextureFormat format, + wgpu::Texture CreateTextureForOutputAttachment(wgpu::TextureFormat format, uint32_t sampleCount, uint32_t mipLevelCount = 1, uint32_t arrayLayerCount = 1) { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = kWidth << (mipLevelCount - 1); descriptor.size.height = kHeight << (mipLevelCount - 1); - descriptor.size.depth = 1; - descriptor.arrayLayerCount = arrayLayerCount; + descriptor.size.depth = arrayLayerCount; descriptor.sampleCount = sampleCount; descriptor.format = format; descriptor.mipLevelCount = mipLevelCount; - descriptor.usage = - dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; return device.CreateTexture(&descriptor); } - void EncodeRenderPassForTest(dawn::CommandEncoder commandEncoder, - const dawn::RenderPassDescriptor& renderPass, - const dawn::RenderPipeline& pipeline, + void EncodeRenderPassForTest(wgpu::CommandEncoder commandEncoder, + const wgpu::RenderPassDescriptor& renderPass, + const wgpu::RenderPipeline& pipeline, const float* uniformData, uint32_t uniformDataSize) { - dawn::Buffer uniformBuffer = - utils::CreateBufferFromData(device, uniformData, uniformDataSize, - dawn::BufferUsageBit::Uniform); - dawn::BindGroup bindGroup = - utils::MakeBindGroup(device, mBindGroupLayout, - {{0, uniformBuffer, 0, uniformDataSize}}); - - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, uniformData, uniformDataSize, wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, uniformBuffer, 0, uniformDataSize}}); + + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); renderPassEncoder.SetPipeline(pipeline); - renderPassEncoder.SetBindGroup(0, bindGroup, 0, nullptr); - renderPassEncoder.Draw(3, 1, 0, 0); + renderPassEncoder.SetBindGroup(0, bindGroup); + renderPassEncoder.Draw(3); renderPassEncoder.EndPass(); } utils::ComboRenderPassDescriptor CreateComboRenderPassDescriptorForTest( - std::initializer_list colorViews, - std::initializer_list resolveTargetViews, - dawn::LoadOp colorLoadOp, - dawn::LoadOp depthStencilLoadOp, + std::initializer_list colorViews, + std::initializer_list resolveTargetViews, + wgpu::LoadOp colorLoadOp, + wgpu::LoadOp depthStencilLoadOp, bool hasDepthStencilAttachment) { ASSERT(colorViews.size() == resolveTargetViews.size()); - constexpr dawn::Color kClearColor = {0.0f, 0.0f, 0.0f, 0.0f}; + constexpr wgpu::Color kClearColor = {0.0f, 0.0f, 0.0f, 0.0f}; constexpr float kClearDepth = 1.0f; utils::ComboRenderPassDescriptor renderPass(colorViews); uint32_t i = 0; - for (const dawn::TextureView& resolveTargetView : resolveTargetViews) { - renderPass.cColorAttachmentsInfoPtr[i]->loadOp = colorLoadOp; - renderPass.cColorAttachmentsInfoPtr[i]->clearColor = kClearColor; - renderPass.cColorAttachmentsInfoPtr[i]->resolveTarget = resolveTargetView; + for (const wgpu::TextureView& resolveTargetView : resolveTargetViews) { + renderPass.cColorAttachments[i].loadOp = colorLoadOp; + renderPass.cColorAttachments[i].clearColor = kClearColor; + renderPass.cColorAttachments[i].resolveTarget = resolveTargetView; ++i; } @@ -150,21 +148,20 @@ class MultisampledRenderingTest : public DawnTest { return renderPass; } - void VerifyResolveTarget(const dawn::Color& inputColor, - dawn::Texture resolveTexture, + void VerifyResolveTarget(const wgpu::Color& inputColor, + wgpu::Texture resolveTexture, uint32_t mipmapLevel = 0, - uint32_t arrayLayer = 0) { - constexpr float kMSAACoverage = 0.5f; - + uint32_t arrayLayer = 0, + const float MSAACoverage = 0.5f) { // In this test we only check the pixel in the middle of the texture. constexpr uint32_t kMiddleX = (kWidth - 1) / 2; constexpr uint32_t kMiddleY = (kHeight - 1) / 2; RGBA8 expectedColor; - expectedColor.r = static_cast(0xFF * inputColor.r * kMSAACoverage); - expectedColor.g = static_cast(0xFF * inputColor.g * kMSAACoverage); - expectedColor.b = static_cast(0xFF * inputColor.b * kMSAACoverage); - expectedColor.a = static_cast(0xFF * inputColor.a * kMSAACoverage); + expectedColor.r = static_cast(0xFF * inputColor.r * MSAACoverage); + expectedColor.g = static_cast(0xFF * inputColor.g * MSAACoverage); + expectedColor.b = static_cast(0xFF * inputColor.b * MSAACoverage); + expectedColor.a = static_cast(0xFF * inputColor.a * MSAACoverage); EXPECT_TEXTURE_RGBA8_EQ(&expectedColor, resolveTexture, kMiddleX, kMiddleY, 1, 1, mipmapLevel, arrayLayer); @@ -173,20 +170,26 @@ class MultisampledRenderingTest : public DawnTest { constexpr static uint32_t kWidth = 3; constexpr static uint32_t kHeight = 3; constexpr static uint32_t kSampleCount = 4; - constexpr static dawn::TextureFormat kColorFormat = dawn::TextureFormat::R8G8B8A8Unorm; - constexpr static dawn::TextureFormat kDepthStencilFormat = dawn::TextureFormat::D32FloatS8Uint; - - dawn::TextureView mMultisampledColorView; - dawn::Texture mResolveTexture; - dawn::TextureView mResolveView; - dawn::Texture mDepthStencilTexture; - dawn::TextureView mDepthStencilView; - dawn::BindGroupLayout mBindGroupLayout; - - private: - dawn::RenderPipeline CreateRenderPipelineForTest(const char* fs, + constexpr static wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; + constexpr static wgpu::TextureFormat kDepthStencilFormat = + wgpu::TextureFormat::Depth24PlusStencil8; + + constexpr static uint32_t kFirstSampleMaskBit = 0x00000001; + constexpr static uint32_t kSecondSampleMaskBit = 0x00000002; + constexpr static uint32_t kThirdSampleMaskBit = 0x00000004; + constexpr static uint32_t kFourthSampleMaskBit = 0x00000008; + + wgpu::Texture mMultisampledColorTexture; + wgpu::TextureView mMultisampledColorView; + wgpu::Texture mResolveTexture; + wgpu::TextureView mResolveView; + wgpu::Texture mDepthStencilTexture; + wgpu::TextureView mDepthStencilView; + + wgpu::RenderPipeline CreateRenderPipelineForTest(const char* fs, uint32_t numColorAttachments, - bool hasDepthStencilAttachment) { + bool hasDepthStencilAttachment, + uint32_t sampleMask = 0xFFFFFFFF) { utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); // Draw a bottom-right triangle. In standard 4xMSAA pattern, for the pixels on diagonal, @@ -197,59 +200,79 @@ class MultisampledRenderingTest : public DawnTest { void main() { gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); })"; - pipelineDescriptor.cVertexStage.module = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, vs); + pipelineDescriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vs); pipelineDescriptor.cFragmentStage.module = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, fs); - - - mBindGroupLayout = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}, - }); - dawn::PipelineLayout pipelineLayout = - utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - pipelineDescriptor.layout = pipelineLayout; + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fs); if (hasDepthStencilAttachment) { pipelineDescriptor.cDepthStencilState.format = kDepthStencilFormat; pipelineDescriptor.cDepthStencilState.depthWriteEnabled = true; - pipelineDescriptor.cDepthStencilState.depthCompare = dawn::CompareFunction::Less; + pipelineDescriptor.cDepthStencilState.depthCompare = wgpu::CompareFunction::Less; pipelineDescriptor.depthStencilState = &pipelineDescriptor.cDepthStencilState; } pipelineDescriptor.sampleCount = kSampleCount; + pipelineDescriptor.sampleMask = sampleMask; pipelineDescriptor.colorStateCount = numColorAttachments; for (uint32_t i = 0; i < numColorAttachments; ++i) { - pipelineDescriptor.cColorStates[i]->format = kColorFormat; + pipelineDescriptor.cColorStates[i].format = kColorFormat; } - return device.CreateRenderPipeline(&pipelineDescriptor); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); + return pipeline; } }; // Test using one multisampled color attachment with resolve target can render correctly. TEST_P(MultisampledRenderingTest, ResolveInto2DTexture) { constexpr bool kTestDepth = false; - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); - constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; constexpr uint32_t kSize = sizeof(kGreen); // Draw a green triangle. { utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( - {mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Clear, dawn::LoadOp::Clear, + {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize); } - dawn::CommandBuffer commandBuffer = commandEncoder.Finish(); - dawn::Queue queue = device.CreateQueue(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + VerifyResolveTarget(kGreen, mResolveTexture); +} + +// Test that a single-layer multisampled texture view can be created and resolved from. +TEST_P(MultisampledRenderingTest, ResolveFromSingleLayerArrayInto2DTexture) { + constexpr bool kTestDepth = false; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); + + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr uint32_t kSize = sizeof(kGreen); + + // Draw a green triangle. + { + wgpu::TextureViewDescriptor desc = {}; + desc.dimension = wgpu::TextureViewDimension::e2DArray; + desc.arrayLayerCount = 1; + + utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( + {mMultisampledColorTexture.CreateView(&desc)}, {mResolveView}, wgpu::LoadOp::Clear, + wgpu::LoadOp::Clear, kTestDepth); + + EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize); + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); queue.Submit(1, &commandBuffer); VerifyResolveTarget(kGreen, mResolveTexture); @@ -258,19 +281,19 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DTexture) { // Test multisampled rendering with depth test works correctly. TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTest) { constexpr bool kTestDepth = true; - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); - constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; - constexpr dawn::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; // In first render pass we draw a green triangle with depth value == 0.2f. { - utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( - {mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Clear, dawn::LoadOp::Clear, - true); - std::array kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color - 0.2f}; // depth + utils::ComboRenderPassDescriptor renderPass = + CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView}, + wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true); + std::array kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color + 0.2f}; // depth constexpr uint32_t kSize = sizeof(kUniformData); EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); } @@ -280,17 +303,16 @@ TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTest) { // the last render pass. { utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( - {mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Load, dawn::LoadOp::Load, + {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, kTestDepth); - std::array kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color - 0.5f}; // depth + std::array kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color + 0.5f}; // depth constexpr uint32_t kSize = sizeof(kUniformData); EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); } - dawn::CommandBuffer commandBuffer = commandEncoder.Finish(); - dawn::Queue queue = device.CreateQueue(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); queue.Submit(1, &commandBuffer); // The color of the pixel in the middle of mResolveTexture should be green if MSAA resolve runs @@ -302,16 +324,16 @@ TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTest) { // works correctly. TEST_P(MultisampledRenderingTest, ResolveInAnotherRenderPass) { constexpr bool kTestDepth = false; - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); - constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; constexpr uint32_t kSize = sizeof(kGreen); // In first render pass we draw a green triangle and do not set the resolve target. { utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( - {mMultisampledColorView}, {nullptr}, dawn::LoadOp::Clear, dawn::LoadOp::Clear, + {mMultisampledColorView}, {nullptr}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize); @@ -320,15 +342,14 @@ TEST_P(MultisampledRenderingTest, ResolveInAnotherRenderPass) { // In second render pass we ony do MSAA resolve with no draw call. { utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( - {mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Load, dawn::LoadOp::Load, + {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, kTestDepth); - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); renderPassEncoder.EndPass(); } - dawn::CommandBuffer commandBuffer = commandEncoder.Finish(); - dawn::Queue queue = device.CreateQueue(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); queue.Submit(1, &commandBuffer); VerifyResolveTarget(kGreen, mResolveTexture); @@ -336,16 +357,19 @@ TEST_P(MultisampledRenderingTest, ResolveInAnotherRenderPass) { // Test doing MSAA resolve into multiple resolve targets works correctly. TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) { - dawn::TextureView multisampledColorView2 = - CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateDefaultView(); - dawn::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1); - dawn::TextureView resolveView2 = resolveTexture2.CreateDefaultView(); + // TODO(dawn:462): Investigate backend validation failure. + DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia() && IsBackendValidationEnabled()); + + wgpu::TextureView multisampledColorView2 = + CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateView(); + wgpu::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1); + wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(); - constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; - constexpr dawn::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; constexpr bool kTestDepth = false; // Draw a red triangle to the first color attachment, and a blue triangle to the second color @@ -353,16 +377,14 @@ TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) { { utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2}, - dawn::LoadOp::Clear, dawn::LoadOp::Clear, kTestDepth); + wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); - std::array kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1 - kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2 + std::array kUniformData = {kRed, kGreen}; constexpr uint32_t kSize = sizeof(kUniformData); - EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); + EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kUniformData[0].r, kSize); } - dawn::CommandBuffer commandBuffer = commandEncoder.Finish(); - dawn::Queue queue = device.CreateQueue(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); queue.Submit(1, &commandBuffer); VerifyResolveTarget(kRed, mResolveTexture); @@ -372,18 +394,18 @@ TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargets) { // Test doing MSAA resolve on one multisampled texture twice works correctly. TEST_P(MultisampledRenderingTest, ResolveOneMultisampledTextureTwice) { constexpr bool kTestDepth = false; - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); - constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; constexpr uint32_t kSize = sizeof(kGreen); - dawn::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1); + wgpu::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1); // In first render pass we draw a green triangle and specify mResolveView as the resolve target. { utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( - {mMultisampledColorView}, {mResolveView}, dawn::LoadOp::Clear, dawn::LoadOp::Clear, + {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize); @@ -391,17 +413,16 @@ TEST_P(MultisampledRenderingTest, ResolveOneMultisampledTextureTwice) { // In second render pass we do MSAA resolve into resolveTexture2. { - dawn::TextureView resolveView2 = resolveTexture2.CreateDefaultView(); + wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( - {mMultisampledColorView}, {resolveView2}, dawn::LoadOp::Load, dawn::LoadOp::Load, + {mMultisampledColorView}, {resolveView2}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, kTestDepth); - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); renderPassEncoder.EndPass(); } - dawn::CommandBuffer commandBuffer = commandEncoder.Finish(); - dawn::Queue queue = device.CreateQueue(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); queue.Submit(1, &commandBuffer); VerifyResolveTarget(kGreen, mResolveTexture); @@ -410,37 +431,39 @@ TEST_P(MultisampledRenderingTest, ResolveOneMultisampledTextureTwice) { // Test using a layer of a 2D texture as resolve target works correctly. TEST_P(MultisampledRenderingTest, ResolveIntoOneMipmapLevelOf2DTexture) { + // TODO(dawn:462): Investigate backend validation failure. + DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia() && IsBackendValidationEnabled()); + constexpr uint32_t kBaseMipLevel = 2; - dawn::TextureViewDescriptor textureViewDescriptor; - textureViewDescriptor.dimension = dawn::TextureViewDimension::e2D; + wgpu::TextureViewDescriptor textureViewDescriptor; + textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; textureViewDescriptor.format = kColorFormat; textureViewDescriptor.baseArrayLayer = 0; textureViewDescriptor.arrayLayerCount = 1; textureViewDescriptor.mipLevelCount = 1; textureViewDescriptor.baseMipLevel = kBaseMipLevel; - dawn::Texture resolveTexture = + wgpu::Texture resolveTexture = CreateTextureForOutputAttachment(kColorFormat, 1, kBaseMipLevel + 1, 1); - dawn::TextureView resolveView = resolveTexture.CreateView(&textureViewDescriptor); + wgpu::TextureView resolveView = resolveTexture.CreateView(&textureViewDescriptor); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; constexpr uint32_t kSize = sizeof(kGreen); constexpr bool kTestDepth = false; // Draw a green triangle and do MSAA resolve. { utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( - {mMultisampledColorView}, {resolveView}, dawn::LoadOp::Clear, dawn::LoadOp::Clear, + {mMultisampledColorView}, {resolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); - dawn::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithOneOutputForTest(kTestDepth); EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize); } - dawn::CommandBuffer commandBuffer = commandEncoder.Finish(); - dawn::Queue queue = device.CreateQueue(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); queue.Submit(1, &commandBuffer); VerifyResolveTarget(kGreen, resolveTexture, kBaseMipLevel, 0); @@ -448,11 +471,14 @@ TEST_P(MultisampledRenderingTest, ResolveIntoOneMipmapLevelOf2DTexture) { // Test using a level or a layer of a 2D array texture as resolve target works correctly. TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) { - dawn::TextureView multisampledColorView2 = - CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateDefaultView(); + // TODO(dawn:462): Investigate backend validation failure. + DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia() && IsBackendValidationEnabled()); - dawn::TextureViewDescriptor baseTextureViewDescriptor; - baseTextureViewDescriptor.dimension = dawn::TextureViewDimension::e2D; + wgpu::TextureView multisampledColorView2 = + CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateView(); + + wgpu::TextureViewDescriptor baseTextureViewDescriptor; + baseTextureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; baseTextureViewDescriptor.format = kColorFormat; baseTextureViewDescriptor.arrayLayerCount = 1; baseTextureViewDescriptor.mipLevelCount = 1; @@ -460,29 +486,29 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) { // Create resolveTexture1 with only 1 mipmap level. constexpr uint32_t kBaseArrayLayer1 = 2; constexpr uint32_t kBaseMipLevel1 = 0; - dawn::Texture resolveTexture1 = + wgpu::Texture resolveTexture1 = CreateTextureForOutputAttachment(kColorFormat, 1, kBaseMipLevel1 + 1, kBaseArrayLayer1 + 1); - dawn::TextureViewDescriptor resolveViewDescriptor1 = baseTextureViewDescriptor; + wgpu::TextureViewDescriptor resolveViewDescriptor1 = baseTextureViewDescriptor; resolveViewDescriptor1.baseArrayLayer = kBaseArrayLayer1; resolveViewDescriptor1.baseMipLevel = kBaseMipLevel1; - dawn::TextureView resolveView1 = resolveTexture1.CreateView(&resolveViewDescriptor1); + wgpu::TextureView resolveView1 = resolveTexture1.CreateView(&resolveViewDescriptor1); // Create resolveTexture2 with (kBaseMipLevel2 + 1) mipmap levels and resolve into its last // mipmap level. constexpr uint32_t kBaseArrayLayer2 = 5; constexpr uint32_t kBaseMipLevel2 = 3; - dawn::Texture resolveTexture2 = + wgpu::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1, kBaseMipLevel2 + 1, kBaseArrayLayer2 + 1); - dawn::TextureViewDescriptor resolveViewDescriptor2 = baseTextureViewDescriptor; + wgpu::TextureViewDescriptor resolveViewDescriptor2 = baseTextureViewDescriptor; resolveViewDescriptor2.baseArrayLayer = kBaseArrayLayer2; resolveViewDescriptor2.baseMipLevel = kBaseMipLevel2; - dawn::TextureView resolveView2 = resolveTexture2.CreateView(&resolveViewDescriptor2); + wgpu::TextureView resolveView2 = resolveTexture2.CreateView(&resolveViewDescriptor2); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(); - constexpr dawn::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; - constexpr dawn::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; constexpr bool kTestDepth = false; // Draw a red triangle to the first color attachment, and a green triangle to the second color @@ -490,29 +516,310 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) { { utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( {mMultisampledColorView, multisampledColorView2}, {resolveView1, resolveView2}, - dawn::LoadOp::Clear, dawn::LoadOp::Clear, kTestDepth); + wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); - std::array kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1 - kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2 + std::array kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color1 + kGreen.r, kGreen.g, kGreen.b, kGreen.a}; // color2 constexpr uint32_t kSize = sizeof(kUniformData); EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, kUniformData.data(), kSize); } - dawn::CommandBuffer commandBuffer = commandEncoder.Finish(); - dawn::Queue queue = device.CreateQueue(); + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); queue.Submit(1, &commandBuffer); VerifyResolveTarget(kRed, resolveTexture1, kBaseMipLevel1, kBaseArrayLayer1); VerifyResolveTarget(kGreen, resolveTexture2, kBaseMipLevel2, kBaseArrayLayer2); } +// Test using one multisampled color attachment with resolve target can render correctly +// with a non-default sample mask. +TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithSampleMask) { + // TODO(dawn:491): Remove this condition after enabling sampleMask usage in Metal. + DAWN_SKIP_TEST_IF(IsMetal()); + + constexpr bool kTestDepth = false; + // The second and third samples are included, + // only the second one is covered by the triangle. + constexpr uint32_t kSampleMask = kSecondSampleMaskBit | kThirdSampleMaskBit; + constexpr float kMSAACoverage = 0.25f; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = + CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask); + + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr uint32_t kSize = sizeof(kGreen); + + // Draw a green triangle. + { + utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( + {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, + kTestDepth); + + EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize); + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage); +} + +// Test using one multisampled color attachment with resolve target can render correctly +// with the final sample mask empty. +TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithEmptyFinalSampleMask) { + // TODO(dawn:491): Remove this condition after enabling sampleMask usage in Metal. + DAWN_SKIP_TEST_IF(IsMetal()); + + constexpr bool kTestDepth = false; + // The third and fourth samples are included, + // none of which is covered by the triangle. + constexpr uint32_t kSampleMask = kThirdSampleMaskBit | kFourthSampleMaskBit; + constexpr float kMSAACoverage = 0.00f; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = + CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMask); + + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr uint32_t kSize = sizeof(kGreen); + + // Draw a green triangle. + { + utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( + {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, + kTestDepth); + + EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize); + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + VerifyResolveTarget(kGreen, mResolveTexture, 0, 0, kMSAACoverage); +} + +// Test doing MSAA resolve into multiple resolve targets works correctly with a non-default sample +// mask. +TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithSampleMask) { + // TODO(dawn:491): Remove this condition after enabling sampleMask usage in Metal. + DAWN_SKIP_TEST_IF(IsMetal()); + + wgpu::TextureView multisampledColorView2 = + CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateView(); + wgpu::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1); + wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); + + // The first and fourth samples are included, + // only the first one is covered by the triangle. + constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kFourthSampleMaskBit; + constexpr float kMSAACoverage = 0.25f; + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = CreateRenderPipelineWithTwoOutputsForTest(kSampleMask); + + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; + constexpr bool kTestDepth = false; + + // Draw a red triangle to the first color attachment, and a blue triangle to the second color + // attachment, and do MSAA resolve on two render targets in one render pass. + { + utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( + {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2}, + wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); + + std::array kUniformData = {kRed, kGreen}; + constexpr uint32_t kSize = sizeof(kUniformData); + EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kUniformData[0].r, kSize); + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + VerifyResolveTarget(kRed, mResolveTexture, 0, 0, kMSAACoverage); + VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage); +} + +// Test multisampled rendering with depth test works correctly with a non-default sample mask. +TEST_P(MultisampledRenderingTest, MultisampledRenderingWithDepthTestAndSampleMask) { + // TODO(dawn:491): Remove this condition after enabling sampleMask usage in Metal. + DAWN_SKIP_TEST_IF(IsMetal()); + + // TODO(dawn:491): Find out why this test doesn't work on Windows Intel Vulkan. + DAWN_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan()); + + constexpr bool kTestDepth = true; + // The second sample is included in the first render pass and it's covered by the triangle. + constexpr uint32_t kSampleMaskGreen = kSecondSampleMaskBit; + // The first and second samples are included in the second render pass, + // both are covered by the triangle. + constexpr uint32_t kSampleMaskRed = kFirstSampleMaskBit | kSecondSampleMaskBit; + constexpr float kMSAACoverage = 0.50f; + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipelineGreen = + CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMaskGreen); + wgpu::RenderPipeline pipelineRed = + CreateRenderPipelineWithOneOutputForTest(kTestDepth, kSampleMaskRed); + + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; + + // In first render pass we draw a green triangle with depth value == 0.2f. + // We will only write to the second sample. + { + utils::ComboRenderPassDescriptor renderPass = + CreateComboRenderPassDescriptorForTest({mMultisampledColorView}, {mResolveView}, + wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, true); + std::array kUniformData = {kGreen.r, kGreen.g, kGreen.b, kGreen.a, // Color + 0.2f}; // depth + constexpr uint32_t kSize = sizeof(kUniformData); + EncodeRenderPassForTest(commandEncoder, renderPass, pipelineGreen, kUniformData.data(), + kSize); + } + + // In second render pass we draw a red triangle with depth value == 0.5f. + // We will only write to the first sample, since the second one is red with a smaller depth + // value. + { + utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( + {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Load, wgpu::LoadOp::Load, + kTestDepth); + + std::array kUniformData = {kRed.r, kRed.g, kRed.b, kRed.a, // color + 0.5f}; // depth + constexpr uint32_t kSize = sizeof(kUniformData); + EncodeRenderPassForTest(commandEncoder, renderPass, pipelineRed, kUniformData.data(), + kSize); + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + constexpr wgpu::Color kHalfGreenHalfRed = {(kGreen.r + kRed.r) / 2.0, (kGreen.g + kRed.g) / 2.0, + (kGreen.b + kRed.b) / 2.0, + (kGreen.a + kRed.a) / 2.0}; + + // The color of the pixel in the middle of mResolveTexture should be half green and half + // red if MSAA resolve runs correctly with depth test. + VerifyResolveTarget(kHalfGreenHalfRed, mResolveTexture, 0, 0, kMSAACoverage); +} + +// Test using one multisampled color attachment with resolve target can render correctly +// with non-default sample mask and shader-output mask. +TEST_P(MultisampledRenderingTest, ResolveInto2DTextureWithSampleMaskAndShaderOutputMask) { + // TODO(dawn:491): Remove this condition after enabling sampleMask usage in Metal. + DAWN_SKIP_TEST_IF(IsMetal()); + + // TODO(dawn:491): Remove this when SPIRV-cross adds support for SV_Coverage in HLSL. + DAWN_SKIP_TEST_IF(IsD3D12()); + + constexpr bool kTestDepth = false; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + + // The second and third samples are included in the shader-output mask. + // The first and third samples are included in the sample mask. + // Since we're now looking at a fully covered pixel, the rasterization mask + // includes all the samples. + // Thus the final mask includes only the third sample. + constexpr float kMSAACoverage = 0.25f; + constexpr uint32_t kSampleMask = kFirstSampleMaskBit | kThirdSampleMaskBit; + const char* fs = + R"(#version 450 + layout(location = 0) out vec4 fragColor; + layout (std140, set = 0, binding = 0) uniform uBuffer { + vec4 color; + }; + void main() { + fragColor = color; + gl_SampleMask[0] = 6; + })"; + + wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 1, false, kSampleMask); + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr uint32_t kSize = sizeof(kGreen); + + // Draw a green triangle. + { + utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( + {mMultisampledColorView}, {mResolveView}, wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, + kTestDepth); + + EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kGreen.r, kSize); + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + RGBA8 expectedColor; + expectedColor.r = static_cast(0xFF * kGreen.r * kMSAACoverage); + expectedColor.g = static_cast(0xFF * kGreen.g * kMSAACoverage); + expectedColor.b = static_cast(0xFF * kGreen.b * kMSAACoverage); + expectedColor.a = static_cast(0xFF * kGreen.a * kMSAACoverage); + + EXPECT_TEXTURE_RGBA8_EQ(&expectedColor, mResolveTexture, 1, 0, 1, 1, 0, 0); +} + +// Test doing MSAA resolve into multiple resolve targets works correctly with a non-default +// shader-output mask. +TEST_P(MultisampledRenderingTest, ResolveIntoMultipleResolveTargetsWithShaderOutputMask) { + // TODO(dawn:491): Remove this when SPIRV-cross adds support for SV_Coverage in HLSL. + DAWN_SKIP_TEST_IF(IsD3D12()); + + wgpu::TextureView multisampledColorView2 = + CreateTextureForOutputAttachment(kColorFormat, kSampleCount).CreateView(); + wgpu::Texture resolveTexture2 = CreateTextureForOutputAttachment(kColorFormat, 1); + wgpu::TextureView resolveView2 = resolveTexture2.CreateView(); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + // The second and third samples are included in the shader-output mask, + // only the first one is covered by the triangle. + constexpr float kMSAACoverage = 0.25f; + const char* fs = + R"(#version 450 + layout(location = 0) out vec4 fragColor1; + layout(location = 1) out vec4 fragColor2; + layout (std140, set = 0, binding = 0) uniform uBuffer { + vec4 color1; + vec4 color2; + }; + void main() { + fragColor1 = color1; + fragColor2 = color2; + gl_SampleMask[0] = 6; + })"; + + wgpu::RenderPipeline pipeline = CreateRenderPipelineForTest(fs, 2, false); + constexpr wgpu::Color kGreen = {0.0f, 0.8f, 0.0f, 0.8f}; + constexpr wgpu::Color kRed = {0.8f, 0.0f, 0.0f, 0.8f}; + constexpr bool kTestDepth = false; + + // Draw a red triangle to the first color attachment, and a blue triangle to the second color + // attachment, and do MSAA resolve on two render targets in one render pass. + { + utils::ComboRenderPassDescriptor renderPass = CreateComboRenderPassDescriptorForTest( + {mMultisampledColorView, multisampledColorView2}, {mResolveView, resolveView2}, + wgpu::LoadOp::Clear, wgpu::LoadOp::Clear, kTestDepth); + + std::array kUniformData = {kRed, kGreen}; + constexpr uint32_t kSize = sizeof(kUniformData); + EncodeRenderPassForTest(commandEncoder, renderPass, pipeline, &kUniformData[0].r, kSize); + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + VerifyResolveTarget(kRed, mResolveTexture, 0, 0, kMSAACoverage); + VerifyResolveTarget(kGreen, resolveTexture2, 0, 0, kMSAACoverage); +} + DAWN_INSTANTIATE_TEST(MultisampledRenderingTest, - D3D12Backend, - MetalBackend, - OpenGLBackend, - VulkanBackend, - ForceWorkarounds(MetalBackend, {"emulate_store_and_msaa_resolve"}), - ForceWorkarounds(MetalBackend, {"always_resolve_into_zero_level_and_layer"}), - ForceWorkarounds(MetalBackend, - {"always_resolve_into_zero_level_and_layer", - "emulate_store_and_msaa_resolve"})); + D3D12Backend(), + D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}), + D3D12Backend({}, {"use_d3d12_render_pass"}), + MetalBackend(), + OpenGLBackend(), + VulkanBackend(), + MetalBackend({"emulate_store_and_msaa_resolve"}), + MetalBackend({"always_resolve_into_zero_level_and_layer"}), + MetalBackend({"always_resolve_into_zero_level_and_layer", + "emulate_store_and_msaa_resolve"})); diff --git a/third_party/dawn/src/tests/end2end/MultisampledSamplingTests.cpp b/third_party/dawn/src/tests/end2end/MultisampledSamplingTests.cpp new file mode 100644 index 00000000000..8c017d0fc0d --- /dev/null +++ b/third_party/dawn/src/tests/end2end/MultisampledSamplingTests.cpp @@ -0,0 +1,267 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Math.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +namespace { + // https://github.com/gpuweb/gpuweb/issues/108 + // Vulkan, Metal, and D3D11 have the same standard multisample pattern. D3D12 is the same as + // D3D11 but it was left out of the documentation. + // {0.375, 0.125}, {0.875, 0.375}, {0.125 0.625}, {0.625, 0.875} + // In this test, we store them in -1 to 1 space because it makes it + // simpler to upload vertex data. Y is flipped because there is a flip between clip space and + // rasterization space. + static constexpr std::array, 4> kSamplePositions = { + {{0.375 * 2 - 1, 1 - 0.125 * 2}, + {0.875 * 2 - 1, 1 - 0.375 * 2}, + {0.125 * 2 - 1, 1 - 0.625 * 2}, + {0.625 * 2 - 1, 1 - 0.875 * 2}}}; +} // anonymous namespace + +class MultisampledSamplingTest : public DawnTest { + protected: + static constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::R8Unorm; + static constexpr wgpu::TextureFormat kDepthFormat = wgpu::TextureFormat::Depth32Float; + + static constexpr wgpu::TextureFormat kDepthOutFormat = wgpu::TextureFormat::R32Float; + static constexpr uint32_t kSampleCount = 4; + + // Render pipeline for drawing to a multisampled color and depth attachment. + wgpu::RenderPipeline drawPipeline; + + // A compute pipeline to texelFetch the sample locations and output the results to a buffer. + wgpu::ComputePipeline checkSamplePipeline; + + void SetUp() override { + DawnTest::SetUp(); + { + utils::ComboRenderPipelineDescriptor desc(device); + + desc.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, + R"(#version 450 + layout(location=0) in vec2 pos; + void main() { + gl_Position = vec4(pos, 0.0, 1.0); + })"); + + desc.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, + R"(#version 450 + layout(location = 0) out float fragColor; + void main() { + fragColor = 1.0; + gl_FragDepth = 0.7; + })"); + + desc.cVertexState.vertexBufferCount = 1; + desc.cVertexState.cVertexBuffers[0].attributeCount = 1; + desc.cVertexState.cVertexBuffers[0].arrayStride = 2 * sizeof(float); + desc.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float2; + + desc.cDepthStencilState.format = kDepthFormat; + desc.cDepthStencilState.depthWriteEnabled = true; + desc.depthStencilState = &desc.cDepthStencilState; + + desc.sampleCount = kSampleCount; + desc.colorStateCount = 1; + desc.cColorStates[0].format = kColorFormat; + + desc.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + + drawPipeline = device.CreateRenderPipeline(&desc); + } + { + wgpu::ComputePipelineDescriptor desc = {}; + desc.computeStage.entryPoint = "main"; + desc.computeStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, + R"(#version 450 + layout(set = 0, binding = 0) uniform sampler sampler0; + layout(set = 0, binding = 1) uniform texture2DMS texture0; + layout(set = 0, binding = 2) uniform texture2DMS texture1; + + layout(set = 0, binding = 3, std430) buffer Results { + float colorSamples[4]; + float depthSamples[4]; + }; + + void main() { + for (int i = 0; i < 4; ++i) { + colorSamples[i] = + texelFetch(sampler2DMS(texture0, sampler0), ivec2(0, 0), i).x; + + depthSamples[i] = + texelFetch(sampler2DMS(texture1, sampler0), ivec2(0, 0), i).x; + } + })"); + + checkSamplePipeline = device.CreateComputePipeline(&desc); + } + } +}; + +// Test that the multisampling sample positions are correct. This test works by drawing a +// thin quad multiple times from left to right and from top to bottom on a 1x1 canvas. +// Each time, the quad should cover a single sample position. +// After drawing, a compute shader fetches all of the samples (both color and depth), +// and we check that only the one covered has data. +// We "scan" the vertical and horizontal dimensions separately to check that the triangle +// must cover both the X and Y coordinates of the sample position (no false positives if +// it covers the X position but not the Y, or vice versa). +TEST_P(MultisampledSamplingTest, SamplePositions) { + static constexpr wgpu::Extent3D kTextureSize = {1, 1, 1}; + + wgpu::Texture colorTexture; + { + wgpu::TextureDescriptor desc = {}; + desc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment; + desc.size = kTextureSize; + desc.format = kColorFormat; + desc.sampleCount = kSampleCount; + colorTexture = device.CreateTexture(&desc); + } + + wgpu::Texture depthTexture; + { + wgpu::TextureDescriptor desc = {}; + desc.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment; + desc.size = kTextureSize; + desc.format = kDepthFormat; + desc.sampleCount = kSampleCount; + depthTexture = device.CreateTexture(&desc); + } + + static constexpr float kQuadWidth = 0.075; + std::vector vBufferData; + + // Add vertices for vertical quads + for (uint32_t s = 0; s < kSampleCount; ++s) { + // clang-format off + vBufferData.insert(vBufferData.end(), { + kSamplePositions[s][0] - kQuadWidth, -1.0, + kSamplePositions[s][0] - kQuadWidth, 1.0, + kSamplePositions[s][0] + kQuadWidth, -1.0, + kSamplePositions[s][0] + kQuadWidth, 1.0, + }); + // clang-format on + } + + // Add vertices for horizontal quads + for (uint32_t s = 0; s < kSampleCount; ++s) { + // clang-format off + vBufferData.insert(vBufferData.end(), { + -1.0, kSamplePositions[s][1] - kQuadWidth, + -1.0, kSamplePositions[s][1] + kQuadWidth, + 1.0, kSamplePositions[s][1] - kQuadWidth, + 1.0, kSamplePositions[s][1] + kQuadWidth, + }); + // clang-format on + } + + wgpu::Buffer vBuffer = utils::CreateBufferFromData( + device, vBufferData.data(), static_cast(vBufferData.size() * sizeof(float)), + wgpu::BufferUsage::Vertex); + + static constexpr uint32_t kQuadNumBytes = 8 * sizeof(float); + + wgpu::SamplerDescriptor samplerDesc = {}; + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + wgpu::TextureView colorView = colorTexture.CreateView(); + wgpu::TextureView depthView = depthTexture.CreateView(); + + static constexpr uint64_t kResultSize = 4 * sizeof(float) + 4 * sizeof(float); + uint64_t alignedResultSize = Align(kResultSize, 256); + + wgpu::BufferDescriptor outputBufferDesc = {}; + outputBufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc; + outputBufferDesc.size = alignedResultSize * 8; + wgpu::Buffer outputBuffer = device.CreateBuffer(&outputBufferDesc); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + for (uint32_t iter = 0; iter < 2; ++iter) { + for (uint32_t sample = 0; sample < kSampleCount; ++sample) { + uint32_t sampleOffset = (iter * kSampleCount + sample); + + utils::ComboRenderPassDescriptor renderPass({colorView}, depthView); + renderPass.cDepthStencilAttachmentInfo.clearDepth = 0.f; + + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + renderPassEncoder.SetPipeline(drawPipeline); + renderPassEncoder.SetVertexBuffer(0, vBuffer, kQuadNumBytes * sampleOffset, + kQuadNumBytes); + renderPassEncoder.Draw(4); + renderPassEncoder.EndPass(); + + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetPipeline(checkSamplePipeline); + computePassEncoder.SetBindGroup( + 0, utils::MakeBindGroup( + device, checkSamplePipeline.GetBindGroupLayout(0), + {{0, sampler}, + {1, colorView}, + {2, depthView}, + {3, outputBuffer, alignedResultSize * sampleOffset, kResultSize}})); + computePassEncoder.Dispatch(1); + computePassEncoder.EndPass(); + } + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + std::array expectedData; + + expectedData = {1, 0, 0, 0, 0.7, 0, 0, 0}; + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 0 * alignedResultSize, 8) + << "vertical sample 0"; + + expectedData = {0, 1, 0, 0, 0, 0.7, 0, 0}; + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 1 * alignedResultSize, 8) + << "vertical sample 1"; + + expectedData = {0, 0, 1, 0, 0, 0, 0.7, 0}; + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 2 * alignedResultSize, 8) + << "vertical sample 2"; + + expectedData = {0, 0, 0, 1, 0, 0, 0, 0.7}; + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 3 * alignedResultSize, 8) + << "vertical sample 3"; + + expectedData = {1, 0, 0, 0, 0.7, 0, 0, 0}; + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 4 * alignedResultSize, 8) + << "horizontal sample 0"; + + expectedData = {0, 1, 0, 0, 0, 0.7, 0, 0}; + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 5 * alignedResultSize, 8) + << "horizontal sample 1"; + + expectedData = {0, 0, 1, 0, 0, 0, 0.7, 0}; + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 6 * alignedResultSize, 8) + << "horizontal sample 2"; + + expectedData = {0, 0, 0, 1, 0, 0, 0, 0.7}; + EXPECT_BUFFER_FLOAT_RANGE_EQ(expectedData.data(), outputBuffer, 7 * alignedResultSize, 8) + << "horizontal sample 3"; +} + +DAWN_INSTANTIATE_TEST(MultisampledSamplingTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/NonzeroBufferCreationTests.cpp b/third_party/dawn/src/tests/end2end/NonzeroBufferCreationTests.cpp new file mode 100644 index 00000000000..46f136606eb --- /dev/null +++ b/third_party/dawn/src/tests/end2end/NonzeroBufferCreationTests.cpp @@ -0,0 +1,57 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include + +class NonzeroBufferCreationTests : public DawnTest {}; + +// Verify that each byte of the buffer has all been initialized to 1 with the toggle enabled when it +// is created with CopyDst usage. +TEST_P(NonzeroBufferCreationTests, BufferCreationWithCopyDstUsage) { + constexpr uint32_t kSize = 32u; + + wgpu::BufferDescriptor descriptor; + descriptor.size = kSize; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + std::vector expectedData(kSize, static_cast(1u)); + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(expectedData.data()), buffer, 0, + kSize / sizeof(uint32_t)); +} + +// Verify that each byte of the buffer has all been initialized to 1 with the toggle enabled when it +// is created with MapWrite without CopyDst usage. +TEST_P(NonzeroBufferCreationTests, BufferCreationWithMapWriteWithoutCopyDstUsage) { + constexpr uint32_t kSize = 32u; + + wgpu::BufferDescriptor descriptor; + descriptor.size = kSize; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + std::vector expectedData(kSize, static_cast(1u)); + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(expectedData.data()), buffer, 0, + kSize / sizeof(uint32_t)); +} + +DAWN_INSTANTIATE_TEST(NonzeroBufferCreationTests, + D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}), + MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}), + OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}), + VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"})); diff --git a/third_party/dawn/src/tests/end2end/NonzeroTextureCreationTests.cpp b/third_party/dawn/src/tests/end2end/NonzeroTextureCreationTests.cpp index 531e5fcbe33..97759afd339 100644 --- a/third_party/dawn/src/tests/end2end/NonzeroTextureCreationTests.cpp +++ b/third_party/dawn/src/tests/end2end/NonzeroTextureCreationTests.cpp @@ -1,106 +1,297 @@ -// Copyright 2019 The Dawn Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "tests/DawnTest.h" - -#include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" - -class NonzeroTextureCreationTests : public DawnTest { - protected: - void SetUp() override { - DawnTest::SetUp(); - } - - constexpr static uint32_t kSize = 128; -}; - -// Test that texture clears to 1's because toggle is enabled. -TEST_P(NonzeroTextureCreationTests, TextureCreationClearsOneBits) { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = kSize; - descriptor.size.height = kSize; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; - descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; - dawn::Texture texture = device.CreateTexture(&descriptor); - - RGBA8 filledWithOnes(255, 255, 255, 255); - EXPECT_PIXEL_RGBA8_EQ(filledWithOnes, texture, 0, 0); -} - -// Test that non-zero mip level clears to 1's because toggle is enabled. -TEST_P(NonzeroTextureCreationTests, MipMapClears) { - constexpr uint32_t mipLevels = 4; - - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = kSize; - descriptor.size.height = kSize; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; - descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.mipLevelCount = mipLevels; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; - dawn::Texture texture = device.CreateTexture(&descriptor); - - std::vector expected; - RGBA8 filledWithOnes(255, 255, 255, 255); - for (uint32_t i = 0; i < kSize * kSize; ++i) { - expected.push_back(filledWithOnes); - } - uint32_t mipSize = kSize >> 2; - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, mipSize, mipSize, 2, 0); -} - -// Test that non-zero array layers clears to 1's because toggle is enabled. -TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) { - constexpr uint32_t arrayLayers = 4; - - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = kSize; - descriptor.size.height = kSize; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = arrayLayers; - descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; - dawn::Texture texture = device.CreateTexture(&descriptor); - - std::vector expected; - RGBA8 filledWithOnes(255, 255, 255, 255); - for (uint32_t i = 0; i < kSize * kSize; ++i) { - expected.push_back(filledWithOnes); - } - - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 2); -} - -DAWN_INSTANTIATE_TEST(NonzeroTextureCreationTests, - ForceWorkarounds(D3D12Backend, - {"nonzero_clear_resources_on_creation_for_testing"}, - {"lazy_clear_resource_on_first_use"}), - ForceWorkarounds(OpenGLBackend, - {"nonzero_clear_resources_on_creation_for_testing"}, - {"lazy_clear_resource_on_first_use"}), - ForceWorkarounds(VulkanBackend, - {"nonzero_clear_resources_on_creation_for_testing"}, - {"lazy_clear_resource_on_first_use"})); +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Constants.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class NonzeroTextureCreationTests : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + } + + constexpr static uint32_t kSize = 128; +}; + +// Test that texture clears 0xFF because toggle is enabled. +TEST_P(NonzeroTextureCreationTests, TextureCreationClears) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kSize; + descriptor.size.height = kSize; + descriptor.size.depth = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + RGBA8 filled(255, 255, 255, 255); + EXPECT_PIXEL_RGBA8_EQ(filled, texture, 0, 0); +} + +// Test that a depth texture clears 0xFF because toggle is enabled. +TEST_P(NonzeroTextureCreationTests, Depth32TextureCreationDepthClears) { + // Copies from depth textures not supported on the OpenGL backend right now. + DAWN_SKIP_TEST_IF(IsOpenGL()); + + // Closing the pending command list crashes flakily on D3D12 NVIDIA only. + DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia()); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kSize; + descriptor.size.height = kSize; + descriptor.size.depth = 1; + descriptor.sampleCount = 1; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + descriptor.format = wgpu::TextureFormat::Depth32Float; + + // We can only really test Depth32Float here because Depth24Plus(Stencil8)? may be in an unknown + // format. + // TODO(crbug.com/dawn/145): Test other formats via sampling. + wgpu::Texture texture = device.CreateTexture(&descriptor); + EXPECT_PIXEL_FLOAT_EQ(1.f, texture, 0, 0); +} + +// Test that non-zero mip level clears 0xFF because toggle is enabled. +TEST_P(NonzeroTextureCreationTests, MipMapClears) { + constexpr uint32_t mipLevels = 4; + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kSize; + descriptor.size.height = kSize; + descriptor.size.depth = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + descriptor.mipLevelCount = mipLevels; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + std::vector expected; + RGBA8 filled(255, 255, 255, 255); + for (uint32_t i = 0; i < kSize * kSize; ++i) { + expected.push_back(filled); + } + uint32_t mipSize = kSize >> 2; + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, mipSize, mipSize, 2, 0); +} + +// Test that non-zero array layers clears 0xFF because toggle is enabled. +TEST_P(NonzeroTextureCreationTests, ArrayLayerClears) { + constexpr uint32_t arrayLayers = 4; + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kSize; + descriptor.size.height = kSize; + descriptor.size.depth = arrayLayers; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + std::vector expected; + RGBA8 filled(255, 255, 255, 255); + for (uint32_t i = 0; i < kSize * kSize; ++i) { + expected.push_back(filled); + } + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 2); +} + +// Test that nonrenderable texture formats clear 0x01 because toggle is enabled +TEST_P(NonzeroTextureCreationTests, NonrenderableTextureFormat) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kSize; + descriptor.size.height = kSize; + descriptor.size.depth = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Snorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy + uint32_t bufferSize = 4 * kSize * kSize; + std::vector data(bufferSize, 100); + wgpu::Buffer bufferDst = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(bufferDst, 0, kSize * 4, 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expected(bufferSize, 0x01010101); + EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8); +} + +// Test that textures with more than 1 array layers and nonrenderable texture formats clear to 0x01 +// because toggle is enabled +TEST_P(NonzeroTextureCreationTests, NonRenderableTextureClearWithMultiArrayLayers) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kSize; + descriptor.size.height = kSize; + descriptor.size.depth = 2; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Snorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy + uint32_t bufferSize = 4 * kSize * kSize; + std::vector data(bufferSize, 100); + wgpu::Buffer bufferDst = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(bufferDst, 0, kSize * 4, 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 1}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + std::vector expected(bufferSize, 0x01010101); + EXPECT_BUFFER_U32_RANGE_EQ(expected.data(), bufferDst, 0, 8); +} + +// Test that all subresources of a renderable texture are filled because the toggle is enabled. +TEST_P(NonzeroTextureCreationTests, AllSubresourcesFilled) { + wgpu::TextureDescriptor baseDescriptor; + baseDescriptor.dimension = wgpu::TextureDimension::e2D; + baseDescriptor.size.width = kSize; + baseDescriptor.size.height = kSize; + baseDescriptor.size.depth = 1; + baseDescriptor.sampleCount = 1; + baseDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + baseDescriptor.mipLevelCount = 1; + baseDescriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + + RGBA8 filled(255, 255, 255, 255); + + { + wgpu::TextureDescriptor descriptor = baseDescriptor; + // Some textures may be cleared with render pass load/store ops. + // Test above the max attachment count. + descriptor.size.depth = kMaxColorAttachments + 1; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + for (uint32_t i = 0; i < descriptor.size.depth; ++i) { + EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, 0, i); + } + } + + { + wgpu::TextureDescriptor descriptor = baseDescriptor; + descriptor.mipLevelCount = 3; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + for (uint32_t i = 0; i < descriptor.mipLevelCount; ++i) { + EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, i, 0); + } + } + + { + wgpu::TextureDescriptor descriptor = baseDescriptor; + // Some textures may be cleared with render pass load/store ops. + // Test above the max attachment count. + descriptor.size.depth = kMaxColorAttachments + 1; + descriptor.mipLevelCount = 3; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + for (uint32_t i = 0; i < descriptor.size.depth; ++i) { + for (uint32_t j = 0; j < descriptor.mipLevelCount; ++j) { + EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, j, i); + } + } + } +} + +// Test that all subresources of a nonrenderable texture are filled because the toggle is enabled. +TEST_P(NonzeroTextureCreationTests, NonRenderableAllSubresourcesFilled) { + wgpu::TextureDescriptor baseDescriptor; + baseDescriptor.dimension = wgpu::TextureDimension::e2D; + baseDescriptor.size.width = kSize; + baseDescriptor.size.height = kSize; + baseDescriptor.size.depth = 1; + baseDescriptor.sampleCount = 1; + baseDescriptor.format = wgpu::TextureFormat::RGBA8Snorm; + baseDescriptor.mipLevelCount = 1; + baseDescriptor.usage = wgpu::TextureUsage::CopySrc; + + RGBA8 filled(1, 1, 1, 1); + + { + wgpu::TextureDescriptor descriptor = baseDescriptor; + // Some textures may be cleared with render pass load/store ops. + // Test above the max attachment count. + descriptor.size.depth = kMaxColorAttachments + 1; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + for (uint32_t i = 0; i < descriptor.size.depth; ++i) { + EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, 0, i); + } + } + + { + wgpu::TextureDescriptor descriptor = baseDescriptor; + descriptor.mipLevelCount = 3; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + for (uint32_t i = 0; i < descriptor.mipLevelCount; ++i) { + EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, i, 0); + } + } + + { + wgpu::TextureDescriptor descriptor = baseDescriptor; + // Some textures may be cleared with render pass load/store ops. + // Test above the max attachment count. + descriptor.size.depth = kMaxColorAttachments + 1; + descriptor.mipLevelCount = 3; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + for (uint32_t i = 0; i < descriptor.size.depth; ++i) { + for (uint32_t j = 0; j < descriptor.mipLevelCount; ++j) { + EXPECT_TEXTURE_RGBA8_EQ(&filled, texture, 0, 0, 1, 1, j, i); + } + } + } +} + +DAWN_INSTANTIATE_TEST(NonzeroTextureCreationTests, + D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}, + {"lazy_clear_resource_on_first_use"}), + MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}, + {"lazy_clear_resource_on_first_use"}), + OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}, + {"lazy_clear_resource_on_first_use"}), + VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"}, + {"lazy_clear_resource_on_first_use"})); diff --git a/third_party/dawn/src/tests/end2end/ObjectCachingTests.cpp b/third_party/dawn/src/tests/end2end/ObjectCachingTests.cpp index e7de879f2fb..20705a9d2f9 100644 --- a/third_party/dawn/src/tests/end2end/ObjectCachingTests.cpp +++ b/third_party/dawn/src/tests/end2end/ObjectCachingTests.cpp @@ -15,18 +15,65 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class ObjectCachingTest : public DawnTest {}; // Test that BindGroupLayouts are correctly deduplicated. TEST_P(ObjectCachingTest, BindGroupLayoutDeduplication) { - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}}); - dawn::BindGroupLayout sameBgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}}); - dawn::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout sameBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}); + + EXPECT_NE(bgl.Get(), otherBgl.Get()); + EXPECT_EQ(bgl.Get() == sameBgl.Get(), !UsesWire()); +} + +// Test that two similar bind group layouts won't refer to the same one if they differ by dynamic. +TEST_P(ObjectCachingTest, BindGroupLayoutDynamic) { + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, true}}); + wgpu::BindGroupLayout sameBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, true}}); + wgpu::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, false}}); + + EXPECT_NE(bgl.Get(), otherBgl.Get()); + EXPECT_EQ(bgl.Get() == sameBgl.Get(), !UsesWire()); +} + +// Test that two similar bind group layouts won't refer to the same one if they differ by +// textureComponentType +TEST_P(ObjectCachingTest, BindGroupLayoutTextureComponentType) { + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D, wgpu::TextureComponentType::Float}}); + wgpu::BindGroupLayout sameBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D, wgpu::TextureComponentType::Float}}); + wgpu::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D, wgpu::TextureComponentType::Uint}}); + + EXPECT_NE(bgl.Get(), otherBgl.Get()); + EXPECT_EQ(bgl.Get() == sameBgl.Get(), !UsesWire()); +} + +// Test that two similar bind group layouts won't refer to the same one if they differ by +// viewDimension +TEST_P(ObjectCachingTest, BindGroupLayoutViewDimension) { + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D, wgpu::TextureComponentType::Float}}); + wgpu::BindGroupLayout sameBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D, wgpu::TextureComponentType::Float}}); + wgpu::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2DArray, wgpu::TextureComponentType::Float}}); EXPECT_NE(bgl.Get(), otherBgl.Get()); EXPECT_EQ(bgl.Get() == sameBgl.Get(), !UsesWire()); @@ -34,23 +81,25 @@ TEST_P(ObjectCachingTest, BindGroupLayoutDeduplication) { // Test that an error object doesn't try to uncache itself TEST_P(ObjectCachingTest, ErrorObjectDoesntUncache) { + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + ASSERT_DEVICE_ERROR( - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, {{0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}, - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}})); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}, + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}})); } // Test that PipelineLayouts are correctly deduplicated. TEST_P(ObjectCachingTest, PipelineLayoutDeduplication) { - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}}); - dawn::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}); - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); - dawn::PipelineLayout samePl = utils::MakeBasicPipelineLayout(device, &bgl); - dawn::PipelineLayout otherPl1 = utils::MakeBasicPipelineLayout(device, nullptr); - dawn::PipelineLayout otherPl2 = utils::MakeBasicPipelineLayout(device, &otherBgl); + wgpu::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout samePl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout otherPl1 = utils::MakeBasicPipelineLayout(device, nullptr); + wgpu::PipelineLayout otherPl2 = utils::MakeBasicPipelineLayout(device, &otherBgl); EXPECT_NE(pl.Get(), otherPl1.Get()); EXPECT_NE(pl.Get(), otherPl2.Get()); @@ -59,21 +108,22 @@ TEST_P(ObjectCachingTest, PipelineLayoutDeduplication) { // Test that ShaderModules are correctly deduplicated. TEST_P(ObjectCachingTest, ShaderModuleDeduplication) { - dawn::ShaderModule module = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); })"); - dawn::ShaderModule sameModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule sameModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); })"); - dawn::ShaderModule otherModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule otherModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -86,19 +136,22 @@ TEST_P(ObjectCachingTest, ShaderModuleDeduplication) { // Test that ComputePipeline are correctly deduplicated wrt. their ShaderModule TEST_P(ObjectCachingTest, ComputePipelineDeduplicationOnShaderModule) { - dawn::ShaderModule module = utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"( + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( #version 450 + shared uint i; void main() { - int i = 0; + i = 0; })"); - dawn::ShaderModule sameModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"( + wgpu::ShaderModule sameModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( #version 450 + shared uint i; void main() { - int i = 0; + i = 0; })"); - dawn::ShaderModule otherModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"( + wgpu::ShaderModule otherModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( #version 450 void main() { })"); @@ -106,23 +159,20 @@ TEST_P(ObjectCachingTest, ComputePipelineDeduplicationOnShaderModule) { EXPECT_NE(module.Get(), otherModule.Get()); EXPECT_EQ(module.Get() == sameModule.Get(), !UsesWire()); - dawn::PipelineLayout layout = utils::MakeBasicPipelineLayout(device, nullptr); + wgpu::PipelineLayout layout = utils::MakeBasicPipelineLayout(device, nullptr); - dawn::PipelineStageDescriptor stageDesc; - stageDesc.entryPoint = "main"; - stageDesc.module = module; - - dawn::ComputePipelineDescriptor desc; - desc.computeStage = &stageDesc; + wgpu::ComputePipelineDescriptor desc; + desc.computeStage.entryPoint = "main"; desc.layout = layout; - dawn::ComputePipeline pipeline = device.CreateComputePipeline(&desc); + desc.computeStage.module = module; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&desc); - stageDesc.module = sameModule; - dawn::ComputePipeline samePipeline = device.CreateComputePipeline(&desc); + desc.computeStage.module = sameModule; + wgpu::ComputePipeline samePipeline = device.CreateComputePipeline(&desc); - stageDesc.module = otherModule; - dawn::ComputePipeline otherPipeline = device.CreateComputePipeline(&desc); + desc.computeStage.module = otherModule; + wgpu::ComputePipeline otherPipeline = device.CreateComputePipeline(&desc); EXPECT_NE(pipeline.Get(), otherPipeline.Get()); EXPECT_EQ(pipeline.Get() == samePipeline.Get(), !UsesWire()); @@ -130,37 +180,36 @@ TEST_P(ObjectCachingTest, ComputePipelineDeduplicationOnShaderModule) { // Test that ComputePipeline are correctly deduplicated wrt. their layout TEST_P(ObjectCachingTest, ComputePipelineDeduplicationOnLayout) { - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}}); - dawn::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}); - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); - dawn::PipelineLayout samePl = utils::MakeBasicPipelineLayout(device, &bgl); - dawn::PipelineLayout otherPl = utils::MakeBasicPipelineLayout(device, nullptr); + wgpu::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout samePl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout otherPl = utils::MakeBasicPipelineLayout(device, nullptr); EXPECT_NE(pl.Get(), otherPl.Get()); EXPECT_EQ(pl.Get() == samePl.Get(), !UsesWire()); - dawn::PipelineStageDescriptor stageDesc; - stageDesc.entryPoint = "main"; - stageDesc.module = utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"( + wgpu::ComputePipelineDescriptor desc; + desc.computeStage.entryPoint = "main"; + desc.computeStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( #version 450 + shared uint i; void main() { - int i = 0; + i = 0; })"); - dawn::ComputePipelineDescriptor desc; - desc.computeStage = &stageDesc; - desc.layout = pl; - dawn::ComputePipeline pipeline = device.CreateComputePipeline(&desc); + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&desc); desc.layout = samePl; - dawn::ComputePipeline samePipeline = device.CreateComputePipeline(&desc); + wgpu::ComputePipeline samePipeline = device.CreateComputePipeline(&desc); desc.layout = otherPl; - dawn::ComputePipeline otherPipeline = device.CreateComputePipeline(&desc); + wgpu::ComputePipeline otherPipeline = device.CreateComputePipeline(&desc); EXPECT_NE(pipeline.Get(), otherPipeline.Get()); EXPECT_EQ(pipeline.Get() == samePipeline.Get(), !UsesWire()); @@ -168,37 +217,39 @@ TEST_P(ObjectCachingTest, ComputePipelineDeduplicationOnLayout) { // Test that RenderPipelines are correctly deduplicated wrt. their layout TEST_P(ObjectCachingTest, RenderPipelineDeduplicationOnLayout) { - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer}}); - dawn::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( - device, {{1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout otherBgl = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}); - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); - dawn::PipelineLayout samePl = utils::MakeBasicPipelineLayout(device, &bgl); - dawn::PipelineLayout otherPl = utils::MakeBasicPipelineLayout(device, nullptr); + wgpu::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout samePl = utils::MakeBasicPipelineLayout(device, &bgl); + wgpu::PipelineLayout otherPl = utils::MakeBasicPipelineLayout(device, nullptr); EXPECT_NE(pl.Get(), otherPl.Get()); EXPECT_EQ(pl.Get() == samePl.Get(), !UsesWire()); utils::ComboRenderPipelineDescriptor desc(device); - desc.cVertexStage.module = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + desc.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { gl_Position = vec4(0.0); })"); - desc.cFragmentStage.module = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + desc.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 void main() { })"); desc.layout = pl; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); desc.layout = samePl; - dawn::RenderPipeline samePipeline = device.CreateRenderPipeline(&desc); + wgpu::RenderPipeline samePipeline = device.CreateRenderPipeline(&desc); desc.layout = otherPl; - dawn::RenderPipeline otherPipeline = device.CreateRenderPipeline(&desc); + wgpu::RenderPipeline otherPipeline = device.CreateRenderPipeline(&desc); EXPECT_NE(pipeline.Get(), otherPipeline.Get()); EXPECT_EQ(pipeline.Get() == samePipeline.Get(), !UsesWire()); @@ -206,18 +257,20 @@ TEST_P(ObjectCachingTest, RenderPipelineDeduplicationOnLayout) { // Test that RenderPipelines are correctly deduplicated wrt. their vertex module TEST_P(ObjectCachingTest, RenderPipelineDeduplicationOnVertexModule) { - dawn::ShaderModule module = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { gl_Position = vec4(0.0); })"); - dawn::ShaderModule sameModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule sameModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { gl_Position = vec4(0.0); })"); - dawn::ShaderModule otherModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule otherModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { gl_Position = vec4(1.0); @@ -227,19 +280,20 @@ TEST_P(ObjectCachingTest, RenderPipelineDeduplicationOnVertexModule) { EXPECT_EQ(module.Get() == sameModule.Get(), !UsesWire()); utils::ComboRenderPipelineDescriptor desc(device); - desc.cFragmentStage.module = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + desc.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 void main() { })"); - desc.cVertexStage.module = module; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + desc.vertexStage.module = module; + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); - desc.cVertexStage.module = sameModule; - dawn::RenderPipeline samePipeline = device.CreateRenderPipeline(&desc); + desc.vertexStage.module = sameModule; + wgpu::RenderPipeline samePipeline = device.CreateRenderPipeline(&desc); - desc.cVertexStage.module = otherModule; - dawn::RenderPipeline otherPipeline = device.CreateRenderPipeline(&desc); + desc.vertexStage.module = otherModule; + wgpu::RenderPipeline otherPipeline = device.CreateRenderPipeline(&desc); EXPECT_NE(pipeline.Get(), otherPipeline.Get()); EXPECT_EQ(pipeline.Get() == samePipeline.Get(), !UsesWire()); @@ -247,40 +301,43 @@ TEST_P(ObjectCachingTest, RenderPipelineDeduplicationOnVertexModule) { // Test that RenderPipelines are correctly deduplicated wrt. their fragment module TEST_P(ObjectCachingTest, RenderPipelineDeduplicationOnFragmentModule) { - dawn::ShaderModule module = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 void main() { })"); - dawn::ShaderModule sameModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule sameModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 void main() { })"); - dawn::ShaderModule otherModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule otherModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 + layout (location = 0) out vec4 color; void main() { - int i = 0; + color = vec4(0.0); })"); EXPECT_NE(module.Get(), otherModule.Get()); EXPECT_EQ(module.Get() == sameModule.Get(), !UsesWire()); utils::ComboRenderPipelineDescriptor desc(device); - desc.cVertexStage.module = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + desc.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { gl_Position = vec4(0.0); })"); desc.cFragmentStage.module = module; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); desc.cFragmentStage.module = sameModule; - dawn::RenderPipeline samePipeline = device.CreateRenderPipeline(&desc); + wgpu::RenderPipeline samePipeline = device.CreateRenderPipeline(&desc); desc.cFragmentStage.module = otherModule; - dawn::RenderPipeline otherPipeline = device.CreateRenderPipeline(&desc); + wgpu::RenderPipeline otherPipeline = device.CreateRenderPipeline(&desc); EXPECT_NE(pipeline.Get(), otherPipeline.Get()); EXPECT_EQ(pipeline.Get() == samePipeline.Get(), !UsesWire()); @@ -288,47 +345,47 @@ TEST_P(ObjectCachingTest, RenderPipelineDeduplicationOnFragmentModule) { // Test that Samplers are correctly deduplicated. TEST_P(ObjectCachingTest, SamplerDeduplication) { - dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); - dawn::Sampler sampler = device.CreateSampler(&samplerDesc); + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); - dawn::SamplerDescriptor sameSamplerDesc = utils::GetDefaultSamplerDescriptor(); - dawn::Sampler sameSampler = device.CreateSampler(&sameSamplerDesc); + wgpu::SamplerDescriptor sameSamplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sameSampler = device.CreateSampler(&sameSamplerDesc); - dawn::SamplerDescriptor otherSamplerDescAddressModeU = utils::GetDefaultSamplerDescriptor(); - otherSamplerDescAddressModeU.addressModeU = dawn::AddressMode::ClampToEdge; - dawn::Sampler otherSamplerAddressModeU = device.CreateSampler(&otherSamplerDescAddressModeU); + wgpu::SamplerDescriptor otherSamplerDescAddressModeU = utils::GetDefaultSamplerDescriptor(); + otherSamplerDescAddressModeU.addressModeU = wgpu::AddressMode::ClampToEdge; + wgpu::Sampler otherSamplerAddressModeU = device.CreateSampler(&otherSamplerDescAddressModeU); - dawn::SamplerDescriptor otherSamplerDescAddressModeV = utils::GetDefaultSamplerDescriptor(); - otherSamplerDescAddressModeV.addressModeV = dawn::AddressMode::ClampToEdge; - dawn::Sampler otherSamplerAddressModeV = device.CreateSampler(&otherSamplerDescAddressModeV); + wgpu::SamplerDescriptor otherSamplerDescAddressModeV = utils::GetDefaultSamplerDescriptor(); + otherSamplerDescAddressModeV.addressModeV = wgpu::AddressMode::ClampToEdge; + wgpu::Sampler otherSamplerAddressModeV = device.CreateSampler(&otherSamplerDescAddressModeV); - dawn::SamplerDescriptor otherSamplerDescAddressModeW = utils::GetDefaultSamplerDescriptor(); - otherSamplerDescAddressModeW.addressModeW = dawn::AddressMode::ClampToEdge; - dawn::Sampler otherSamplerAddressModeW = device.CreateSampler(&otherSamplerDescAddressModeW); + wgpu::SamplerDescriptor otherSamplerDescAddressModeW = utils::GetDefaultSamplerDescriptor(); + otherSamplerDescAddressModeW.addressModeW = wgpu::AddressMode::ClampToEdge; + wgpu::Sampler otherSamplerAddressModeW = device.CreateSampler(&otherSamplerDescAddressModeW); - dawn::SamplerDescriptor otherSamplerDescMagFilter = utils::GetDefaultSamplerDescriptor(); - otherSamplerDescMagFilter.magFilter = dawn::FilterMode::Nearest; - dawn::Sampler otherSamplerMagFilter = device.CreateSampler(&otherSamplerDescMagFilter); + wgpu::SamplerDescriptor otherSamplerDescMagFilter = utils::GetDefaultSamplerDescriptor(); + otherSamplerDescMagFilter.magFilter = wgpu::FilterMode::Nearest; + wgpu::Sampler otherSamplerMagFilter = device.CreateSampler(&otherSamplerDescMagFilter); - dawn::SamplerDescriptor otherSamplerDescMinFilter = utils::GetDefaultSamplerDescriptor(); - otherSamplerDescMinFilter.minFilter = dawn::FilterMode::Nearest; - dawn::Sampler otherSamplerMinFilter = device.CreateSampler(&otherSamplerDescMinFilter); + wgpu::SamplerDescriptor otherSamplerDescMinFilter = utils::GetDefaultSamplerDescriptor(); + otherSamplerDescMinFilter.minFilter = wgpu::FilterMode::Nearest; + wgpu::Sampler otherSamplerMinFilter = device.CreateSampler(&otherSamplerDescMinFilter); - dawn::SamplerDescriptor otherSamplerDescMipmapFilter = utils::GetDefaultSamplerDescriptor(); - otherSamplerDescMipmapFilter.mipmapFilter = dawn::FilterMode::Nearest; - dawn::Sampler otherSamplerMipmapFilter = device.CreateSampler(&otherSamplerDescMipmapFilter); + wgpu::SamplerDescriptor otherSamplerDescMipmapFilter = utils::GetDefaultSamplerDescriptor(); + otherSamplerDescMipmapFilter.mipmapFilter = wgpu::FilterMode::Nearest; + wgpu::Sampler otherSamplerMipmapFilter = device.CreateSampler(&otherSamplerDescMipmapFilter); - dawn::SamplerDescriptor otherSamplerDescLodMinClamp = utils::GetDefaultSamplerDescriptor(); + wgpu::SamplerDescriptor otherSamplerDescLodMinClamp = utils::GetDefaultSamplerDescriptor(); otherSamplerDescLodMinClamp.lodMinClamp += 1; - dawn::Sampler otherSamplerLodMinClamp = device.CreateSampler(&otherSamplerDescLodMinClamp); + wgpu::Sampler otherSamplerLodMinClamp = device.CreateSampler(&otherSamplerDescLodMinClamp); - dawn::SamplerDescriptor otherSamplerDescLodMaxClamp = utils::GetDefaultSamplerDescriptor(); + wgpu::SamplerDescriptor otherSamplerDescLodMaxClamp = utils::GetDefaultSamplerDescriptor(); otherSamplerDescLodMaxClamp.lodMaxClamp += 1; - dawn::Sampler otherSamplerLodMaxClamp = device.CreateSampler(&otherSamplerDescLodMaxClamp); + wgpu::Sampler otherSamplerLodMaxClamp = device.CreateSampler(&otherSamplerDescLodMaxClamp); - dawn::SamplerDescriptor otherSamplerDescCompareFunction = utils::GetDefaultSamplerDescriptor(); - otherSamplerDescCompareFunction.compareFunction = dawn::CompareFunction::Always; - dawn::Sampler otherSamplerCompareFunction = + wgpu::SamplerDescriptor otherSamplerDescCompareFunction = utils::GetDefaultSamplerDescriptor(); + otherSamplerDescCompareFunction.compare = wgpu::CompareFunction::Always; + wgpu::Sampler otherSamplerCompareFunction = device.CreateSampler(&otherSamplerDescCompareFunction); EXPECT_NE(sampler.Get(), otherSamplerAddressModeU.Get()); @@ -343,4 +400,8 @@ TEST_P(ObjectCachingTest, SamplerDeduplication) { EXPECT_EQ(sampler.Get() == sameSampler.Get(), !UsesWire()); } -DAWN_INSTANTIATE_TEST(ObjectCachingTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(ObjectCachingTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/OpArrayLengthTests.cpp b/third_party/dawn/src/tests/end2end/OpArrayLengthTests.cpp new file mode 100644 index 00000000000..2d8e465b332 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/OpArrayLengthTests.cpp @@ -0,0 +1,269 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Assert.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class OpArrayLengthTest : public DawnTest { + protected: + void SetUp() { + DawnTest::SetUp(); + + // Create buffers of various size to check the length() implementation + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = 4; + bufferDesc.usage = wgpu::BufferUsage::Storage; + mStorageBuffer4 = device.CreateBuffer(&bufferDesc); + + bufferDesc.size = 256; + mStorageBuffer256 = device.CreateBuffer(&bufferDesc); + + bufferDesc.size = 512 + 256; + mStorageBuffer512 = device.CreateBuffer(&bufferDesc); + + // Put them all in a bind group for tests to bind them easily. + wgpu::ShaderStage kAllStages = + wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Compute; + mBindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, kAllStages, wgpu::BindingType::ReadonlyStorageBuffer}, + {1, kAllStages, wgpu::BindingType::ReadonlyStorageBuffer}, + {2, kAllStages, wgpu::BindingType::ReadonlyStorageBuffer}}); + + mBindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + { + {0, mStorageBuffer4, 0, 4}, + {1, mStorageBuffer256, 0, wgpu::kWholeSize}, + {2, mStorageBuffer512, 256, wgpu::kWholeSize}, + }); + + // Common shader code to use these buffers in shaders, assuming they are in bindgroup index + // 0. + mShaderInterface = R"( + // The length should be 1 because the buffer is 4-byte long. + layout(std430, set = 0, binding = 0) readonly buffer Buffer1 { + float data[]; + } buffer1; + + // The length should be 64 because the buffer is 256 bytes long. + layout(std430, set = 0, binding = 1) readonly buffer Buffer2 { + float data[]; + } buffer2; + + // The length should be (512 - 16*4) / 8 = 56 because the buffer is 512 bytes long + // and the structure is 8 bytes big. + struct Buffer3Data {float a; int b;}; + layout(std430, set = 0, binding = 2) readonly buffer Buffer3 { + mat4 garbage; + Buffer3Data data[]; + } buffer3; + )"; + + // See comments in the shader for an explanation of these values + mExpectedLengths = {1, 64, 56}; + } + + wgpu::Buffer mStorageBuffer4; + wgpu::Buffer mStorageBuffer256; + wgpu::Buffer mStorageBuffer512; + + wgpu::BindGroupLayout mBindGroupLayout; + wgpu::BindGroup mBindGroup; + std::string mShaderInterface; + std::array mExpectedLengths; +}; + +// Test OpArrayLength in the compute stage +TEST_P(OpArrayLengthTest, Compute) { + // TODO(cwallez@chromium.org): The computations for length() of unsized buffer is broken on + // Nvidia OpenGL. See https://bugs.chromium.org/p/dawn/issues/detail?id=197 + DAWN_SKIP_TEST_IF(IsNvidia() && IsOpenGL()); + + // Create a buffer to hold the result sizes and create a bindgroup for it. + wgpu::BufferDescriptor bufferDesc; + bufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc; + bufferDesc.size = sizeof(uint32_t) * mExpectedLengths.size(); + wgpu::Buffer resultBuffer = device.CreateBuffer(&bufferDesc); + + wgpu::BindGroupLayout resultLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + + wgpu::BindGroup resultBindGroup = + utils::MakeBindGroup(device, resultLayout, {{0, resultBuffer, 0, wgpu::kWholeSize}}); + + // Create the compute pipeline that stores the length()s in the result buffer. + wgpu::BindGroupLayout bgls[] = {mBindGroupLayout, resultLayout}; + wgpu::PipelineLayoutDescriptor plDesc; + plDesc.bindGroupLayoutCount = 2; + plDesc.bindGroupLayouts = bgls; + wgpu::PipelineLayout pl = device.CreatePipelineLayout(&plDesc); + + wgpu::ComputePipelineDescriptor pipelineDesc; + pipelineDesc.layout = pl; + pipelineDesc.computeStage.entryPoint = "main"; + pipelineDesc.computeStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, + (R"(#version 450 + layout(std430, set = 1, binding = 0) buffer ResultBuffer { + uint result[3]; + }; + )" + mShaderInterface + R"( + void main() { + result[0] = buffer1.data.length(); + result[1] = buffer2.data.length(); + result[2] = buffer3.data.length(); + })") + .c_str()); + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&pipelineDesc); + + // Run a single instance of the compute shader + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, mBindGroup); + pass.SetBindGroup(1, resultBindGroup); + pass.Dispatch(1); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_BUFFER_U32_RANGE_EQ(mExpectedLengths.data(), resultBuffer, 0, 3); +} + +// Test OpArrayLength in the fragment stage +TEST_P(OpArrayLengthTest, Fragment) { + // TODO(cwallez@chromium.org): The computations for length() of unsized buffer is broken on + // Nvidia OpenGL. See https://bugs.chromium.org/p/dawn/issues/detail?id=197 + DAWN_SKIP_TEST_IF(IsNvidia() && IsOpenGL()); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + + // Create the pipeline that computes the length of the buffers and writes it to the only render + // pass pixel. + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f); + gl_PointSize = 1.0; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, + (R"( + #version 450 + )" + mShaderInterface + R"( + layout(location = 0) out vec4 fragColor; + void main() { + fragColor.r = buffer1.data.length() / 255.0f; + fragColor.g = buffer2.data.length() / 255.0f; + fragColor.b = buffer3.data.length() / 255.0f; + fragColor.a = 0.0f; + })") + .c_str()); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList; + descriptor.cColorStates[0].format = renderPass.colorFormat; + descriptor.layout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + + // "Draw" the lengths to the texture. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, mBindGroup); + pass.Draw(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + RGBA8 expectedColor = RGBA8(mExpectedLengths[0], mExpectedLengths[1], mExpectedLengths[2], 0); + EXPECT_PIXEL_RGBA8_EQ(expectedColor, renderPass.color, 0, 0); +} + +// Test OpArrayLength in the vertex stage +TEST_P(OpArrayLengthTest, Vertex) { + // TODO(cwallez@chromium.org): The computations for length() of unsized buffer is broken on + // Nvidia OpenGL. See https://bugs.chromium.org/p/dawn/issues/detail?id=197 + DAWN_SKIP_TEST_IF(IsNvidia() && IsOpenGL()); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 1, 1); + + // Create the pipeline that computes the length of the buffers and writes it to the only render + // pass pixel. + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, + (R"( + #version 450 + )" + mShaderInterface + R"( + layout(location = 0) out vec4 pointColor; + void main() { + pointColor.r = buffer1.data.length() / 255.0f; + pointColor.g = buffer2.data.length() / 255.0f; + pointColor.b = buffer3.data.length() / 255.0f; + pointColor.a = 0.0f; + + gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f); + gl_PointSize = 1.0; + })") + .c_str()); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + layout(location = 0) in vec4 pointColor; + void main() { + fragColor = pointColor; + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList; + descriptor.cColorStates[0].format = renderPass.colorFormat; + descriptor.layout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + + // "Draw" the lengths to the texture. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, mBindGroup); + pass.Draw(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + RGBA8 expectedColor = RGBA8(mExpectedLengths[0], mExpectedLengths[1], mExpectedLengths[2], 0); + EXPECT_PIXEL_RGBA8_EQ(expectedColor, renderPass.color, 0, 0); +} + +DAWN_INSTANTIATE_TEST(OpArrayLengthTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/PipelineLayoutTests.cpp b/third_party/dawn/src/tests/end2end/PipelineLayoutTests.cpp new file mode 100644 index 00000000000..5c8eeeb276b --- /dev/null +++ b/third_party/dawn/src/tests/end2end/PipelineLayoutTests.cpp @@ -0,0 +1,66 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Constants.h" +#include "tests/DawnTest.h" + +#include + +class PipelineLayoutTests : public DawnTest {}; + +// Test creating a PipelineLayout with multiple BGLs where the first BGL uses the max number of +// dynamic buffers. This is a regression test for crbug.com/dawn/449 which would overflow when +// dynamic offset bindings were at max. Test is successful if the pipeline layout is created +// without error. +TEST_P(PipelineLayoutTests, DynamicBuffersOverflow) { + // Create the first bind group layout which uses max number of dynamic buffers bindings. + wgpu::BindGroupLayout bglA; + { + std::vector entries; + for (uint32_t i = 0; i < kMaxDynamicStorageBuffersPerPipelineLayout; i++) { + entries.push_back( + {i, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer, true}); + } + + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = static_cast(entries.size()); + descriptor.entries = entries.data(); + bglA = device.CreateBindGroupLayout(&descriptor); + } + + // Create the second bind group layout that has one non-dynamic buffer binding. + // It is in the fragment stage to avoid the max per-stage storage buffer limit. + wgpu::BindGroupLayout bglB; + { + wgpu::BindGroupLayoutDescriptor descriptor; + wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer, false}; + descriptor.entryCount = 1; + descriptor.entries = &entry; + bglB = device.CreateBindGroupLayout(&descriptor); + } + + // Create a pipeline layout using both bind group layouts. + wgpu::PipelineLayoutDescriptor descriptor; + std::vector bindgroupLayouts = {bglA, bglB}; + descriptor.bindGroupLayoutCount = bindgroupLayouts.size(); + descriptor.bindGroupLayouts = bindgroupLayouts.data(); + device.CreatePipelineLayout(&descriptor); +} + +DAWN_INSTANTIATE_TEST(PipelineLayoutTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/PrimitiveTopologyTests.cpp b/third_party/dawn/src/tests/end2end/PrimitiveTopologyTests.cpp index a13b1c5f7cd..0c0c2851767 100644 --- a/third_party/dawn/src/tests/end2end/PrimitiveTopologyTests.cpp +++ b/third_party/dawn/src/tests/end2end/PrimitiveTopologyTests.cpp @@ -16,9 +16,10 @@ #include "common/Assert.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" -// Primitive topology tests work by drawing the following vertices with all the different primitive topology states: +// Primitive topology tests work by drawing the following vertices with all the different primitive +// topology states: // ------------------------------------- // | | // | 1 2 5 | @@ -81,8 +82,8 @@ // ------------------------------------- // // Each of these different states is a superset of some of the previous states, -// so for every state, we check any new added test locations that are not contained in previous states -// We also check that the test locations of subsequent states are untouched +// so for every state, we check any new added test locations that are not contained in previous +// states We also check that the test locations of subsequent states are untouched constexpr static unsigned int kRTSize = 32; @@ -91,11 +92,13 @@ struct TestLocation { }; constexpr TestLocation GetMidpoint(const TestLocation& a, const TestLocation& b) noexcept { - return { (a.x + b.x) / 2, (a.y + b.y) / 2 }; + return {(a.x + b.x) / 2, (a.y + b.y) / 2}; } -constexpr TestLocation GetCentroid(const TestLocation& a, const TestLocation& b, const TestLocation& c) noexcept { - return { (a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3 }; +constexpr TestLocation GetCentroid(const TestLocation& a, + const TestLocation& b, + const TestLocation& c) noexcept { + return {(a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3}; } // clang-format off @@ -134,152 +137,164 @@ constexpr static TestLocation kTriangleStripTestLocations[] = { constexpr static float kRTSizef = static_cast(kRTSize); constexpr static float kVertices[] = { - 2.f * (kPointTestLocations[0].x + 0.5f) / kRTSizef - 1.f, 2.f * (kPointTestLocations[0].y + 0.5f) / kRTSizef - 1.0f, 0.f, 1.f, - 2.f * (kPointTestLocations[1].x + 0.5f) / kRTSizef - 1.f, 2.f * (kPointTestLocations[1].y + 0.5f) / kRTSizef - 1.0f, 0.f, 1.f, - 2.f * (kPointTestLocations[2].x + 0.5f) / kRTSizef - 1.f, 2.f * (kPointTestLocations[2].y + 0.5f) / kRTSizef - 1.0f, 0.f, 1.f, - 2.f * (kPointTestLocations[3].x + 0.5f) / kRTSizef - 1.f, 2.f * (kPointTestLocations[3].y + 0.5f) / kRTSizef - 1.0f, 0.f, 1.f, - 2.f * (kPointTestLocations[4].x + 0.5f) / kRTSizef - 1.f, 2.f * (kPointTestLocations[4].y + 0.5f) / kRTSizef - 1.0f, 0.f, 1.f, - 2.f * (kPointTestLocations[5].x + 0.5f) / kRTSizef - 1.f, 2.f * (kPointTestLocations[5].y + 0.5f) / kRTSizef - 1.0f, 0.f, 1.f, + 2.f * (kPointTestLocations[0].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[0].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f, + 2.f * (kPointTestLocations[1].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[1].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f, + 2.f * (kPointTestLocations[2].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[2].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f, + 2.f * (kPointTestLocations[3].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[3].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f, + 2.f * (kPointTestLocations[4].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[4].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f, + 2.f * (kPointTestLocations[5].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[5].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f, }; // clang-format on class PrimitiveTopologyTest : public DawnTest { - protected: - void SetUp() override { - DawnTest::SetUp(); + protected: + void SetUp() override { + DawnTest::SetUp(); - renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout(location = 0) in vec4 pos; void main() { gl_Position = pos; - })" - ); + gl_PointSize = 1.0; + })"); - fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + fsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); })"); - vertexBuffer = utils::CreateBufferFromData(device, kVertices, sizeof(kVertices), dawn::BufferUsageBit::Vertex); - } - - struct LocationSpec { - const TestLocation* locations; - size_t count; - bool include; - }; - - template - constexpr LocationSpec TestPoints(TestLocation const (&points)[N], bool include) noexcept { - return { points, N, include }; + vertexBuffer = utils::CreateBufferFromData(device, kVertices, sizeof(kVertices), + wgpu::BufferUsage::Vertex); + } + + struct LocationSpec { + const TestLocation* locations; + size_t count; + bool include; + }; + + template + constexpr LocationSpec TestPoints(TestLocation const (&points)[N], bool include) noexcept { + return {points, N, include}; + } + + // Draw the vertices with the given primitive topology and check the pixel values of the test + // locations + void DoTest(wgpu::PrimitiveTopology primitiveTopology, + const std::vector& locationSpecs) { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = primitiveTopology; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + descriptor.cColorStates[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(6); + pass.EndPass(); } - // Draw the vertices with the given primitive topology and check the pixel values of the test locations - void DoTest(dawn::PrimitiveTopology primitiveTopology, const std::vector &locationSpecs) { - utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; - descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = primitiveTopology; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float); - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4; - descriptor.cColorStates[0]->format = renderPass.colorFormat; - - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); - - static const uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass( - &renderPass.renderPassInfo); - pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.Draw(6, 1, 0, 0); - pass.EndPass(); - } - - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - for (auto& locationSpec : locationSpecs) { - for (size_t i = 0; i < locationSpec.count; ++i) { - // If this pixel is included, check that it is green. Otherwise, check that it is black - RGBA8 color = locationSpec.include ? RGBA8(0, 255, 0, 255) : RGBA8(0, 0, 0, 0); - EXPECT_PIXEL_RGBA8_EQ(color, renderPass.color, locationSpec.locations[i].x, locationSpec.locations[i].y) - << "Expected (" << locationSpec.locations[i].x << ", " << locationSpec.locations[i].y << ") to be " << color; - } + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + for (auto& locationSpec : locationSpecs) { + for (size_t i = 0; i < locationSpec.count; ++i) { + // If this pixel is included, check that it is green. Otherwise, check that it is + // black + RGBA8 color = locationSpec.include ? RGBA8::kGreen : RGBA8::kZero; + EXPECT_PIXEL_RGBA8_EQ(color, renderPass.color, locationSpec.locations[i].x, + locationSpec.locations[i].y) + << "Expected (" << locationSpec.locations[i].x << ", " + << locationSpec.locations[i].y << ") to be " << color; } } + } - utils::BasicRenderPass renderPass; - dawn::ShaderModule vsModule; - dawn::ShaderModule fsModule; - dawn::Buffer vertexBuffer; + utils::BasicRenderPass renderPass; + wgpu::ShaderModule vsModule; + wgpu::ShaderModule fsModule; + wgpu::Buffer vertexBuffer; }; // Test Point primitive topology TEST_P(PrimitiveTopologyTest, PointList) { - DoTest(dawn::PrimitiveTopology::PointList, { - // Check that the points are drawn - TestPoints(kPointTestLocations, true), - - // Check that line and triangle locations are untouched - TestPoints(kLineTestLocations, false), - TestPoints(kLineStripTestLocations, false), - TestPoints(kTriangleTestLocations, false), - TestPoints(kTriangleStripTestLocations, false), - }); + DoTest(wgpu::PrimitiveTopology::PointList, + { + // Check that the points are drawn + TestPoints(kPointTestLocations, true), + + // Check that line and triangle locations are untouched + TestPoints(kLineTestLocations, false), + TestPoints(kLineStripTestLocations, false), + TestPoints(kTriangleTestLocations, false), + TestPoints(kTriangleStripTestLocations, false), + }); } // Test Line primitive topology TEST_P(PrimitiveTopologyTest, LineList) { - DoTest(dawn::PrimitiveTopology::LineList, { - // Check that lines are drawn - TestPoints(kLineTestLocations, true), - - // Check that line strip and triangle locations are untouched - TestPoints(kLineStripTestLocations, false), - TestPoints(kTriangleTestLocations, false), - TestPoints(kTriangleStripTestLocations, false), - }); + DoTest(wgpu::PrimitiveTopology::LineList, + { + // Check that lines are drawn + TestPoints(kLineTestLocations, true), + + // Check that line strip and triangle locations are untouched + TestPoints(kLineStripTestLocations, false), + TestPoints(kTriangleTestLocations, false), + TestPoints(kTriangleStripTestLocations, false), + }); } // Test LineStrip primitive topology TEST_P(PrimitiveTopologyTest, LineStrip) { - DoTest(dawn::PrimitiveTopology::LineStrip, { - // Check that lines are drawn - TestPoints(kLineTestLocations, true), - TestPoints(kLineStripTestLocations, true), - - // Check that triangle locations are untouched - TestPoints(kTriangleTestLocations, false), - TestPoints(kTriangleStripTestLocations, false), - }); + DoTest(wgpu::PrimitiveTopology::LineStrip, { + // Check that lines are drawn + TestPoints(kLineTestLocations, true), + TestPoints(kLineStripTestLocations, true), + + // Check that triangle locations are untouched + TestPoints(kTriangleTestLocations, false), + TestPoints(kTriangleStripTestLocations, false), + }); } // Test Triangle primitive topology TEST_P(PrimitiveTopologyTest, TriangleList) { - DoTest(dawn::PrimitiveTopology::TriangleList, { - // Check that triangles are drawn - TestPoints(kTriangleTestLocations, true), - - // Check that triangle strip locations are untouched - TestPoints(kTriangleStripTestLocations, false), - }); + DoTest(wgpu::PrimitiveTopology::TriangleList, + { + // Check that triangles are drawn + TestPoints(kTriangleTestLocations, true), + + // Check that triangle strip locations are untouched + TestPoints(kTriangleStripTestLocations, false), + }); } // Test TriangleStrip primitive topology TEST_P(PrimitiveTopologyTest, TriangleStrip) { - DoTest(dawn::PrimitiveTopology::TriangleStrip, { - TestPoints(kTriangleTestLocations, true), - TestPoints(kTriangleStripTestLocations, true), - }); + DoTest(wgpu::PrimitiveTopology::TriangleStrip, + { + TestPoints(kTriangleTestLocations, true), + TestPoints(kTriangleStripTestLocations, true), + }); } -DAWN_INSTANTIATE_TEST(PrimitiveTopologyTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(PrimitiveTopologyTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/QueryTests.cpp b/third_party/dawn/src/tests/end2end/QueryTests.cpp new file mode 100644 index 00000000000..fc244f21c3b --- /dev/null +++ b/third_party/dawn/src/tests/end2end/QueryTests.cpp @@ -0,0 +1,103 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains test for deprecated parts of Dawn's API while following WebGPU's evolution. +// It contains test for the "old" behavior that will be deleted once users are migrated, tests that +// a deprecation warning is emitted when the "old" behavior is used, and tests that an error is +// emitted when both the old and the new behavior are used (when applicable). + +#include "tests/DawnTest.h" + +class OcclusionQueryTests : public DawnTest {}; + +// Test creating query set with the type of Occlusion +TEST_P(OcclusionQueryTests, QuerySetCreation) { + wgpu::QuerySetDescriptor descriptor; + descriptor.count = 1; + descriptor.type = wgpu::QueryType::Occlusion; + device.CreateQuerySet(&descriptor); +} + +// Test destroying query set +TEST_P(OcclusionQueryTests, QuerySetDestroy) { + wgpu::QuerySetDescriptor descriptor; + descriptor.count = 1; + descriptor.type = wgpu::QueryType::Occlusion; + wgpu::QuerySet querySet = device.CreateQuerySet(&descriptor); + querySet.Destroy(); +} + +DAWN_INSTANTIATE_TEST(OcclusionQueryTests, D3D12Backend()); + +class PipelineStatisticsQueryTests : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + + // Skip all tests if pipeline statistics extension is not supported + DAWN_SKIP_TEST_IF(!SupportsExtensions({"pipeline_statistics_query"})); + } + + std::vector GetRequiredExtensions() override { + std::vector requiredExtensions = {}; + if (SupportsExtensions({"pipeline_statistics_query"})) { + requiredExtensions.push_back("pipeline_statistics_query"); + } + + return requiredExtensions; + } +}; + +// Test creating query set with the type of PipelineStatistics +TEST_P(PipelineStatisticsQueryTests, QuerySetCreation) { + wgpu::QuerySetDescriptor descriptor; + descriptor.count = 1; + descriptor.type = wgpu::QueryType::PipelineStatistics; + wgpu::PipelineStatisticName pipelineStatistics[2] = { + wgpu::PipelineStatisticName::ClipperInvocations, + wgpu::PipelineStatisticName::VertexShaderInvocations}; + descriptor.pipelineStatistics = pipelineStatistics; + descriptor.pipelineStatisticsCount = 2; + device.CreateQuerySet(&descriptor); +} + +DAWN_INSTANTIATE_TEST(PipelineStatisticsQueryTests, D3D12Backend()); + +class TimestampQueryTests : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + + // Skip all tests if timestamp extension is not supported + DAWN_SKIP_TEST_IF(!SupportsExtensions({"timestamp_query"})); + } + + std::vector GetRequiredExtensions() override { + std::vector requiredExtensions = {}; + if (SupportsExtensions({"timestamp_query"})) { + requiredExtensions.push_back("timestamp_query"); + } + return requiredExtensions; + } +}; + +// Test creating query set with the type of Timestamp +TEST_P(TimestampQueryTests, QuerySetCreation) { + wgpu::QuerySetDescriptor descriptor; + descriptor.count = 1; + descriptor.type = wgpu::QueryType::Timestamp; + device.CreateQuerySet(&descriptor); +} + +DAWN_INSTANTIATE_TEST(TimestampQueryTests, D3D12Backend()); diff --git a/third_party/dawn/src/tests/end2end/QueueTests.cpp b/third_party/dawn/src/tests/end2end/QueueTests.cpp new file mode 100644 index 00000000000..4e5febdc0be --- /dev/null +++ b/third_party/dawn/src/tests/end2end/QueueTests.cpp @@ -0,0 +1,495 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file contains test for deprecated parts of Dawn's API while following WebGPU's evolution. +// It contains test for the "old" behavior that will be deleted once users are migrated, tests that +// a deprecation warning is emitted when the "old" behavior is used, and tests that an error is +// emitted when both the old and the new behavior are used (when applicable). + +#include "tests/DawnTest.h" + +#include "common/Math.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" + +class QueueTests : public DawnTest {}; + +// Test that GetDefaultQueue always returns the same object. +TEST_P(QueueTests, GetDefaultQueueSameObject) { + wgpu::Queue q1 = device.GetDefaultQueue(); + wgpu::Queue q2 = device.GetDefaultQueue(); + EXPECT_EQ(q1.Get(), q2.Get()); +} + +DAWN_INSTANTIATE_TEST(QueueTests, + D3D12Backend(), + MetalBackend(), + NullBackend(), + OpenGLBackend(), + VulkanBackend()); + +class QueueWriteBufferTests : public DawnTest {}; + +// Test the simplest WriteBuffer setting one u32 at offset 0. +TEST_P(QueueWriteBufferTests, SmallDataAtZero) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + uint32_t value = 0x01020304; + queue.WriteBuffer(buffer, 0, &value, sizeof(value)); + + EXPECT_BUFFER_U32_EQ(value, buffer, 0); +} + +// Test an empty WriteBuffer +TEST_P(QueueWriteBufferTests, ZeroSized) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + uint32_t initialValue = 0x42; + queue.WriteBuffer(buffer, 0, &initialValue, sizeof(initialValue)); + + queue.WriteBuffer(buffer, 0, nullptr, 0); + + // The content of the buffer isn't changed + EXPECT_BUFFER_U32_EQ(initialValue, buffer, 0); +} + +// Call WriteBuffer at offset 0 via a u32 twice. Test that data is updated accoordingly. +TEST_P(QueueWriteBufferTests, SetTwice) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + uint32_t value = 0x01020304; + queue.WriteBuffer(buffer, 0, &value, sizeof(value)); + + EXPECT_BUFFER_U32_EQ(value, buffer, 0); + + value = 0x05060708; + queue.WriteBuffer(buffer, 0, &value, sizeof(value)); + + EXPECT_BUFFER_U32_EQ(value, buffer, 0); +} + +// Test that WriteBuffer offset works. +TEST_P(QueueWriteBufferTests, SmallDataAtOffset) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4000; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + constexpr uint64_t kOffset = 2000; + uint32_t value = 0x01020304; + queue.WriteBuffer(buffer, kOffset, &value, sizeof(value)); + + EXPECT_BUFFER_U32_EQ(value, buffer, kOffset); +} + +// Stress test for many calls to WriteBuffer +TEST_P(QueueWriteBufferTests, ManyWriteBuffer) { + // Note: Increasing the size of the buffer will likely cause timeout issues. + // In D3D12, timeout detection occurs when the GPU scheduler tries but cannot preempt the task + // executing these commands in-flight. If this takes longer than ~2s, a device reset occurs and + // fails the test. Since GPUs may or may not complete by then, this test must be disabled OR + // modified to be well-below the timeout limit. + + // TODO (jiawei.shao@intel.com): find out why this test fails on Intel Vulkan Linux bots. + DAWN_SKIP_TEST_IF(IsIntel() && IsVulkan() && IsLinux()); + // TODO(https://bugs.chromium.org/p/dawn/issues/detail?id=228): Re-enable + // once the issue with Metal on 10.14.6 is fixed. + DAWN_SKIP_TEST_IF(IsMacOS() && IsIntel() && IsMetal()); + + constexpr uint64_t kSize = 4000 * 1000; + constexpr uint32_t kElements = 250 * 250; + wgpu::BufferDescriptor descriptor; + descriptor.size = kSize; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + std::vector expectedData; + for (uint32_t i = 0; i < kElements; ++i) { + queue.WriteBuffer(buffer, i * sizeof(uint32_t), &i, sizeof(i)); + expectedData.push_back(i); + } + + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); +} + +// Test using WriteBuffer for lots of data +TEST_P(QueueWriteBufferTests, LargeWriteBuffer) { + constexpr uint64_t kSize = 4000 * 1000; + constexpr uint32_t kElements = 1000 * 1000; + wgpu::BufferDescriptor descriptor; + descriptor.size = kSize; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + std::vector expectedData; + for (uint32_t i = 0; i < kElements; ++i) { + expectedData.push_back(i); + } + + queue.WriteBuffer(buffer, 0, expectedData.data(), kElements * sizeof(uint32_t)); + + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); +} + +// Test using WriteBuffer for super large data block +TEST_P(QueueWriteBufferTests, SuperLargeWriteBuffer) { + constexpr uint64_t kSize = 12000 * 1000; + constexpr uint64_t kElements = 3000 * 1000; + wgpu::BufferDescriptor descriptor; + descriptor.size = kSize; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + std::vector expectedData; + for (uint32_t i = 0; i < kElements; ++i) { + expectedData.push_back(i); + } + + queue.WriteBuffer(buffer, 0, expectedData.data(), kElements * sizeof(uint32_t)); + + EXPECT_BUFFER_U32_RANGE_EQ(expectedData.data(), buffer, 0, kElements); +} + +DAWN_INSTANTIATE_TEST(QueueWriteBufferTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +class QueueWriteTextureTests : public DawnTest { + protected: + static constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::RGBA8Unorm; + + struct TextureSpec { + wgpu::Origin3D copyOrigin; + wgpu::Extent3D textureSize; + uint32_t level; + }; + + struct DataSpec { + uint64_t size; + uint64_t offset; + uint32_t bytesPerRow; + uint32_t rowsPerImage; + }; + + static DataSpec MinimumDataSpec(wgpu::Extent3D writeSize, + uint32_t bytesPerRow = 0, + uint32_t rowsPerImage = 0) { + if (bytesPerRow == 0) { + bytesPerRow = writeSize.width * utils::GetTexelBlockSizeInBytes(kTextureFormat); + } + if (rowsPerImage == 0) { + rowsPerImage = writeSize.height; + } + uint32_t totalDataSize = + utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, writeSize, kTextureFormat); + return {totalDataSize, 0, bytesPerRow, rowsPerImage}; + } + + static void PackTextureData(const uint8_t* srcData, + uint32_t width, + uint32_t height, + uint32_t srcBytesPerRow, + RGBA8* dstData, + uint32_t dstTexelPerRow, + uint32_t texelBlockSize) { + for (uint64_t y = 0; y < height; ++y) { + for (uint64_t x = 0; x < width; ++x) { + uint64_t src = x * texelBlockSize + y * srcBytesPerRow; + uint64_t dst = x + y * dstTexelPerRow; + + dstData[dst] = {srcData[src], srcData[src + 1], srcData[src + 2], srcData[src + 3]}; + } + } + } + + static void FillData(uint8_t* data, size_t count) { + for (size_t i = 0; i < count; ++i) { + data[i] = static_cast(i % 253); + } + } + + void DoTest(const TextureSpec& textureSpec, + const DataSpec& dataSpec, + const wgpu::Extent3D& copySize) { + // Create data of size `size` and populate it + std::vector data(dataSpec.size); + FillData(data.data(), data.size()); + + // Create a texture that is `width` x `height` with (`level` + 1) mip levels. + wgpu::TextureDescriptor descriptor = {}; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size = textureSpec.textureSize; + descriptor.format = kTextureFormat; + descriptor.mipLevelCount = textureSpec.level + 1; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::TextureDataLayout textureDataLayout = utils::CreateTextureDataLayout( + dataSpec.offset, dataSpec.bytesPerRow, dataSpec.rowsPerImage); + + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, textureSpec.level, textureSpec.copyOrigin); + + queue.WriteTexture(&textureCopyView, data.data(), dataSpec.size, &textureDataLayout, + ©Size); + + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(kTextureFormat); + wgpu::Extent3D mipSize = {textureSpec.textureSize.width >> textureSpec.level, + textureSpec.textureSize.height >> textureSpec.level, + textureSpec.textureSize.depth}; + uint32_t alignedBytesPerRow = Align(dataSpec.bytesPerRow, bytesPerTexel); + uint32_t appliedRowsPerImage = + dataSpec.rowsPerImage > 0 ? dataSpec.rowsPerImage : mipSize.height; + uint32_t bytesPerImage = dataSpec.bytesPerRow * appliedRowsPerImage; + + const uint32_t maxArrayLayer = textureSpec.copyOrigin.z + copySize.depth; + + uint64_t dataOffset = dataSpec.offset; + const uint32_t texelCountLastLayer = + (alignedBytesPerRow / bytesPerTexel) * (mipSize.height - 1) + mipSize.width; + for (uint32_t slice = textureSpec.copyOrigin.z; slice < maxArrayLayer; ++slice) { + // Pack the data in the specified copy region to have the same + // format as the expected texture data. + std::vector expected(texelCountLastLayer); + PackTextureData(&data[dataOffset], copySize.width, copySize.height, + dataSpec.bytesPerRow, expected.data(), copySize.width, bytesPerTexel); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, textureSpec.copyOrigin.x, + textureSpec.copyOrigin.y, copySize.width, copySize.height, + textureSpec.level, slice) + << "Write to texture failed copying " << dataSpec.size << "-byte data with offset " + << dataSpec.offset << " and bytes per row " << dataSpec.bytesPerRow << " to [(" + << textureSpec.copyOrigin.x << ", " << textureSpec.copyOrigin.y << "), (" + << textureSpec.copyOrigin.x + copySize.width << ", " + << textureSpec.copyOrigin.y + copySize.height << ")) region of " + << textureSpec.textureSize.width << " x " << textureSpec.textureSize.height + << " texture at mip level " << textureSpec.level << " layer " << slice << std::endl; + + dataOffset += bytesPerImage; + } + } +}; + +// Test writing the whole texture for varying texture sizes. +TEST_P(QueueWriteTextureTests, VaryingTextureSize) { + DAWN_SKIP_TEST_IF(IsSwiftshader()); + + for (unsigned int w : {127, 128}) { + for (unsigned int h : {63, 64}) { + for (unsigned int d : {1, 3, 4}) { + TextureSpec textureSpec; + textureSpec.textureSize = {w, h, d}; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + + DoTest(textureSpec, MinimumDataSpec({w, h, d}), {w, h, d}); + } + } + } +} + +// Test writing a pixel with an offset. +TEST_P(QueueWriteTextureTests, VaryingTextureOffset) { + constexpr uint32_t kWidth = 259; + constexpr uint32_t kHeight = 127; + DataSpec pixelData = MinimumDataSpec({1, 1, 1}); + + constexpr wgpu::Extent3D kCopySize = {1, 1, 1}; + constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, 1}; + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = kTextureSize; + defaultTextureSpec.level = 0; + + for (unsigned int w : {0u, kWidth / 7, kWidth / 3, kWidth - 1}) { + for (unsigned int h : {0u, kHeight / 7, kHeight / 3, kHeight - 1}) { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {w, h, 0}; + DoTest(textureSpec, pixelData, kCopySize); + } + } +} + +// Test writing a pixel with an offset to a texture array +TEST_P(QueueWriteTextureTests, VaryingTextureArrayOffset) { + constexpr uint32_t kWidth = 259; + constexpr uint32_t kHeight = 127; + constexpr uint32_t kDepth = 62; + DataSpec pixelData = MinimumDataSpec({1, 1, 1}); + + constexpr wgpu::Extent3D kCopySize = {1, 1, 1}; + constexpr wgpu::Extent3D kTextureSize = {kWidth, kHeight, kDepth}; + TextureSpec defaultTextureSpec; + defaultTextureSpec.textureSize = kTextureSize; + defaultTextureSpec.level = 0; + + for (unsigned int w : {0u, kWidth / 7, kWidth / 3, kWidth - 1}) { + for (unsigned int h : {0u, kHeight / 7, kHeight / 3, kHeight - 1}) { + for (unsigned int d : {0u, kDepth / 7, kDepth / 3, kDepth - 1}) { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.copyOrigin = {w, h, d}; + DoTest(textureSpec, pixelData, kCopySize); + } + } + } +} + +// Test writing with varying write sizes. +TEST_P(QueueWriteTextureTests, VaryingWriteSize) { + constexpr uint32_t kWidth = 257; + constexpr uint32_t kHeight = 127; + for (unsigned int w : {13, 63, 128, 256}) { + for (unsigned int h : {16, 19, 32, 63}) { + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + textureSpec.textureSize = {kWidth, kHeight, 1}; + DoTest(textureSpec, MinimumDataSpec({w, h, 1}), {w, h, 1}); + } + } +} + +// Test writing with varying write sizes to texture arrays. +TEST_P(QueueWriteTextureTests, VaryingArrayWriteSize) { + constexpr uint32_t kWidth = 257; + constexpr uint32_t kHeight = 127; + constexpr uint32_t kDepth = 65; + for (unsigned int w : {13, 63, 128, 256}) { + for (unsigned int h : {16, 19, 32, 63}) { + for (unsigned int d : {3, 6}) { + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.level = 0; + textureSpec.textureSize = {kWidth, kHeight, kDepth}; + DoTest(textureSpec, MinimumDataSpec({w, h, d}), {w, h, d}); + } + } + } +} + +// Test writing to varying mips +TEST_P(QueueWriteTextureTests, TextureWriteToMip) { + constexpr uint32_t kWidth = 259; + constexpr uint32_t kHeight = 127; + + TextureSpec defaultTextureSpec; + defaultTextureSpec.copyOrigin = {0, 0, 0}; + defaultTextureSpec.textureSize = {kWidth, kHeight, 1}; + + for (unsigned int i = 1; i < 4; ++i) { + TextureSpec textureSpec = defaultTextureSpec; + textureSpec.level = i; + DoTest(textureSpec, MinimumDataSpec({kWidth >> i, kHeight >> i, 1}), + {kWidth >> i, kHeight >> i, 1}); + } +} + +// Test writing with different multiples of texel block size as data offset +TEST_P(QueueWriteTextureTests, VaryingDataOffset) { + constexpr uint32_t kWidth = 259; + constexpr uint32_t kHeight = 127; + + TextureSpec textureSpec; + textureSpec.copyOrigin = {0, 0, 0}; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.level = 0; + + for (unsigned int i : {1, 2, 4, 17, 64, 128, 300}) { + DataSpec dataSpec = MinimumDataSpec({kWidth, kHeight, 1}); + uint64_t offset = i * utils::GetTexelBlockSizeInBytes(kTextureFormat); + dataSpec.size += offset; + dataSpec.offset += offset; + DoTest(textureSpec, dataSpec, {kWidth, kHeight, 1}); + } +} + +// Test writing with rowsPerImage greater than needed. +TEST_P(QueueWriteTextureTests, VaryingRowsPerImage) { + constexpr uint32_t kWidth = 65; + constexpr uint32_t kHeight = 31; + constexpr uint32_t kDepth = 17; + + constexpr wgpu::Extent3D copySize = {kWidth - 1, kHeight - 1, kDepth - 1}; + + for (unsigned int r : {1, 2, 3, 64, 200}) { + TextureSpec textureSpec; + textureSpec.copyOrigin = {1, 1, 1}; + textureSpec.textureSize = {kWidth, kHeight, kDepth}; + textureSpec.level = 0; + + DataSpec dataSpec = MinimumDataSpec(copySize, 0, copySize.height + r); + DoTest(textureSpec, dataSpec, copySize); + } +} + +// Test with bytesPerRow greater than needed +TEST_P(QueueWriteTextureTests, VaryingBytesPerRow) { + constexpr uint32_t kWidth = 257; + constexpr uint32_t kHeight = 129; + + TextureSpec textureSpec; + textureSpec.textureSize = {kWidth, kHeight, 1}; + textureSpec.copyOrigin = {1, 2, 0}; + textureSpec.level = 0; + + constexpr wgpu::Extent3D copyExtent = {17, 19, 1}; + + for (unsigned int b : {1, 2, 3, 4}) { + uint32_t bytesPerRow = + copyExtent.width * utils::GetTexelBlockSizeInBytes(kTextureFormat) + b; + DoTest(textureSpec, MinimumDataSpec(copyExtent, bytesPerRow, 0), copyExtent); + } +} + +// Test with bytesPerRow greater than needed in a write to a texture array. +TEST_P(QueueWriteTextureTests, VaryingArrayBytesPerRow) { + constexpr uint32_t kWidth = 257; + constexpr uint32_t kHeight = 129; + constexpr uint32_t kLayers = 65; + + TextureSpec textureSpec; + textureSpec.textureSize = {kWidth, kHeight, kLayers}; + textureSpec.copyOrigin = {1, 2, 3}; + textureSpec.level = 0; + + constexpr wgpu::Extent3D copyExtent = {17, 19, 21}; + + // Test with bytesPerRow divisible by blockWidth + for (unsigned int b : {1, 2, 3, 65, 300}) { + uint32_t bytesPerRow = + (copyExtent.width + b) * utils::GetTexelBlockSizeInBytes(kTextureFormat); + uint32_t rowsPerImage = 23; + DoTest(textureSpec, MinimumDataSpec(copyExtent, bytesPerRow, rowsPerImage), copyExtent); + } + + // Test with bytesPerRow not divisible by blockWidth + for (unsigned int b : {1, 2, 3, 19, 301}) { + uint32_t bytesPerRow = + copyExtent.width * utils::GetTexelBlockSizeInBytes(kTextureFormat) + b; + uint32_t rowsPerImage = 23; + DoTest(textureSpec, MinimumDataSpec(copyExtent, bytesPerRow, rowsPerImage), copyExtent); + } +} + +DAWN_INSTANTIATE_TEST(QueueWriteTextureTests, MetalBackend(), VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/RenderBundleTests.cpp b/third_party/dawn/src/tests/end2end/RenderBundleTests.cpp new file mode 100644 index 00000000000..c58eb8cac73 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/RenderBundleTests.cpp @@ -0,0 +1,203 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/ComboRenderBundleEncoderDescriptor.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +constexpr uint32_t kRTSize = 4; +const RGBA8 kColors[2] = {RGBA8::kGreen, RGBA8::kBlue}; + +// RenderBundleTest tests simple usage of RenderBundles to draw. The implementaiton +// of RenderBundle is shared significantly with render pass execution which is +// tested in all other rendering tests. +class RenderBundleTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + + renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(location = 0) in vec4 pos; + void main() { + gl_Position = pos; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + layout (set = 0, binding = 0) uniform fragmentUniformBuffer { + vec4 color; + }; + void main() { + fragColor = color; + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + descriptor.cColorStates[0].format = renderPass.colorFormat; + + pipeline = device.CreateRenderPipeline(&descriptor); + + float colors0[] = {kColors[0].r / 255.f, kColors[0].g / 255.f, kColors[0].b / 255.f, + kColors[0].a / 255.f}; + float colors1[] = {kColors[1].r / 255.f, kColors[1].g / 255.f, kColors[1].b / 255.f, + kColors[1].a / 255.f}; + + wgpu::Buffer buffer0 = utils::CreateBufferFromData(device, colors0, 4 * sizeof(float), + wgpu::BufferUsage::Uniform); + wgpu::Buffer buffer1 = utils::CreateBufferFromData(device, colors1, 4 * sizeof(float), + wgpu::BufferUsage::Uniform); + + bindGroups[0] = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, buffer0, 0, 4 * sizeof(float)}}); + bindGroups[1] = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, buffer1, 0, 4 * sizeof(float)}}); + + vertexBuffer = utils::CreateBufferFromData( + device, wgpu::BufferUsage::Vertex, + {// The bottom left triangle + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, + + // The top right triangle + -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}); + } + + utils::BasicRenderPass renderPass; + wgpu::RenderPipeline pipeline; + wgpu::Buffer vertexBuffer; + wgpu::BindGroup bindGroups[2]; +}; + +// Basic test of RenderBundle. +TEST_P(RenderBundleTest, Basic) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.colorFormat; + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.SetBindGroup(0, bindGroups[0]); + renderBundleEncoder.Draw(6); + + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(kColors[0], renderPass.color, 1, 3); + EXPECT_PIXEL_RGBA8_EQ(kColors[0], renderPass.color, 3, 1); +} + +// Test execution of multiple render bundles +TEST_P(RenderBundleTest, MultipleBundles) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.colorFormat; + + wgpu::RenderBundle renderBundles[2]; + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.SetBindGroup(0, bindGroups[0]); + renderBundleEncoder.Draw(3); + + renderBundles[0] = renderBundleEncoder.Finish(); + } + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.SetBindGroup(0, bindGroups[1]); + renderBundleEncoder.Draw(3, 1, 3); + + renderBundles[1] = renderBundleEncoder.Finish(); + } + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.ExecuteBundles(2, renderBundles); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(kColors[0], renderPass.color, 1, 3); + EXPECT_PIXEL_RGBA8_EQ(kColors[1], renderPass.color, 3, 1); +} + +// Test execution of a bundle along with render pass commands. +TEST_P(RenderBundleTest, BundleAndRenderPassCommands) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.colorFormat; + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.SetBindGroup(0, bindGroups[0]); + renderBundleEncoder.Draw(3); + + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.ExecuteBundles(1, &renderBundle); + + pass.SetPipeline(pipeline); + pass.SetVertexBuffer(0, vertexBuffer); + pass.SetBindGroup(0, bindGroups[1]); + pass.Draw(3, 1, 3); + + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(kColors[0], renderPass.color, 1, 3); + EXPECT_PIXEL_RGBA8_EQ(kColors[1], renderPass.color, 3, 1); +} + +DAWN_INSTANTIATE_TEST(RenderBundleTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/RenderPassLoadOpTests.cpp b/third_party/dawn/src/tests/end2end/RenderPassLoadOpTests.cpp index 5915b31b9e1..011c6e0fe63 100644 --- a/third_party/dawn/src/tests/end2end/RenderPassLoadOpTests.cpp +++ b/third_party/dawn/src/tests/end2end/RenderPassLoadOpTests.cpp @@ -15,73 +15,68 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" #include constexpr static unsigned int kRTSize = 16; class DrawQuad { - public: - DrawQuad() {} - DrawQuad(dawn::Device device, const char* vsSource, const char* fsSource) - : device(device) { - vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, vsSource); - fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, fsSource); - - pipelineLayout = utils::MakeBasicPipelineLayout(device, nullptr); - } - - void Draw(dawn::RenderPassEncoder* pass) { - - utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.layout = pipelineLayout; - descriptor.cVertexStage.module = vsModule; - descriptor.cFragmentStage.module = fsModule; - - auto renderPipeline = device.CreateRenderPipeline(&descriptor); - - pass->SetPipeline(renderPipeline); - pass->Draw(6, 1, 0, 0); - } - - private: - dawn::Device device; - dawn::ShaderModule vsModule = {}; - dawn::ShaderModule fsModule = {}; - dawn::PipelineLayout pipelineLayout = {}; + public: + DrawQuad() { + } + DrawQuad(wgpu::Device device, const char* vsSource, const char* fsSource) : device(device) { + vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vsSource); + fsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fsSource); + + pipelineLayout = utils::MakeBasicPipelineLayout(device, nullptr); + } + + void Draw(wgpu::RenderPassEncoder* pass) { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = pipelineLayout; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + + auto renderPipeline = device.CreateRenderPipeline(&descriptor); + + pass->SetPipeline(renderPipeline); + pass->Draw(6, 1, 0, 0); + } + + private: + wgpu::Device device; + wgpu::ShaderModule vsModule = {}; + wgpu::ShaderModule fsModule = {}; + wgpu::PipelineLayout pipelineLayout = {}; }; class RenderPassLoadOpTests : public DawnTest { - protected: - void SetUp() override { - DawnTest::SetUp(); - - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = kRTSize; - descriptor.size.height = kRTSize; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; - descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; - renderTarget = device.CreateTexture(&descriptor); - - renderTargetView = renderTarget.CreateDefaultView(); - - RGBA8 zero(0, 0, 0, 0); - std::fill(expectZero.begin(), expectZero.end(), zero); - - RGBA8 green(0, 255, 0, 255); - std::fill(expectGreen.begin(), expectGreen.end(), green); - - RGBA8 blue(0, 0, 255, 255); - std::fill(expectBlue.begin(), expectBlue.end(), blue); - - // draws a blue quad on the right half of the screen - const char* vsSource = R"( + protected: + void SetUp() override { + DawnTest::SetUp(); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kRTSize; + descriptor.size.height = kRTSize; + descriptor.size.depth = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + renderTarget = device.CreateTexture(&descriptor); + + renderTargetView = renderTarget.CreateView(); + + std::fill(expectZero.begin(), expectZero.end(), RGBA8::kZero); + + std::fill(expectGreen.begin(), expectGreen.end(), RGBA8::kGreen); + + std::fill(expectBlue.begin(), expectBlue.end(), RGBA8::kBlue); + + // draws a blue quad on the right half of the screen + const char* vsSource = R"( #version 450 void main() { const vec2 pos[6] = vec2[6]( @@ -90,24 +85,24 @@ class RenderPassLoadOpTests : public DawnTest { gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); } )"; - const char* fsSource = R"( + const char* fsSource = R"( #version 450 layout(location = 0) out vec4 color; void main() { color = vec4(0.f, 0.f, 1.f, 1.f); } )"; - blueQuad = DrawQuad(device, vsSource, fsSource); - } + blueQuad = DrawQuad(device, vsSource, fsSource); + } - dawn::Texture renderTarget; - dawn::TextureView renderTargetView; + wgpu::Texture renderTarget; + wgpu::TextureView renderTargetView; - std::array expectZero; - std::array expectGreen; - std::array expectBlue; + std::array expectZero; + std::array expectGreen; + std::array expectBlue; - DrawQuad blueQuad = {}; + DrawQuad blueQuad = {}; }; // Tests clearing, loading, and drawing into color attachments @@ -120,7 +115,7 @@ TEST_P(RenderPassLoadOpTests, ColorClearThenLoadAndDraw) { auto commandsClearZero = commandsClearZeroEncoder.Finish(); utils::ComboRenderPassDescriptor renderPassClearGreen({renderTargetView}); - renderPassClearGreen.cColorAttachmentsInfoPtr[0]->clearColor = {0.0f, 1.0f, 0.0f, 1.0f}; + renderPassClearGreen.cColorAttachments[0].clearColor = {0.0f, 1.0f, 0.0f, 1.0f}; auto commandsClearGreenEncoder = device.CreateCommandEncoder(); auto clearGreenPass = commandsClearGreenEncoder.BeginRenderPass(&renderPassClearGreen); clearGreenPass.EndPass(); @@ -134,8 +129,8 @@ TEST_P(RenderPassLoadOpTests, ColorClearThenLoadAndDraw) { // Part 2: draw a blue quad into the right half of the render target, and check result utils::ComboRenderPassDescriptor renderPassLoad({renderTargetView}); - renderPassLoad.cColorAttachmentsInfoPtr[0]->loadOp = dawn::LoadOp::Load; - dawn::CommandBuffer commandsLoad; + renderPassLoad.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; + wgpu::CommandBuffer commandsLoad; { auto encoder = device.CreateCommandEncoder(); auto pass = encoder.BeginRenderPass(&renderPassLoad); @@ -148,7 +143,12 @@ TEST_P(RenderPassLoadOpTests, ColorClearThenLoadAndDraw) { // Left half should still be green EXPECT_TEXTURE_RGBA8_EQ(expectGreen.data(), renderTarget, 0, 0, kRTSize / 2, kRTSize, 0, 0); // Right half should now be blue - EXPECT_TEXTURE_RGBA8_EQ(expectBlue.data(), renderTarget, kRTSize / 2, 0, kRTSize / 2, kRTSize, 0, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectBlue.data(), renderTarget, kRTSize / 2, 0, kRTSize / 2, kRTSize, + 0, 0); } -DAWN_INSTANTIATE_TEST(RenderPassLoadOpTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(RenderPassLoadOpTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/RenderPassTests.cpp b/third_party/dawn/src/tests/end2end/RenderPassTests.cpp index 516334bec79..74069fff48d 100644 --- a/third_party/dawn/src/tests/end2end/RenderPassTests.cpp +++ b/third_party/dawn/src/tests/end2end/RenderPassTests.cpp @@ -15,28 +15,27 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr uint32_t kRTSize = 16; -constexpr dawn::TextureFormat kFormat = dawn::TextureFormat::R8G8B8A8Unorm; +constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; class RenderPassTest : public DawnTest { -protected: + protected: void SetUp() override { DawnTest::SetUp(); // Shaders to draw a bottom-left triangle in blue. - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + mVSModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { const vec2 pos[3] = vec2[3]( - vec2(-1.f, -1.f), vec2(1.f, 1.f), vec2(-1.f, 1.f)); + vec2(-1.f, 1.f), vec2(1.f, -1.f), vec2(-1.f, -1.f)); gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -44,80 +43,130 @@ class RenderPassTest : public DawnTest { })"); utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = mVSModule; descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip; - descriptor.cColorStates[0]->format = kFormat; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cColorStates[0].format = kFormat; pipeline = device.CreateRenderPipeline(&descriptor); } - dawn::Texture CreateDefault2DTexture() { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; + wgpu::Texture CreateDefault2DTexture() { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = kRTSize; descriptor.size.height = kRTSize; descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; descriptor.sampleCount = 1; descriptor.format = kFormat; descriptor.mipLevelCount = 1; - descriptor.usage = - dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; return device.CreateTexture(&descriptor); } - dawn::RenderPipeline pipeline; + wgpu::ShaderModule mVSModule; + wgpu::RenderPipeline pipeline; }; // Test using two different render passes in one commandBuffer works correctly. TEST_P(RenderPassTest, TwoRenderPassesInOneCommandBuffer) { if (IsOpenGL() || IsMetal()) { - // crbug.com/950768 - // This test is consistently failing on OpenGL and flaky on Metal. - return; + // crbug.com/950768 + // This test is consistently failing on OpenGL and flaky on Metal. + return; } - constexpr RGBA8 kRed(255, 0, 0, 255); - constexpr RGBA8 kGreen(0, 255, 0, 255); - constexpr RGBA8 kBlue(0, 0, 255, 255); - - dawn::Texture renderTarget1 = CreateDefault2DTexture(); - dawn::Texture renderTarget2 = CreateDefault2DTexture(); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::Texture renderTarget1 = CreateDefault2DTexture(); + wgpu::Texture renderTarget2 = CreateDefault2DTexture(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { // In the first render pass we clear renderTarget1 to red and draw a blue triangle in the // bottom left of renderTarget1. - utils::ComboRenderPassDescriptor renderPass({renderTarget1.CreateDefaultView()}); - renderPass.cColorAttachmentsInfoPtr[0]->clearColor = {1.0f, 0.0f, 0.0f, 1.0f}; + utils::ComboRenderPassDescriptor renderPass({renderTarget1.CreateView()}); + renderPass.cColorAttachments[0].clearColor = {1.0f, 0.0f, 0.0f, 1.0f}; - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline); - pass.Draw(3, 1, 0, 0); + pass.Draw(3); pass.EndPass(); } { // In the second render pass we clear renderTarget2 to green and draw a blue triangle in the // bottom left of renderTarget2. - utils::ComboRenderPassDescriptor renderPass({renderTarget2.CreateDefaultView()}); - renderPass.cColorAttachmentsInfoPtr[0]->clearColor = {0.0f, 1.0f, 0.0f, 1.0f}; + utils::ComboRenderPassDescriptor renderPass({renderTarget2.CreateView()}); + renderPass.cColorAttachments[0].clearColor = {0.0f, 1.0f, 0.0f, 1.0f}; - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline); - pass.Draw(3, 1, 0, 0); + pass.Draw(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(kBlue, renderTarget1, 1, kRTSize - 1); - EXPECT_PIXEL_RGBA8_EQ(kRed, renderTarget1, kRTSize - 1, 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kBlue, renderTarget1, 1, kRTSize - 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderTarget1, kRTSize - 1, 1); + + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kBlue, renderTarget2, 1, kRTSize - 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderTarget2, kRTSize - 1, 1); +} + +// Verify that the content in the color attachment will not be changed if there is no corresponding +// fragment shader outputs in the render pipeline, the load operation is LoadOp::Load and the store +// operation is StoreOp::Store. +TEST_P(RenderPassTest, NoCorrespondingFragmentShaderOutputs) { + wgpu::Texture renderTarget = CreateDefault2DTexture(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::TextureView renderTargetView = renderTarget.CreateView(); + + utils::ComboRenderPassDescriptor renderPass({renderTargetView}); + renderPass.cColorAttachments[0].clearColor = {1.0f, 0.0f, 0.0f, 1.0f}; + renderPass.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + renderPass.cColorAttachments[0].storeOp = wgpu::StoreOp::Store; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + + { + // First we draw a blue triangle in the bottom left of renderTarget. + pass.SetPipeline(pipeline); + pass.Draw(3); + } + + { + // Next we use a pipeline whose fragment shader has no outputs. + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + void main() { + })"); + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = mVSModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cColorStates[0].format = kFormat; + + wgpu::RenderPipeline pipelineWithNoFragmentOutput = + device.CreateRenderPipeline(&descriptor); + + pass.SetPipeline(pipelineWithNoFragmentOutput); + pass.Draw(3); + } + + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(kBlue, renderTarget2, 1, kRTSize - 1); - EXPECT_PIXEL_RGBA8_EQ(kGreen, renderTarget2, kRTSize - 1, 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kBlue, renderTarget, 2, kRTSize - 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderTarget, kRTSize - 1, 1); } -DAWN_INSTANTIATE_TEST(RenderPassTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(RenderPassTest, + D3D12Backend(), + D3D12Backend({}, {"use_d3d12_render_pass"}), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/SamplerTests.cpp b/third_party/dawn/src/tests/end2end/SamplerTests.cpp index 4e72fddbbc4..ad5ed921424 100644 --- a/third_party/dawn/src/tests/end2end/SamplerTests.cpp +++ b/third_party/dawn/src/tests/end2end/SamplerTests.cpp @@ -19,38 +19,42 @@ #include "common/Assert.h" #include "common/Constants.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" constexpr static unsigned int kRTSize = 64; namespace { struct AddressModeTestCase { - dawn::AddressMode mMode; + wgpu::AddressMode mMode; uint8_t mExpected2; uint8_t mExpected3; }; AddressModeTestCase addressModes[] = { - { dawn::AddressMode::Repeat, 0, 255, }, - { dawn::AddressMode::MirroredRepeat, 255, 0, }, - { dawn::AddressMode::ClampToEdge, 255, 255, }, + { + wgpu::AddressMode::Repeat, + 0, + 255, + }, + { + wgpu::AddressMode::MirrorRepeat, + 255, + 0, + }, + { + wgpu::AddressMode::ClampToEdge, + 255, + 255, + }, }; -} +} // namespace class SamplerTest : public DawnTest { -protected: + protected: void SetUp() override { DawnTest::SetUp(); mRenderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - mBindGroupLayout = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}, - {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}, - }); - - auto pipelineLayout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - - auto vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + auto vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { const vec2 pos[6] = vec2[6](vec2(-2.f, -2.f), @@ -62,7 +66,7 @@ class SamplerTest : public DawnTest { gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); } )"); - auto fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + auto fsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(set = 0, binding = 0) uniform sampler sampler0; layout(set = 0, binding = 1) uniform texture2D texture0; @@ -74,91 +78,81 @@ class SamplerTest : public DawnTest { )"); utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); - pipelineDescriptor.layout = pipelineLayout; - pipelineDescriptor.cVertexStage.module = vsModule; + pipelineDescriptor.vertexStage.module = vsModule; pipelineDescriptor.cFragmentStage.module = fsModule; - pipelineDescriptor.cColorStates[0]->format = mRenderPass.colorFormat; + pipelineDescriptor.cColorStates[0].format = mRenderPass.colorFormat; mPipeline = device.CreateRenderPipeline(&pipelineDescriptor); + mBindGroupLayout = mPipeline.GetBindGroupLayout(0); - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = 2; descriptor.size.height = 2; descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled; - dawn::Texture texture = device.CreateTexture(&descriptor); + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + wgpu::Texture texture = device.CreateTexture(&descriptor); // Create a 2x2 checkerboard texture, with black in the top left and bottom right corners. - const uint32_t rowPixels = kTextureRowPitchAlignment / sizeof(RGBA8); + const uint32_t rowPixels = kTextureBytesPerRowAlignment / sizeof(RGBA8); RGBA8 data[rowPixels * 2]; - RGBA8 black(0, 0, 0, 255); - RGBA8 white(255, 255, 255, 255); - data[0] = data[rowPixels + 1] = black; - data[1] = data[rowPixels] = white; - - dawn::Buffer stagingBuffer = utils::CreateBufferFromData(device, data, sizeof(data), dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 256, 0); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); - dawn::Extent3D copySize = {2, 2, 1}; - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + data[0] = data[rowPixels + 1] = RGBA8::kBlack; + data[1] = data[rowPixels] = RGBA8::kWhite; + + wgpu::Buffer stagingBuffer = + utils::CreateBufferFromData(device, data, sizeof(data), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 256, 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {2, 2, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - dawn::CommandBuffer copy = encoder.Finish(); + wgpu::CommandBuffer copy = encoder.Finish(); queue.Submit(1, ©); - mTextureView = texture.CreateDefaultView(); + mTextureView = texture.CreateView(); } void TestAddressModes(AddressModeTestCase u, AddressModeTestCase v, AddressModeTestCase w) { - dawn::Sampler sampler; + wgpu::Sampler sampler; { - dawn::SamplerDescriptor descriptor; - descriptor.minFilter = dawn::FilterMode::Nearest; - descriptor.magFilter = dawn::FilterMode::Nearest; - descriptor.mipmapFilter = dawn::FilterMode::Nearest; + wgpu::SamplerDescriptor descriptor = {}; + descriptor.minFilter = wgpu::FilterMode::Nearest; + descriptor.magFilter = wgpu::FilterMode::Nearest; + descriptor.mipmapFilter = wgpu::FilterMode::Nearest; descriptor.addressModeU = u.mMode; descriptor.addressModeV = v.mMode; descriptor.addressModeW = w.mMode; - descriptor.lodMinClamp = kLodMin; - descriptor.lodMaxClamp = kLodMax; - descriptor.compareFunction = dawn::CompareFunction::Never; sampler = device.CreateSampler(&descriptor); } - dawn::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, { - {0, sampler}, - {1, mTextureView} - }); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, mBindGroupLayout, {{0, sampler}, {1, mTextureView}}); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&mRenderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&mRenderPass.renderPassInfo); pass.SetPipeline(mPipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.Draw(6, 1, 0, 0); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); RGBA8 expectedU2(u.mExpected2, u.mExpected2, u.mExpected2, 255); RGBA8 expectedU3(u.mExpected3, u.mExpected3, u.mExpected3, 255); RGBA8 expectedV2(v.mExpected2, v.mExpected2, v.mExpected2, 255); RGBA8 expectedV3(v.mExpected3, v.mExpected3, v.mExpected3, 255); - RGBA8 black(0, 0, 0, 255); - RGBA8 white(255, 255, 255, 255); - EXPECT_PIXEL_RGBA8_EQ(black, mRenderPass.color, 0, 0); - EXPECT_PIXEL_RGBA8_EQ(white, mRenderPass.color, 0, 1); - EXPECT_PIXEL_RGBA8_EQ(white, mRenderPass.color, 1, 0); - EXPECT_PIXEL_RGBA8_EQ(black, mRenderPass.color, 1, 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kBlack, mRenderPass.color, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kWhite, mRenderPass.color, 0, 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kWhite, mRenderPass.color, 1, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kBlack, mRenderPass.color, 1, 1); EXPECT_PIXEL_RGBA8_EQ(expectedU2, mRenderPass.color, 2, 0); EXPECT_PIXEL_RGBA8_EQ(expectedU3, mRenderPass.color, 3, 0); EXPECT_PIXEL_RGBA8_EQ(expectedV2, mRenderPass.color, 0, 2); @@ -167,9 +161,9 @@ class SamplerTest : public DawnTest { } utils::BasicRenderPass mRenderPass; - dawn::BindGroupLayout mBindGroupLayout; - dawn::RenderPipeline mPipeline; - dawn::TextureView mTextureView; + wgpu::BindGroupLayout mBindGroupLayout; + wgpu::RenderPipeline mPipeline; + wgpu::TextureView mTextureView; }; // Test drawing a rect with a checkerboard texture with different address modes. @@ -183,4 +177,8 @@ TEST_P(SamplerTest, AddressMode) { } } -DAWN_INSTANTIATE_TEST(SamplerTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(SamplerTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/ScissorTests.cpp b/third_party/dawn/src/tests/end2end/ScissorTests.cpp index 6208d92f991..98722d02d53 100644 --- a/third_party/dawn/src/tests/end2end/ScissorTests.cpp +++ b/third_party/dawn/src/tests/end2end/ScissorTests.cpp @@ -15,12 +15,13 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" -class ScissorTest: public DawnTest { +class ScissorTest : public DawnTest { protected: - dawn::RenderPipeline CreateQuadPipeline(dawn::TextureFormat format) { - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::RenderPipeline CreateQuadPipeline(wgpu::TextureFormat format) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 const vec2 pos[6] = vec2[6]( vec2(-1.0f, -1.0f), vec2(-1.0f, 1.0f), vec2(1.0f, -1.0f), @@ -30,7 +31,8 @@ class ScissorTest: public DawnTest { gl_Position = vec4(pos[gl_VertexIndex], 0.5, 1.0); })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -38,9 +40,9 @@ class ScissorTest: public DawnTest { })"); utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.cColorStates[0]->format = format; + descriptor.cColorStates[0].format = format; return device.CreateRenderPipeline(&descriptor); } @@ -49,105 +51,109 @@ class ScissorTest: public DawnTest { // Test that by default the scissor test is disabled and the whole attachment can be drawn to. TEST_P(ScissorTest, DefaultsToWholeRenderTarget) { utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 100, 100); - dawn::RenderPipeline pipeline = CreateQuadPipeline(renderPass.colorFormat); + wgpu::RenderPipeline pipeline = CreateQuadPipeline(renderPass.colorFormat); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.Draw(6, 1, 0, 0); + pass.Draw(6); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 0); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 99); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 99, 0); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 99, 99); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 99); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 99, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 99, 99); } // Test setting the scissor to something larger than the attachments. TEST_P(ScissorTest, LargerThanAttachment) { utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 100, 100); - dawn::RenderPipeline pipeline = CreateQuadPipeline(renderPass.colorFormat); + wgpu::RenderPipeline pipeline = CreateQuadPipeline(renderPass.colorFormat); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); pass.SetScissorRect(0, 0, 200, 200); - pass.Draw(6, 1, 0, 0); + pass.Draw(6); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 0); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 99); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 99, 0); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 99, 99); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 99); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 99, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 99, 99); } // Test setting a partial scissor (not empty, not full attachment) TEST_P(ScissorTest, PartialRect) { utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 100, 100); - dawn::RenderPipeline pipeline = CreateQuadPipeline(renderPass.colorFormat); + wgpu::RenderPipeline pipeline = CreateQuadPipeline(renderPass.colorFormat); constexpr uint32_t kX = 3; constexpr uint32_t kY = 7; constexpr uint32_t kW = 5; constexpr uint32_t kH = 13; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); pass.SetScissorRect(kX, kY, kW, kH); - pass.Draw(6, 1, 0, 0); + pass.Draw(6); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); // Test the two opposite corners of the scissor box. With one pixel inside and on outside - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kX - 1, kY - 1); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, kX, kY); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, kX - 1, kY - 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, kX, kY); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, kX + kW, kY + kH); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, kX + kW - 1, kY + kH - 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, kX + kW, kY + kH); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, kX + kW - 1, kY + kH - 1); } // Test that the scissor setting doesn't get inherited between renderpasses TEST_P(ScissorTest, NoInheritanceBetweenRenderPass) { utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 100, 100); - dawn::RenderPipeline pipeline = CreateQuadPipeline(renderPass.colorFormat); + wgpu::RenderPipeline pipeline = CreateQuadPipeline(renderPass.colorFormat); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); // RenderPass 1 set the scissor { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetScissorRect(1, 1, 1, 1); pass.EndPass(); } // RenderPass 2 draw a full quad, it shouldn't be scissored { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.Draw(6, 1, 0, 0); + pass.Draw(6); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 0); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 99); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 99, 0); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 99, 99); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 99); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 99, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 99, 99); } -DAWN_INSTANTIATE_TEST(ScissorTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(ScissorTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/ShaderFloat16Tests.cpp b/third_party/dawn/src/tests/end2end/ShaderFloat16Tests.cpp new file mode 100644 index 00000000000..fa0fd4784c8 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/ShaderFloat16Tests.cpp @@ -0,0 +1,111 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Math.h" +#include "tests/DawnTest.h" + +#include "utils/WGPUHelpers.h" + +class ShaderFloat16Tests : public DawnTest { + protected: + std::vector GetRequiredExtensions() override { + mIsShaderFloat16Supported = SupportsExtensions({"shader_float16"}); + if (!mIsShaderFloat16Supported) { + return {}; + } + + return {"shader_float16"}; + } + + bool IsShaderFloat16Supported() const { + return mIsShaderFloat16Supported; + } + + bool mIsShaderFloat16Supported = false; +}; + +// Test basic 16bit float arithmetic and 16bit storage features. +TEST_P(ShaderFloat16Tests, Basic16BitFloatFeaturesTest) { + DAWN_SKIP_TEST_IF(!IsShaderFloat16Supported()); + + uint16_t uniformData[] = {Float32ToFloat16(1.23), Float32ToFloat16(0.0)}; // 0.0 is a padding. + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, &uniformData, sizeof(uniformData), wgpu::BufferUsage::Uniform); + + uint16_t bufferInData[] = {Float32ToFloat16(2.34), Float32ToFloat16(0.0)}; // 0.0 is a padding. + wgpu::Buffer bufferIn = utils::CreateBufferFromData(device, &bufferInData, sizeof(bufferInData), + wgpu::BufferUsage::Storage); + + // TODO(xinghua.cao@intel.com): the zero for padding is required now. No need to + // createBufferFromData once buffer lazy-zero-init is done. + uint16_t bufferOutData[] = {Float32ToFloat16(0.0), Float32ToFloat16(0.0)}; + wgpu::Buffer bufferOut = + utils::CreateBufferFromData(device, &bufferOutData, sizeof(bufferOutData), + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc); + + wgpu::ShaderModule module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + + #extension GL_AMD_gpu_shader_half_float : require + + struct S { + float16_t f; + float16_t padding; + }; + layout(std140, set = 0, binding = 0) uniform uniformBuf { + S c; + }; + + layout(std140, set = 0, binding = 1) readonly buffer bufA { + S a; + } ; + + layout(std140, set = 0, binding = 2) buffer bufB { + S b; + } ; + + void main() { + b.f = a.f + c.f; + } + + )"); + + wgpu::ComputePipelineDescriptor csDesc; + csDesc.computeStage.module = module; + csDesc.computeStage.entryPoint = "main"; + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + { + {0, uniformBuffer, 0, sizeof(uniformData)}, + {1, bufferIn, 0, sizeof(bufferInData)}, + {2, bufferOut, 0, sizeof(bufferOutData)}, + }); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bindGroup); + pass.Dispatch(1); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + uint16_t expected[] = {Float32ToFloat16(3.57), Float32ToFloat16(0.0)}; // 0.0 is a padding. + + EXPECT_BUFFER_U16_RANGE_EQ(expected, bufferOut, 0, 2); +} + +DAWN_INSTANTIATE_TEST(ShaderFloat16Tests, MetalBackend(), VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/StorageTextureTests.cpp b/third_party/dawn/src/tests/end2end/StorageTextureTests.cpp new file mode 100644 index 00000000000..b09f648f3ca --- /dev/null +++ b/third_party/dawn/src/tests/end2end/StorageTextureTests.cpp @@ -0,0 +1,1143 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Assert.h" +#include "common/Constants.h" +#include "common/Math.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" + +class StorageTextureTests : public DawnTest { + public: + static void FillExpectedData(void* pixelValuePtr, + wgpu::TextureFormat format, + uint32_t x, + uint32_t y, + uint32_t arrayLayer) { + const uint32_t pixelValue = 1 + x + kWidth * (y + kHeight * arrayLayer); + ASSERT(pixelValue <= 255u / 4); + + switch (format) { + // 32-bit unsigned integer formats + case wgpu::TextureFormat::R32Uint: { + uint32_t* valuePtr = static_cast(pixelValuePtr); + *valuePtr = pixelValue; + break; + } + + case wgpu::TextureFormat::RG32Uint: { + uint32_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = pixelValue; + valuePtr[1] = pixelValue * 2; + break; + } + + case wgpu::TextureFormat::RGBA32Uint: { + uint32_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = pixelValue; + valuePtr[1] = pixelValue * 2; + valuePtr[2] = pixelValue * 3; + valuePtr[3] = pixelValue * 4; + break; + } + + // 32-bit signed integer formats + case wgpu::TextureFormat::R32Sint: { + int32_t* valuePtr = static_cast(pixelValuePtr); + *valuePtr = static_cast(pixelValue); + break; + } + + case wgpu::TextureFormat::RG32Sint: { + int32_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = static_cast(pixelValue); + valuePtr[1] = -static_cast(pixelValue); + break; + } + + case wgpu::TextureFormat::RGBA32Sint: { + int32_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = static_cast(pixelValue); + valuePtr[1] = -static_cast(pixelValue); + valuePtr[2] = static_cast(pixelValue * 2); + valuePtr[3] = -static_cast(pixelValue * 2); + break; + } + + // 32-bit float formats + case wgpu::TextureFormat::R32Float: { + float_t* valuePtr = static_cast(pixelValuePtr); + *valuePtr = static_cast(pixelValue * 1.1f); + break; + } + + case wgpu::TextureFormat::RG32Float: { + float_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = static_cast(pixelValue * 1.1f); + valuePtr[1] = -static_cast(pixelValue * 2.2f); + break; + } + + case wgpu::TextureFormat::RGBA32Float: { + float_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = static_cast(pixelValue * 1.1f); + valuePtr[1] = -static_cast(pixelValue * 1.1f); + valuePtr[2] = static_cast(pixelValue * 2.2f); + valuePtr[3] = -static_cast(pixelValue * 2.2f); + break; + } + + // 16-bit (unsigned integer, signed integer and float) 4-component formats + case wgpu::TextureFormat::RGBA16Uint: { + uint16_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = static_cast(pixelValue); + valuePtr[1] = static_cast(pixelValue * 2); + valuePtr[2] = static_cast(pixelValue * 3); + valuePtr[3] = static_cast(pixelValue * 4); + break; + } + case wgpu::TextureFormat::RGBA16Sint: { + int16_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = static_cast(pixelValue); + valuePtr[1] = -static_cast(pixelValue); + valuePtr[2] = static_cast(pixelValue * 2); + valuePtr[3] = -static_cast(pixelValue * 2); + break; + } + + case wgpu::TextureFormat::RGBA16Float: { + uint16_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = Float32ToFloat16(static_cast(pixelValue)); + valuePtr[1] = Float32ToFloat16(-static_cast(pixelValue)); + valuePtr[2] = Float32ToFloat16(static_cast(pixelValue * 2)); + valuePtr[3] = Float32ToFloat16(-static_cast(pixelValue * 2)); + break; + } + + // 8-bit (normalized/non-normalized signed/unsigned integer) 4-component formats + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8Uint: { + RGBA8* valuePtr = static_cast(pixelValuePtr); + *valuePtr = RGBA8(pixelValue, pixelValue * 2, pixelValue * 3, pixelValue * 4); + break; + } + + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGBA8Sint: { + int8_t* valuePtr = static_cast(pixelValuePtr); + valuePtr[0] = static_cast(pixelValue); + valuePtr[1] = -static_cast(pixelValue); + valuePtr[2] = static_cast(pixelValue) * 2; + valuePtr[3] = -static_cast(pixelValue) * 2; + break; + } + + default: + UNREACHABLE(); + break; + } + } + + std::string GetGLSLImageDeclaration(wgpu::TextureFormat format, + std::string accessQualifier, + bool is2DArray, + uint32_t binding) { + std::ostringstream ostream; + ostream << "layout(set = 0, binding = " << binding << ", " + << utils::GetGLSLImageFormatQualifier(format) << ") uniform " << accessQualifier + << " " << utils::GetColorTextureComponentTypePrefix(format) << "image2D"; + if (is2DArray) { + ostream << "Array"; + } + ostream << " storageImage" << binding << ";"; + return ostream.str(); + } + + const char* GetExpectedPixelValue(wgpu::TextureFormat format) { + switch (format) { + // non-normalized unsigned integer formats + case wgpu::TextureFormat::R32Uint: + return "uvec4(value, 0, 0, 1u)"; + + case wgpu::TextureFormat::RG32Uint: + return "uvec4(value, value * 2, 0, 1);"; + + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA32Uint: + return "uvec4(value, value * 2, value * 3, value * 4);"; + + // non-normalized signed integer formats + case wgpu::TextureFormat::R32Sint: + return "ivec4(value, 0, 0, 1)"; + + case wgpu::TextureFormat::RG32Sint: + return "ivec4(value, -value, 0, 1);"; + + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA32Sint: + return "ivec4(value, -value, value * 2, -value * 2);"; + + // float formats + case wgpu::TextureFormat::R32Float: + return "vec4(value * 1.1f, 0, 0, 1);"; + + case wgpu::TextureFormat::RG32Float: + return "vec4(value * 1.1f, -(value * 2.2f), 0, 1);"; + + case wgpu::TextureFormat::RGBA16Float: + return "vec4(value, -float(value), float(value * 2), -float(value * 2));"; + + case wgpu::TextureFormat::RGBA32Float: + return "vec4(value * 1.1f, -(value * 1.1f), value * 2.2f, -(value * 2.2f));"; + + // normalized signed/unsigned integer formats + case wgpu::TextureFormat::RGBA8Unorm: + return "vec4(value / 255.0, value / 255.0 * 2, value / 255.0 * 3, value / 255.0 * " + "4);"; + + case wgpu::TextureFormat::RGBA8Snorm: + return "vec4(value / 127.0, -(value / 127.0), (value * 2 / 127.0), -(value * 2 / " + "127.0));"; + + default: + UNREACHABLE(); + break; + } + } + + const char* GetGLSLComparisonFunction(wgpu::TextureFormat format) { + switch (format) { + // non-normalized unsigned integer formats + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA32Uint: + return R"(bool IsEqualTo(uvec4 pixel, uvec4 expected) { + return pixel == expected; + })"; + + // non-normalized signed integer formats + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA32Sint: + return R"(bool IsEqualTo(ivec4 pixel, ivec4 expected) { + return pixel == expected; + })"; + + // float formats + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RGBA16Float: + case wgpu::TextureFormat::RGBA32Float: + return R"(bool IsEqualTo(vec4 pixel, vec4 expected) { + return pixel == expected; + })"; + + // normalized signed/unsigned integer formats + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8Snorm: + // On Windows Intel drivers the tests will fail if tolerance <= 0.00000001f. + return R"(bool IsEqualTo(vec4 pixel, vec4 expected) { + const float tolerance = 0.0000001f; + return all(lessThan(abs(pixel - expected), vec4(tolerance))); + })"; + + default: + UNREACHABLE(); + break; + } + + return ""; + } + + std::string CommonReadOnlyTestCode(wgpu::TextureFormat format, bool is2DArray = false) { + std::ostringstream ostream; + + const char* prefix = utils::GetColorTextureComponentTypePrefix(format); + + ostream << GetGLSLImageDeclaration(format, "readonly", is2DArray, 0) << "\n" + << GetGLSLComparisonFunction(format) << "bool doTest() {\n"; + if (is2DArray) { + ostream << R"(ivec3 size = imageSize(storageImage0); + const uint layerCount = size.z;)"; + } else { + ostream << R"(ivec2 size = imageSize(storageImage0); + const uint layerCount = 1;)"; + } + ostream << R"(for (uint layer = 0; layer < layerCount; ++layer) { + for (uint y = 0; y < size.y; ++y) { + for (uint x = 0; x < size.x; ++x) { + uint value = )" + << kComputeExpectedValueGLSL << ";\n" + << prefix << "vec4 expected = " << GetExpectedPixelValue(format) << ";\n" + << prefix << R"(vec4 pixel = imageLoad(storageImage0, )"; + if (is2DArray) { + ostream << "ivec3(x, y, layer));"; + } else { + ostream << "ivec2(x, y));"; + } + ostream << R"( + if (!IsEqualTo(pixel, expected)) { + return false; + } + } + } + } + return true; + })"; + + return ostream.str(); + } + + std::string CommonWriteOnlyTestCode(wgpu::TextureFormat format, bool is2DArray = false) { + std::ostringstream ostream; + + const char* prefix = utils::GetColorTextureComponentTypePrefix(format); + + ostream << R"( + #version 450 + )" << GetGLSLImageDeclaration(format, "writeonly", is2DArray, 0) + << R"( + void main() { + )"; + if (is2DArray) { + ostream << R"(ivec3 size = imageSize(storageImage0); + const uint layerCount = size.z; + )"; + } else { + ostream << R"(ivec2 size = imageSize(storageImage0); + const uint layerCount = 1; + )"; + } + + ostream << R"(for (uint layer = 0; layer < layerCount; ++layer) { + for (uint y = 0; y < size.y; ++y) { + for (uint x = 0; x < size.x; ++x) { + uint value = )" + << kComputeExpectedValueGLSL << ";\n" + << prefix << "vec4 expected = " << GetExpectedPixelValue(format) << ";\n"; + if (is2DArray) { + ostream << "ivec3 texcoord = ivec3(x, y, layer);\n"; + } else { + ostream << "ivec2 texcoord = ivec2(x, y);\n"; + } + + ostream << R"( imageStore(storageImage0, texcoord, expected); + } + } + } + })"; + + return ostream.str(); + } + + std::string CommonReadWriteTestCode(wgpu::TextureFormat format, bool is2DArray = false) { + std::ostringstream ostream; + + ostream << R"( + #version 450 + )" << GetGLSLImageDeclaration(format, "writeonly", is2DArray, 0) + << GetGLSLImageDeclaration(format, "readonly", is2DArray, 1) << R"( + void main() { + )"; + if (is2DArray) { + ostream << R"(ivec3 size = imageSize(storageImage0); + const uint layerCount = size.z; + )"; + } else { + ostream << R"(ivec2 size = imageSize(storageImage0); + const uint layerCount = 1; + )"; + } + + ostream << R"(for (uint layer = 0; layer < layerCount; ++layer) { + for (uint y = 0; y < size.y; ++y) { + for (uint x = 0; x < size.x; ++x) {)" + "\n"; + if (is2DArray) { + ostream << "ivec3 texcoord = ivec3(x, y, layer);\n"; + } else { + ostream << "ivec2 texcoord = ivec2(x, y);\n"; + } + + ostream + << R"( imageStore(storageImage0, texcoord, imageLoad(storageImage1, texcoord)); + } + } + } + })"; + return ostream.str(); + } + + static std::vector GetExpectedData(wgpu::TextureFormat format, + uint32_t arrayLayerCount = 1) { + const uint32_t texelSizeInBytes = utils::GetTexelBlockSizeInBytes(format); + + std::vector outputData(texelSizeInBytes * kWidth * kHeight * arrayLayerCount); + + for (uint32_t i = 0; i < outputData.size() / texelSizeInBytes; ++i) { + uint8_t* pixelValuePtr = &outputData[i * texelSizeInBytes]; + const uint32_t x = i % kWidth; + const uint32_t y = (i % (kWidth * kHeight)) / kWidth; + const uint32_t arrayLayer = i / (kWidth * kHeight); + FillExpectedData(pixelValuePtr, format, x, y, arrayLayer); + } + + return outputData; + } + + wgpu::Texture CreateTexture(wgpu::TextureFormat format, + wgpu::TextureUsage usage, + uint32_t width = kWidth, + uint32_t height = kHeight, + uint32_t arrayLayerCount = 1) { + wgpu::TextureDescriptor descriptor; + descriptor.size = {width, height, arrayLayerCount}; + descriptor.format = format; + descriptor.usage = usage; + return device.CreateTexture(&descriptor); + } + + wgpu::Buffer CreateEmptyBufferForTextureCopy(uint32_t texelSize, uint32_t arrayLayerCount = 1) { + ASSERT(kWidth * texelSize <= kTextureBytesPerRowAlignment); + const size_t uploadBufferSize = + kTextureBytesPerRowAlignment * (kHeight * arrayLayerCount - 1) + kWidth * texelSize; + wgpu::BufferDescriptor descriptor; + descriptor.size = uploadBufferSize; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + return device.CreateBuffer(&descriptor); + } + + wgpu::Texture CreateTextureWithTestData(const std::vector& initialTextureData, + wgpu::TextureFormat format) { + uint32_t texelSize = utils::GetTexelBlockSizeInBytes(format); + ASSERT(kWidth * texelSize <= kTextureBytesPerRowAlignment); + + const uint32_t bytesPerTextureRow = texelSize * kWidth; + const uint32_t arrayLayerCount = + static_cast(initialTextureData.size() / texelSize / (kWidth * kHeight)); + const size_t uploadBufferSize = + kTextureBytesPerRowAlignment * (kHeight * arrayLayerCount - 1) + + kWidth * bytesPerTextureRow; + + std::vector uploadBufferData(uploadBufferSize); + for (uint32_t layer = 0; layer < arrayLayerCount; ++layer) { + const size_t initialDataOffset = bytesPerTextureRow * kHeight * layer; + for (size_t y = 0; y < kHeight; ++y) { + for (size_t x = 0; x < bytesPerTextureRow; ++x) { + uint8_t data = + initialTextureData[initialDataOffset + bytesPerTextureRow * y + x]; + size_t indexInUploadBuffer = + (kHeight * layer + y) * kTextureBytesPerRowAlignment + x; + uploadBufferData[indexInUploadBuffer] = data; + } + } + } + wgpu::Buffer uploadBuffer = + utils::CreateBufferFromData(device, uploadBufferData.data(), uploadBufferSize, + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + + wgpu::Texture outputTexture = + CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopyDst, kWidth, + kHeight, arrayLayerCount); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + const wgpu::Extent3D copyExtent = {kWidth, kHeight, arrayLayerCount}; + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(uploadBuffer, 0, kTextureBytesPerRowAlignment, 0); + wgpu::TextureCopyView textureCopyView; + textureCopyView.texture = outputTexture; + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Extent); + + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + return outputTexture; + } + + wgpu::ComputePipeline CreateComputePipeline(const char* computeShader) { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader); + wgpu::ComputePipelineDescriptor computeDescriptor; + computeDescriptor.layout = nullptr; + computeDescriptor.computeStage.module = csModule; + computeDescriptor.computeStage.entryPoint = "main"; + return device.CreateComputePipeline(&computeDescriptor); + } + + wgpu::RenderPipeline CreateRenderPipeline(const char* vertexShader, + const char* fragmentShader) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vertexShader); + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader); + + utils::ComboRenderPipelineDescriptor desc(device); + desc.vertexStage.module = vsModule; + desc.cFragmentStage.module = fsModule; + desc.cColorStates[0].format = kOutputAttachmentFormat; + desc.primitiveTopology = wgpu::PrimitiveTopology::PointList; + return device.CreateRenderPipeline(&desc); + } + + void CheckDrawsGreen(const char* vertexShader, + const char* fragmentShader, + wgpu::Texture readonlyStorageTexture) { + wgpu::RenderPipeline pipeline = CreateRenderPipeline(vertexShader, fragmentShader); + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), {{0, readonlyStorageTexture.CreateView()}}); + + // Clear the output attachment to red at the beginning of the render pass. + wgpu::Texture outputTexture = + CreateTexture(kOutputAttachmentFormat, + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, 1, 1); + utils::ComboRenderPassDescriptor renderPassDescriptor({outputTexture.CreateView()}); + renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + renderPassDescriptor.cColorAttachments[0].clearColor = {1.f, 0.f, 0.f, 1.f}; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor); + renderPassEncoder.SetBindGroup(0, bindGroup); + renderPassEncoder.SetPipeline(pipeline); + renderPassEncoder.Draw(1); + renderPassEncoder.EndPass(); + + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + // Check if the contents in the output texture are all as expected (green). + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, outputTexture, 0, 0); + } + + void CheckResultInStorageBuffer(wgpu::Texture readonlyStorageTexture, + const std::string& computeShader) { + wgpu::ComputePipeline pipeline = CreateComputePipeline(computeShader.c_str()); + + // Clear the content of the result buffer into 0. + constexpr uint32_t kInitialValue = 0; + wgpu::Buffer resultBuffer = + utils::CreateBufferFromData(device, &kInitialValue, sizeof(kInitialValue), + wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, readonlyStorageTexture.CreateView()}, {1, resultBuffer}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computeEncoder = encoder.BeginComputePass(); + computeEncoder.SetBindGroup(0, bindGroup); + computeEncoder.SetPipeline(pipeline); + computeEncoder.Dispatch(1); + computeEncoder.EndPass(); + + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + // Check if the contents in the result buffer are what we expect. + constexpr uint32_t kExpectedValue = 1u; + EXPECT_BUFFER_U32_RANGE_EQ(&kExpectedValue, resultBuffer, 0, 1u); + } + + void WriteIntoStorageTextureInRenderPass(wgpu::Texture writeonlyStorageTexture, + const char* kVertexShader, + const char* kFragmentShader) { + // Create a render pipeline that writes the expected pixel values into the storage texture + // without fragment shader outputs. + wgpu::RenderPipeline pipeline = CreateRenderPipeline(kVertexShader, kFragmentShader); + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), {{0, writeonlyStorageTexture.CreateView()}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + // TODO(jiawei.shao@intel.com): remove the output attachment when Dawn supports beginning a + // render pass with no attachments. + wgpu::Texture dummyOutputTexture = + CreateTexture(kOutputAttachmentFormat, + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, 1, 1); + utils::ComboRenderPassDescriptor renderPassDescriptor({dummyOutputTexture.CreateView()}); + wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor); + renderPassEncoder.SetBindGroup(0, bindGroup); + renderPassEncoder.SetPipeline(pipeline); + renderPassEncoder.Draw(1); + renderPassEncoder.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + } + + void WriteIntoStorageTextureInComputePass(wgpu::Texture writeonlyStorageTexture, + const char* computeShader) { + // Create a compute pipeline that writes the expected pixel values into the storage texture. + wgpu::ComputePipeline pipeline = CreateComputePipeline(computeShader); + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), {{0, writeonlyStorageTexture.CreateView()}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup); + computePassEncoder.SetPipeline(pipeline); + computePassEncoder.Dispatch(1); + computePassEncoder.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + } + + void ReadWriteIntoStorageTextureInComputePass(wgpu::Texture readonlyStorageTexture, + wgpu::Texture writeonlyStorageTexture, + const char* computeShader) { + // Create a compute pipeline that writes the expected pixel values into the storage texture. + wgpu::ComputePipeline pipeline = CreateComputePipeline(computeShader); + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), + {{0, writeonlyStorageTexture.CreateView()}, {1, readonlyStorageTexture.CreateView()}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup); + computePassEncoder.SetPipeline(pipeline); + computePassEncoder.Dispatch(1); + computePassEncoder.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + } + + void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture, + wgpu::TextureFormat format, + uint32_t arrayLayerCount = 1) { + const uint32_t texelSize = utils::GetTexelBlockSizeInBytes(format); + const std::vector& expectedData = GetExpectedData(format, arrayLayerCount); + CheckOutputStorageTexture(writeonlyStorageTexture, texelSize, expectedData); + } + + void CheckOutputStorageTexture(wgpu::Texture writeonlyStorageTexture, + uint32_t texelSize, + const std::vector& expectedData) { + // Copy the content from the write-only storage texture to the result buffer. + const uint32_t arrayLayerCount = + static_cast(expectedData.size() / texelSize / (kWidth * kHeight)); + wgpu::Buffer resultBuffer = CreateEmptyBufferForTextureCopy(texelSize, arrayLayerCount); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + const wgpu::Extent3D copyExtent = {kWidth, kHeight, arrayLayerCount}; + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(writeonlyStorageTexture, 0, {0, 0, 0}); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(resultBuffer, 0, kTextureBytesPerRowAlignment, 0); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Extent); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + // Check if the contents in the result buffer are what we expect. + for (size_t layer = 0; layer < arrayLayerCount; ++layer) { + for (size_t y = 0; y < kHeight; ++y) { + const size_t resultBufferOffset = + kTextureBytesPerRowAlignment * (kHeight * layer + y); + const size_t expectedDataOffset = texelSize * kWidth * (kHeight * layer + y); + EXPECT_BUFFER_U32_RANGE_EQ( + reinterpret_cast(expectedData.data() + expectedDataOffset), + resultBuffer, resultBufferOffset, kWidth); + } + } + } + + static constexpr size_t kWidth = 4u; + static constexpr size_t kHeight = 4u; + static constexpr wgpu::TextureFormat kOutputAttachmentFormat = wgpu::TextureFormat::RGBA8Unorm; + + const char* kSimpleVertexShader = R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + gl_PointSize = 1.0f; + })"; + + const char* kComputeExpectedValueGLSL = "1 + x + size.x * (y + size.y * layer)"; +}; + +// Test that using read-only storage texture and write-only storage texture in BindGroupLayout is +// valid on all backends. This test is a regression test for chromium:1061156 and passes by not +// asserting or crashing. +TEST_P(StorageTextureTests, BindGroupLayoutWithStorageTextureBindingType) { + // wgpu::BindingType::ReadonlyStorageTexture is a valid binding type to create a bind group + // layout. + { + wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::Compute, + wgpu::BindingType::ReadonlyStorageTexture}; + entry.storageTextureFormat = wgpu::TextureFormat::R32Float; + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = 1; + descriptor.entries = &entry; + device.CreateBindGroupLayout(&descriptor); + } + + // wgpu::BindingType::WriteonlyStorageTexture is a valid binding type to create a bind group + // layout. + { + wgpu::BindGroupLayoutEntry entry = {0, wgpu::ShaderStage::Compute, + wgpu::BindingType::WriteonlyStorageTexture}; + entry.storageTextureFormat = wgpu::TextureFormat::R32Float; + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = 1; + descriptor.entries = &entry; + device.CreateBindGroupLayout(&descriptor); + } +} + +// Test that read-only storage textures are supported in compute shader. +TEST_P(StorageTextureTests, ReadonlyStorageTextureInComputeShader) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed()); + + for (wgpu::TextureFormat format : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(format)) { + continue; + } + + // Prepare the read-only storage texture and fill it with the expected data. + const std::vector kInitialTextureData = GetExpectedData(format); + wgpu::Texture readonlyStorageTexture = + CreateTextureWithTestData(kInitialTextureData, format); + + // Create a compute shader that reads the pixels from the read-only storage texture and + // writes 1 to DstBuffer if they all have to expected value. + std::ostringstream csStream; + csStream << R"( + #version 450 + layout(set = 0, binding = 1, std430) buffer DstBuffer { + uint result; + } dstBuffer; + )" << CommonReadOnlyTestCode(format) + << R"( + void main() { + if (doTest()) { + dstBuffer.result = 1; + } else { + dstBuffer.result = 0; + } + })"; + + CheckResultInStorageBuffer(readonlyStorageTexture, csStream.str()); + } +} + +// Test that read-only storage textures are supported in vertex shader. +TEST_P(StorageTextureTests, ReadonlyStorageTextureInVertexShader) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed()); + + for (wgpu::TextureFormat format : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(format)) { + continue; + } + + // Prepare the read-only storage texture and fill it with the expected data. + const std::vector kInitialTextureData = GetExpectedData(format); + wgpu::Texture readonlyStorageTexture = + CreateTextureWithTestData(kInitialTextureData, format); + + // Create a rendering pipeline that reads the pixels from the read-only storage texture and + // uses green as the output color, otherwise uses red instead. + std::ostringstream vsStream; + vsStream << R"( + #version 450 + layout(location = 0) out vec4 o_color; + )" << CommonReadOnlyTestCode(format) + << R"( + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + if (doTest()) { + o_color = vec4(0.f, 1.f, 0.f, 1.f); + } else { + o_color = vec4(1.f, 0.f, 0.f, 1.f); + } + gl_PointSize = 1.0f; + })"; + const char* kFragmentShader = R"( + #version 450 + layout(location = 0) in vec4 o_color; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = o_color; + })"; + CheckDrawsGreen(vsStream.str().c_str(), kFragmentShader, readonlyStorageTexture); + } +} + +// Test that read-only storage textures are supported in fragment shader. +TEST_P(StorageTextureTests, ReadonlyStorageTextureInFragmentShader) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed()); + + for (wgpu::TextureFormat format : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(format)) { + continue; + } + + // Prepare the read-only storage texture and fill it with the expected data. + const std::vector kInitialTextureData = GetExpectedData(format); + wgpu::Texture readonlyStorageTexture = + CreateTextureWithTestData(kInitialTextureData, format); + + // Create a rendering pipeline that reads the pixels from the read-only storage texture and + // uses green as the output color if the pixel value is expected, otherwise uses red + // instead. + std::ostringstream fsStream; + fsStream << R"( + #version 450 + layout(location = 0) out vec4 o_color; + )" << CommonReadOnlyTestCode(format) + << R"( + void main() { + if (doTest()) { + o_color = vec4(0.f, 1.f, 0.f, 1.f); + } else { + o_color = vec4(1.f, 0.f, 0.f, 1.f); + } + })"; + CheckDrawsGreen(kSimpleVertexShader, fsStream.str().c_str(), readonlyStorageTexture); + } +} + +// Test that write-only storage textures are supported in compute shader. +TEST_P(StorageTextureTests, WriteonlyStorageTextureInComputeShader) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed()); + + for (wgpu::TextureFormat format : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(format)) { + continue; + } + + // TODO(jiawei.shao@intel.com): investigate why this test fails with RGBA8Snorm on Linux + // Intel OpenGL driver. + if (format == wgpu::TextureFormat::RGBA8Snorm && IsIntel() && IsOpenGL() && IsLinux()) { + continue; + } + + // Prepare the write-only storage texture. + wgpu::Texture writeonlyStorageTexture = + CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc); + + // Write the expected pixel values into the write-only storage texture. + const std::string computeShader = CommonWriteOnlyTestCode(format); + WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, computeShader.c_str()); + + // Verify the pixel data in the write-only storage texture is expected. + CheckOutputStorageTexture(writeonlyStorageTexture, format); + } +} + +// Test that reading from one read-only storage texture then writing into another write-only storage +// texture in one dispatch are supported in compute shader. +TEST_P(StorageTextureTests, ReadWriteDifferentStorageTextureInOneDispatchInComputeShader) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed()); + + for (wgpu::TextureFormat format : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(format)) { + continue; + } + + // TODO(jiawei.shao@intel.com): investigate why this test fails with RGBA8Snorm on Linux + // Intel OpenGL driver. + if (format == wgpu::TextureFormat::RGBA8Snorm && IsIntel() && IsOpenGL() && IsLinux()) { + continue; + } + + // Prepare the read-only storage texture. + const std::vector kInitialTextureData = GetExpectedData(format); + wgpu::Texture readonlyStorageTexture = + CreateTextureWithTestData(kInitialTextureData, format); + + // Prepare the write-only storage texture. + wgpu::Texture writeonlyStorageTexture = + CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc); + + // Write the expected pixel values into the write-only storage texture. + const std::string computeShader = CommonReadWriteTestCode(format); + ReadWriteIntoStorageTextureInComputePass(readonlyStorageTexture, writeonlyStorageTexture, + computeShader.c_str()); + + // Verify the pixel data in the write-only storage texture is expected. + CheckOutputStorageTexture(writeonlyStorageTexture, format); + } +} + +// Test that write-only storage textures are supported in fragment shader. +TEST_P(StorageTextureTests, WriteonlyStorageTextureInFragmentShader) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed()); + + for (wgpu::TextureFormat format : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(format)) { + continue; + } + + // TODO(jiawei.shao@intel.com): investigate why this test fails with RGBA8Snorm on Linux + // Intel OpenGL driver. + if (format == wgpu::TextureFormat::RGBA8Snorm && IsIntel() && IsOpenGL() && IsLinux()) { + continue; + } + + // Prepare the write-only storage texture. + wgpu::Texture writeonlyStorageTexture = + CreateTexture(format, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc); + + // Write the expected pixel values into the write-only storage texture. + const std::string fragmentShader = CommonWriteOnlyTestCode(format); + WriteIntoStorageTextureInRenderPass(writeonlyStorageTexture, kSimpleVertexShader, + fragmentShader.c_str()); + + // Verify the pixel data in the write-only storage texture is expected. + CheckOutputStorageTexture(writeonlyStorageTexture, format); + } +} + +// Verify 2D array read-only storage texture works correctly. +TEST_P(StorageTextureTests, Readonly2DArrayStorageTexture) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed()); + + constexpr uint32_t kArrayLayerCount = 3u; + + constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint; + + const std::vector initialTextureData = + GetExpectedData(kTextureFormat, kArrayLayerCount); + wgpu::Texture readonlyStorageTexture = + CreateTextureWithTestData(initialTextureData, kTextureFormat); + + // Create a compute shader that reads the pixels from the read-only storage texture and writes 1 + // to DstBuffer if they all have to expected value. + std::ostringstream csStream; + csStream << R"( + #version 450 + layout (set = 0, binding = 1, std430) buffer DstBuffer { + uint result; + } dstBuffer; + )" << CommonReadOnlyTestCode(kTextureFormat, true) + << R"( + void main() { + if (doTest()) { + dstBuffer.result = 1; + } else { + dstBuffer.result = 0; + } + })"; + + CheckResultInStorageBuffer(readonlyStorageTexture, csStream.str()); +} + +// Verify 2D array write-only storage texture works correctly. +TEST_P(StorageTextureTests, Writeonly2DArrayStorageTexture) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed()); + + constexpr uint32_t kArrayLayerCount = 3u; + + constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint; + + // Prepare the write-only storage texture. + wgpu::Texture writeonlyStorageTexture = + CreateTexture(kTextureFormat, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc, + kWidth, kHeight, kArrayLayerCount); + + // Write the expected pixel values into the write-only storage texture. + const std::string computeShader = CommonWriteOnlyTestCode(kTextureFormat, true); + WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, computeShader.c_str()); + + // Verify the pixel data in the write-only storage texture is expected. + CheckOutputStorageTexture(writeonlyStorageTexture, kTextureFormat, kArrayLayerCount); +} + +DAWN_INSTANTIATE_TEST(StorageTextureTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +class StorageTextureZeroInitTests : public StorageTextureTests { + public: + static std::vector GetExpectedData() { + constexpr wgpu::TextureFormat kTextureFormat = wgpu::TextureFormat::R32Uint; + + const uint32_t texelSizeInBytes = utils::GetTexelBlockSizeInBytes(kTextureFormat); + const size_t kDataCount = texelSizeInBytes * kWidth * kHeight; + std::vector outputData(kDataCount, 0); + + uint32_t* outputDataPtr = reinterpret_cast(&outputData[0]); + *outputDataPtr = 1u; + + return outputData; + } + + const char* kCommonReadOnlyZeroInitTestCode = R"( + bool doTest() { + for (uint y = 0; y < 4; ++y) { + for (uint x = 0; x < 4; ++x) { + uvec4 pixel = imageLoad(srcImage, ivec2(x, y)); + if (pixel != uvec4(0, 0, 0, 1u)) { + return false; + } + } + } + return true; + })"; + + const char* kCommonWriteOnlyZeroInitTestCode = R"( + #version 450 + layout(set = 0, binding = 0, r32ui) uniform writeonly uimage2D dstImage; + void main() { + imageStore(dstImage, ivec2(0, 0), uvec4(1u, 0, 0, 1u)); + })"; +}; + +// Verify that the texture is correctly cleared to 0 before its first usage as a read-only storage +// texture in a render pass. +TEST_P(StorageTextureZeroInitTests, ReadonlyStorageTextureClearsToZeroInRenderPass) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed()); + + wgpu::Texture readonlyStorageTexture = + CreateTexture(wgpu::TextureFormat::R32Uint, wgpu::TextureUsage::Storage); + + // Create a rendering pipeline that reads the pixels from the read-only storage texture and uses + // green as the output color, otherwise uses red instead. + const char* kVertexShader = kSimpleVertexShader; + const std::string kFragmentShader = std::string(R"( + #version 450 + layout(set = 0, binding = 0, r32ui) uniform readonly uimage2D srcImage; + layout(location = 0) out vec4 o_color;)") + + kCommonReadOnlyZeroInitTestCode + + R"( + + void main() { + if (doTest()) { + o_color = vec4(0.f, 1.f, 0.f, 1.f); + } else { + o_color = vec4(1.f, 0.f, 0.f, 1.f); + } + })"; + CheckDrawsGreen(kVertexShader, kFragmentShader.c_str(), readonlyStorageTexture); +} + +// Verify that the texture is correctly cleared to 0 before its first usage as a read-only storage +// texture in a compute pass. +TEST_P(StorageTextureZeroInitTests, ReadonlyStorageTextureClearsToZeroInComputePass) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsSpvcParserBeingUsed()); + + wgpu::Texture readonlyStorageTexture = + CreateTexture(wgpu::TextureFormat::R32Uint, wgpu::TextureUsage::Storage); + + // Create a compute shader that reads the pixels from the read-only storage texture and writes 1 + // to DstBuffer if they all have to expected value. + const std::string kComputeShader = std::string(R"( + #version 450 + layout (set = 0, binding = 0, r32ui) uniform readonly uimage2D srcImage; + layout (set = 0, binding = 1, std430) buffer DstBuffer { + uint result; + } dstBuffer;)") + kCommonReadOnlyZeroInitTestCode + + R"( + + void main() { + if (doTest()) { + dstBuffer.result = 1; + } else { + dstBuffer.result = 0; + } + })"; + + CheckResultInStorageBuffer(readonlyStorageTexture, kComputeShader); +} + +// Verify that the texture is correctly cleared to 0 before its first usage as a write-only storage +// storage texture in a render pass. +TEST_P(StorageTextureZeroInitTests, WriteonlyStorageTextureClearsToZeroInRenderPass) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed()); + + // Prepare the write-only storage texture. + constexpr uint32_t kTexelSizeR32Uint = 4u; + wgpu::Texture writeonlyStorageTexture = CreateTexture( + wgpu::TextureFormat::R32Uint, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc); + + WriteIntoStorageTextureInRenderPass(writeonlyStorageTexture, kSimpleVertexShader, + kCommonWriteOnlyZeroInitTestCode); + CheckOutputStorageTexture(writeonlyStorageTexture, kTexelSizeR32Uint, GetExpectedData()); +} + +// Verify that the texture is correctly cleared to 0 before its first usage as a write-only storage +// texture in a compute pass. +TEST_P(StorageTextureZeroInitTests, WriteonlyStorageTextureClearsToZeroInComputePass) { + // When we run dawn_end2end_tests with "--use-spvc-parser", extracting the binding type of a + // read-only image will always return shaderc_spvc_binding_type_writeonly_storage_texture. + // TODO(jiawei.shao@intel.com): enable this test when we specify "--use-spvc-parser" after the + // bug in spvc parser is fixed. + DAWN_SKIP_TEST_IF(IsD3D12() && IsSpvcParserBeingUsed()); + + // Prepare the write-only storage texture. + constexpr uint32_t kTexelSizeR32Uint = 4u; + wgpu::Texture writeonlyStorageTexture = CreateTexture( + wgpu::TextureFormat::R32Uint, wgpu::TextureUsage::Storage | wgpu::TextureUsage::CopySrc); + + WriteIntoStorageTextureInComputePass(writeonlyStorageTexture, kCommonWriteOnlyZeroInitTestCode); + CheckOutputStorageTexture(writeonlyStorageTexture, kTexelSizeR32Uint, GetExpectedData()); +} + +DAWN_INSTANTIATE_TEST(StorageTextureZeroInitTests, + D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}), + OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}), + MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}), + VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"})); diff --git a/third_party/dawn/src/tests/end2end/SubresourceOutputAttachmentTests.cpp b/third_party/dawn/src/tests/end2end/SubresourceOutputAttachmentTests.cpp new file mode 100644 index 00000000000..ff45abc9541 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/SubresourceOutputAttachmentTests.cpp @@ -0,0 +1,162 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Assert.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +// Test that rendering to a subresource of a texture works. +class SubresourceOutputAttachmentTest : public DawnTest { + constexpr static uint32_t kRTSize = 2; + + protected: + enum class Type { Color, Depth, Stencil }; + + void DoSingleTest(Type type, + wgpu::TextureFormat format, + wgpu::Texture renderTarget, + uint32_t textureSize, + uint32_t baseArrayLayer, + uint32_t baseMipLevel) { + wgpu::TextureViewDescriptor renderTargetViewDesc; + renderTargetViewDesc.baseArrayLayer = baseArrayLayer; + renderTargetViewDesc.arrayLayerCount = 1; + renderTargetViewDesc.baseMipLevel = baseMipLevel; + renderTargetViewDesc.mipLevelCount = 1; + wgpu::TextureView renderTargetView = renderTarget.CreateView(&renderTargetViewDesc); + + RGBA8 expectedColor(0, 255, 0, 255); + float expectedDepth = 0.3f; + uint8_t expectedStencil = 7; + + utils::ComboRenderPassDescriptor renderPass = [&]() { + switch (type) { + case Type::Color: { + utils::ComboRenderPassDescriptor renderPass({renderTargetView}); + renderPass.cColorAttachments[0].clearColor = { + static_cast(expectedColor.r) / 255.f, + static_cast(expectedColor.g) / 255.f, + static_cast(expectedColor.b) / 255.f, + static_cast(expectedColor.a) / 255.f, + }; + return renderPass; + } + case Type::Depth: { + utils::ComboRenderPassDescriptor renderPass({}, renderTargetView); + renderPass.cDepthStencilAttachmentInfo.clearDepth = expectedDepth; + return renderPass; + } + case Type::Stencil: { + utils::ComboRenderPassDescriptor renderPass({}, renderTargetView); + renderPass.cDepthStencilAttachmentInfo.clearStencil = expectedStencil; + return renderPass; + } + default: + UNREACHABLE(); + } + }(); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder passEncoder = commandEncoder.BeginRenderPass(&renderPass); + passEncoder.EndPass(); + wgpu::CommandBuffer commands = commandEncoder.Finish(); + queue.Submit(1, &commands); + + const uint32_t renderTargetSize = textureSize >> baseMipLevel; + switch (type) { + case Type::Color: { + std::vector expected(renderTargetSize * renderTargetSize, expectedColor); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderTarget, 0, 0, renderTargetSize, + renderTargetSize, baseMipLevel, baseArrayLayer); + break; + } + case Type::Depth: { + std::vector expected(renderTargetSize * renderTargetSize, expectedDepth); + EXPECT_TEXTURE_FLOAT_EQ(expected.data(), renderTarget, 0, 0, renderTargetSize, + renderTargetSize, baseMipLevel, baseArrayLayer); + break; + } + case Type::Stencil: + // TODO(crbug.com/dawn/439): sample / copy of the stencil aspect. + default: + UNREACHABLE(); + } + } + + void DoTest(Type type) { + constexpr uint32_t kArrayLayerCount = 5; + constexpr uint32_t kMipLevelCount = 4; + + wgpu::TextureFormat format; + switch (type) { + case Type::Color: + format = wgpu::TextureFormat::RGBA8Unorm; + break; + case Type::Depth: + format = wgpu::TextureFormat::Depth32Float; + break; + case Type::Stencil: + format = wgpu::TextureFormat::Depth24PlusStencil8; + break; + default: + UNREACHABLE(); + } + + constexpr uint32_t kTextureSize = kRTSize << (kMipLevelCount - 1); + + wgpu::TextureDescriptor renderTargetDesc; + renderTargetDesc.dimension = wgpu::TextureDimension::e2D; + renderTargetDesc.size.width = kTextureSize; + renderTargetDesc.size.height = kTextureSize; + renderTargetDesc.size.depth = kArrayLayerCount; + renderTargetDesc.sampleCount = 1; + renderTargetDesc.format = format; + renderTargetDesc.mipLevelCount = kMipLevelCount; + renderTargetDesc.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + + wgpu::Texture renderTarget = device.CreateTexture(&renderTargetDesc); + + // Test rendering into the first, middle, and last of each of array layer and mip level. + for (uint32_t arrayLayer : {0u, kArrayLayerCount / 2, kArrayLayerCount - 1u}) { + for (uint32_t mipLevel : {0u, kMipLevelCount / 2, kMipLevelCount - 1u}) { + DoSingleTest(type, format, renderTarget, kTextureSize, arrayLayer, mipLevel); + } + } + } +}; + +// Test rendering into a subresource of a color texture +TEST_P(SubresourceOutputAttachmentTest, ColorTexture) { + DoTest(Type::Color); +} + +// Test rendering into a subresource of a depth texture +TEST_P(SubresourceOutputAttachmentTest, DepthTexture) { + DoTest(Type::Depth); +} + +// Test rendering into a subresource of a stencil texture +// TODO(crbug.com/dawn/439): sample / copy of the stencil aspect. +TEST_P(SubresourceOutputAttachmentTest, DISABLED_StencilTexture) { + DoTest(Type::Stencil); +} + +DAWN_INSTANTIATE_TEST(SubresourceOutputAttachmentTest, + D3D12Backend(), + D3D12Backend({}, {"use_d3d12_render_pass"}), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/SwapChainTests.cpp b/third_party/dawn/src/tests/end2end/SwapChainTests.cpp new file mode 100644 index 00000000000..bfa10a1ca6a --- /dev/null +++ b/third_party/dawn/src/tests/end2end/SwapChainTests.cpp @@ -0,0 +1,221 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Constants.h" +#include "common/Log.h" +#include "utils/GLFWUtils.h" +#include "utils/WGPUHelpers.h" + +#include "GLFW/glfw3.h" + +class SwapChainTests : public DawnTest { + public: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + + glfwSetErrorCallback([](int code, const char* message) { + dawn::ErrorLog() << "GLFW error " << code << " " << message; + }); + glfwInit(); + + // The SwapChainTests don't create OpenGL contexts so we don't need to call + // SetupGLFWWindowHintsForBackend. Set GLFW_NO_API anyway to avoid GLFW bringing up a GL + // context that we won't use. + ASSERT_TRUE(!IsOpenGL()); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + window = glfwCreateWindow(400, 400, "SwapChainValidationTests window", nullptr, nullptr); + + int width; + int height; + glfwGetFramebufferSize(window, &width, &height); + + surface = utils::CreateSurfaceForWindow(GetInstance(), window); + ASSERT_NE(surface, nullptr); + + baseDescriptor.width = width; + baseDescriptor.height = height; + baseDescriptor.usage = wgpu::TextureUsage::OutputAttachment; + baseDescriptor.format = wgpu::TextureFormat::BGRA8Unorm; + baseDescriptor.presentMode = wgpu::PresentMode::Mailbox; + } + + void TearDown() override { + // Destroy the surface before the window as required by webgpu-native. + surface = wgpu::Surface(); + if (window != nullptr) { + glfwDestroyWindow(window); + } + DawnTest::TearDown(); + } + + void ClearTexture(wgpu::TextureView view, wgpu::Color color) { + utils::ComboRenderPassDescriptor desc({view}); + desc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + desc.cColorAttachments[0].clearColor = color; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&desc); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + protected: + GLFWwindow* window = nullptr; + wgpu::Surface surface; + + wgpu::SwapChainDescriptor baseDescriptor; +}; + +// Basic test for creating a swapchain and presenting one frame. +TEST_P(SwapChainTests, Basic) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor); + ClearTexture(swapchain.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0}); + swapchain.Present(); +} + +// Test replacing the swapchain +TEST_P(SwapChainTests, ReplaceBasic) { + wgpu::SwapChain swapchain1 = device.CreateSwapChain(surface, &baseDescriptor); + ClearTexture(swapchain1.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0}); + swapchain1.Present(); + + wgpu::SwapChain swapchain2 = device.CreateSwapChain(surface, &baseDescriptor); + ClearTexture(swapchain2.GetCurrentTextureView(), {0.0, 1.0, 0.0, 1.0}); + swapchain2.Present(); +} + +// Test replacing the swapchain after GetCurrentTextureView +TEST_P(SwapChainTests, ReplaceAfterGet) { + wgpu::SwapChain swapchain1 = device.CreateSwapChain(surface, &baseDescriptor); + ClearTexture(swapchain1.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0}); + + wgpu::SwapChain swapchain2 = device.CreateSwapChain(surface, &baseDescriptor); + ClearTexture(swapchain2.GetCurrentTextureView(), {0.0, 1.0, 0.0, 1.0}); + swapchain2.Present(); +} + +// Test destroying the swapchain after GetCurrentTextureView +TEST_P(SwapChainTests, DestroyAfterGet) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor); + ClearTexture(swapchain.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0}); +} + +// Test destroying the surface before the swapchain +TEST_P(SwapChainTests, DestroySurface) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor); + surface = nullptr; +} + +// Test destroying the surface before the swapchain but after GetCurrentTextureView +TEST_P(SwapChainTests, DestroySurfaceAfterGet) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor); + ClearTexture(swapchain.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0}); + surface = nullptr; +} + +// Test switching between present modes. +TEST_P(SwapChainTests, SwitchPresentMode) { + constexpr wgpu::PresentMode kAllPresentModes[] = { + wgpu::PresentMode::Immediate, + wgpu::PresentMode::Fifo, + wgpu::PresentMode::Mailbox, + }; + + for (wgpu::PresentMode mode1 : kAllPresentModes) { + for (wgpu::PresentMode mode2 : kAllPresentModes) { + wgpu::SwapChainDescriptor desc = baseDescriptor; + + desc.presentMode = mode1; + wgpu::SwapChain swapchain1 = device.CreateSwapChain(surface, &desc); + ClearTexture(swapchain1.GetCurrentTextureView(), {0.0, 0.0, 0.0, 1.0}); + swapchain1.Present(); + + desc.presentMode = mode2; + wgpu::SwapChain swapchain2 = device.CreateSwapChain(surface, &desc); + ClearTexture(swapchain2.GetCurrentTextureView(), {0.0, 0.0, 0.0, 1.0}); + swapchain2.Present(); + } + } +} + +// Test resizing the swapchain and without resizing the window. +TEST_P(SwapChainTests, ResizingSwapChainOnly) { + for (int i = 0; i < 10; i++) { + wgpu::SwapChainDescriptor desc = baseDescriptor; + desc.width += i * 10; + desc.height -= i * 10; + + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &desc); + ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f}); + swapchain.Present(); + } +} + +// Test resizing the window but not the swapchain. +TEST_P(SwapChainTests, ResizingWindowOnly) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor); + + for (int i = 0; i < 10; i++) { + glfwSetWindowSize(window, 400 - 10 * i, 400 + 10 * i); + glfwPollEvents(); + + ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f}); + swapchain.Present(); + } +} + +// Test resizing both the window and the swapchain at the same time. +TEST_P(SwapChainTests, ResizingWindowAndSwapChain) { + for (int i = 0; i < 10; i++) { + glfwSetWindowSize(window, 400 - 10 * i, 400 + 10 * i); + glfwPollEvents(); + + int width; + int height; + glfwGetFramebufferSize(window, &width, &height); + + wgpu::SwapChainDescriptor desc = baseDescriptor; + desc.width = width; + desc.height = height; + + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &desc); + ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f}); + swapchain.Present(); + } +} + +// Test switching devices on the same adapter. +TEST_P(SwapChainTests, SwitchingDevice) { + wgpu::Device device2 = GetAdapter().CreateDevice(); + + for (int i = 0; i < 3; i++) { + wgpu::Device deviceToUse; + if (i % 2 == 0) { + deviceToUse = device; + } else { + deviceToUse = device2; + } + + wgpu::SwapChain swapchain = deviceToUse.CreateSwapChain(surface, &baseDescriptor); + swapchain.GetCurrentTextureView(); + swapchain.Present(); + } +} + +DAWN_INSTANTIATE_TEST(SwapChainTests, MetalBackend()); diff --git a/third_party/dawn/src/tests/end2end/SwapChainValidationTests.cpp b/third_party/dawn/src/tests/end2end/SwapChainValidationTests.cpp new file mode 100644 index 00000000000..e92bf847e96 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/SwapChainValidationTests.cpp @@ -0,0 +1,333 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Constants.h" +#include "common/Log.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/GLFWUtils.h" +#include "utils/WGPUHelpers.h" + +#include "GLFW/glfw3.h" + +class SwapChainValidationTests : public DawnTest { + public: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + + glfwSetErrorCallback([](int code, const char* message) { + dawn::ErrorLog() << "GLFW error " << code << " " << message; + }); + DAWN_SKIP_TEST_IF(!glfwInit()); + + // The SwapChainValidationTests don't create devices so we don't need to call + // SetupGLFWWindowHintsForBackend. Set GLFW_NO_API anyway to avoid GLFW bringing up a GL + // context that we won't use. + ASSERT_TRUE(!IsOpenGL()); + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + window = glfwCreateWindow(400, 400, "SwapChainValidationTests window", nullptr, nullptr); + + surface = utils::CreateSurfaceForWindow(GetInstance(), window); + ASSERT_NE(surface, nullptr); + + goodDescriptor.width = 1; + goodDescriptor.height = 1; + goodDescriptor.usage = wgpu::TextureUsage::OutputAttachment; + goodDescriptor.format = wgpu::TextureFormat::BGRA8Unorm; + goodDescriptor.presentMode = wgpu::PresentMode::Mailbox; + + badDescriptor = goodDescriptor; + badDescriptor.width = 0; + } + + void TearDown() override { + // Destroy the surface before the window as required by webgpu-native. + surface = wgpu::Surface(); + if (window != nullptr) { + glfwDestroyWindow(window); + } + DawnTest::TearDown(); + } + + protected: + GLFWwindow* window = nullptr; + wgpu::Surface surface; + wgpu::SwapChainDescriptor goodDescriptor; + wgpu::SwapChainDescriptor badDescriptor; + + // Checks that an OutputAttachment view is an error by trying to create a render pass on it. + void CheckTextureViewIsError(wgpu::TextureView view) { + utils::ComboRenderPassDescriptor renderPassDesc({view}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Checks that an OutputAttachment view is an error by trying to create a render pass on it. + void CheckTextureViewIsDestroyed(wgpu::TextureView view) { + utils::ComboRenderPassDescriptor renderPassDesc({view}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + } +}; + +// Control case for a successful swapchain creation and presenting. +TEST_P(SwapChainValidationTests, CreationSuccess) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor); + wgpu::TextureView view = swapchain.GetCurrentTextureView(); + swapchain.Present(); +} + +// Checks that the creation size must be a valid 2D texture size. +TEST_P(SwapChainValidationTests, InvalidCreationSize) { + // A width of 0 is invalid. + { + wgpu::SwapChainDescriptor desc = goodDescriptor; + desc.width = 0; + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); + } + // A height of 0 is invalid. + { + wgpu::SwapChainDescriptor desc = goodDescriptor; + desc.height = 0; + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); + } + + // A width of kMaxTextureSize is valid but kMaxTextureSize + 1 isn't. + { + wgpu::SwapChainDescriptor desc = goodDescriptor; + desc.width = kMaxTextureSize; + device.CreateSwapChain(surface, &desc); + + desc.width = kMaxTextureSize + 1; + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); + } + + // A height of kMaxTextureSize is valid but kMaxTextureSize + 1 isn't. + { + wgpu::SwapChainDescriptor desc = goodDescriptor; + desc.height = kMaxTextureSize; + device.CreateSwapChain(surface, &desc); + + desc.height = kMaxTextureSize + 1; + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); + } +} + +// Checks that the creation usage must be OutputAttachment +TEST_P(SwapChainValidationTests, InvalidCreationUsage) { + wgpu::SwapChainDescriptor desc = goodDescriptor; + desc.usage = wgpu::TextureUsage::Sampled; + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); +} + +// Checks that the creation format must (currently) be BGRA8Unorm +TEST_P(SwapChainValidationTests, InvalidCreationFormat) { + wgpu::SwapChainDescriptor desc = goodDescriptor; + desc.format = wgpu::TextureFormat::RGBA8Unorm; + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); +} + +// Checks that the implementation must be zero. +TEST_P(SwapChainValidationTests, InvalidWithImplementation) { + wgpu::SwapChainDescriptor desc = goodDescriptor; + desc.implementation = 1; + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &desc)); +} + +// Check swapchain operations with an error swapchain are errors +TEST_P(SwapChainValidationTests, OperationsOnErrorSwapChain) { + wgpu::SwapChain swapchain; + ASSERT_DEVICE_ERROR(swapchain = device.CreateSwapChain(surface, &badDescriptor)); + + wgpu::TextureView view; + ASSERT_DEVICE_ERROR(view = swapchain.GetCurrentTextureView()); + CheckTextureViewIsError(view); + + ASSERT_DEVICE_ERROR(swapchain.Present()); +} + +// Check it is invalid to call present without getting a current view. +TEST_P(SwapChainValidationTests, PresentWithoutCurrentView) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor); + + // Check it is invalid if we never called GetCurrentTextureView + ASSERT_DEVICE_ERROR(swapchain.Present()); + + // Check it is invalid if we never called since the last present. + swapchain.GetCurrentTextureView(); + swapchain.Present(); + ASSERT_DEVICE_ERROR(swapchain.Present()); +} + +// Check that the current view is in the destroyed state after the swapchain is destroyed. +TEST_P(SwapChainValidationTests, ViewDestroyedAfterSwapChainDestruction) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor); + wgpu::TextureView view = swapchain.GetCurrentTextureView(); + swapchain = nullptr; + + CheckTextureViewIsDestroyed(view); +} + +// Check that the current view is the destroyed state after present. +TEST_P(SwapChainValidationTests, ViewDestroyedAfterPresent) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor); + wgpu::TextureView view = swapchain.GetCurrentTextureView(); + swapchain.Present(); + + CheckTextureViewIsDestroyed(view); +} + +// Check that returned view is of the current format / usage / dimension / size / sample count +TEST_P(SwapChainValidationTests, ReturnedViewCharacteristics) { + utils::ComboRenderPipelineDescriptor pipelineDesc(device); + pipelineDesc.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); + })"); + pipelineDesc.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + })"); + // Validation will check that the sample count of the view matches this format. + pipelineDesc.sampleCount = 1; + pipelineDesc.colorStateCount = 2; + // Validation will check that the format of the view matches this format. + pipelineDesc.cColorStates[0].format = wgpu::TextureFormat::BGRA8Unorm; + pipelineDesc.cColorStates[1].format = wgpu::TextureFormat::R8Unorm; + device.CreateRenderPipeline(&pipelineDesc); + + // Create a second texture to be used as render pass attachment. Validation will check that the + // size of the view matches the size of this texture. + wgpu::TextureDescriptor textureDesc; + textureDesc.usage = wgpu::TextureUsage::OutputAttachment; + textureDesc.dimension = wgpu::TextureDimension::e2D; + textureDesc.size = {1, 1, 1}; + textureDesc.format = wgpu::TextureFormat::R8Unorm; + textureDesc.sampleCount = 1; + wgpu::Texture secondTexture = device.CreateTexture(&textureDesc); + + // Get the swapchain view and try to use it in the render pass to trigger all the validation. + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor); + wgpu::TextureView view = swapchain.GetCurrentTextureView(); + + // Validation will also check the dimension of the view is 2D, and it's usage contains + // OutputAttachment + utils::ComboRenderPassDescriptor renderPassDesc({view, secondTexture.CreateView()}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + + queue.Submit(1, &commands); + + // Check that view doesn't have extra formats like Sampled. + // TODO(cwallez@chromium.org): also check for [Readonly]Storage once that's implemented. + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}}); + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl, {{0, view}})); +} + +// Check that failing to create a new swapchain doesn't replace the previous one. +TEST_P(SwapChainValidationTests, ErrorSwapChainDoesntReplacePreviousOne) { + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor); + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &badDescriptor)); + + wgpu::TextureView view = swapchain.GetCurrentTextureView(); + swapchain.Present(); +} + +// Check that after replacement, all swapchain operations are errors and the view is destroyed. +TEST_P(SwapChainValidationTests, ReplacedSwapChainIsInvalid) { + { + wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor); + device.CreateSwapChain(surface, &goodDescriptor); + ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTextureView()); + } + + { + wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor); + wgpu::TextureView view = replacedSwapChain.GetCurrentTextureView(); + device.CreateSwapChain(surface, &goodDescriptor); + + CheckTextureViewIsDestroyed(view); + ASSERT_DEVICE_ERROR(replacedSwapChain.Present()); + } +} + +// Check that after surface destruction, all swapchain operations are errors and the view is +// destroyed. The test is split in two to reset the wgpu::Surface in the middle. +TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_GetView) { + wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor); + surface = nullptr; + ASSERT_DEVICE_ERROR(replacedSwapChain.GetCurrentTextureView()); +} +TEST_P(SwapChainValidationTests, SwapChainIsInvalidAfterSurfaceDestruction_AfterGetView) { + wgpu::SwapChain replacedSwapChain = device.CreateSwapChain(surface, &goodDescriptor); + wgpu::TextureView view = replacedSwapChain.GetCurrentTextureView(); + surface = nullptr; + + CheckTextureViewIsDestroyed(view); + ASSERT_DEVICE_ERROR(replacedSwapChain.Present()); +} + +// Test that after Device is Lost, all swap chain operations fail +static void ToMockDeviceLostCallback(const char* message, void* userdata) { + DawnTest* self = static_cast(userdata); + self->StartExpectDeviceError(); +} + +// Test that new swap chain present fails after device is lost +TEST_P(SwapChainValidationTests, NewSwapChainPresentFailsAfterDeviceLost) { + device.SetDeviceLostCallback(ToMockDeviceLostCallback, this); + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor); + wgpu::TextureView view = swapchain.GetCurrentTextureView(); + + device.LoseForTesting(); + ASSERT_DEVICE_ERROR(swapchain.Present()); +} + +// Test that new swap chain get current texture view fails after device is lost +TEST_P(SwapChainValidationTests, NewSwapChainGetCurrentTextureViewFailsAfterDevLost) { + device.SetDeviceLostCallback(ToMockDeviceLostCallback, this); + wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &goodDescriptor); + + device.LoseForTesting(); + ASSERT_DEVICE_ERROR(swapchain.GetCurrentTextureView()); +} + +// Test that creation of a new swapchain fails after device is lost +TEST_P(SwapChainValidationTests, CreateNewSwapChainFailsAfterDevLost) { + device.SetDeviceLostCallback(ToMockDeviceLostCallback, this); + device.LoseForTesting(); + + ASSERT_DEVICE_ERROR(device.CreateSwapChain(surface, &goodDescriptor)); +} + +DAWN_INSTANTIATE_TEST(SwapChainValidationTests, MetalBackend(), NullBackend()); diff --git a/third_party/dawn/src/tests/end2end/TextureFormatTests.cpp b/third_party/dawn/src/tests/end2end/TextureFormatTests.cpp new file mode 100644 index 00000000000..cad7b31ea0c --- /dev/null +++ b/third_party/dawn/src/tests/end2end/TextureFormatTests.cpp @@ -0,0 +1,740 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Assert.h" +#include "common/Math.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" + +#include + +// An expectation for float buffer content that can correctly compare different NaN values and +// supports a basic tolerance for comparison of finite values. +class ExpectFloatWithTolerance : public detail::Expectation { + public: + ExpectFloatWithTolerance(std::vector expected, float tolerance) + : mExpected(std::move(expected)), mTolerance(tolerance) { + } + + testing::AssertionResult Check(const void* data, size_t size) override { + ASSERT(size == sizeof(float) * mExpected.size()); + + const float* actual = static_cast(data); + + for (size_t i = 0; i < mExpected.size(); ++i) { + float expectedValue = mExpected[i]; + float actualValue = actual[i]; + + if (!FloatsMatch(expectedValue, actualValue)) { + testing::AssertionResult result = testing::AssertionFailure() + << "Expected data[" << i << "] to be close to " + << expectedValue << ", actual " << actualValue + << std::endl; + return result; + } + } + return testing::AssertionSuccess(); + } + + private: + bool FloatsMatch(float expected, float actual) { + if (std::isnan(expected)) { + return std::isnan(actual); + } + + if (std::isinf(expected)) { + return std::isinf(actual) && std::signbit(expected) == std::signbit(actual); + } + + if (mTolerance == 0.0f) { + return expected == actual; + } + + float error = std::abs(expected - actual); + return error < mTolerance; + } + + std::vector mExpected; + float mTolerance; +}; + +// An expectation for float16 buffers that can correctly compare NaNs (all NaNs are equivalent). +class ExpectFloat16 : public detail::Expectation { + public: + ExpectFloat16(std::vector expected) : mExpected(std::move(expected)) { + } + + testing::AssertionResult Check(const void* data, size_t size) override { + ASSERT(size == sizeof(uint16_t) * mExpected.size()); + + const uint16_t* actual = static_cast(data); + + for (size_t i = 0; i < mExpected.size(); ++i) { + uint16_t expectedValue = mExpected[i]; + uint16_t actualValue = actual[i]; + + if (!Floats16Match(expectedValue, actualValue)) { + testing::AssertionResult result = testing::AssertionFailure() + << "Expected data[" << i << "] to be " + << expectedValue << ", actual " << actualValue + << std::endl; + return result; + } + } + return testing::AssertionSuccess(); + } + + private: + bool Floats16Match(float expected, float actual) { + if (IsFloat16NaN(expected)) { + return IsFloat16NaN(actual); + } + + return expected == actual; + } + + std::vector mExpected; +}; + +class TextureFormatTest : public DawnTest { + protected: + void SetUp() { + DawnTest::SetUp(); + } + + // Structure containing all the information that tests need to know about the format. + struct FormatTestInfo { + wgpu::TextureFormat format; + uint32_t texelByteSize; + wgpu::TextureComponentType type; + uint32_t componentCount; + }; + + // Returns a reprensentation of a format that can be used to contain the "uncompressed" values + // of the format. That the equivalent format with all channels 32bit-sized. + FormatTestInfo GetUncompressedFormatInfo(FormatTestInfo formatInfo) { + std::array floatFormats = { + wgpu::TextureFormat::R32Float, + wgpu::TextureFormat::RG32Float, + wgpu::TextureFormat::RGBA32Float, + wgpu::TextureFormat::RGBA32Float, + }; + std::array sintFormats = { + wgpu::TextureFormat::R32Sint, + wgpu::TextureFormat::RG32Sint, + wgpu::TextureFormat::RGBA32Sint, + wgpu::TextureFormat::RGBA32Sint, + }; + std::array uintFormats = { + wgpu::TextureFormat::R32Uint, + wgpu::TextureFormat::RG32Uint, + wgpu::TextureFormat::RGBA32Uint, + wgpu::TextureFormat::RGBA32Uint, + }; + std::array componentCounts = {1, 2, 4, 4}; + + ASSERT(formatInfo.componentCount > 0 && formatInfo.componentCount <= 4); + wgpu::TextureFormat format; + switch (formatInfo.type) { + case wgpu::TextureComponentType::Float: + format = floatFormats[formatInfo.componentCount - 1]; + break; + case wgpu::TextureComponentType::Sint: + format = sintFormats[formatInfo.componentCount - 1]; + break; + case wgpu::TextureComponentType::Uint: + format = uintFormats[formatInfo.componentCount - 1]; + break; + default: + UNREACHABLE(); + } + + uint32_t componentCount = componentCounts[formatInfo.componentCount - 1]; + return {format, 4 * componentCount, formatInfo.type, componentCount}; + } + + // Return a pipeline that can be used in a full-texture draw to sample from the texture in the + // bindgroup and output its decompressed values to the render target. + wgpu::RenderPipeline CreateSamplePipeline(FormatTestInfo sampleFormatInfo, + FormatTestInfo renderFormatInfo) { + utils::ComboRenderPipelineDescriptor desc(device); + + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[3] = vec2[3]( + vec2(-3.0f, -1.0f), + vec2( 3.0f, -1.0f), + vec2( 0.0f, 2.0f) + ); + gl_Position = vec4(pos[gl_VertexIndex], 0.0f, 1.0f); + })"); + + // Compute the prefix needed for GLSL types that handle our texture's data. + const char* prefix = utils::GetColorTextureComponentTypePrefix(sampleFormatInfo.format); + + std::ostringstream fsSource; + fsSource << "#version 450\n"; + fsSource << "layout(set=0, binding=0) uniform sampler mySampler;\n"; + fsSource << "layout(set=0, binding=1) uniform " << prefix << "texture2D myTexture;\n"; + fsSource << "layout(location=0) out " << prefix << "vec4 fragColor;\n"; + + fsSource << "void main() {\n"; + fsSource << " fragColor = texelFetch(" << prefix + << "sampler2D(myTexture, mySampler), ivec2(gl_FragCoord), 0);\n"; + fsSource << "}"; + + wgpu::ShaderModule fsModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Fragment, fsSource.str().c_str()); + + desc.vertexStage.module = vsModule; + desc.cFragmentStage.module = fsModule; + desc.cColorStates[0].format = renderFormatInfo.format; + + return device.CreateRenderPipeline(&desc); + } + + // The sampling test uploads the sample data in a texture with the sampleFormatInfo.format. + // It then samples from it and renders the results in a texture with the + // renderFormatInfo.format format. Finally it checks that the data rendered matches + // expectedRenderData, using the cutom expectation if present. + void DoSampleTest(FormatTestInfo sampleFormatInfo, + const void* sampleData, + size_t sampleDataSize, + FormatTestInfo renderFormatInfo, + const void* expectedRenderData, + size_t expectedRenderDataSize, + detail::Expectation* customExpectation) { + // The input data should contain an exact number of texels + ASSERT(sampleDataSize % sampleFormatInfo.texelByteSize == 0); + uint32_t width = sampleDataSize / sampleFormatInfo.texelByteSize; + + // The input data must be a multiple of 4 byte in length for WriteBuffer + ASSERT(sampleDataSize % 4 == 0); + ASSERT(expectedRenderDataSize % 4 == 0); + + // Create the texture we will sample from + wgpu::TextureDescriptor sampleTextureDesc; + sampleTextureDesc.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + sampleTextureDesc.size = {width, 1, 1}; + sampleTextureDesc.format = sampleFormatInfo.format; + wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDesc); + + wgpu::Buffer uploadBuffer = utils::CreateBufferFromData(device, sampleData, sampleDataSize, + wgpu::BufferUsage::CopySrc); + + // Create the texture that we will render results to + ASSERT(expectedRenderDataSize == width * renderFormatInfo.texelByteSize); + + wgpu::TextureDescriptor renderTargetDesc; + renderTargetDesc.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment; + renderTargetDesc.size = {width, 1, 1}; + renderTargetDesc.format = renderFormatInfo.format; + + wgpu::Texture renderTarget = device.CreateTexture(&renderTargetDesc); + + // Create the readback buffer for the data in renderTarget + wgpu::BufferDescriptor readbackBufferDesc; + readbackBufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; + readbackBufferDesc.size = 4 * width * sampleFormatInfo.componentCount; + wgpu::Buffer readbackBuffer = device.CreateBuffer(&readbackBufferDesc); + + // Prepare objects needed to sample from texture in the renderpass + wgpu::RenderPipeline pipeline = CreateSamplePipeline(sampleFormatInfo, renderFormatInfo); + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, sampleTexture.CreateView()}}); + + // Encode commands for the test that fill texture, sample it to render to renderTarget then + // copy renderTarget in a buffer so we can read it easily. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + { + wgpu::BufferCopyView bufferView = utils::CreateBufferCopyView(uploadBuffer, 0, 256, 0); + wgpu::TextureCopyView textureView = + utils::CreateTextureCopyView(sampleTexture, 0, {0, 0, 0}); + wgpu::Extent3D extent{width, 1, 1}; + encoder.CopyBufferToTexture(&bufferView, &textureView, &extent); + } + + utils::ComboRenderPassDescriptor renderPassDesc({renderTarget.CreateView()}); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDesc); + renderPass.SetPipeline(pipeline); + renderPass.SetBindGroup(0, bindGroup); + renderPass.Draw(3); + renderPass.EndPass(); + + { + wgpu::BufferCopyView bufferView = + utils::CreateBufferCopyView(readbackBuffer, 0, 256, 0); + wgpu::TextureCopyView textureView = + utils::CreateTextureCopyView(renderTarget, 0, {0, 0, 0}); + wgpu::Extent3D extent{width, 1, 1}; + encoder.CopyTextureToBuffer(&textureView, &bufferView, &extent); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // For floats use a special expectation that understands how to compare NaNs and support a + // tolerance. + if (customExpectation != nullptr) { + AddBufferExpectation(__FILE__, __LINE__, readbackBuffer, 0, expectedRenderDataSize, + customExpectation); + } else { + EXPECT_BUFFER_U32_RANGE_EQ(static_cast(expectedRenderData), + readbackBuffer, 0, + expectedRenderDataSize / sizeof(uint32_t)); + } + } + + // Helper functions used to run tests that convert the typeful test objects to typeless void* + + template + void DoFormatSamplingTest(FormatTestInfo formatInfo, + const std::vector& textureData, + const std::vector& expectedRenderData, + detail::Expectation* customExpectation = nullptr) { + FormatTestInfo renderFormatInfo = GetUncompressedFormatInfo(formatInfo); + DoSampleTest(formatInfo, textureData.data(), textureData.size() * sizeof(TextureData), + renderFormatInfo, expectedRenderData.data(), + expectedRenderData.size() * sizeof(RenderData), customExpectation); + } + + template + void DoFloatFormatSamplingTest(FormatTestInfo formatInfo, + const std::vector& textureData, + const std::vector& expectedRenderData, + float floatTolerance = 0.0f) { + // Use a special expectation that understands how to compare NaNs and supports a tolerance. + DoFormatSamplingTest(formatInfo, textureData, expectedRenderData, + new ExpectFloatWithTolerance(expectedRenderData, floatTolerance)); + } + + template + void DoFormatRenderingTest(FormatTestInfo formatInfo, + const std::vector& textureData, + const std::vector& expectedRenderData, + detail::Expectation* customExpectation = nullptr) { + FormatTestInfo sampleFormatInfo = GetUncompressedFormatInfo(formatInfo); + DoSampleTest(sampleFormatInfo, textureData.data(), textureData.size() * sizeof(TextureData), + formatInfo, expectedRenderData.data(), + expectedRenderData.size() * sizeof(RenderData), customExpectation); + } + + // Below are helper functions for types that are very similar to one another so the logic is + // shared. + + template + void DoUnormTest(FormatTestInfo formatInfo) { + static_assert(!std::is_signed::value && std::is_integral::value, ""); + ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize); + ASSERT(formatInfo.type == wgpu::TextureComponentType::Float); + + T maxValue = std::numeric_limits::max(); + std::vector textureData = {0, 1, maxValue, maxValue}; + std::vector uncompressedData = {0.0f, 1.0f / maxValue, 1.0f, 1.0f}; + + DoFormatSamplingTest(formatInfo, textureData, uncompressedData); + DoFormatRenderingTest(formatInfo, uncompressedData, textureData); + } + + template + void DoSnormTest(FormatTestInfo formatInfo) { + static_assert(std::is_signed::value && std::is_integral::value, ""); + ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize); + ASSERT(formatInfo.type == wgpu::TextureComponentType::Float); + + T maxValue = std::numeric_limits::max(); + T minValue = std::numeric_limits::min(); + std::vector textureData = {0, 1, maxValue, minValue}; + std::vector uncompressedData = {0.0f, 1.0f / maxValue, 1.0f, -1.0f}; + + DoFloatFormatSamplingTest(formatInfo, textureData, uncompressedData, 0.0001f / maxValue); + // Snorm formats aren't renderable because they are not guaranteed renderable in Vulkan + } + + template + void DoUintTest(FormatTestInfo formatInfo) { + static_assert(!std::is_signed::value && std::is_integral::value, ""); + ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize); + ASSERT(formatInfo.type == wgpu::TextureComponentType::Uint); + + T maxValue = std::numeric_limits::max(); + std::vector textureData = {0, 1, maxValue, maxValue}; + std::vector uncompressedData = {0, 1, maxValue, maxValue}; + + DoFormatSamplingTest(formatInfo, textureData, uncompressedData); + DoFormatRenderingTest(formatInfo, uncompressedData, textureData); + } + + template + void DoSintTest(FormatTestInfo formatInfo) { + static_assert(std::is_signed::value && std::is_integral::value, ""); + ASSERT(sizeof(T) * formatInfo.componentCount == formatInfo.texelByteSize); + ASSERT(formatInfo.type == wgpu::TextureComponentType::Sint); + + T maxValue = std::numeric_limits::max(); + T minValue = std::numeric_limits::min(); + std::vector textureData = {0, 1, maxValue, minValue}; + std::vector uncompressedData = {0, 1, maxValue, minValue}; + + DoFormatSamplingTest(formatInfo, textureData, uncompressedData); + DoFormatRenderingTest(formatInfo, uncompressedData, textureData); + } + + void DoFloat32Test(FormatTestInfo formatInfo) { + ASSERT(sizeof(float) * formatInfo.componentCount == formatInfo.texelByteSize); + ASSERT(formatInfo.type == wgpu::TextureComponentType::Float); + + std::vector textureData = {+0.0f, -0.0f, 1.0f, 1.0e-29f, + 1.0e29f, NAN, INFINITY, -INFINITY}; + + DoFloatFormatSamplingTest(formatInfo, textureData, textureData); + DoFormatRenderingTest(formatInfo, textureData, textureData, + new ExpectFloatWithTolerance(textureData, 0.0f)); + } + + void DoFloat16Test(FormatTestInfo formatInfo) { + ASSERT(sizeof(int16_t) * formatInfo.componentCount == formatInfo.texelByteSize); + ASSERT(formatInfo.type == wgpu::TextureComponentType::Float); + + std::vector uncompressedData = {+0.0f, -0.0f, 1.0f, 1.01e-4f, + 1.0e4f, NAN, INFINITY, -INFINITY}; + std::vector textureData; + for (float value : uncompressedData) { + textureData.push_back(Float32ToFloat16(value)); + } + + DoFloatFormatSamplingTest(formatInfo, textureData, uncompressedData, 1.0e-5f); + + // Use a special expectation that knows that all Float16 NaNs are equivalent. + DoFormatRenderingTest(formatInfo, uncompressedData, textureData, + new ExpectFloat16(textureData)); + } +}; + +// Test the R8Unorm format +TEST_P(TextureFormatTest, R8Unorm) { + DoUnormTest({wgpu::TextureFormat::R8Unorm, 1, wgpu::TextureComponentType::Float, 1}); +} + +// Test the RG8Unorm format +TEST_P(TextureFormatTest, RG8Unorm) { + DoUnormTest({wgpu::TextureFormat::RG8Unorm, 2, wgpu::TextureComponentType::Float, 2}); +} + +// Test the RGBA8Unorm format +TEST_P(TextureFormatTest, RGBA8Unorm) { + DoUnormTest( + {wgpu::TextureFormat::RGBA8Unorm, 4, wgpu::TextureComponentType::Float, 4}); +} + +// Test the BGRA8Unorm format +TEST_P(TextureFormatTest, BGRA8Unorm) { + uint8_t maxValue = std::numeric_limits::max(); + std::vector textureData = {maxValue, 1, 0, maxValue}; + std::vector uncompressedData = {0.0f, 1.0f / maxValue, 1.0f, 1.0f}; + DoFormatSamplingTest({wgpu::TextureFormat::BGRA8Unorm, 4, wgpu::TextureComponentType::Float, 4}, + textureData, uncompressedData); + DoFormatRenderingTest( + {wgpu::TextureFormat::BGRA8Unorm, 4, wgpu::TextureComponentType::Float, 4}, + uncompressedData, textureData); +} + +// Test the R8Snorm format +TEST_P(TextureFormatTest, R8Snorm) { + DoSnormTest({wgpu::TextureFormat::R8Snorm, 1, wgpu::TextureComponentType::Float, 1}); +} + +// Test the RG8Snorm format +TEST_P(TextureFormatTest, RG8Snorm) { + DoSnormTest({wgpu::TextureFormat::RG8Snorm, 2, wgpu::TextureComponentType::Float, 2}); +} + +// Test the RGBA8Snorm format +TEST_P(TextureFormatTest, RGBA8Snorm) { + DoSnormTest({wgpu::TextureFormat::RGBA8Snorm, 4, wgpu::TextureComponentType::Float, 4}); +} + +// Test the R8Uint format +TEST_P(TextureFormatTest, R8Uint) { + DoUintTest({wgpu::TextureFormat::R8Uint, 1, wgpu::TextureComponentType::Uint, 1}); +} + +// Test the RG8Uint format +TEST_P(TextureFormatTest, RG8Uint) { + DoUintTest({wgpu::TextureFormat::RG8Uint, 2, wgpu::TextureComponentType::Uint, 2}); +} + +// Test the RGBA8Uint format +TEST_P(TextureFormatTest, RGBA8Uint) { + DoUintTest({wgpu::TextureFormat::RGBA8Uint, 4, wgpu::TextureComponentType::Uint, 4}); +} + +// Test the R16Uint format +TEST_P(TextureFormatTest, R16Uint) { + DoUintTest({wgpu::TextureFormat::R16Uint, 2, wgpu::TextureComponentType::Uint, 1}); +} + +// Test the RG16Uint format +TEST_P(TextureFormatTest, RG16Uint) { + DoUintTest({wgpu::TextureFormat::RG16Uint, 4, wgpu::TextureComponentType::Uint, 2}); +} + +// Test the RGBA16Uint format +TEST_P(TextureFormatTest, RGBA16Uint) { + DoUintTest({wgpu::TextureFormat::RGBA16Uint, 8, wgpu::TextureComponentType::Uint, 4}); +} + +// Test the R32Uint format +TEST_P(TextureFormatTest, R32Uint) { + DoUintTest({wgpu::TextureFormat::R32Uint, 4, wgpu::TextureComponentType::Uint, 1}); +} + +// Test the RG32Uint format +TEST_P(TextureFormatTest, RG32Uint) { + DoUintTest({wgpu::TextureFormat::RG32Uint, 8, wgpu::TextureComponentType::Uint, 2}); +} + +// Test the RGBA32Uint format +TEST_P(TextureFormatTest, RGBA32Uint) { + DoUintTest( + {wgpu::TextureFormat::RGBA32Uint, 16, wgpu::TextureComponentType::Uint, 4}); +} + +// Test the R8Sint format +TEST_P(TextureFormatTest, R8Sint) { + DoSintTest({wgpu::TextureFormat::R8Sint, 1, wgpu::TextureComponentType::Sint, 1}); +} + +// Test the RG8Sint format +TEST_P(TextureFormatTest, RG8Sint) { + DoSintTest({wgpu::TextureFormat::RG8Sint, 2, wgpu::TextureComponentType::Sint, 2}); +} + +// Test the RGBA8Sint format +TEST_P(TextureFormatTest, RGBA8Sint) { + DoSintTest({wgpu::TextureFormat::RGBA8Sint, 4, wgpu::TextureComponentType::Sint, 4}); +} + +// Test the R16Sint format +TEST_P(TextureFormatTest, R16Sint) { + DoSintTest({wgpu::TextureFormat::R16Sint, 2, wgpu::TextureComponentType::Sint, 1}); +} + +// Test the RG16Sint format +TEST_P(TextureFormatTest, RG16Sint) { + DoSintTest({wgpu::TextureFormat::RG16Sint, 4, wgpu::TextureComponentType::Sint, 2}); +} + +// Test the RGBA16Sint format +TEST_P(TextureFormatTest, RGBA16Sint) { + DoSintTest({wgpu::TextureFormat::RGBA16Sint, 8, wgpu::TextureComponentType::Sint, 4}); +} + +// Test the R32Sint format +TEST_P(TextureFormatTest, R32Sint) { + DoSintTest({wgpu::TextureFormat::R32Sint, 4, wgpu::TextureComponentType::Sint, 1}); +} + +// Test the RG32Sint format +TEST_P(TextureFormatTest, RG32Sint) { + DoSintTest({wgpu::TextureFormat::RG32Sint, 8, wgpu::TextureComponentType::Sint, 2}); +} + +// Test the RGBA32Sint format +TEST_P(TextureFormatTest, RGBA32Sint) { + DoSintTest({wgpu::TextureFormat::RGBA32Sint, 16, wgpu::TextureComponentType::Sint, 4}); +} + +// Test the R32Float format +TEST_P(TextureFormatTest, R32Float) { + DoFloat32Test({wgpu::TextureFormat::R32Float, 4, wgpu::TextureComponentType::Float, 1}); +} + +// Test the RG32Float format +TEST_P(TextureFormatTest, RG32Float) { + DoFloat32Test({wgpu::TextureFormat::RG32Float, 8, wgpu::TextureComponentType::Float, 2}); +} + +// Test the RGBA32Float format +TEST_P(TextureFormatTest, RGBA32Float) { + DoFloat32Test({wgpu::TextureFormat::RGBA32Float, 16, wgpu::TextureComponentType::Float, 4}); +} + +// Test the R16Float format +TEST_P(TextureFormatTest, R16Float) { + // TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by + // swiftshader + DAWN_SKIP_TEST_IF(IsVulkan() && IsSwiftshader()); + + DoFloat16Test({wgpu::TextureFormat::R16Float, 2, wgpu::TextureComponentType::Float, 1}); +} + +// Test the RG16Float format +TEST_P(TextureFormatTest, RG16Float) { + // TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by + // swiftshader + DAWN_SKIP_TEST_IF(IsVulkan() && IsSwiftshader()); + + DoFloat16Test({wgpu::TextureFormat::RG16Float, 4, wgpu::TextureComponentType::Float, 2}); +} + +// Test the RGBA16Float format +TEST_P(TextureFormatTest, RGBA16Float) { + // TODO(https://crbug.com/swiftshader/147) Rendering INFINITY isn't handled correctly by + // swiftshader + DAWN_SKIP_TEST_IF(IsVulkan() && IsSwiftshader()); + + DoFloat16Test({wgpu::TextureFormat::RGBA16Float, 8, wgpu::TextureComponentType::Float, 4}); +} + +// Test the RGBA8Unorm format +TEST_P(TextureFormatTest, RGBA8UnormSrgb) { + uint8_t maxValue = std::numeric_limits::max(); + std::vector textureData = {0, 1, maxValue, 64, 35, 68, 152, 168}; + + std::vector uncompressedData; + for (size_t i = 0; i < textureData.size(); i += 4) { + uncompressedData.push_back(SRGBToLinear(textureData[i + 0] / float(maxValue))); + uncompressedData.push_back(SRGBToLinear(textureData[i + 1] / float(maxValue))); + uncompressedData.push_back(SRGBToLinear(textureData[i + 2] / float(maxValue))); + // Alpha is linear for sRGB formats + uncompressedData.push_back(textureData[i + 3] / float(maxValue)); + } + + DoFloatFormatSamplingTest( + {wgpu::TextureFormat::RGBA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4}, textureData, + uncompressedData, 1.0e-3); + DoFormatRenderingTest( + {wgpu::TextureFormat::RGBA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4}, + uncompressedData, textureData); +} + +// Test the BGRA8UnormSrgb format +TEST_P(TextureFormatTest, BGRA8UnormSrgb) { + // TODO(cwallez@chromium.org): This format doesn't exist in OpenGL, emulate it using + // RGBA8UnormSrgb and swizzling / shader twiddling + DAWN_SKIP_TEST_IF(IsOpenGL()); + + uint8_t maxValue = std::numeric_limits::max(); + std::vector textureData = {0, 1, maxValue, 64, 35, 68, 152, 168}; + + std::vector uncompressedData; + for (size_t i = 0; i < textureData.size(); i += 4) { + // Note that R and B are swapped + uncompressedData.push_back(SRGBToLinear(textureData[i + 2] / float(maxValue))); + uncompressedData.push_back(SRGBToLinear(textureData[i + 1] / float(maxValue))); + uncompressedData.push_back(SRGBToLinear(textureData[i + 0] / float(maxValue))); + // Alpha is linear for sRGB formats + uncompressedData.push_back(textureData[i + 3] / float(maxValue)); + } + + DoFloatFormatSamplingTest( + {wgpu::TextureFormat::BGRA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4}, textureData, + uncompressedData, 1.0e-3); + DoFormatRenderingTest( + {wgpu::TextureFormat::BGRA8UnormSrgb, 4, wgpu::TextureComponentType::Float, 4}, + uncompressedData, textureData); +} + +// Test the RGB10A2Unorm format +TEST_P(TextureFormatTest, RGB10A2Unorm) { + auto MakeRGB10A2 = [](uint32_t r, uint32_t g, uint32_t b, uint32_t a) -> uint32_t { + ASSERT((r & 0x3FF) == r); + ASSERT((g & 0x3FF) == g); + ASSERT((b & 0x3FF) == b); + ASSERT((a & 0x3) == a); + return r | g << 10 | b << 20 | a << 30; + }; + + std::vector textureData = {MakeRGB10A2(0, 0, 0, 0), MakeRGB10A2(1023, 1023, 1023, 1), + MakeRGB10A2(243, 576, 765, 2), MakeRGB10A2(0, 0, 0, 3)}; + // clang-format off + std::vector uncompressedData = { + 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 1.0f, 1.0f, 1 / 3.0f, + 243 / 1023.0f, 576 / 1023.0f, 765 / 1023.0f, 2 / 3.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + // clang-format on + + DoFloatFormatSamplingTest( + {wgpu::TextureFormat::RGB10A2Unorm, 4, wgpu::TextureComponentType::Float, 4}, textureData, + uncompressedData, 1.0e-5); + DoFormatRenderingTest( + {wgpu::TextureFormat::RGB10A2Unorm, 4, wgpu::TextureComponentType::Float, 4}, + uncompressedData, textureData); +} + +// Test the RG11B10Float format +TEST_P(TextureFormatTest, RG11B10Float) { + constexpr uint32_t kFloat11Zero = 0; + constexpr uint32_t kFloat11Infinity = 0x7C0; + constexpr uint32_t kFloat11Nan = 0x7C1; + constexpr uint32_t kFloat11One = 0x3C0; + + constexpr uint32_t kFloat10Zero = 0; + constexpr uint32_t kFloat10Infinity = 0x3E0; + constexpr uint32_t kFloat10Nan = 0x3E1; + constexpr uint32_t kFloat10One = 0x1E0; + + auto MakeRG11B10 = [](uint32_t r, uint32_t g, uint32_t b) { + ASSERT((r & 0x7FF) == r); + ASSERT((g & 0x7FF) == g); + ASSERT((b & 0x3FF) == b); + return r | g << 11 | b << 22; + }; + + // Test each of (0, 1, INFINITY, NaN) for each component but never two with the same value at a + // time. + std::vector textureData = { + MakeRG11B10(kFloat11Zero, kFloat11Infinity, kFloat10Nan), + MakeRG11B10(kFloat11Infinity, kFloat11Nan, kFloat10One), + MakeRG11B10(kFloat11Nan, kFloat11One, kFloat10Zero), + MakeRG11B10(kFloat11One, kFloat11Zero, kFloat10Infinity), + }; + + // This is one of the only 3-channel formats, so we don't have specific testing for them. Alpha + // should slways be sampled as 1 + // clang-format off + std::vector uncompressedData = { + 0.0f, INFINITY, NAN, 1.0f, + INFINITY, NAN, 1.0f, 1.0f, + NAN, 1.0f, 0.0f, 1.0f, + 1.0f, 0.0f, INFINITY, 1.0f + }; + // clang-format on + + DoFloatFormatSamplingTest( + {wgpu::TextureFormat::RG11B10Float, 4, wgpu::TextureComponentType::Float, 4}, textureData, + uncompressedData); + // This format is not renderable. +} + +// TODO(cwallez@chromium.org): Add tests for depth-stencil formats when we know if they are copyable +// in WebGPU. + +DAWN_INSTANTIATE_TEST(TextureFormatTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/TextureSubresourceTests.cpp b/third_party/dawn/src/tests/end2end/TextureSubresourceTests.cpp new file mode 100644 index 00000000000..741e77af735 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/TextureSubresourceTests.cpp @@ -0,0 +1,202 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class TextureSubresourceTest : public DawnTest { + public: + static constexpr uint32_t kSize = 4u; + static constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::Texture CreateTexture(uint32_t mipLevelCount, + uint32_t arrayLayerCount, + wgpu::TextureUsage usage) { + wgpu::TextureDescriptor texDesc; + texDesc.dimension = wgpu::TextureDimension::e2D; + texDesc.size = {kSize, kSize, arrayLayerCount}; + texDesc.sampleCount = 1; + texDesc.mipLevelCount = mipLevelCount; + texDesc.usage = usage; + texDesc.format = kFormat; + return device.CreateTexture(&texDesc); + } + + wgpu::TextureView CreateTextureView(wgpu::Texture texture, + uint32_t baseMipLevel, + uint32_t baseArrayLayer) { + wgpu::TextureViewDescriptor viewDesc; + viewDesc.format = kFormat; + viewDesc.baseArrayLayer = baseArrayLayer; + viewDesc.arrayLayerCount = 1; + viewDesc.baseMipLevel = baseMipLevel; + viewDesc.mipLevelCount = 1; + viewDesc.dimension = wgpu::TextureViewDimension::e2D; + return texture.CreateView(&viewDesc); + } + + void DrawTriangle(const wgpu::TextureView& view) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[3] = vec2[3]( + vec2(-1.f, 1.f), vec2(-1.f, -1.f), vec2(1.f, -1.f)); + gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(1.0, 0.0, 0.0, 1.0); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cColorStates[0].format = kFormat; + + wgpu::RenderPipeline rp = device.CreateRenderPipeline(&descriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + utils::ComboRenderPassDescriptor renderPassDesc({view}); + renderPassDesc.cColorAttachments[0].clearColor = {0.0f, 0.0f, 0.0f, 1.0f}; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetPipeline(rp); + pass.Draw(3); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + void SampleAndDraw(const wgpu::TextureView& samplerView, const wgpu::TextureView& renderView) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[6] = vec2[6]( + vec2(-1.f, -1.f), vec2(1.f, 1.f), vec2(-1.f, 1.f), + vec2(-1.f, -1.f), vec2(1.f, -1.f), vec2(1.f, 1.f)); + gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (set = 0, binding = 0) uniform sampler samp; + layout (set = 0, binding = 1) uniform texture2D tex; + layout (location = 0) out vec4 fragColor; + void main() { + fragColor = texture(sampler2D(tex, samp), gl_FragCoord.xy / 4); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::TriangleStrip; + descriptor.cColorStates[0].format = kFormat; + + wgpu::SamplerDescriptor samplerDescriptor = {}; + wgpu::Sampler sampler = device.CreateSampler(&samplerDescriptor); + + wgpu::RenderPipeline rp = device.CreateRenderPipeline(&descriptor); + wgpu::BindGroupLayout bgl = rp.GetBindGroupLayout(0); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, bgl, {{0, sampler}, {1, samplerView}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + utils::ComboRenderPassDescriptor renderPassDesc({renderView}); + renderPassDesc.cColorAttachments[0].clearColor = {0.0f, 0.0f, 0.0f, 1.0f}; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetPipeline(rp); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } +}; + +// Test different mipmap levels +TEST_P(TextureSubresourceTest, MipmapLevelsTest) { + // Create a texture with 2 mipmap levels and 1 layer + wgpu::Texture texture = + CreateTexture(2, 1, + wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopySrc); + + // Create two views on different mipmap levels. + wgpu::TextureView samplerView = CreateTextureView(texture, 0, 0); + wgpu::TextureView renderView = CreateTextureView(texture, 1, 0); + + // Draw a red triangle at the bottom-left half + DrawTriangle(samplerView); + + // Sample from one subresource and draw into another subresource in the same texture + SampleAndDraw(samplerView, renderView); + + // Verify that pixel at bottom-left corner is red, while pixel at top-right corner is background + // black in render view (mip level 1). + RGBA8 topRight = RGBA8::kBlack; + RGBA8 bottomLeft = RGBA8::kRed; + EXPECT_TEXTURE_RGBA8_EQ(&topRight, texture, kSize / 2 - 1, 0, 1, 1, 1, 0); + EXPECT_TEXTURE_RGBA8_EQ(&bottomLeft, texture, 0, kSize / 2 - 1, 1, 1, 1, 0); +} + +// Test different array layers +TEST_P(TextureSubresourceTest, ArrayLayersTest) { + // Create a texture with 1 mipmap level and 2 layers + wgpu::Texture texture = + CreateTexture(1, 2, + wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopySrc); + + // Create two views on different layers + wgpu::TextureView samplerView = CreateTextureView(texture, 0, 0); + wgpu::TextureView renderView = CreateTextureView(texture, 0, 1); + + // Draw a red triangle at the bottom-left half + DrawTriangle(samplerView); + + // Sample from one subresource and draw into another subresource in the same texture + SampleAndDraw(samplerView, renderView); + + // Verify that pixel at bottom-left corner is red, while pixel at top-right corner is background + // black in render view (array layer 1). + RGBA8 topRight = RGBA8::kBlack; + RGBA8 bottomLeft = RGBA8::kRed; + EXPECT_TEXTURE_RGBA8_EQ(&topRight, texture, kSize - 1, 0, 1, 1, 0, 1); + EXPECT_TEXTURE_RGBA8_EQ(&bottomLeft, texture, 0, kSize - 1, 1, 1, 0, 1); +} + +// TODO (yunchao.he@intel.com): +// * add tests for storage texture and sampler across miplevel or +// arraylayer dimensions in the same texture +// +// * add tests for copy operation upon texture subresource if needed +// +// * add tests for clear operation upon texture subresource if needed + +DAWN_INSTANTIATE_TEST(TextureSubresourceTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/TextureViewTests.cpp b/third_party/dawn/src/tests/end2end/TextureViewTests.cpp index e2cf9eff51c..46971691796 100644 --- a/third_party/dawn/src/tests/end2end/TextureViewTests.cpp +++ b/third_party/dawn/src/tests/end2end/TextureViewTests.cpp @@ -18,27 +18,26 @@ #include "common/Constants.h" #include "common/Math.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" #include constexpr static unsigned int kRTSize = 64; -constexpr dawn::TextureFormat kDefaultFormat = dawn::TextureFormat::R8G8B8A8Unorm; +constexpr wgpu::TextureFormat kDefaultFormat = wgpu::TextureFormat::RGBA8Unorm; constexpr uint32_t kBytesPerTexel = 4; namespace { - dawn::Texture Create2DTexture(dawn::Device device, + wgpu::Texture Create2DTexture(wgpu::Device device, uint32_t width, uint32_t height, uint32_t arrayLayerCount, uint32_t mipLevelCount, - dawn::TextureUsageBit usage) { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; + wgpu::TextureUsage usage) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = width; descriptor.size.height = height; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = arrayLayerCount; + descriptor.size.depth = arrayLayerCount; descriptor.sampleCount = 1; descriptor.format = kDefaultFormat; descriptor.mipLevelCount = mipLevelCount; @@ -46,8 +45,8 @@ namespace { return device.CreateTexture(&descriptor); } - dawn::ShaderModule CreateDefaultVertexShaderModule(dawn::Device device) { - return utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule CreateDefaultVertexShaderModule(wgpu::Device device) { + return utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 layout (location = 0) out vec2 o_texCoord; void main() { @@ -71,7 +70,7 @@ namespace { } // anonymous namespace class TextureViewSamplingTest : public DawnTest { -protected: + protected: // Generates an arbitrary pixel value per-layer-per-level, used for the "actual" uploaded // textures and the "expected" results. static int GenerateTestPixelValue(uint32_t layer, uint32_t level) { @@ -83,29 +82,18 @@ class TextureViewSamplingTest : public DawnTest { mRenderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); - mBindGroupLayout = utils::MakeBindGroupLayout( - device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}, - {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture}, - }); - - dawn::FilterMode kFilterMode = dawn::FilterMode::Nearest; - dawn::AddressMode kAddressMode = dawn::AddressMode::ClampToEdge; + wgpu::FilterMode kFilterMode = wgpu::FilterMode::Nearest; + wgpu::AddressMode kAddressMode = wgpu::AddressMode::ClampToEdge; - dawn::SamplerDescriptor samplerDescriptor; + wgpu::SamplerDescriptor samplerDescriptor = {}; samplerDescriptor.minFilter = kFilterMode; samplerDescriptor.magFilter = kFilterMode; samplerDescriptor.mipmapFilter = kFilterMode; samplerDescriptor.addressModeU = kAddressMode; samplerDescriptor.addressModeV = kAddressMode; samplerDescriptor.addressModeW = kAddressMode; - samplerDescriptor.lodMinClamp = kLodMin; - samplerDescriptor.lodMaxClamp = kLodMax; - samplerDescriptor.compareFunction = dawn::CompareFunction::Never; mSampler = device.CreateSampler(&samplerDescriptor); - mPipelineLayout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - mVSModule = CreateDefaultVertexShaderModule(device); } @@ -114,12 +102,12 @@ class TextureViewSamplingTest : public DawnTest { const uint32_t textureWidthLevel0 = 1 << mipLevelCount; const uint32_t textureHeightLevel0 = 1 << mipLevelCount; - constexpr dawn::TextureUsageBit kUsage = - dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled; - mTexture = Create2DTexture( - device, textureWidthLevel0, textureHeightLevel0, arrayLayerCount, mipLevelCount, kUsage); + constexpr wgpu::TextureUsage kUsage = + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled; + mTexture = Create2DTexture(device, textureWidthLevel0, textureHeightLevel0, arrayLayerCount, + mipLevelCount, kUsage); - mDefaultTextureViewDescriptor.dimension = dawn::TextureViewDimension::e2DArray; + mDefaultTextureViewDescriptor.dimension = wgpu::TextureViewDimension::e2DArray; mDefaultTextureViewDescriptor.format = kDefaultFormat; mDefaultTextureViewDescriptor.baseMipLevel = 0; mDefaultTextureViewDescriptor.mipLevelCount = mipLevelCount; @@ -128,12 +116,12 @@ class TextureViewSamplingTest : public DawnTest { // Create a texture with pixel = (0, 0, 0, level * 10 + layer + 1) at level `level` and // layer `layer`. - static_assert((kTextureRowPitchAlignment % sizeof(RGBA8)) == 0, - "Texture row pitch alignment must be a multiple of sizeof(RGBA8)."); - constexpr uint32_t kPixelsPerRowPitch = kTextureRowPitchAlignment / sizeof(RGBA8); + static_assert((kTextureBytesPerRowAlignment % sizeof(RGBA8)) == 0, + "Texture bytes per row alignment must be a multiple of sizeof(RGBA8)."); + constexpr uint32_t kPixelsPerRowPitch = kTextureBytesPerRowAlignment / sizeof(RGBA8); ASSERT_LE(textureWidthLevel0, kPixelsPerRowPitch); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); for (uint32_t layer = 0; layer < arrayLayerCount; ++layer) { for (uint32_t level = 0; level < mipLevelCount; ++level) { const uint32_t texWidth = textureWidthLevel0 >> level; @@ -143,54 +131,50 @@ class TextureViewSamplingTest : public DawnTest { constexpr uint32_t kPaddedTexWidth = kPixelsPerRowPitch; std::vector data(kPaddedTexWidth * texHeight, RGBA8(0, 0, 0, pixelValue)); - dawn::Buffer stagingBuffer = utils::CreateBufferFromData( - device, data.data(), data.size() * sizeof(RGBA8), - dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(stagingBuffer, 0, kTextureRowPitchAlignment, 0); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(mTexture, level, layer, {0, 0, 0}); - dawn::Extent3D copySize = {texWidth, texHeight, 1}; + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), data.size() * sizeof(RGBA8), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, kTextureBytesPerRowAlignment, 0); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(mTexture, level, {0, 0, layer}); + wgpu::Extent3D copySize = {texWidth, texHeight, 1}; encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); } } - dawn::CommandBuffer copy = encoder.Finish(); + wgpu::CommandBuffer copy = encoder.Finish(); queue.Submit(1, ©); } - void Verify(const dawn::TextureView &textureView, const char* fragmentShader, int expected) { - dawn::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, { - {0, mSampler}, - {1, textureView} - }); - - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, fragmentShader); + void Verify(const wgpu::TextureView& textureView, const char* fragmentShader, int expected) { + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader); utils::ComboRenderPipelineDescriptor textureDescriptor(device); - textureDescriptor.cVertexStage.module = mVSModule; + textureDescriptor.vertexStage.module = mVSModule; textureDescriptor.cFragmentStage.module = fsModule; - textureDescriptor.layout = mPipelineLayout; - textureDescriptor.cColorStates[0]->format = mRenderPass.colorFormat; + textureDescriptor.cColorStates[0].format = mRenderPass.colorFormat; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&textureDescriptor); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&textureDescriptor); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, mSampler}, {1, textureView}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&mRenderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&mRenderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetBindGroup(0, bindGroup, 0, nullptr); - pass.Draw(6, 1, 0, 0); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); RGBA8 expectedPixel(0, 0, 0, expected); EXPECT_PIXEL_RGBA8_EQ(expectedPixel, mRenderPass.color, 0, 0); - EXPECT_PIXEL_RGBA8_EQ( - expectedPixel, mRenderPass.color, mRenderPass.width - 1, mRenderPass.height - 1); + EXPECT_PIXEL_RGBA8_EQ(expectedPixel, mRenderPass.color, mRenderPass.width - 1, + mRenderPass.height - 1); // TODO(jiawei.shao@intel.com): add tests for 3D textures once Dawn supports 3D textures } @@ -203,13 +187,13 @@ class TextureViewSamplingTest : public DawnTest { initTexture(textureArrayLayers, textureMipLevels); - dawn::TextureViewDescriptor descriptor = mDefaultTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::e2D; + wgpu::TextureViewDescriptor descriptor = mDefaultTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::e2D; descriptor.baseArrayLayer = textureViewBaseLayer; descriptor.arrayLayerCount = 1; descriptor.baseMipLevel = textureViewBaseMipLevel; descriptor.mipLevelCount = 1; - dawn::TextureView textureView = mTexture.CreateView(&descriptor); + wgpu::TextureView textureView = mTexture.CreateView(&descriptor); const char* fragmentShader = R"( #version 450 @@ -242,13 +226,13 @@ class TextureViewSamplingTest : public DawnTest { initTexture(textureArrayLayers, textureMipLevels); - dawn::TextureViewDescriptor descriptor = mDefaultTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::e2DArray; + wgpu::TextureViewDescriptor descriptor = mDefaultTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::e2DArray; descriptor.baseArrayLayer = textureViewBaseLayer; descriptor.arrayLayerCount = kTextureViewLayerCount; descriptor.baseMipLevel = textureViewBaseMipLevel; descriptor.mipLevelCount = 1; - dawn::TextureView textureView = mTexture.CreateView(&descriptor); + wgpu::TextureView textureView = mTexture.CreateView(&descriptor); const char* fragmentShader = R"( #version 450 @@ -275,13 +259,13 @@ class TextureViewSamplingTest : public DawnTest { std::string CreateFragmentShaderForCubeMapFace(uint32_t layer, bool isCubeMapArray) { // Reference: https://en.wikipedia.org/wiki/Cube_mapping const std::array kCoordsToCubeMapFace = {{ - " 1.f, tc, -sc", // Positive X - "-1.f, tc, sc", // Negative X - " sc, 1.f, -tc", // Positive Y - " sc, -1.f, tc", // Negative Y - " sc, tc, 1.f", // Positive Z - " -sc, tc, -1.f", // Negative Z - }}; + " 1.f, tc, -sc", // Positive X + "-1.f, tc, sc", // Negative X + " sc, 1.f, -tc", // Positive Y + " sc, -1.f, tc", // Negative Y + " sc, tc, 1.f", // Positive Z + " -sc, tc, -1.f", // Negative Z + }}; const std::string textureType = isCubeMapArray ? "textureCubeArray" : "textureCube"; const std::string samplerType = isCubeMapArray ? "samplerCubeArray" : "samplerCube"; @@ -292,13 +276,15 @@ class TextureViewSamplingTest : public DawnTest { stream << R"( #version 450 layout(set = 0, binding = 0) uniform sampler sampler0; - layout(set = 0, binding = 1) uniform )" << textureType << R"( texture0; + layout(set = 0, binding = 1) uniform )" + << textureType << R"( texture0; layout(location = 0) in vec2 texCoord; layout(location = 0) out vec4 fragColor; void main() { float sc = 2.f * texCoord.x - 1.f; float tc = 2.f * texCoord.y - 1.f; - fragColor = texture()" << samplerType << "(texture0, sampler0), "; + fragColor = texture()" + << samplerType << "(texture0, sampler0), "; if (isCubeMapArray) { stream << "vec4(" << coordToCubeMapFace << ", " << cubeMapArrayIndex; @@ -321,18 +307,20 @@ class TextureViewSamplingTest : public DawnTest { ASSERT_TRUE((textureViewLayerCount == 6) || (isCubeMapArray && textureViewLayerCount % 6 == 0)); + wgpu::TextureViewDimension dimension = (isCubeMapArray) + ? wgpu::TextureViewDimension::CubeArray + : wgpu::TextureViewDimension::Cube; - dawn::TextureViewDescriptor descriptor = mDefaultTextureViewDescriptor; - descriptor.dimension = (isCubeMapArray) ? - dawn::TextureViewDimension::CubeArray : dawn::TextureViewDimension::Cube; + wgpu::TextureViewDescriptor descriptor = mDefaultTextureViewDescriptor; + descriptor.dimension = dimension; descriptor.baseArrayLayer = textureViewBaseLayer; descriptor.arrayLayerCount = textureViewLayerCount; - dawn::TextureView cubeMapTextureView = mTexture.CreateView(&descriptor); + wgpu::TextureView cubeMapTextureView = mTexture.CreateView(&descriptor); // Check the data in the every face of the cube map (array) texture view. for (uint32_t layer = 0; layer < textureViewLayerCount; ++layer) { - const std::string &fragmentShader = + const std::string& fragmentShader = CreateFragmentShaderForCubeMapFace(layer, isCubeMapArray); int expected = GenerateTestPixelValue(textureViewBaseLayer + layer, 0); @@ -340,12 +328,10 @@ class TextureViewSamplingTest : public DawnTest { } } - dawn::BindGroupLayout mBindGroupLayout; - dawn::PipelineLayout mPipelineLayout; - dawn::Sampler mSampler; - dawn::Texture mTexture; - dawn::TextureViewDescriptor mDefaultTextureViewDescriptor; - dawn::ShaderModule mVSModule; + wgpu::Sampler mSampler; + wgpu::Texture mTexture; + wgpu::TextureViewDescriptor mDefaultTextureViewDescriptor; + wgpu::ShaderModule mVSModule; utils::BasicRenderPass mRenderPass; }; @@ -358,7 +344,7 @@ TEST_P(TextureViewSamplingTest, Default2DArrayTexture) { constexpr uint32_t kMipLevels = 1; initTexture(kLayers, kMipLevels); - dawn::TextureView textureView = mTexture.CreateDefaultView(); + wgpu::TextureView textureView = mTexture.CreateView(); const char* fragmentShader = R"( #version 450 @@ -375,8 +361,8 @@ TEST_P(TextureViewSamplingTest, Default2DArrayTexture) { } )"; - const int expected = GenerateTestPixelValue(0, 0) + GenerateTestPixelValue(1, 0) + - GenerateTestPixelValue(2, 0); + const int expected = + GenerateTestPixelValue(0, 0) + GenerateTestPixelValue(1, 0) + GenerateTestPixelValue(2, 0); Verify(textureView, fragmentShader, expected); } @@ -440,7 +426,8 @@ TEST_P(TextureViewSamplingTest, TextureCubeMapArrayViewOnPartOfTexture) { TextureCubeMapTest(20, 3, 12, true); } -// Test sampling from a cube map texture array view that covers the last layer of a 2D array texture. +// Test sampling from a cube map texture array view that covers the last layer of a 2D array +// texture. TEST_P(TextureViewSamplingTest, TextureCubeMapArrayViewCoveringLastLayer) { // Test failing on the GPU FYI Mac Pro (AMD), see // https://bugs.chromium.org/p/dawn/issues/detail?id=58 @@ -462,37 +449,37 @@ TEST_P(TextureViewSamplingTest, TextureCubeMapArrayViewSingleCubeMap) { class TextureViewRenderingTest : public DawnTest { protected: - void TextureLayerAsColorAttachmentTest(dawn::TextureViewDimension dimension, + void TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension dimension, uint32_t layerCount, uint32_t levelCount, uint32_t textureViewBaseLayer, uint32_t textureViewBaseLevel) { - ASSERT(dimension == dawn::TextureViewDimension::e2D || - dimension == dawn::TextureViewDimension::e2DArray); + ASSERT(dimension == wgpu::TextureViewDimension::e2D || + dimension == wgpu::TextureViewDimension::e2DArray); ASSERT_LT(textureViewBaseLayer, layerCount); ASSERT_LT(textureViewBaseLevel, levelCount); const uint32_t textureWidthLevel0 = 1 << levelCount; const uint32_t textureHeightLevel0 = 1 << levelCount; - constexpr dawn::TextureUsageBit kUsage = dawn::TextureUsageBit::OutputAttachment | - dawn::TextureUsageBit::TransferSrc; - dawn::Texture texture = Create2DTexture( - device, textureWidthLevel0, textureHeightLevel0, layerCount, levelCount, kUsage); + constexpr wgpu::TextureUsage kUsage = + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = Create2DTexture(device, textureWidthLevel0, textureHeightLevel0, + layerCount, levelCount, kUsage); - dawn::TextureViewDescriptor descriptor; + wgpu::TextureViewDescriptor descriptor; descriptor.format = kDefaultFormat; descriptor.dimension = dimension; descriptor.baseArrayLayer = textureViewBaseLayer; descriptor.arrayLayerCount = 1; descriptor.baseMipLevel = textureViewBaseLevel; descriptor.mipLevelCount = 1; - dawn::TextureView textureView = texture.CreateView(&descriptor); + wgpu::TextureView textureView = texture.CreateView(&descriptor); - dawn::ShaderModule vsModule = CreateDefaultVertexShaderModule(device); + wgpu::ShaderModule vsModule = CreateDefaultVertexShaderModule(device); // Clear textureView with Red(255, 0, 0, 255) and render Green(0, 255, 0, 255) into it utils::ComboRenderPassDescriptor renderPassInfo({textureView}); - renderPassInfo.cColorAttachmentsInfoPtr[0]->clearColor = {1.0f, 0.0f, 0.0f, 1.0f}; + renderPassInfo.cColorAttachments[0].clearColor = {1.0f, 0.0f, 0.0f, 1.0f}; const char* oneColorFragmentShader = R"( #version 450 @@ -502,38 +489,38 @@ class TextureViewRenderingTest : public DawnTest { fragColor = vec4(0.0, 1.0, 0.0, 1.0); } )"; - dawn::ShaderModule oneColorFsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, oneColorFragmentShader); + wgpu::ShaderModule oneColorFsModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Fragment, oneColorFragmentShader); utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); - pipelineDescriptor.cVertexStage.module = vsModule; + pipelineDescriptor.vertexStage.module = vsModule; pipelineDescriptor.cFragmentStage.module = oneColorFsModule; - pipelineDescriptor.cColorStates[0]->format = kDefaultFormat; + pipelineDescriptor.cColorStates[0].format = kDefaultFormat; - dawn::RenderPipeline oneColorPipeline = device.CreateRenderPipeline(&pipelineDescriptor); + wgpu::RenderPipeline oneColorPipeline = device.CreateRenderPipeline(&pipelineDescriptor); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassInfo); pass.SetPipeline(oneColorPipeline); - pass.Draw(6, 1, 0, 0); + pass.Draw(6); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); // Check if the right pixels (Green) have been written into the right part of the texture. uint32_t textureViewWidth = textureWidthLevel0 >> textureViewBaseLevel; uint32_t textureViewHeight = textureHeightLevel0 >> textureViewBaseLevel; - uint32_t rowPitch = Align(kBytesPerTexel * textureWidthLevel0, kTextureRowPitchAlignment); + uint32_t bytesPerRow = + Align(kBytesPerTexel * textureWidthLevel0, kTextureBytesPerRowAlignment); uint32_t expectedDataSize = - rowPitch / kBytesPerTexel * (textureWidthLevel0 - 1) + textureHeightLevel0; + bytesPerRow / kBytesPerTexel * (textureWidthLevel0 - 1) + textureHeightLevel0; constexpr RGBA8 kExpectedPixel(0, 255, 0, 255); std::vector expected(expectedDataSize, kExpectedPixel); - EXPECT_TEXTURE_RGBA8_EQ( - expected.data(), texture, 0, 0, textureViewWidth, textureViewHeight, - textureViewBaseLevel, textureViewBaseLayer); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, textureViewWidth, textureViewHeight, + textureViewBaseLevel, textureViewBaseLayer); } }; @@ -546,15 +533,15 @@ TEST_P(TextureViewRenderingTest, Texture2DViewOnALevelOf2DTextureAsColorAttachme // Rendering into the first level { constexpr uint32_t kBaseLevel = 0; - TextureLayerAsColorAttachmentTest( - dawn::TextureViewDimension::e2D, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension::e2D, kLayers, kMipLevels, + kBaseLayer, kBaseLevel); } // Rendering into the last level { constexpr uint32_t kBaseLevel = kMipLevels - 1; - TextureLayerAsColorAttachmentTest( - dawn::TextureViewDimension::e2D, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension::e2D, kLayers, kMipLevels, + kBaseLayer, kBaseLevel); } } @@ -567,17 +554,16 @@ TEST_P(TextureViewRenderingTest, Texture2DViewOnALayerOf2DArrayTextureAsColorAtt // Rendering into the first layer { constexpr uint32_t kBaseLayer = 0; - TextureLayerAsColorAttachmentTest( - dawn::TextureViewDimension::e2D, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension::e2D, kLayers, kMipLevels, + kBaseLayer, kBaseLevel); } // Rendering into the last layer { constexpr uint32_t kBaseLayer = kLayers - 1; - TextureLayerAsColorAttachmentTest( - dawn::TextureViewDimension::e2D, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension::e2D, kLayers, kMipLevels, + kBaseLayer, kBaseLevel); } - } // Test rendering into a 1-layer 2D array texture view created on a mipmap level of a 2D texture. @@ -589,15 +575,15 @@ TEST_P(TextureViewRenderingTest, Texture2DArrayViewOnALevelOf2DTextureAsColorAtt // Rendering into the first level { constexpr uint32_t kBaseLevel = 0; - TextureLayerAsColorAttachmentTest( - dawn::TextureViewDimension::e2DArray, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension::e2DArray, kLayers, kMipLevels, + kBaseLayer, kBaseLevel); } // Rendering into the last level { constexpr uint32_t kBaseLevel = kMipLevels - 1; - TextureLayerAsColorAttachmentTest( - dawn::TextureViewDimension::e2DArray, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension::e2DArray, kLayers, kMipLevels, + kBaseLayer, kBaseLevel); } } @@ -610,18 +596,46 @@ TEST_P(TextureViewRenderingTest, Texture2DArrayViewOnALayerOf2DArrayTextureAsCol // Rendering into the first layer { constexpr uint32_t kBaseLayer = 0; - TextureLayerAsColorAttachmentTest( - dawn::TextureViewDimension::e2DArray, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension::e2DArray, kLayers, kMipLevels, + kBaseLayer, kBaseLevel); } // Rendering into the last layer { constexpr uint32_t kBaseLayer = kLayers - 1; - TextureLayerAsColorAttachmentTest( - dawn::TextureViewDimension::e2DArray, kLayers, kMipLevels, kBaseLayer, kBaseLevel); + TextureLayerAsColorAttachmentTest(wgpu::TextureViewDimension::e2DArray, kLayers, kMipLevels, + kBaseLayer, kBaseLevel); } } -DAWN_INSTANTIATE_TEST(TextureViewSamplingTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(TextureViewSamplingTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +DAWN_INSTANTIATE_TEST(TextureViewRenderingTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +class TextureViewTest : public DawnTest {}; + +// This is a regression test for crbug.com/dawn/399 where creating a texture view with only copy +// usage would cause the Vulkan validation layers to warn +TEST_P(TextureViewTest, OnlyCopySrcDst) { + wgpu::TextureDescriptor descriptor; + descriptor.size = {4, 4, 1}; + descriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::Texture texture = device.CreateTexture(&descriptor); + wgpu::TextureView view = texture.CreateView(); +} -DAWN_INSTANTIATE_TEST(TextureViewRenderingTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(TextureViewTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/TextureZeroInitTests.cpp b/third_party/dawn/src/tests/end2end/TextureZeroInitTests.cpp index 3419553f806..41bb49b8771 100644 --- a/third_party/dawn/src/tests/end2end/TextureZeroInitTests.cpp +++ b/third_party/dawn/src/tests/end2end/TextureZeroInitTests.cpp @@ -1,260 +1,1402 @@ -// Copyright 2019 The Dawn Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "tests/DawnTest.h" - -#include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" - -class TextureZeroInitTest : public DawnTest { - protected: - void SetUp() override { - DawnTest::SetUp(); - } - dawn::TextureDescriptor CreateTextureDescriptor(uint32_t mipLevelCount, - uint32_t arrayLayerCount, - dawn::TextureUsageBit usage) { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = kSize; - descriptor.size.height = kSize; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = arrayLayerCount; - descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.mipLevelCount = mipLevelCount; - descriptor.usage = usage; - return descriptor; - } - dawn::TextureViewDescriptor CreateTextureViewDescriptor(uint32_t baseMipLevel, - uint32_t baseArrayLayer) { - dawn::TextureViewDescriptor descriptor; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.baseArrayLayer = baseArrayLayer; - descriptor.arrayLayerCount = 1; - descriptor.baseMipLevel = baseMipLevel; - descriptor.mipLevelCount = 1; - descriptor.dimension = dawn::TextureViewDimension::e2D; - return descriptor; - } - constexpr static uint32_t kSize = 128; -}; - -// This tests that the code path of CopyTextureToBuffer clears correctly to Zero after first usage -TEST_P(TextureZeroInitTest, RecycleTextureMemoryClear) { - dawn::TextureDescriptor descriptor = CreateTextureDescriptor( - 1, 1, dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc); - dawn::Texture texture = device.CreateTexture(&descriptor); - - // Texture's first usage is in EXPECT_PIXEL_RGBA8_EQ's call to CopyTextureToBuffer - RGBA8 filledWithZeros(0, 0, 0, 0); - EXPECT_PIXEL_RGBA8_EQ(filledWithZeros, texture, 0, 0); -} - -// Test that non-zero mip level clears subresource to Zero after first use -// This goes through the BeginRenderPass's code path -TEST_P(TextureZeroInitTest, MipMapClearsToZero) { - dawn::TextureDescriptor descriptor = CreateTextureDescriptor( - 4, 1, dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc); - dawn::Texture texture = device.CreateTexture(&descriptor); - - dawn::TextureViewDescriptor viewDescriptor = CreateTextureViewDescriptor(2, 0); - dawn::TextureView view = texture.CreateView(&viewDescriptor); - - utils::BasicRenderPass renderPass = - utils::BasicRenderPass(kSize, kSize, texture, dawn::TextureFormat::R8G8B8A8Unorm); - - renderPass.renderPassInfo.cColorAttachmentsInfoPtr[0]->attachment = view; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - { - // Texture's first usage is in BeginRenderPass's call to RecordRenderPass - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); - pass.EndPass(); - } - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - uint32_t mipSize = kSize >> 2; - std::vector expected(mipSize * mipSize, {0, 0, 0, 0}); - - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, mipSize, mipSize, 2, 0); -} - -// Test that non-zero array layers clears subresource to Zero after first use. -// This goes through the BeginRenderPass's code path -TEST_P(TextureZeroInitTest, ArrayLayerClearsToZero) { - dawn::TextureDescriptor descriptor = CreateTextureDescriptor( - 1, 4, dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferSrc); - dawn::Texture texture = device.CreateTexture(&descriptor); - - dawn::TextureViewDescriptor viewDescriptor = CreateTextureViewDescriptor(0, 2); - dawn::TextureView view = texture.CreateView(&viewDescriptor); - - utils::BasicRenderPass renderPass = - utils::BasicRenderPass(kSize, kSize, texture, dawn::TextureFormat::R8G8B8A8Unorm); - - renderPass.renderPassInfo.cColorAttachmentsInfoPtr[0]->attachment = view; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); - pass.EndPass(); - } - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - std::vector expected(kSize * kSize, {0, 0, 0, 0}); - - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, kSize, kSize, 0, 2); -} - -// This tests CopyBufferToTexture fully overwrites copy so lazy init is not needed. -// TODO(natlee@microsoft.com): Add backdoor to dawn native to query the number of zero-inited -// subresources -TEST_P(TextureZeroInitTest, CopyBufferToTexture) { - dawn::TextureDescriptor descriptor = CreateTextureDescriptor( - 4, 1, - dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled | - dawn::TextureUsageBit::TransferSrc); - dawn::Texture texture = device.CreateTexture(&descriptor); - - std::vector data(4 * kSize * kSize, 100); - dawn::Buffer stagingBuffer = utils::CreateBufferFromData( - device, data.data(), static_cast(data.size()), dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); - dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); - dawn::Extent3D copySize = {kSize, kSize, 1}; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - std::vector expected(kSize * kSize, {100, 100, 100, 100}); - - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 0); -} - -// Test for a copy only to a subset of the subresource, lazy init is necessary to clear the other -// half. -TEST_P(TextureZeroInitTest, CopyBufferToTextureHalf) { - dawn::TextureDescriptor descriptor = CreateTextureDescriptor( - 4, 1, - dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::Sampled | - dawn::TextureUsageBit::TransferSrc); - dawn::Texture texture = device.CreateTexture(&descriptor); - - std::vector data(4 * kSize * kSize, 100); - dawn::Buffer stagingBuffer = utils::CreateBufferFromData( - device, data.data(), static_cast(data.size()), dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); - dawn::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, 0, {0, 0, 0}); - dawn::Extent3D copySize = {kSize / 2, kSize, 1}; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - std::vector expected100((kSize / 2) * kSize, {100, 100, 100, 100}); - std::vector expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0}); - // first half filled with 100, by the buffer data - EXPECT_TEXTURE_RGBA8_EQ(expected100.data(), texture, 0, 0, kSize / 2, kSize, 0, 0); - // second half should be cleared - EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kSize / 2, 0, kSize / 2, kSize, 0, 0); -} - -// This tests CopyTextureToTexture fully overwrites copy so lazy init is not needed. -TEST_P(TextureZeroInitTest, CopyTextureToTexture) { - dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor( - 1, 1, dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::TransferSrc); - dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); - - dawn::TextureCopyView srcTextureCopyView = - utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); - - dawn::TextureDescriptor dstDescriptor = CreateTextureDescriptor( - 1, 1, - dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferDst | - dawn::TextureUsageBit::TransferSrc); - dawn::Texture dstTexture = device.CreateTexture(&dstDescriptor); - - dawn::TextureCopyView dstTextureCopyView = - utils::CreateTextureCopyView(dstTexture, 0, 0, {0, 0, 0}); - - dawn::Extent3D copySize = {kSize, kSize, 1}; - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - std::vector expected(kSize * kSize, {0, 0, 0, 0}); - - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); - EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, 0, 0, kSize, kSize, 0, 0); -} - -// This Tests the CopyTextureToTexture's copy only to a subset of the subresource, lazy init is -// necessary to clear the other half. -TEST_P(TextureZeroInitTest, CopyTextureToTextureHalf) { - dawn::TextureDescriptor srcDescriptor = CreateTextureDescriptor( - 1, 1, - dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::TransferSrc | - dawn::TextureUsageBit::TransferDst); - dawn::Texture srcTexture = device.CreateTexture(&srcDescriptor); - - // fill srcTexture with 100 - { - std::vector data(4 * kSize * kSize, 100); - dawn::Buffer stagingBuffer = - utils::CreateBufferFromData(device, data.data(), static_cast(data.size()), - dawn::BufferUsageBit::TransferSrc); - dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(stagingBuffer, 0, 0, 0); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); - dawn::Extent3D copySize = {kSize, kSize, 1}; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - } - - dawn::TextureCopyView srcTextureCopyView = - utils::CreateTextureCopyView(srcTexture, 0, 0, {0, 0, 0}); - - dawn::TextureDescriptor dstDescriptor = CreateTextureDescriptor( - 1, 1, - dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::TransferDst | - dawn::TextureUsageBit::TransferSrc); - dawn::Texture dstTexture = device.CreateTexture(&dstDescriptor); - - dawn::TextureCopyView dstTextureCopyView = - utils::CreateTextureCopyView(dstTexture, 0, 0, {0, 0, 0}); - dawn::Extent3D copySize = {kSize / 2, kSize, 1}; - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); - dawn::CommandBuffer commands = encoder.Finish(); - queue.Submit(1, &commands); - - std::vector expectedWithZeros((kSize / 2) * kSize, {0, 0, 0, 0}); - std::vector expectedWith100(kSize * kSize, {100, 100, 100, 100}); - - EXPECT_TEXTURE_RGBA8_EQ(expectedWith100.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); - EXPECT_TEXTURE_RGBA8_EQ(expectedWith100.data(), dstTexture, 0, 0, kSize / 2, kSize, 0, 0); - EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), dstTexture, kSize / 2, 0, kSize / 2, kSize, 0, - 0); -} - -DAWN_INSTANTIATE_TEST(TextureZeroInitTest, - ForceWorkarounds(VulkanBackend, - {"nonzero_clear_resources_on_creation_for_testing"})); +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Math.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +#define EXPECT_LAZY_CLEAR(N, statement) \ + do { \ + if (UsesWire()) { \ + statement; \ + } else { \ + size_t lazyClearsBefore = dawn_native::GetLazyClearCountForTesting(device.Get()); \ + statement; \ + size_t lazyClearsAfter = dawn_native::GetLazyClearCountForTesting(device.Get()); \ + EXPECT_EQ(N, lazyClearsAfter - lazyClearsBefore); \ + } \ + } while (0) + +class TextureZeroInitTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + } + wgpu::TextureDescriptor CreateTextureDescriptor(uint32_t mipLevelCount, + uint32_t arrayLayerCount, + wgpu::TextureUsage usage, + wgpu::TextureFormat format) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kSize; + descriptor.size.height = kSize; + descriptor.size.depth = arrayLayerCount; + descriptor.sampleCount = 1; + descriptor.format = format; + descriptor.mipLevelCount = mipLevelCount; + descriptor.usage = usage; + return descriptor; + } + wgpu::TextureViewDescriptor CreateTextureViewDescriptor(uint32_t baseMipLevel, + uint32_t baseArrayLayer) { + wgpu::TextureViewDescriptor descriptor; + descriptor.format = kColorFormat; + descriptor.baseArrayLayer = baseArrayLayer; + descriptor.arrayLayerCount = 1; + descriptor.baseMipLevel = baseMipLevel; + descriptor.mipLevelCount = 1; + descriptor.dimension = wgpu::TextureViewDimension::e2D; + return descriptor; + } + wgpu::RenderPipeline CreatePipelineForTest() { + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest(); + const char* fs = + R"(#version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(1.0, 0.0, 0.0, 1.0); + })"; + pipelineDescriptor.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fs); + + pipelineDescriptor.cDepthStencilState.depthCompare = wgpu::CompareFunction::Equal; + pipelineDescriptor.cDepthStencilState.stencilFront.compare = wgpu::CompareFunction::Equal; + pipelineDescriptor.depthStencilState = &pipelineDescriptor.cDepthStencilState; + + return device.CreateRenderPipeline(&pipelineDescriptor); + } + wgpu::ShaderModule CreateBasicVertexShaderForTest() { + return utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"(#version 450 + const vec2 pos[6] = vec2[6](vec2(-1.0f, -1.0f), + vec2(-1.0f, 1.0f), + vec2( 1.0f, -1.0f), + vec2( 1.0f, 1.0f), + vec2(-1.0f, 1.0f), + vec2( 1.0f, -1.0f) + ); + + void main() { + gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0); + })"); + } + wgpu::ShaderModule CreateSampledTextureFragmentShaderForTest() { + return utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, + R"(#version 450 + layout(set = 0, binding = 0) uniform sampler sampler0; + layout(set = 0, binding = 1) uniform texture2D texture0; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = texelFetch(sampler2D(texture0, sampler0), ivec2(gl_FragCoord), 0); + })"); + } + constexpr static uint32_t kSize = 128; + constexpr static uint32_t kUnalignedSize = 127; + // All three texture formats used (RGBA8Unorm, Depth24PlusStencil8, and RGBA8Snorm) have the + // same byte size of 4. + constexpr static uint32_t kFormatBlockByteSize = 4; + constexpr static wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; + constexpr static wgpu::TextureFormat kDepthStencilFormat = + wgpu::TextureFormat::Depth24PlusStencil8; + constexpr static wgpu::TextureFormat kNonrenderableColorFormat = + wgpu::TextureFormat::RGBA8Snorm; +}; + +// This tests that the code path of CopyTextureToBuffer clears correctly to Zero after first usage +TEST_P(TextureZeroInitTest, CopyTextureToBufferSource) { + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + // Texture's first usage is in EXPECT_PIXEL_RGBA8_EQ's call to CopyTextureToBuffer + RGBA8 filledWithZeros(0, 0, 0, 0); + EXPECT_LAZY_CLEAR(1u, EXPECT_PIXEL_RGBA8_EQ(filledWithZeros, texture, 0, 0)); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); +} + +// This tests that the code path of CopyTextureToBuffer with multiple texture array layers clears +// correctly to Zero after first usage +TEST_P(TextureZeroInitTest, CopyMultipleTextureArrayLayersToBufferSource) { + constexpr uint32_t kArrayLayers = 6u; + + const wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, kArrayLayers, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + const uint32_t bytesPerRow = utils::GetMinimumBytesPerRow(kColorFormat, kSize); + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + bufferDescriptor.size = + utils::GetBytesInBufferTextureCopy(kColorFormat, kSize, bytesPerRow, kSize, kArrayLayers); + wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor); + + const wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(buffer, 0, bytesPerRow, 0); + const wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + const wgpu::Extent3D copySize = {kSize, kSize, kArrayLayers}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + + // Expect texture to be lazy initialized. + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commandBuffer)); + + // Expect texture subresource initialized to be true + EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, kArrayLayers)); + + const std::vector kExpectedAllZero(kSize * kSize, {0, 0, 0, 0}); + for (uint32_t layer = 0; layer < kArrayLayers; ++layer) { + EXPECT_TEXTURE_RGBA8_EQ(kExpectedAllZero.data(), texture, 0, 0, kSize, kSize, 0, layer); + } +} + +// Test that non-zero mip level clears subresource to Zero after first use +// This goes through the BeginRenderPass's code path +TEST_P(TextureZeroInitTest, RenderingMipMapClearsToZero) { + uint32_t baseMipLevel = 2; + uint32_t levelCount = 4; + uint32_t baseArrayLayer = 0; + uint32_t layerCount = 1; + + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + levelCount, layerCount, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::TextureViewDescriptor viewDescriptor = + CreateTextureViewDescriptor(baseMipLevel, baseArrayLayer); + wgpu::TextureView view = texture.CreateView(&viewDescriptor); + + utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); + + // Specify loadOp Load. Clear should be used to zero-initialize. + renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; + // Specify non-zero clear color. It should still be cleared to zero. + renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f}; + renderPass.renderPassInfo.cColorAttachments[0].attachment = view; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + // Texture's first usage is in BeginRenderPass's call to RecordRenderPass + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.EndPass(); + } + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + uint32_t mipSize = kSize >> 2; + std::vector expected(mipSize * mipSize, {0, 0, 0, 0}); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, mipSize, mipSize, baseMipLevel, + baseArrayLayer); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + renderPass.color.Get(), baseMipLevel, 1, baseArrayLayer, 1)); +} + +// Test that non-zero array layers clears subresource to Zero after first use. +// This goes through the BeginRenderPass's code path +TEST_P(TextureZeroInitTest, RenderingArrayLayerClearsToZero) { + uint32_t baseMipLevel = 0; + uint32_t levelCount = 1; + uint32_t baseArrayLayer = 2; + uint32_t layerCount = 4; + + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + levelCount, layerCount, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::TextureViewDescriptor viewDescriptor = + CreateTextureViewDescriptor(baseMipLevel, baseArrayLayer); + wgpu::TextureView view = texture.CreateView(&viewDescriptor); + + utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); + + // Specify loadOp Load. Clear should be used to zero-initialize. + renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; + // Specify non-zero clear color. It should still be cleared to zero. + renderPass.renderPassInfo.cColorAttachments[0].clearColor = {0.5f, 0.5f, 0.5f, 0.5f}; + renderPass.renderPassInfo.cColorAttachments[0].attachment = view; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.EndPass(); + } + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + std::vector expected(kSize * kSize, {0, 0, 0, 0}); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, kSize, kSize, baseMipLevel, + baseArrayLayer); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized( + renderPass.color.Get(), baseMipLevel, 1, baseArrayLayer, 1)); +} + +// This tests CopyBufferToTexture fully overwrites copy so lazy init is not needed. +TEST_P(TextureZeroInitTest, CopyBufferToTexture) { + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 1, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + std::vector data(kFormatBlockByteSize * kSize * kSize, 100); + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, kSize * sizeof(uint32_t), 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + std::vector expected(kSize * kSize, {100, 100, 100, 100}); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), texture, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); +} + +// Test for a copy only to a subset of the subresource, lazy init is necessary to clear the other +// half. +TEST_P(TextureZeroInitTest, CopyBufferToTextureHalf) { + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 1, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + std::vector data(kFormatBlockByteSize * kSize * kSize, 100); + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, kSize * sizeof(uint16_t), 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize / 2, kSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + std::vector expected100((kSize / 2) * kSize, {100, 100, 100, 100}); + std::vector expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0}); + // first half filled with 100, by the buffer data + EXPECT_TEXTURE_RGBA8_EQ(expected100.data(), texture, 0, 0, kSize / 2, kSize, 0, 0); + // second half should be cleared + EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kSize / 2, 0, kSize / 2, kSize, 0, 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); +} + +// This tests CopyBufferToTexture fully overwrites a range of subresources, so lazy initialization +// is needed for neither the subresources involved in the copy nor the other subresources. +TEST_P(TextureZeroInitTest, CopyBufferToTextureMultipleArrayLayers) { + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, 6, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + constexpr uint32_t kBaseArrayLayer = 2u; + constexpr uint32_t kCopyLayerCount = 3u; + std::vector data(kFormatBlockByteSize * kSize * kSize * kCopyLayerCount, 100); + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + + const wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, kSize * kFormatBlockByteSize, 0); + const wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, 0, {0, 0, kBaseArrayLayer}); + const wgpu::Extent3D copySize = {kSize, kSize, kCopyLayerCount}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + + // The copy overwrites the whole subresources so we don't need to do lazy initialization on + // them. + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + // Expect texture subresource initialized to be true + EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, kBaseArrayLayer, + kCopyLayerCount)); + + const std::vector expected100(kSize * kSize, {100, 100, 100, 100}); + for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) { + EXPECT_TEXTURE_RGBA8_EQ(expected100.data(), texture, 0, 0, kSize, kSize, 0, layer); + } +} + +// This tests CopyTextureToTexture fully overwrites copy so lazy init is not needed. +TEST_P(TextureZeroInitTest, CopyTextureToTexture) { + wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, kColorFormat); + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + wgpu::TextureCopyView srcTextureCopyView = + utils::CreateTextureCopyView(srcTexture, 0, {0, 0, 0}); + + wgpu::TextureDescriptor dstDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor); + + wgpu::TextureCopyView dstTextureCopyView = + utils::CreateTextureCopyView(dstTexture, 0, {0, 0, 0}); + + wgpu::Extent3D copySize = {kSize, kSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + std::vector expected(kSize * kSize, {0, 0, 0, 0}); + + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), dstTexture, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(dstTexture.Get(), 0, 1, 0, 1)); +} + +// This Tests the CopyTextureToTexture's copy only to a subset of the subresource, lazy init is +// necessary to clear the other half. +TEST_P(TextureZeroInitTest, CopyTextureToTextureHalf) { + wgpu::TextureDescriptor srcDescriptor = CreateTextureDescriptor( + 1, 1, + wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst, + kColorFormat); + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + // fill srcTexture with 100 + { + std::vector data(kFormatBlockByteSize * kSize * kSize, 100); + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, kSize * kFormatBlockByteSize, 0); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(srcTexture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + wgpu::TextureCopyView srcTextureCopyView = + utils::CreateTextureCopyView(srcTexture, 0, {0, 0, 0}); + + wgpu::TextureDescriptor dstDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture dstTexture = device.CreateTexture(&dstDescriptor); + + wgpu::TextureCopyView dstTextureCopyView = + utils::CreateTextureCopyView(dstTexture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize / 2, kSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + std::vector expectedWithZeros((kSize / 2) * kSize, {0, 0, 0, 0}); + std::vector expectedWith100(kSize * kSize, {100, 100, 100, 100}); + + EXPECT_TEXTURE_RGBA8_EQ(expectedWith100.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectedWith100.data(), dstTexture, 0, 0, kSize / 2, kSize, 0, 0); + EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), dstTexture, kSize / 2, 0, kSize / 2, kSize, 0, + 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(dstTexture.Get(), 0, 1, 0, 1)); +} + +// This tests the texture with depth attachment and load op load will init depth stencil texture to +// 0s. +TEST_P(TextureZeroInitTest, RenderingLoadingDepth) { + wgpu::TextureDescriptor srcDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::OutputAttachment, + kColorFormat); + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, + kDepthStencilFormat); + wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); + + utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + // Set clearDepth to non-zero. It should still be cleared to 0 by the loadOp. + renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.5f; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; + renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 0; + renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.SetPipeline(CreatePipelineForTest()); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + // Expect 0 lazy clears, depth stencil texture will clear using loadop + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + // Expect the texture to be red because depth test passed. + std::vector expected(kSize * kSize, {255, 0, 0, 255}); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); +} + +// This tests the texture with stencil attachment and load op load will init depth stencil texture +// to 0s. +TEST_P(TextureZeroInitTest, RenderingLoadingStencil) { + wgpu::TextureDescriptor srcDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::OutputAttachment, + kColorFormat); + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, + kDepthStencilFormat); + wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); + + utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; + renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 0.0f; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + // Set clearStencil to non-zero. It should still be cleared to 0 by the loadOp. + renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 2; + renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.SetPipeline(CreatePipelineForTest()); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + // Expect 0 lazy clears, depth stencil texture will clear using loadop + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + // Expect the texture to be red because stencil test passed. + std::vector expected(kSize * kSize, {255, 0, 0, 255}); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); +} + +// This tests the texture with depth stencil attachment and load op load will init depth stencil +// texture to 0s. +TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencil) { + wgpu::TextureDescriptor srcDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::OutputAttachment, + kColorFormat); + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + wgpu::TextureDescriptor depthStencilDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, + kDepthStencilFormat); + wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); + + utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.SetPipeline(CreatePipelineForTest()); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + // Expect 0 lazy clears, depth stencil texture will clear using loadop + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + // Expect the texture to be red because both depth and stencil tests passed. + std::vector expected(kSize * kSize, {255, 0, 0, 255}); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(srcTexture.Get(), 0, 1, 0, 1)); +} + +// This tests the color attachments clear to 0s +TEST_P(TextureZeroInitTest, ColorAttachmentsClear) { + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc, kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + utils::BasicRenderPass renderPass = utils::BasicRenderPass(kSize, kSize, texture, kColorFormat); + renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + std::vector expected(kSize * kSize, {0, 0, 0, 0}); + EXPECT_TEXTURE_RGBA8_EQ(expected.data(), renderPass.color, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, + dawn_native::IsTextureSubresourceInitialized(renderPass.color.Get(), 0, 1, 0, 1)); +} + +// This tests the clearing of sampled textures in render pass +TEST_P(TextureZeroInitTest, RenderPassSampledTextureClear) { + // Create needed resources + wgpu::TextureDescriptor descriptor = + CreateTextureDescriptor(1, 1, wgpu::TextureUsage::Sampled, kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat); + wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); + + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + // Create render pipeline + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + renderPipelineDescriptor.cColorStates[0].format = kColorFormat; + renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest(); + renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest(); + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); + + // Create bindgroup + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, texture.CreateView()}}); + + // Encode pass and submit + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); + renderPassDesc.cColorAttachments[0].clearColor = {1.0, 1.0, 1.0, 1.0}; + renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetPipeline(renderPipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + // Expect 1 lazy clear for sampled texture + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + // Expect the rendered texture to be cleared + std::vector expectedWithZeros(kSize * kSize, {0, 0, 0, 0}); + EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(renderTexture.Get(), 0, 1, 0, 1)); +} + +// This tests the clearing of sampled textures during compute pass +TEST_P(TextureZeroInitTest, ComputePassSampledTextureClear) { + // Create needed resources + wgpu::TextureDescriptor descriptor = + CreateTextureDescriptor(1, 1, wgpu::TextureUsage::Sampled, kColorFormat); + descriptor.size.width = 1; + descriptor.size.height = 1; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + uint32_t bufferSize = kFormatBlockByteSize * sizeof(uint32_t); + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = bufferSize; + bufferDescriptor.usage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst; + wgpu::Buffer bufferTex = device.CreateBuffer(&bufferDescriptor); + // Add data to buffer to ensure it is initialized + uint32_t data = 100; + queue.WriteBuffer(bufferTex, 0, &data, sizeof(data)); + + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + // Create compute pipeline + wgpu::ComputePipelineDescriptor computePipelineDescriptor; + wgpu::ProgrammableStageDescriptor computeStage; + const char* cs = + R"(#version 450 + layout(binding = 0) uniform texture2D sampleTex; + layout(std430, binding = 1) buffer BufferTex { + vec4 result; + } bufferTex; + layout(binding = 2) uniform sampler sampler0; + void main() { + bufferTex.result = + texelFetch(sampler2D(sampleTex, sampler0), ivec2(0,0), 0); + })"; + computePipelineDescriptor.computeStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, cs); + computePipelineDescriptor.computeStage.entryPoint = "main"; + wgpu::ComputePipeline computePipeline = + device.CreateComputePipeline(&computePipelineDescriptor); + + // Create bindgroup + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, computePipeline.GetBindGroupLayout(0), + {{0, texture.CreateView()}, {1, bufferTex, 0, bufferSize}, {2, sampler}}); + + // Encode the pass and submit + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(computePipeline); + pass.SetBindGroup(0, bindGroup); + pass.Dispatch(1); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + // Expect the buffer to be zeroed out by the compute pass + std::vector expectedWithZeros(bufferSize, 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferTex, 0, kFormatBlockByteSize); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); +} + +// This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures +TEST_P(TextureZeroInitTest, NonRenderableTextureClear) { + wgpu::TextureDescriptor descriptor = + CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy + uint32_t bytesPerRow = Align(kSize * kFormatBlockByteSize, kTextureBytesPerRowAlignment); + uint32_t bufferSize = bytesPerRow * kSize; + std::vector data(bufferSize, 100); + wgpu::Buffer bufferDst = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(bufferDst, 0, bytesPerRow, 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + std::vector expectedWithZeros(bufferSize, 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, kSize); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); +} + +// This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures +TEST_P(TextureZeroInitTest, NonRenderableTextureClearUnalignedSize) { + wgpu::TextureDescriptor descriptor = + CreateTextureDescriptor(1, 1, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat); + descriptor.size.width = kUnalignedSize; + descriptor.size.height = kUnalignedSize; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy + uint32_t bytesPerRow = + Align(kUnalignedSize * kFormatBlockByteSize, kTextureBytesPerRowAlignment); + uint32_t bufferSize = bytesPerRow * kUnalignedSize; + std::vector data(bufferSize, 100); + wgpu::Buffer bufferDst = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(bufferDst, 0, bytesPerRow, 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kUnalignedSize, kUnalignedSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + std::vector expectedWithZeros(bufferSize, 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, kUnalignedSize); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); +} + +// This tests that the code path of CopyTextureToBuffer clears correctly for non-renderable textures +// with more than 1 array layers +TEST_P(TextureZeroInitTest, NonRenderableTextureClearWithMultiArrayLayers) { + wgpu::TextureDescriptor descriptor = + CreateTextureDescriptor(1, 2, wgpu::TextureUsage::CopySrc, kNonrenderableColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + // Set buffer with dirty data so we know it is cleared by the lazy cleared texture copy + uint32_t bufferSize = kFormatBlockByteSize * kSize * kSize; + std::vector data(bufferSize, 100); + wgpu::Buffer bufferDst = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(bufferDst, 0, kSize * kFormatBlockByteSize, 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 1}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + std::vector expectedWithZeros(bufferSize, 0); + EXPECT_BUFFER_U32_RANGE_EQ(expectedWithZeros.data(), bufferDst, 0, 8); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 1, 1)); +} + +// This tests that storeOp clear resets resource state to uninitialized. +// Start with a sample texture that is initialized with data. +// Then expect the render texture to not store the data from sample texture +// because it will be lazy cleared by the EXPECT_TEXTURE_RGBA8_EQ call. +TEST_P(TextureZeroInitTest, RenderPassStoreOpClear) { + // Create needed resources + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopyDst, kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat); + wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); + + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + // Fill the sample texture with data + std::vector data(kFormatBlockByteSize * kSize * kSize, 1); + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, kSize * kFormatBlockByteSize, 0); + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + // Expect 0 lazy clears because the texture will be completely copied to + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + // Create render pipeline + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest(); + renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest(); + renderPipelineDescriptor.cColorStates[0].format = kColorFormat; + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); + + // Create bindgroup + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, texture.CreateView()}}); + + // Encode pass and submit + encoder = device.CreateCommandEncoder(); + utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); + renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0}; + renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetPipeline(renderPipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.EndPass(); + commands = encoder.Finish(); + // Expect 0 lazy clears, sample texture is initialized by copyBufferToTexture and render texture + // is cleared by loadop + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + // Expect the rendered texture to be cleared + std::vector expectedWithZeros(kSize * kSize, {0, 0, 0, 0}); + EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0, + kSize, kSize, 0, 0)); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(renderTexture.Get(), 0, 1, 0, 1)); +} + +// This tests storeOp Clear on depth and stencil textures. +// We put the depth stencil texture through 2 passes: +// 1) LoadOp::Clear and StoreOp::Clear, fail the depth and stencil test set in the render pipeline. +// This means nothing is drawn and subresource is set as uninitialized. +// 2) LoadOp::Load and StoreOp::Clear, pass the depth and stencil test set in the render pipeline. +// Because LoadOp is Load and the subresource is uninitialized, the texture will be cleared to +// 0's This means the depth and stencil test will pass and the red square is drawn. +TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencilStoreOpClear) { + wgpu::TextureDescriptor srcDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::OutputAttachment, + kColorFormat); + wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor); + + wgpu::TextureDescriptor depthStencilDescriptor = + CreateTextureDescriptor(1, 1, + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc | + wgpu::TextureUsage::CopyDst, + kDepthStencilFormat); + wgpu::Texture depthStencilTexture = device.CreateTexture(&depthStencilDescriptor); + + // Setup the renderPass for the first pass. + // We want to fail the depth and stencil test here so that nothing gets drawn and we can + // see that the subresource correctly gets set as unintialized in the second pass + utils::ComboRenderPassDescriptor renderPassDescriptor({srcTexture.CreateView()}, + depthStencilTexture.CreateView()); + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; + renderPassDescriptor.cDepthStencilAttachmentInfo.clearDepth = 1.0f; + renderPassDescriptor.cDepthStencilAttachmentInfo.clearStencil = 1u; + renderPassDescriptor.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.SetPipeline(CreatePipelineForTest()); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + // Expect 0 lazy clears, depth stencil texture will clear using loadop + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + // The depth stencil test should fail and not draw because the depth stencil texture is + // cleared to 1's by using loadOp clear and set values from descriptor. + std::vector expectedBlack(kSize * kSize, {0, 0, 0, 0}); + EXPECT_TEXTURE_RGBA8_EQ(expectedBlack.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be false since storeop is clear, sets + // subresource as uninitialized + EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, + 1, 0, 1)); + } + + // Now we put the depth stencil texture back into renderpass, it should be cleared by loadop + // because storeOp clear sets the subresource as uninitialized + { + renderPassDescriptor.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPassDescriptor.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.SetPipeline(CreatePipelineForTest()); + pass.Draw(6); + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + // Expect 0 lazy clears, depth stencil texture will clear using loadop + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commandBuffer)); + + // Now the depth stencil test should pass since depth stencil texture is cleared to 0's by + // loadop load and uninitialized subresource, so we should have a red square + std::vector expectedRed(kSize * kSize, {255, 0, 0, 255}); + EXPECT_TEXTURE_RGBA8_EQ(expectedRed.data(), srcTexture, 0, 0, kSize, kSize, 0, 0); + + // Expect texture subresource initialized to be false since storeop is clear, sets + // subresource as uninitialized + EXPECT_EQ(false, dawn_native::IsTextureSubresourceInitialized(depthStencilTexture.Get(), 0, + 1, 0, 1)); + } +} + +// Test that if one mip of a texture is initialized and another is uninitialized, lazy clearing the +// uninitialized mip does not clear the initialized mip. +TEST_P(TextureZeroInitTest, PreservesInitializedMip) { + wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor( + 2, 1, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled, + kColorFormat); + wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor); + + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat); + wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); + + // Fill the sample texture's second mip with data + uint32_t mipSize = kSize >> 1; + std::vector data(kFormatBlockByteSize * mipSize * mipSize, 2); + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, mipSize * kFormatBlockByteSize, 0); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(sampleTexture, 1, {0, 0, 0}); + wgpu::Extent3D copySize = {mipSize, mipSize, 1}; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + // Expect 0 lazy clears because the texture subresource will be completely copied to + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + // Create render pipeline + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest(); + renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest(); + renderPipelineDescriptor.cColorStates[0].format = kColorFormat; + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); + + // Create bindgroup + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, sampleTexture.CreateView()}}); + + // Encode pass and submit + encoder = device.CreateCommandEncoder(); + utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); + renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0}; + renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetPipeline(renderPipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.EndPass(); + commands = encoder.Finish(); + // Expect 1 lazy clears, because not all mips of the sample texture are initialized by + // copyBufferToTexture. + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + // Expect the rendered texture to be cleared since we copied from the uninitialized first + // mip. + std::vector expectedWithZeros(kSize * kSize, {0, 0, 0, 0}); + EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0, + kSize, kSize, 0, 0)); + + // Expect the first mip to have been lazy cleared to 0. + EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), sampleTexture, 0, 0, + kSize, kSize, 0, 0)); + + // Expect the second mip to still be filled with 2. + std::vector expectedWithTwos(mipSize * mipSize, {2, 2, 2, 2}); + EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0, + mipSize, mipSize, 1, 0)); + + // Expect the whole texture to be initialized + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(sampleTexture.Get(), 0, 2, 0, 1)); +} + +// Test that if one layer of a texture is initialized and another is uninitialized, lazy clearing +// the uninitialized layer does not clear the initialized layer. +TEST_P(TextureZeroInitTest, PreservesInitializedArrayLayer) { + wgpu::TextureDescriptor sampleTextureDescriptor = CreateTextureDescriptor( + 1, 2, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled, + kColorFormat); + wgpu::Texture sampleTexture = device.CreateTexture(&sampleTextureDescriptor); + + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + wgpu::TextureDescriptor renderTextureDescriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::OutputAttachment, kColorFormat); + wgpu::Texture renderTexture = device.CreateTexture(&renderTextureDescriptor); + + // Fill the sample texture's second array layer with data + std::vector data(kFormatBlockByteSize * kSize * kSize, 2); + wgpu::Buffer stagingBuffer = utils::CreateBufferFromData( + device, data.data(), static_cast(data.size()), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(stagingBuffer, 0, kSize * kFormatBlockByteSize, 0); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(sampleTexture, 0, {0, 0, 1}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + // Expect 0 lazy clears because the texture subresource will be completely copied to + EXPECT_LAZY_CLEAR(0u, queue.Submit(1, &commands)); + + // Create render pipeline + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + renderPipelineDescriptor.vertexStage.module = CreateBasicVertexShaderForTest(); + renderPipelineDescriptor.cFragmentStage.module = CreateSampledTextureFragmentShaderForTest(); + renderPipelineDescriptor.cColorStates[0].format = kColorFormat; + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); + + // Only sample from the uninitialized first layer. + wgpu::TextureViewDescriptor textureViewDescriptor; + textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; + textureViewDescriptor.arrayLayerCount = 1; + + // Create bindgroup + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, sampler}, {1, sampleTexture.CreateView(&textureViewDescriptor)}}); + + // Encode pass and submit + encoder = device.CreateCommandEncoder(); + utils::ComboRenderPassDescriptor renderPassDesc({renderTexture.CreateView()}); + renderPassDesc.cColorAttachments[0].clearColor = {0.0, 0.0, 0.0, 0.0}; + renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + renderPassDesc.cColorAttachments[0].storeOp = wgpu::StoreOp::Clear; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetPipeline(renderPipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(6); + pass.EndPass(); + commands = encoder.Finish(); + // Expect 1 lazy clears, because not all array layers of the sample texture are initialized by + // copyBufferToTexture. + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + + // Expect the rendered texture to be cleared since we copied from the uninitialized first + // array layer. + std::vector expectedWithZeros(kSize * kSize, {0, 0, 0, 0}); + EXPECT_LAZY_CLEAR(1u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), renderTexture, 0, 0, + kSize, kSize, 0, 0)); + + // Expect the first array layer to have been lazy cleared to 0. + EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithZeros.data(), sampleTexture, 0, 0, + kSize, kSize, 0, 0)); + + // Expect the second array layer to still be filled with 2. + std::vector expectedWithTwos(kSize * kSize, {2, 2, 2, 2}); + EXPECT_LAZY_CLEAR(0u, EXPECT_TEXTURE_RGBA8_EQ(expectedWithTwos.data(), sampleTexture, 0, 0, + kSize, kSize, 0, 1)); + + // Expect the whole texture to be initialized + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(sampleTexture.Get(), 0, 1, 0, 2)); +} + +// This is a regression test for crbug.com/dawn/451 where the lazy texture +// init path on D3D12 had a divide-by-zero exception in the copy split logic. +TEST_P(TextureZeroInitTest, CopyTextureToBufferNonRenderableUnaligned) { + wgpu::TextureDescriptor descriptor; + descriptor.size.width = kUnalignedSize; + descriptor.size.height = kUnalignedSize; + descriptor.size.depth = 1; + descriptor.format = wgpu::TextureFormat::R8Snorm; + descriptor.usage = wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + + { + uint32_t bytesPerRow = Align(kUnalignedSize, kTextureBytesPerRowAlignment); + + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = kUnalignedSize * bytesPerRow; + bufferDesc.usage = wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc); + + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(buffer, 0, bytesPerRow, 0); + wgpu::Extent3D copySize = {kUnalignedSize, kUnalignedSize, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, ©Size); + + wgpu::CommandBuffer commands = encoder.Finish(); + EXPECT_LAZY_CLEAR(1u, queue.Submit(1, &commands)); + } + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); +} + +// In this test WriteTexture fully overwrites a texture +TEST_P(TextureZeroInitTest, WriteWholeTexture) { + // TODO(dawn:483): Remove this condition after implementing WriteTexture in those backends. + DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12()); + + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize, kSize, 1}; + + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = 0; + textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize; + textureDataLayout.rowsPerImage = kSize; + + std::vector data( + utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, + copySize, kColorFormat) / + sizeof(RGBA8), + {100, 100, 100, 100}); + + // The write overwrites the whole texture so we don't need to do lazy initialization. + EXPECT_LAZY_CLEAR(0u, + queue.WriteTexture(&textureCopyView, data.data(), data.size() * sizeof(RGBA8), + &textureDataLayout, ©Size)); + + // Expect texture initialized to be true + EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); + + EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kSize, kSize, 0, 0); +} + +// Test WriteTexture to a subset of the texture, lazy init is necessary to clear the other +// half. +TEST_P(TextureZeroInitTest, WriteTextureHalf) { + // TODO(dawn:483): Remove this condition after implementing WriteTexture in those backends. + DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12()); + + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 1, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + wgpu::TextureCopyView textureCopyView = utils::CreateTextureCopyView(texture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {kSize / 2, kSize, 1}; + + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = 0; + textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize / 2; + textureDataLayout.rowsPerImage = kSize; + + std::vector data( + utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, + copySize, kColorFormat) / + sizeof(RGBA8), + {100, 100, 100, 100}); + + EXPECT_LAZY_CLEAR(1u, + queue.WriteTexture(&textureCopyView, data.data(), data.size() * sizeof(RGBA8), + &textureDataLayout, ©Size)); + + // Expect texture initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, 0, 1)); + + std::vector expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0}); + // first half filled with 100, by the data + EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kSize / 2, kSize, 0, 0); + // second half should be cleared + EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kSize / 2, 0, kSize / 2, kSize, 0, 0); +} + +// In this test WriteTexture fully overwrites a range of subresources, so lazy initialization +// is needed for neither the subresources involved in the write nor the other subresources. +TEST_P(TextureZeroInitTest, WriteWholeTextureArray) { + // TODO(dawn:483): Remove this condition after implementing WriteTexture in those backends. + DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12()); + + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 1, 6, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + constexpr uint32_t kBaseArrayLayer = 2u; + constexpr uint32_t kCopyLayerCount = 3u; + + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, 0, {0, 0, kBaseArrayLayer}); + wgpu::Extent3D copySize = {kSize, kSize, kCopyLayerCount}; + + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = 0; + textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize; + textureDataLayout.rowsPerImage = kSize; + + std::vector data( + utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, + copySize, kColorFormat) / + sizeof(RGBA8), + {100, 100, 100, 100}); + + // The write overwrites the whole subresources so we don't need to do lazy initialization on + // them. + EXPECT_LAZY_CLEAR(0u, + queue.WriteTexture(&textureCopyView, data.data(), data.size() * sizeof(RGBA8), + &textureDataLayout, ©Size)); + + // Expect texture subresource initialized to be true + EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, kBaseArrayLayer, + kCopyLayerCount)); + + for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) { + EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kSize, kSize, 0, layer); + } +} + +// Test WriteTexture to a subset of the subresource, lazy init is necessary to clear the other +// half. +TEST_P(TextureZeroInitTest, WriteTextureArrayHalf) { + // TODO(dawn:483): Remove this condition after implementing WriteTexture in those backends. + DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12()); + + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 6, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + constexpr uint32_t kBaseArrayLayer = 2u; + constexpr uint32_t kCopyLayerCount = 3u; + + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, 0, {0, 0, kBaseArrayLayer}); + wgpu::Extent3D copySize = {kSize / 2, kSize, kCopyLayerCount}; + + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = 0; + textureDataLayout.bytesPerRow = kSize * kFormatBlockByteSize / 2; + textureDataLayout.rowsPerImage = kSize; + + std::vector data( + utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, + copySize, kColorFormat) / + sizeof(RGBA8), + {100, 100, 100, 100}); + + EXPECT_LAZY_CLEAR(1u, + queue.WriteTexture(&textureCopyView, data.data(), data.size() * sizeof(RGBA8), + &textureDataLayout, ©Size)); + + // Expect texture subresource initialized to be true + EXPECT_EQ(true, dawn_native::IsTextureSubresourceInitialized(texture.Get(), 0, 1, + kBaseArrayLayer, kCopyLayerCount)); + + std::vector expectedZeros((kSize / 2) * kSize, {0, 0, 0, 0}); + for (uint32_t layer = kBaseArrayLayer; layer < kBaseArrayLayer + kCopyLayerCount; ++layer) { + // first half filled with 100, by the data + EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kSize / 2, kSize, 0, layer); + // second half should be cleared + EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kSize / 2, 0, kSize / 2, kSize, 0, + layer); + } +} + +// In this test WriteTexture fully overwrites a texture at mip level. +TEST_P(TextureZeroInitTest, WriteWholeTextureAtMipLevel) { + // TODO(dawn:483): Remove this condition after implementing WriteTexture in those backends. + DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12()); + + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 1, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc, kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + constexpr uint32_t kMipLevel = 2; + constexpr uint32_t kMipSize = kSize >> kMipLevel; + + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, kMipLevel, {0, 0, 0}); + wgpu::Extent3D copySize = {kMipSize, kMipSize, 1}; + + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = 0; + textureDataLayout.bytesPerRow = kMipSize * kFormatBlockByteSize; + textureDataLayout.rowsPerImage = kMipSize; + + std::vector data( + utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, + copySize, kColorFormat) / + sizeof(RGBA8), + {100, 100, 100, 100}); + + // The write overwrites the whole texture so we don't need to do lazy initialization. + EXPECT_LAZY_CLEAR(0u, + queue.WriteTexture(&textureCopyView, data.data(), data.size() * sizeof(RGBA8), + &textureDataLayout, ©Size)); + + // Expect texture initialized to be true + EXPECT_TRUE(dawn_native::IsTextureSubresourceInitialized(texture.Get(), kMipLevel, 1, 0, 1)); + + EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kMipSize, kMipSize, kMipLevel, 0); +} + +// Test WriteTexture to a subset of the texture at mip level, lazy init is necessary to clear the +// other half. +TEST_P(TextureZeroInitTest, WriteTextureHalfAtMipLevel) { + // TODO(dawn:483): Remove this condition after implementing WriteTexture in those backends. + DAWN_SKIP_TEST_IF(IsOpenGL() || IsD3D12()); + + wgpu::TextureDescriptor descriptor = CreateTextureDescriptor( + 4, 1, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | wgpu::TextureUsage::CopySrc, + kColorFormat); + wgpu::Texture texture = device.CreateTexture(&descriptor); + + constexpr uint32_t kMipLevel = 2; + constexpr uint32_t kMipSize = kSize >> kMipLevel; + + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, kMipLevel, {0, 0, 0}); + wgpu::Extent3D copySize = {kMipSize / 2, kMipSize, 1}; + + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = 0; + textureDataLayout.bytesPerRow = kMipSize * kFormatBlockByteSize / 2; + textureDataLayout.rowsPerImage = kMipSize; + + std::vector data( + utils::RequiredBytesInCopy(textureDataLayout.bytesPerRow, textureDataLayout.rowsPerImage, + copySize, kColorFormat) / + sizeof(RGBA8), + {100, 100, 100, 100}); + + EXPECT_LAZY_CLEAR(1u, + queue.WriteTexture(&textureCopyView, data.data(), data.size() * sizeof(RGBA8), + &textureDataLayout, ©Size)); + + // Expect texture initialized to be true + EXPECT_EQ(true, + dawn_native::IsTextureSubresourceInitialized(texture.Get(), kMipLevel, 1, 0, 1)); + + std::vector expectedZeros((kMipSize / 2) * kMipSize, {0, 0, 0, 0}); + // first half filled with 100, by the data + EXPECT_TEXTURE_RGBA8_EQ(data.data(), texture, 0, 0, kMipSize / 2, kMipSize, kMipLevel, 0); + // second half should be cleared + EXPECT_TEXTURE_RGBA8_EQ(expectedZeros.data(), texture, kMipSize / 2, 0, kMipSize / 2, kMipSize, + kMipLevel, 0); +} + +DAWN_INSTANTIATE_TEST(TextureZeroInitTest, + D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}), + D3D12Backend({"nonzero_clear_resources_on_creation_for_testing"}, + {"use_d3d12_render_pass"}), + OpenGLBackend({"nonzero_clear_resources_on_creation_for_testing"}), + MetalBackend({"nonzero_clear_resources_on_creation_for_testing"}), + VulkanBackend({"nonzero_clear_resources_on_creation_for_testing"})); diff --git a/third_party/dawn/src/tests/end2end/VertexFormatTests.cpp b/third_party/dawn/src/tests/end2end/VertexFormatTests.cpp index d27f0a821e9..a4e773af6cd 100644 --- a/third_party/dawn/src/tests/end2end/VertexFormatTests.cpp +++ b/third_party/dawn/src/tests/end2end/VertexFormatTests.cpp @@ -17,7 +17,7 @@ #include "common/Assert.h" #include "common/Math.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" // Vertex format tests all work the same way: the test will render a triangle. // Each test will set up a vertex buffer, and the vertex shader will check that @@ -54,141 +54,141 @@ class VertexFormatTest : public DawnTest { utils::BasicRenderPass renderPass; - bool IsNormalizedFormat(dawn::VertexFormat format) { + bool IsNormalizedFormat(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UChar2Norm: - case dawn::VertexFormat::UChar4Norm: - case dawn::VertexFormat::Char2Norm: - case dawn::VertexFormat::Char4Norm: - case dawn::VertexFormat::UShort2Norm: - case dawn::VertexFormat::UShort4Norm: - case dawn::VertexFormat::Short2Norm: - case dawn::VertexFormat::Short4Norm: + case wgpu::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::Char2Norm: + case wgpu::VertexFormat::Char4Norm: + case wgpu::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Short4Norm: return true; default: return false; } } - bool IsUnsignedFormat(dawn::VertexFormat format) { + bool IsUnsignedFormat(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UInt: - case dawn::VertexFormat::UChar2: - case dawn::VertexFormat::UChar4: - case dawn::VertexFormat::UShort2: - case dawn::VertexFormat::UShort4: - case dawn::VertexFormat::UInt2: - case dawn::VertexFormat::UInt3: - case dawn::VertexFormat::UInt4: - case dawn::VertexFormat::UChar2Norm: - case dawn::VertexFormat::UChar4Norm: - case dawn::VertexFormat::UShort2Norm: - case dawn::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::UInt: + case wgpu::VertexFormat::UChar2: + case wgpu::VertexFormat::UChar4: + case wgpu::VertexFormat::UShort2: + case wgpu::VertexFormat::UShort4: + case wgpu::VertexFormat::UInt2: + case wgpu::VertexFormat::UInt3: + case wgpu::VertexFormat::UInt4: + case wgpu::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort4Norm: return true; default: return false; } } - bool IsFloatFormat(dawn::VertexFormat format) { + bool IsFloatFormat(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::Half2: - case dawn::VertexFormat::Half4: - case dawn::VertexFormat::Float: - case dawn::VertexFormat::Float2: - case dawn::VertexFormat::Float3: - case dawn::VertexFormat::Float4: + case wgpu::VertexFormat::Half2: + case wgpu::VertexFormat::Half4: + case wgpu::VertexFormat::Float: + case wgpu::VertexFormat::Float2: + case wgpu::VertexFormat::Float3: + case wgpu::VertexFormat::Float4: return true; default: return false; } } - bool IsHalfFormat(dawn::VertexFormat format) { + bool IsHalfFormat(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::Half2: - case dawn::VertexFormat::Half4: + case wgpu::VertexFormat::Half2: + case wgpu::VertexFormat::Half4: return true; default: return false; } } - uint32_t BytesPerComponents(dawn::VertexFormat format) { + uint32_t BytesPerComponents(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::Char2: - case dawn::VertexFormat::Char4: - case dawn::VertexFormat::UChar2: - case dawn::VertexFormat::UChar4: - case dawn::VertexFormat::UChar2Norm: - case dawn::VertexFormat::UChar4Norm: - case dawn::VertexFormat::Char2Norm: - case dawn::VertexFormat::Char4Norm: + case wgpu::VertexFormat::Char2: + case wgpu::VertexFormat::Char4: + case wgpu::VertexFormat::UChar2: + case wgpu::VertexFormat::UChar4: + case wgpu::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::Char2Norm: + case wgpu::VertexFormat::Char4Norm: return 1; - case dawn::VertexFormat::UShort2: - case dawn::VertexFormat::UShort4: - case dawn::VertexFormat::Short2: - case dawn::VertexFormat::Short4: - case dawn::VertexFormat::UShort2Norm: - case dawn::VertexFormat::UShort4Norm: - case dawn::VertexFormat::Short2Norm: - case dawn::VertexFormat::Short4Norm: - case dawn::VertexFormat::Half2: - case dawn::VertexFormat::Half4: + case wgpu::VertexFormat::UShort2: + case wgpu::VertexFormat::UShort4: + case wgpu::VertexFormat::Short2: + case wgpu::VertexFormat::Short4: + case wgpu::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Short4Norm: + case wgpu::VertexFormat::Half2: + case wgpu::VertexFormat::Half4: return 2; - case dawn::VertexFormat::UInt: - case dawn::VertexFormat::Int: - case dawn::VertexFormat::Float: - case dawn::VertexFormat::UInt2: - case dawn::VertexFormat::UInt3: - case dawn::VertexFormat::UInt4: - case dawn::VertexFormat::Int2: - case dawn::VertexFormat::Int3: - case dawn::VertexFormat::Int4: - case dawn::VertexFormat::Float2: - case dawn::VertexFormat::Float3: - case dawn::VertexFormat::Float4: + case wgpu::VertexFormat::UInt: + case wgpu::VertexFormat::Int: + case wgpu::VertexFormat::Float: + case wgpu::VertexFormat::UInt2: + case wgpu::VertexFormat::UInt3: + case wgpu::VertexFormat::UInt4: + case wgpu::VertexFormat::Int2: + case wgpu::VertexFormat::Int3: + case wgpu::VertexFormat::Int4: + case wgpu::VertexFormat::Float2: + case wgpu::VertexFormat::Float3: + case wgpu::VertexFormat::Float4: return 4; default: DAWN_UNREACHABLE(); } } - uint32_t ComponentCount(dawn::VertexFormat format) { + uint32_t ComponentCount(wgpu::VertexFormat format) { switch (format) { - case dawn::VertexFormat::UInt: - case dawn::VertexFormat::Int: - case dawn::VertexFormat::Float: + case wgpu::VertexFormat::UInt: + case wgpu::VertexFormat::Int: + case wgpu::VertexFormat::Float: return 1; - case dawn::VertexFormat::UChar2: - case dawn::VertexFormat::UShort2: - case dawn::VertexFormat::UInt2: - case dawn::VertexFormat::Char2: - case dawn::VertexFormat::Short2: - case dawn::VertexFormat::Int2: - case dawn::VertexFormat::UChar2Norm: - case dawn::VertexFormat::Char2Norm: - case dawn::VertexFormat::UShort2Norm: - case dawn::VertexFormat::Short2Norm: - case dawn::VertexFormat::Half2: - case dawn::VertexFormat::Float2: + case wgpu::VertexFormat::UChar2: + case wgpu::VertexFormat::UShort2: + case wgpu::VertexFormat::UInt2: + case wgpu::VertexFormat::Char2: + case wgpu::VertexFormat::Short2: + case wgpu::VertexFormat::Int2: + case wgpu::VertexFormat::UChar2Norm: + case wgpu::VertexFormat::Char2Norm: + case wgpu::VertexFormat::UShort2Norm: + case wgpu::VertexFormat::Short2Norm: + case wgpu::VertexFormat::Half2: + case wgpu::VertexFormat::Float2: return 2; - case dawn::VertexFormat::Int3: - case dawn::VertexFormat::UInt3: - case dawn::VertexFormat::Float3: + case wgpu::VertexFormat::Int3: + case wgpu::VertexFormat::UInt3: + case wgpu::VertexFormat::Float3: return 3; - case dawn::VertexFormat::UChar4: - case dawn::VertexFormat::UShort4: - case dawn::VertexFormat::UInt4: - case dawn::VertexFormat::Char4: - case dawn::VertexFormat::Short4: - case dawn::VertexFormat::Int4: - case dawn::VertexFormat::UChar4Norm: - case dawn::VertexFormat::Char4Norm: - case dawn::VertexFormat::UShort4Norm: - case dawn::VertexFormat::Short4Norm: - case dawn::VertexFormat::Half4: - case dawn::VertexFormat::Float4: + case wgpu::VertexFormat::UChar4: + case wgpu::VertexFormat::UShort4: + case wgpu::VertexFormat::UInt4: + case wgpu::VertexFormat::Char4: + case wgpu::VertexFormat::Short4: + case wgpu::VertexFormat::Int4: + case wgpu::VertexFormat::UChar4Norm: + case wgpu::VertexFormat::Char4Norm: + case wgpu::VertexFormat::UShort4Norm: + case wgpu::VertexFormat::Short4Norm: + case wgpu::VertexFormat::Half4: + case wgpu::VertexFormat::Float4: return 4; default: DAWN_UNREACHABLE(); @@ -220,7 +220,7 @@ class VertexFormatTest : public DawnTest { // The length of vertexData is fixed to 3, it aligns to triangle vertex number template - dawn::RenderPipeline MakeTestPipeline(dawn::VertexFormat format, std::vector& expectedData) { + wgpu::RenderPipeline MakeTestPipeline(wgpu::VertexFormat format, std::vector& expectedData) { bool isFloat = IsFloatFormat(format); bool isNormalized = IsNormalizedFormat(format); bool isUnsigned = IsUnsignedFormat(format); @@ -263,8 +263,8 @@ class VertexFormatTest : public DawnTest { vs << "void main() {\n"; // Hard code the triangle in the shader so that we don't have to add a vertex input for it. - vs << " const vec2 pos[3] = vec2[3](vec2(-1.0f, 0.0f), vec2(-1.0f, -1.0f), vec2(0.0f, " - "-1.0f));\n"; + vs << " const vec2 pos[3] = vec2[3](vec2(-1.0f, 0.0f), vec2(-1.0f, 1.0f), vec2(0.0f, " + "1.0f));\n"; vs << " gl_Position = vec4(pos[gl_VertexIndex], 0.0, 1.0);\n"; // Declare expected values. @@ -340,11 +340,11 @@ class VertexFormatTest : public DawnTest { vs << " }\n"; vs << "}\n"; - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, vs.str().c_str()); + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vs.str().c_str()); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) in vec4 color; layout(location = 0) out vec4 fragColor; @@ -360,43 +360,45 @@ class VertexFormatTest : public DawnTest { } utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.cVertexInput.bufferCount = 1; - descriptor.cVertexInput.cBuffers[0].stride = strideBytes; - descriptor.cVertexInput.cBuffers[0].attributeCount = 1; - descriptor.cVertexInput.cAttributes[0].format = format; - descriptor.cColorStates[0]->format = renderPass.colorFormat; + descriptor.cVertexState.vertexBufferCount = 1; + descriptor.cVertexState.cVertexBuffers[0].arrayStride = strideBytes; + descriptor.cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor.cVertexState.cAttributes[0].format = format; + descriptor.cColorStates[0].format = renderPass.colorFormat; return device.CreateRenderPipeline(&descriptor); } template - void DoVertexFormatTest(dawn::VertexFormat format, + void DoVertexFormatTest(wgpu::VertexFormat format, std::vector vertex, std::vector expectedData) { - dawn::RenderPipeline pipeline = MakeTestPipeline(format, expectedData); - dawn::Buffer vertexBuffer = - utils::CreateBufferFromData(device, vertex.data(), vertex.size() * sizeof(VertexType), - dawn::BufferUsageBit::Vertex); - uint64_t zeroOffset = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPipeline pipeline = MakeTestPipeline(format, expectedData); + wgpu::Buffer vertexBuffer = utils::CreateBufferFromData( + device, vertex.data(), vertex.size() * sizeof(VertexType), wgpu::BufferUsage::Vertex); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset); - pass.Draw(3, 1, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0); } }; TEST_P(VertexFormatTest, UChar2) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), 0, @@ -416,10 +418,14 @@ TEST_P(VertexFormatTest, UChar2) { std::numeric_limits::max(), 0, std::numeric_limits::min(), 2, 200, 201, }; - DoVertexFormatTest(dawn::VertexFormat::UChar2, vertexData, expectedData); + DoVertexFormatTest(wgpu::VertexFormat::UChar2, vertexData, expectedData); } TEST_P(VertexFormatTest, UChar4) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), 0, @@ -435,10 +441,14 @@ TEST_P(VertexFormatTest, UChar4) { 203, }; - DoVertexFormatTest(dawn::VertexFormat::UChar4, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UChar4, vertexData, vertexData); } TEST_P(VertexFormatTest, Char2) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), 0, @@ -458,10 +468,14 @@ TEST_P(VertexFormatTest, Char2) { std::numeric_limits::max(), 0, std::numeric_limits::min(), -2, 120, -121, }; - DoVertexFormatTest(dawn::VertexFormat::Char2, vertexData, expectedData); + DoVertexFormatTest(wgpu::VertexFormat::Char2, vertexData, expectedData); } TEST_P(VertexFormatTest, Char4) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), 0, @@ -477,10 +491,14 @@ TEST_P(VertexFormatTest, Char4) { -123, }; - DoVertexFormatTest(dawn::VertexFormat::Char4, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Char4, vertexData, vertexData); } TEST_P(VertexFormatTest, UChar2Norm) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), std::numeric_limits::min(), @@ -503,10 +521,14 @@ TEST_P(VertexFormatTest, UChar2Norm) { 200, 201}; - DoVertexFormatTest(dawn::VertexFormat::UChar2Norm, vertexData, expectedData); + DoVertexFormatTest(wgpu::VertexFormat::UChar2Norm, vertexData, expectedData); } TEST_P(VertexFormatTest, UChar4Norm) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), std::numeric_limits::min(), 0, @@ -520,10 +542,14 @@ TEST_P(VertexFormatTest, UChar4Norm) { 202, 203}; - DoVertexFormatTest(dawn::VertexFormat::UChar4Norm, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UChar4Norm, vertexData, vertexData); } TEST_P(VertexFormatTest, Char2Norm) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), std::numeric_limits::min(), @@ -548,10 +574,14 @@ TEST_P(VertexFormatTest, Char2Norm) { -121, }; - DoVertexFormatTest(dawn::VertexFormat::Char2Norm, vertexData, expectedData); + DoVertexFormatTest(wgpu::VertexFormat::Char2Norm, vertexData, expectedData); } TEST_P(VertexFormatTest, Char4Norm) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), std::numeric_limits::min(), 0, @@ -565,10 +595,14 @@ TEST_P(VertexFormatTest, Char4Norm) { 102, -123}; - DoVertexFormatTest(dawn::VertexFormat::Char4Norm, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Char4Norm, vertexData, vertexData); } TEST_P(VertexFormatTest, UShort2) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), 0, std::numeric_limits::min(), @@ -576,10 +610,14 @@ TEST_P(VertexFormatTest, UShort2) { 65432, 4890}; - DoVertexFormatTest(dawn::VertexFormat::UShort2, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UShort2, vertexData, vertexData); } TEST_P(VertexFormatTest, UShort4) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), std::numeric_limits::max(), @@ -595,10 +633,14 @@ TEST_P(VertexFormatTest, UShort4) { 3467, }; - DoVertexFormatTest(dawn::VertexFormat::UShort4, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UShort4, vertexData, vertexData); } TEST_P(VertexFormatTest, Short2) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), 0, std::numeric_limits::min(), @@ -606,10 +648,14 @@ TEST_P(VertexFormatTest, Short2) { 3876, -3948}; - DoVertexFormatTest(dawn::VertexFormat::Short2, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Short2, vertexData, vertexData); } TEST_P(VertexFormatTest, Short4) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), 0, @@ -625,10 +671,14 @@ TEST_P(VertexFormatTest, Short4) { -2987, }; - DoVertexFormatTest(dawn::VertexFormat::Short4, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Short4, vertexData, vertexData); } TEST_P(VertexFormatTest, UShort2Norm) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), std::numeric_limits::min(), std::numeric_limits::max() / 2u, @@ -636,10 +686,14 @@ TEST_P(VertexFormatTest, UShort2Norm) { 3456, 6543}; - DoVertexFormatTest(dawn::VertexFormat::UShort2Norm, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UShort2Norm, vertexData, vertexData); } TEST_P(VertexFormatTest, UShort4Norm) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), std::numeric_limits::min(), 0, @@ -653,10 +707,14 @@ TEST_P(VertexFormatTest, UShort4Norm) { 2987, 2987}; - DoVertexFormatTest(dawn::VertexFormat::UShort4Norm, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UShort4Norm, vertexData, vertexData); } TEST_P(VertexFormatTest, Short2Norm) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), std::numeric_limits::min(), std::numeric_limits::max() / 2, @@ -664,10 +722,14 @@ TEST_P(VertexFormatTest, Short2Norm) { 4987, -6789}; - DoVertexFormatTest(dawn::VertexFormat::Short2Norm, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Short2Norm, vertexData, vertexData); } TEST_P(VertexFormatTest, Short4Norm) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), std::numeric_limits::min(), 0, @@ -681,120 +743,180 @@ TEST_P(VertexFormatTest, Short4Norm) { 20432, -2083}; - DoVertexFormatTest(dawn::VertexFormat::Short4Norm, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Short4Norm, vertexData, vertexData); } TEST_P(VertexFormatTest, Half2) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = Float32ToFloat16(std::vector({14.8f, -0.0f, 22.5f, 1.3f, +0.0f, -24.8f})); - DoVertexFormatTest(dawn::VertexFormat::Half2, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Half2, vertexData, vertexData); } TEST_P(VertexFormatTest, Half4) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = Float32ToFloat16(std::vector( {+0.0f, -16.8f, 18.2f, -0.0f, 12.5f, 1.3f, 14.8f, -12.4f, 22.5f, -48.8f, 47.4f, -24.8f})); - DoVertexFormatTest(dawn::VertexFormat::Half4, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Half4, vertexData, vertexData); } TEST_P(VertexFormatTest, Float) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {1.3f, +0.0f, -0.0f}; - DoVertexFormatTest(dawn::VertexFormat::Float, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Float, vertexData, vertexData); vertexData = std::vector{+1.0f, -1.0f, 18.23f}; - DoVertexFormatTest(dawn::VertexFormat::Float, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Float, vertexData, vertexData); } TEST_P(VertexFormatTest, Float2) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {18.23f, -0.0f, +0.0f, +1.0f, 1.3f, -1.0f}; - DoVertexFormatTest(dawn::VertexFormat::Float2, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Float2, vertexData, vertexData); } TEST_P(VertexFormatTest, Float3) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { +0.0f, -1.0f, -0.0f, 1.0f, 1.3f, 99.45f, 23.6f, -81.2f, 55.0f, }; - DoVertexFormatTest(dawn::VertexFormat::Float3, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Float3, vertexData, vertexData); } TEST_P(VertexFormatTest, Float4) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { 19.2f, -19.3f, +0.0f, 1.0f, -0.0f, 1.0f, 1.3f, -1.0f, 13.078f, 21.1965f, -1.1f, -1.2f, }; - DoVertexFormatTest(dawn::VertexFormat::Float4, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Float4, vertexData, vertexData); } TEST_P(VertexFormatTest, UInt) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()}; - DoVertexFormatTest(dawn::VertexFormat::UInt, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UInt, vertexData, vertexData); } TEST_P(VertexFormatTest, UInt2) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), 32, std::numeric_limits::max(), 64, std::numeric_limits::max(), 128}; - DoVertexFormatTest(dawn::VertexFormat::UInt2, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UInt2, vertexData, vertexData); } TEST_P(VertexFormatTest, UInt3) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), 32, 64, std::numeric_limits::max(), 164, 128, std::numeric_limits::max(), 1283, 256}; - DoVertexFormatTest(dawn::VertexFormat::UInt3, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UInt3, vertexData, vertexData); } TEST_P(VertexFormatTest, UInt4) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), 32, 64, 5460, std::numeric_limits::max(), 164, 128, 0, std::numeric_limits::max(), 1283, 256, 4567}; - DoVertexFormatTest(dawn::VertexFormat::UInt4, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::UInt4, vertexData, vertexData); } TEST_P(VertexFormatTest, Int) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = {std::numeric_limits::max(), std::numeric_limits::min(), std::numeric_limits::max()}; - DoVertexFormatTest(dawn::VertexFormat::Int, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Int, vertexData, vertexData); } TEST_P(VertexFormatTest, Int2) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), std::numeric_limits::min(), std::numeric_limits::max(), std::numeric_limits::min(), std::numeric_limits::max(), std::numeric_limits::min()}; - DoVertexFormatTest(dawn::VertexFormat::Int2, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Int2, vertexData, vertexData); } TEST_P(VertexFormatTest, Int3) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), std::numeric_limits::min(), 64, std::numeric_limits::max(), std::numeric_limits::min(), 128, std::numeric_limits::max(), std::numeric_limits::min(), 256}; - DoVertexFormatTest(dawn::VertexFormat::Int3, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Int3, vertexData, vertexData); } TEST_P(VertexFormatTest, Int4) { + // TODO(cwallez@chromium.org): Failing because of a SPIRV-Cross issue. + // See http://crbug.com/dawn/259 + DAWN_SKIP_TEST_IF(IsMetal() && IsIntel()); + std::vector vertexData = { std::numeric_limits::max(), std::numeric_limits::min(), 64, -5460, std::numeric_limits::max(), std::numeric_limits::min(), -128, 0, std::numeric_limits::max(), std::numeric_limits::min(), 256, -4567}; - DoVertexFormatTest(dawn::VertexFormat::Int4, vertexData, vertexData); + DoVertexFormatTest(wgpu::VertexFormat::Int4, vertexData, vertexData); } -DAWN_INSTANTIATE_TEST(VertexFormatTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(VertexFormatTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/VertexStateTests.cpp b/third_party/dawn/src/tests/end2end/VertexStateTests.cpp new file mode 100644 index 00000000000..1c92f3450b5 --- /dev/null +++ b/third_party/dawn/src/tests/end2end/VertexStateTests.cpp @@ -0,0 +1,664 @@ +// Copyright 2017 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Assert.h" +#include "common/Math.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +using wgpu::InputStepMode; +using wgpu::VertexFormat; + +// Input state tests all work the same way: the test will render triangles in a grid up to 4x4. Each +// triangle is position in the grid such that X will correspond to the "triangle number" and the Y +// to the instance number. Each test will set up an input state and buffers, and the vertex shader +// will check that the vertex attributes corresponds to predetermined values. On success it outputs +// green, otherwise red. +// +// The predetermined values are "K * gl_VertexID + componentIndex" for vertex-indexed buffers, and +// "K * gl_InstanceID + componentIndex" for instance-indexed buffers. + +constexpr static unsigned int kRTSize = 400; +constexpr static unsigned int kRTCellOffset = 50; +constexpr static unsigned int kRTCellSize = 100; + +class VertexStateTest : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + + renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + } + + bool ShouldComponentBeDefault(VertexFormat format, int component) { + EXPECT_TRUE(component >= 0 && component < 4); + switch (format) { + case VertexFormat::Float4: + case VertexFormat::UChar4Norm: + return component >= 4; + case VertexFormat::Float3: + return component >= 3; + case VertexFormat::Float2: + case VertexFormat::UChar2Norm: + return component >= 2; + case VertexFormat::Float: + return component >= 1; + default: + DAWN_UNREACHABLE(); + } + } + + struct ShaderTestSpec { + uint32_t location; + VertexFormat format; + InputStepMode step; + }; + wgpu::RenderPipeline MakeTestPipeline(const wgpu::VertexStateDescriptor& vertexState, + int multiplier, + const std::vector& testSpec) { + std::ostringstream vs; + vs << "#version 450\n"; + + // TODO(cwallez@chromium.org): this only handles float attributes, we should extend it to + // other types Adds line of the form + // layout(location=1) in vec4 input1; + for (const auto& input : testSpec) { + vs << "layout(location=" << input.location << ") in vec4 input" << input.location + << ";\n"; + } + + vs << "layout(location = 0) out vec4 color;\n"; + vs << "void main() {\n"; + + // Hard code the triangle in the shader so that we don't have to add a vertex input for it. + // Also this places the triangle in the grid based on its VertexID and InstanceID + vs << " const vec2 pos[3] = vec2[3](vec2(0.5f, 1.0f), vec2(0.0f, 0.0f), vec2(1.0f, " + "0.0f));\n"; + vs << " vec2 offset = vec2(float(gl_VertexIndex / 3), float(gl_InstanceIndex));\n"; + vs << " vec2 worldPos = pos[gl_VertexIndex % 3] + offset;\n"; + vs << " vec4 position = vec4(worldPos / 2 - vec2(1.0f), 0.0f, 1.0f);\n"; + vs << " gl_Position = vec4(position.x, -position.y, position.z, position.w);\n"; + + // Perform the checks by successively ANDing a boolean + vs << " bool success = true;\n"; + for (const auto& input : testSpec) { + for (int component = 0; component < 4; ++component) { + vs << " success = success && (input" << input.location << "[" << component + << "] == "; + if (ShouldComponentBeDefault(input.format, component)) { + vs << (component == 3 ? "1.0f" : "0.0f"); + } else { + if (input.step == InputStepMode::Vertex) { + vs << multiplier << " * gl_VertexIndex + " << component << ".0f"; + } else { + vs << multiplier << " * gl_InstanceIndex + " << component << ".0f"; + } + } + vs << ");\n"; + } + } + + // Choose the color + vs << " if (success) {\n"; + vs << " color = vec4(0.0f, 1.0f, 0.0f, 1.0f);\n"; + vs << " } else {\n"; + vs << " color = vec4(1.0f, 0.0f, 0.0f, 1.0f);\n"; + vs << " }\n;"; + vs << "}\n"; + + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vs.str().c_str()); + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) in vec4 color; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = color; + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.vertexState = &vertexState; + descriptor.cColorStates[0].format = renderPass.colorFormat; + + return device.CreateRenderPipeline(&descriptor); + } + + struct VertexAttributeSpec { + uint32_t location; + uint64_t offset; + VertexFormat format; + }; + struct VertexBufferSpec { + uint64_t arrayStride; + InputStepMode step; + std::vector attributes; + }; + + utils::ComboVertexStateDescriptor MakeVertexState( + const std::vector& buffers) { + utils::ComboVertexStateDescriptor vertexState; + uint32_t vertexBufferCount = 0; + uint32_t totalNumAttributes = 0; + for (const VertexBufferSpec& buffer : buffers) { + vertexState.cVertexBuffers[vertexBufferCount].arrayStride = buffer.arrayStride; + vertexState.cVertexBuffers[vertexBufferCount].stepMode = buffer.step; + + vertexState.cVertexBuffers[vertexBufferCount].attributes = + &vertexState.cAttributes[totalNumAttributes]; + + for (const VertexAttributeSpec& attribute : buffer.attributes) { + vertexState.cAttributes[totalNumAttributes].shaderLocation = attribute.location; + vertexState.cAttributes[totalNumAttributes].offset = attribute.offset; + vertexState.cAttributes[totalNumAttributes].format = attribute.format; + totalNumAttributes++; + } + vertexState.cVertexBuffers[vertexBufferCount].attributeCount = + static_cast(buffer.attributes.size()); + + vertexBufferCount++; + } + + vertexState.vertexBufferCount = vertexBufferCount; + return vertexState; + } + + template + wgpu::Buffer MakeVertexBuffer(std::vector data) { + return utils::CreateBufferFromData(device, data.data(), + static_cast(data.size() * sizeof(T)), + wgpu::BufferUsage::Vertex); + } + + struct DrawVertexBuffer { + uint32_t location; + wgpu::Buffer* buffer; + }; + void DoTestDraw(const wgpu::RenderPipeline& pipeline, + unsigned int triangles, + unsigned int instances, + std::vector vertexBuffers) { + EXPECT_LE(triangles, 4u); + EXPECT_LE(instances, 4u); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + + for (const DrawVertexBuffer& buffer : vertexBuffers) { + pass.SetVertexBuffer(buffer.location, *buffer.buffer); + } + + pass.Draw(triangles * 3, instances); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + CheckResult(triangles, instances); + } + + void CheckResult(unsigned int triangles, unsigned int instances) { + // Check that the center of each triangle is pure green, so that if a single vertex shader + // instance fails, linear interpolation makes the pixel check fail. + for (unsigned int triangle = 0; triangle < 4; triangle++) { + for (unsigned int instance = 0; instance < 4; instance++) { + unsigned int x = kRTCellOffset + kRTCellSize * triangle; + unsigned int y = kRTCellOffset + kRTCellSize * instance; + if (triangle < triangles && instance < instances) { + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, x, y); + } else { + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, x, y); + } + } + } + } + + utils::BasicRenderPass renderPass; +}; + +// Test compilation and usage of the fixture :) +TEST_P(VertexStateTest, Basic) { + utils::ComboVertexStateDescriptor vertexState = MakeVertexState( + {{4 * sizeof(float), InputStepMode::Vertex, {{0, 0, VertexFormat::Float4}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Vertex}}); + + // clang-format off + wgpu::Buffer buffer0 = MakeVertexBuffer({ + 0, 1, 2, 3, + 1, 2, 3, 4, + 2, 3, 4, 5 + }); + // clang-format on + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}}); +} + +// Test a stride of 0 works +TEST_P(VertexStateTest, ZeroStride) { + // This test was failing only on AMD but the OpenGL backend doesn't gather PCI info yet. + DAWN_SKIP_TEST_IF(IsLinux() && IsOpenGL()); + + utils::ComboVertexStateDescriptor vertexState = + MakeVertexState({{0, InputStepMode::Vertex, {{0, 0, VertexFormat::Float4}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 0, {{0, VertexFormat::Float4, InputStepMode::Vertex}}); + + wgpu::Buffer buffer0 = MakeVertexBuffer({ + 0, + 1, + 2, + 3, + }); + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}}); +} + +// Test attributes defaults to (0, 0, 0, 1) if the input state doesn't have all components +TEST_P(VertexStateTest, AttributeExpanding) { + // This test was failing only on AMD but the OpenGL backend doesn't gather PCI info yet. + DAWN_SKIP_TEST_IF(IsLinux() && IsOpenGL()); + + // R32F case + { + utils::ComboVertexStateDescriptor vertexState = + MakeVertexState({{0, InputStepMode::Vertex, {{0, 0, VertexFormat::Float}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 0, {{0, VertexFormat::Float, InputStepMode::Vertex}}); + + wgpu::Buffer buffer0 = MakeVertexBuffer({0, 1, 2, 3}); + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}}); + } + // RG32F case + { + utils::ComboVertexStateDescriptor vertexState = + MakeVertexState({{0, InputStepMode::Vertex, {{0, 0, VertexFormat::Float2}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 0, {{0, VertexFormat::Float2, InputStepMode::Vertex}}); + + wgpu::Buffer buffer0 = MakeVertexBuffer({0, 1, 2, 3}); + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}}); + } + // RGB32F case + { + utils::ComboVertexStateDescriptor vertexState = + MakeVertexState({{0, InputStepMode::Vertex, {{0, 0, VertexFormat::Float3}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 0, {{0, VertexFormat::Float3, InputStepMode::Vertex}}); + + wgpu::Buffer buffer0 = MakeVertexBuffer({0, 1, 2, 3}); + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}}); + } +} + +// Test a stride larger than the attributes +TEST_P(VertexStateTest, StrideLargerThanAttributes) { + // This test was failing only on AMD but the OpenGL backend doesn't gather PCI info yet. + DAWN_SKIP_TEST_IF(IsLinux() && IsOpenGL()); + + utils::ComboVertexStateDescriptor vertexState = MakeVertexState( + {{8 * sizeof(float), InputStepMode::Vertex, {{0, 0, VertexFormat::Float4}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Vertex}}); + + // clang-format off + wgpu::Buffer buffer0 = MakeVertexBuffer({ + 0, 1, 2, 3, 0, 0, 0, 0, + 1, 2, 3, 4, 0, 0, 0, 0, + 2, 3, 4, 5, 0, 0, 0, 0, + }); + // clang-format on + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}}); +} + +// Test two attributes at an offset, vertex version +TEST_P(VertexStateTest, TwoAttributesAtAnOffsetVertex) { + utils::ComboVertexStateDescriptor vertexState = MakeVertexState( + {{8 * sizeof(float), + InputStepMode::Vertex, + {{0, 0, VertexFormat::Float4}, {1, 4 * sizeof(float), VertexFormat::Float4}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Vertex}}); + + // clang-format off + wgpu::Buffer buffer0 = MakeVertexBuffer({ + 0, 1, 2, 3, 0, 1, 2, 3, + 1, 2, 3, 4, 1, 2, 3, 4, + 2, 3, 4, 5, 2, 3, 4, 5, + }); + // clang-format on + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}}); +} + +// Test two attributes at an offset, instance version +TEST_P(VertexStateTest, TwoAttributesAtAnOffsetInstance) { + utils::ComboVertexStateDescriptor vertexState = MakeVertexState( + {{8 * sizeof(float), + InputStepMode::Instance, + {{0, 0, VertexFormat::Float4}, {1, 4 * sizeof(float), VertexFormat::Float4}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Instance}}); + + // clang-format off + wgpu::Buffer buffer0 = MakeVertexBuffer({ + 0, 1, 2, 3, 0, 1, 2, 3, + 1, 2, 3, 4, 1, 2, 3, 4, + 2, 3, 4, 5, 2, 3, 4, 5, + }); + // clang-format on + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{0, &buffer0}}); +} + +// Test a pure-instance input state +TEST_P(VertexStateTest, PureInstance) { + utils::ComboVertexStateDescriptor vertexState = MakeVertexState( + {{4 * sizeof(float), InputStepMode::Instance, {{0, 0, VertexFormat::Float4}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Instance}}); + + // clang-format off + wgpu::Buffer buffer0 = MakeVertexBuffer({ + 0, 1, 2, 3, + 1, 2, 3, 4, + 2, 3, 4, 5, + 3, 4, 5, 6, + }); + // clang-format on + DoTestDraw(pipeline, 1, 4, {DrawVertexBuffer{0, &buffer0}}); +} + +// Test with mixed everything, vertex vs. instance, different stride and offsets +// different attribute types +TEST_P(VertexStateTest, MixedEverything) { + utils::ComboVertexStateDescriptor vertexState = MakeVertexState( + {{12 * sizeof(float), + InputStepMode::Vertex, + {{0, 0, VertexFormat::Float}, {1, 6 * sizeof(float), VertexFormat::Float2}}}, + {10 * sizeof(float), + InputStepMode::Instance, + {{2, 0, VertexFormat::Float3}, {3, 5 * sizeof(float), VertexFormat::Float4}}}}); + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 1, + {{0, VertexFormat::Float, InputStepMode::Vertex}, + {1, VertexFormat::Float2, InputStepMode::Vertex}, + {2, VertexFormat::Float3, InputStepMode::Instance}, + {3, VertexFormat::Float4, InputStepMode::Instance}}); + + // clang-format off + wgpu::Buffer buffer0 = MakeVertexBuffer({ + 0, 1, 2, 3, 0, 0, 0, 1, 2, 3, 0, 0, + 1, 2, 3, 4, 0, 0, 1, 2, 3, 4, 0, 0, + 2, 3, 4, 5, 0, 0, 2, 3, 4, 5, 0, 0, + 3, 4, 5, 6, 0, 0, 3, 4, 5, 6, 0, 0, + }); + wgpu::Buffer buffer1 = MakeVertexBuffer({ + 0, 1, 2, 3, 0, 0, 1, 2, 3, 0, + 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, + 2, 3, 4, 5, 0, 2, 3, 4, 5, 0, + 3, 4, 5, 6, 0, 3, 4, 5, 6, 0, + }); + // clang-format on + DoTestDraw(pipeline, 1, 1, {{0, &buffer0}, {1, &buffer1}}); +} + +// Test input state is unaffected by unused vertex slot +TEST_P(VertexStateTest, UnusedVertexSlot) { + // Instance input state, using slot 1 + utils::ComboVertexStateDescriptor instanceVertexState = MakeVertexState( + {{0, InputStepMode::Vertex, {}}, + {4 * sizeof(float), InputStepMode::Instance, {{0, 0, VertexFormat::Float4}}}}); + wgpu::RenderPipeline instancePipeline = MakeTestPipeline( + instanceVertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Instance}}); + + // clang-format off + wgpu::Buffer buffer = MakeVertexBuffer({ + 0, 1, 2, 3, + 1, 2, 3, 4, + 2, 3, 4, 5, + 3, 4, 5, 6, + }); + // clang-format on + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetVertexBuffer(0, buffer); + pass.SetVertexBuffer(1, buffer); + + pass.SetPipeline(instancePipeline); + pass.Draw(3, 4); + + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + CheckResult(1, 4); +} + +// Test setting a different pipeline with a different input state. +// This was a problem with the D3D12 backend where SetVertexBuffer +// was getting the input from the last set pipeline, not the current. +// SetVertexBuffer should be reapplied when the input state changes. +TEST_P(VertexStateTest, MultiplePipelinesMixedVertexState) { + // Basic input state, using slot 0 + utils::ComboVertexStateDescriptor vertexVertexState = MakeVertexState( + {{4 * sizeof(float), InputStepMode::Vertex, {{0, 0, VertexFormat::Float4}}}}); + wgpu::RenderPipeline vertexPipeline = + MakeTestPipeline(vertexVertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Vertex}}); + + // Instance input state, using slot 1 + utils::ComboVertexStateDescriptor instanceVertexState = MakeVertexState( + {{0, InputStepMode::Instance, {}}, + {4 * sizeof(float), InputStepMode::Instance, {{0, 0, VertexFormat::Float4}}}}); + wgpu::RenderPipeline instancePipeline = MakeTestPipeline( + instanceVertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Instance}}); + + // clang-format off + wgpu::Buffer buffer = MakeVertexBuffer({ + 0, 1, 2, 3, + 1, 2, 3, 4, + 2, 3, 4, 5, + 3, 4, 5, 6, + }); + // clang-format on + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetVertexBuffer(0, buffer); + pass.SetVertexBuffer(1, buffer); + + pass.SetPipeline(vertexPipeline); + pass.Draw(3); + + pass.SetPipeline(instancePipeline); + pass.Draw(3, 4); + + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + CheckResult(1, 4); +} + +// Checks that using the last vertex buffer doesn't overflow the vertex buffer table in Metal. +TEST_P(VertexStateTest, LastAllowedVertexBuffer) { + constexpr uint32_t kBufferIndex = kMaxVertexBuffers - 1; + + utils::ComboVertexStateDescriptor vertexState; + // All the other vertex buffers default to no attributes + vertexState.vertexBufferCount = kMaxVertexBuffers; + vertexState.cVertexBuffers[kBufferIndex].arrayStride = 4 * sizeof(float); + vertexState.cVertexBuffers[kBufferIndex].stepMode = InputStepMode::Vertex; + vertexState.cVertexBuffers[kBufferIndex].attributeCount = 1; + vertexState.cVertexBuffers[kBufferIndex].attributes = &vertexState.cAttributes[0]; + vertexState.cAttributes[0].shaderLocation = 0; + vertexState.cAttributes[0].offset = 0; + vertexState.cAttributes[0].format = VertexFormat::Float4; + + wgpu::RenderPipeline pipeline = + MakeTestPipeline(vertexState, 1, {{0, VertexFormat::Float4, InputStepMode::Vertex}}); + + wgpu::Buffer buffer0 = MakeVertexBuffer({0, 1, 2, 3, 1, 2, 3, 4, 2, 3, 4, 5}); + DoTestDraw(pipeline, 1, 1, {DrawVertexBuffer{kMaxVertexBuffers - 1, &buffer0}}); +} + +// Test that overlapping vertex attributes are permitted and load data correctly +TEST_P(VertexStateTest, OverlappingVertexAttributes) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 3, 3); + + utils::ComboVertexStateDescriptor vertexState = + MakeVertexState({{16, + InputStepMode::Vertex, + { + // "****" represents the bytes we'll actually read in the shader. + {0, 0 /* offset */, VertexFormat::Float4}, // |****|----|----|----| + {1, 4 /* offset */, VertexFormat::UInt2}, // |****|****| + {2, 8 /* offset */, VertexFormat::Half4}, // |-----****| + {3, 0 /* offset */, VertexFormat::Float}, // |****| + }}}); + + struct Data { + float fvalue; + uint32_t uints[2]; + uint16_t halfs[2]; + }; + static_assert(sizeof(Data) == 16, ""); + Data data{1.f, {2u, 3u}, {Float32ToFloat16(4.f), Float32ToFloat16(5.f)}}; + + wgpu::Buffer vertexBuffer = + utils::CreateBufferFromData(device, &data, sizeof(data), wgpu::BufferUsage::Vertex); + + utils::ComboRenderPipelineDescriptor pipelineDesc(device); + pipelineDesc.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(location = 0) in vec4 attr0; + layout(location = 1) in uvec2 attr1; + layout(location = 2) in vec4 attr2; + layout(location = 3) in float attr3; + + layout(location = 0) out vec4 color; + + void main() { + gl_Position = vec4(0.0, 0.0, 0.0, 1.0); + gl_PointSize = 1.0; + + bool success = ( + attr0.x == 1.0f && + attr1.x == 2u && + attr1.y == 3u && + attr2.z == 4.0f && + attr2.w == 5.0f && + attr3 == 1.0f + ); + color = success ? vec4(0.0, 1.0, 0.0, 1.0) : vec4(1.0, 0.0, 0.0, 1.0); + })"); + pipelineDesc.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) in vec4 color; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = color; + })"); + pipelineDesc.vertexState = &vertexState; + pipelineDesc.cColorStates[0].format = renderPass.colorFormat; + pipelineDesc.primitiveTopology = wgpu::PrimitiveTopology::PointList; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(1); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 1, 1); +} + +DAWN_INSTANTIATE_TEST(VertexStateTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); + +// TODO for the input state: +// - Add more vertex formats +// - Add checks that the stride is enough to contain all attributes +// - Add checks stride less than some limit +// - Add checks for alignement of vertex buffers and attributes if needed +// - Check for attribute narrowing +// - Check that the input state and the pipeline vertex input types match + +class OptionalVertexStateTest : public DawnTest {}; + +// Test that vertex input is not required in render pipeline descriptor. +TEST_P(OptionalVertexStateTest, Basic) { + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 3, 3); + + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.0f, 0.0f, 0.0f, 1.0f); + gl_PointSize = 1.0; + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(0.0f, 1.0f, 0.0f, 1.0f); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList; + descriptor.vertexState = nullptr; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + pass.SetPipeline(pipeline); + pass.Draw(1); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 1, 1); +} + +DAWN_INSTANTIATE_TEST(OptionalVertexStateTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/ViewportOrientationTests.cpp b/third_party/dawn/src/tests/end2end/ViewportOrientationTests.cpp index b23d4d1e17f..7c2a04af085 100644 --- a/third_party/dawn/src/tests/end2end/ViewportOrientationTests.cpp +++ b/third_party/dawn/src/tests/end2end/ViewportOrientationTests.cpp @@ -15,7 +15,7 @@ #include "tests/DawnTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class ViewportOrientationTests : public DawnTest {}; @@ -23,13 +23,16 @@ class ViewportOrientationTests : public DawnTest {}; TEST_P(ViewportOrientationTests, OriginAt0x0) { utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, 2, 2); - dawn::ShaderModule vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { - gl_Position = vec4(-0.5f, -0.5f, 0.0f, 1.0f); + gl_Position = vec4(-0.5f, 0.5f, 0.0f, 1.0f); + gl_PointSize = 1.0; })"); - dawn::ShaderModule fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -37,28 +40,32 @@ TEST_P(ViewportOrientationTests, OriginAt0x0) { })"); utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; - descriptor.primitiveTopology = dawn::PrimitiveTopology::PointList; - descriptor.cColorStates[0]->format = renderPass.colorFormat; + descriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList; + descriptor.cColorStates[0].format = renderPass.colorFormat; - dawn::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); pass.SetPipeline(pipeline); - pass.Draw(1, 1, 0, 0); + pass.Draw(1); pass.EndPass(); } - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), renderPass.color, 0, 0); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, 0, 1); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, 1, 0); - EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), renderPass.color, 1, 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, 0, 1); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, 1, 0); + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kZero, renderPass.color, 1, 1); } -DAWN_INSTANTIATE_TEST(ViewportOrientationTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend); +DAWN_INSTANTIATE_TEST(ViewportOrientationTests, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/ViewportTests.cpp b/third_party/dawn/src/tests/end2end/ViewportTests.cpp new file mode 100644 index 00000000000..2145ab445cb --- /dev/null +++ b/third_party/dawn/src/tests/end2end/ViewportTests.cpp @@ -0,0 +1,411 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class ViewportTest : public DawnTest { + protected: + wgpu::RenderPipeline CreatePipelineForTest(wgpu::CompareFunction depthCompare) { + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + + // Draw two triangles: + // 1. The top-left triangle is red. Its depth values are >= 0.5. After viewport is applied, + // the depth might be >= 0.25 if minDepth is 0 and maxDepth is 0.5. + // 2. The bottom-right triangle is green. Its depth values are <= 0.5. After viewport is + // applied, the depth might be <= 0.25 if minDepth is 0 and maxDepth is 0.5. + const char* vs = + R"(#version 450 + layout(location = 0) out vec4 color; + const vec3 pos[6] = vec3[6](vec3(-1.0f, 1.0f, 1.0f), + vec3(-1.0f, -1.0f, 0.5f), + vec3( 1.0f, 1.0f, 0.5f), + vec3( 1.0f, 1.0f, 0.5f), + vec3(-1.0f, -1.0f, 0.5f), + vec3( 1.0f, -1.0f, 0.0f)); + void main() { + gl_Position = vec4(pos[gl_VertexIndex], 1.0); + if (gl_VertexIndex < 3) { + color = vec4(1.0, 0.0, 0.0, 1.0); + } else { + color = vec4(0.0, 1.0, 0.0, 1.0); + } + })"; + pipelineDescriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vs); + + const char* fs = + R"(#version 450 + layout(location = 0) in vec4 color; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = color; + })"; + pipelineDescriptor.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fs); + + pipelineDescriptor.cDepthStencilState.depthCompare = depthCompare; + pipelineDescriptor.depthStencilState = &pipelineDescriptor.cDepthStencilState; + + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + wgpu::Texture Create2DTextureForTest(wgpu::TextureFormat format) { + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.dimension = wgpu::TextureDimension::e2D; + textureDescriptor.format = format; + textureDescriptor.usage = + wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + textureDescriptor.mipLevelCount = 1; + textureDescriptor.sampleCount = 1; + textureDescriptor.size = {kSize, kSize, 1}; + return device.CreateTexture(&textureDescriptor); + } + + enum ColorType { + TopLeftTriangleColor, + BottomRightTriangleColor, + BackgroundColor, + + ColorTypeCount, + }; + + struct ViewportParams { + float x, y, width, height, minDepth, maxDepth; + }; + + struct TestInfo { + ViewportParams viewport; + ColorType topLeftPoint; + ColorType bottomRightPoint; + float clearDepth = 1.0f; + bool setViewport = true; + }; + + void DoTest(const TestInfo& info) { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + + // Create render targets for 2 render passes. + wgpu::Texture colorTexture1 = Create2DTextureForTest(wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture depthStencilTexture1 = + Create2DTextureForTest(wgpu::TextureFormat::Depth24PlusStencil8); + + wgpu::Texture colorTexture2 = Create2DTextureForTest(wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture depthStencilTexture2 = + Create2DTextureForTest(wgpu::TextureFormat::Depth24PlusStencil8); + + // Create render pass 1 + // Note that we may explicitly call SetViewport() in this pass + { + utils::ComboRenderPassDescriptor renderPassDescriptor1( + {colorTexture1.CreateView()}, depthStencilTexture1.CreateView()); + renderPassDescriptor1.cColorAttachments[0].clearColor = {0.0, 0.0, 1.0, 1.0}; + renderPassDescriptor1.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + + renderPassDescriptor1.cDepthStencilAttachmentInfo.clearDepth = info.clearDepth; + renderPassDescriptor1.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; + + wgpu::RenderPassEncoder renderPass1 = + commandEncoder.BeginRenderPass(&renderPassDescriptor1); + renderPass1.SetPipeline(CreatePipelineForTest(wgpu::CompareFunction::Less)); + if (info.setViewport) { + ViewportParams viewport = info.viewport; + renderPass1.SetViewport(viewport.x, viewport.y, viewport.width, viewport.height, + viewport.minDepth, viewport.maxDepth); + } + renderPass1.Draw(6); + renderPass1.EndPass(); + } + + // Create render pass 2 + // Note that we never explicitly call SetViewport() in this pass. + // Its viewport(x, y, width, height, minDepth, maxDepth) should be + // (0, 0, rendertarget's width, rendertarget's height, 0.0, 1.0) by default. + { + utils::ComboRenderPassDescriptor renderPassDescriptor2( + {colorTexture2.CreateView()}, depthStencilTexture2.CreateView()); + renderPassDescriptor2.cColorAttachments[0].clearColor = {0.0, 0.0, 1.0, 1.0}; + renderPassDescriptor2.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + + renderPassDescriptor2.cDepthStencilAttachmentInfo.clearDepth = 0.5; + renderPassDescriptor2.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; + + wgpu::RenderPassEncoder renderPass2 = + commandEncoder.BeginRenderPass(&renderPassDescriptor2); + renderPass2.SetPipeline(CreatePipelineForTest(wgpu::CompareFunction::Greater)); + renderPass2.Draw(6); + renderPass2.EndPass(); + } + + wgpu::CommandBuffer commandBuffer = commandEncoder.Finish(); + queue.Submit(1, &commandBuffer); + + const RGBA8 kColor[ColorTypeCount] = { + RGBA8::kRed, // top-left triangle is red + RGBA8::kGreen, // bottom-right triangle is green + RGBA8::kBlue, // background is blue + }; + + EXPECT_PIXEL_RGBA8_EQ(kColor[info.topLeftPoint], colorTexture1, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(kColor[info.bottomRightPoint], colorTexture1, kSize - 1, kSize - 1); + + // In render pass 2. Point(0, 0) is tend to be covered by the top-left triangle. Point(3, 3) + // is tend to be covered by the bottom-right triangle. However, the bottom-right triangle's + // depth values are <= 0.5. And the depthCompare is Greater. As a result, point(0, 0) will + // be drawn as usual, its color is the top-left triangle's color. But point(3, 3) will not + // be drawn by any triangles. Its color is the backgroud color. + EXPECT_PIXEL_RGBA8_EQ(kColor[TopLeftTriangleColor], colorTexture2, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(kColor[BackgroundColor], colorTexture2, kSize - 1, kSize - 1); + } + + static constexpr uint32_t kSize = 4; +}; + +// The viewport is the same size as the backbuffer if it is not explicitly specified. And minDepth +// and maxDepth are 0.0 and 1.0 respectively. The viewport parameters below are not really used. +// Point(0, 0) is covered by the top-left triangle. Likewise, point(3, 3) is covered by the +// bottom-right triangle. +TEST_P(ViewportTest, Default) { + ViewportParams viewport = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; + TestInfo info = {viewport, TopLeftTriangleColor, BottomRightTriangleColor, 1.0, false}; + DoTest(info); +} + +// Explicitly specify the viewport as its default value. The result is the same as it is in the test +// above. +TEST_P(ViewportTest, Basic) { + ViewportParams viewport = {0.0, 0.0, 4.0, 4.0, 0.0, 1.0}; + TestInfo info = {viewport, TopLeftTriangleColor, BottomRightTriangleColor}; + DoTest(info); +} + +// Shift the viewport toward top-left by (2, 2). So the top-left triangle is outside of the back +// buffer. We can't see it. And point(0, 0) is covered by the bottom-right triangle now. Point(3, 3) +// is not covered by any triangles. +TEST_P(ViewportTest, ShiftToTopLeft) { + ViewportParams viewport = {-2.0, -2.0, 4.0, 4.0, 0.0, 1.0}; + TestInfo info = {viewport, BottomRightTriangleColor, BackgroundColor}; + DoTest(info); +} + +// Shift the viewport toward bottom-right by (2, 2). So Point(0, 0) is not covered by any triangles. +// The top-left triangle is moved to the bottom-right of back buffer. Point(3, 3) is covered by it. +// While the bottom-right triangle is moved outside of back buffer now. +TEST_P(ViewportTest, ShiftToBottomRight) { + ViewportParams viewport = {2.0, 2.0, 4.0, 4.0, 0.0, 1.0}; + TestInfo info = {viewport, BackgroundColor, TopLeftTriangleColor}; + DoTest(info); +} + +// After applying the minDepth/maxDepth value in viewport and projecting to framebuffer coordinate, +// depth values of the top-left triangle are >= 0.25. They are greater than the depth values in +// depth buffer, so it is not drawn at all. As a result, point(0, 0) is not covered by any +// triangles. But the bottom-right triangle is drawn as usual. +TEST_P(ViewportTest, ApplyDepth) { + ViewportParams viewport = {0.0, 0.0, 4.0, 4.0, 0.0, 0.5}; + TestInfo info = {viewport, BackgroundColor, BottomRightTriangleColor, 0.25}; + DoTest(info); +} + +// Shift the viewport toward top-left by (2, 2). So the top-left triangle is outside of the back +// buffer. We can't see it. And point(0, 0) is covered by the bottom-right triangle now. Its depth +// value is < 0.25. So it is drawn as usual. Point(3, 3) is not covered by any triangles. +TEST_P(ViewportTest, ShiftToTopLeftAndApplyDepth) { + // Test failing on Linux Vulkan Intel. + // See https://bugs.chromium.org/p/dawn/issues/detail?id=187 + DAWN_SKIP_TEST_IF(IsLinux() && IsVulkan() && IsIntel()); + + ViewportParams viewport = {-2.0, -2.0, 4.0, 4.0, 0.0, 0.5}; + TestInfo info = {viewport, BottomRightTriangleColor, BackgroundColor, 0.25}; + DoTest(info); +} + +// Shift the viewport toward bottom-right by (2, 2). So point(0, 0) is not covered by any triangles. +// The top-left triangle is moved to the bottom-right of back buffer. However, depth values of the +// top-left triangle are >= 0.25. They are greater than the depth values in depth buffer, so it is +// not drawn at all. So point(3, 3) is not covered by any triangle, either. +TEST_P(ViewportTest, ShiftToBottomRightAndApplyDepth) { + ViewportParams viewport = {2.0, 2.0, 4.0, 4.0, 0.0, 0.5}; + TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25}; + DoTest(info); +} + +// Enlarge the viewport by 2 times. So the entire back buffer is covered by the top-left triangle. +TEST_P(ViewportTest, EnlargeViewport) { + ViewportParams viewport = {0.0, 0.0, 8.0, 8.0, 0.0, 1.0}; + TestInfo info = {viewport, TopLeftTriangleColor, TopLeftTriangleColor}; + DoTest(info); +} + +// Enlarge the viewport by 2 times and shift toward top-left by (2, 2). back buffer sits exactly +// at the center of the whole viewport. So, point(0, 0) is covered by the top-left triangle, and +// point(3, 3) is covered by the bottom-right triangle. +TEST_P(ViewportTest, EnlargeViewportAndShiftToTopLeft) { + ViewportParams viewport = {-2.0, -2.0, 8.0, 8.0, 0.0, 1.0}; + TestInfo info = {viewport, TopLeftTriangleColor, BottomRightTriangleColor}; + DoTest(info); +} + +// Enlarge the viewport by 2 times and shift toward bottom-right by (2, 2). Point(0, 0) is not +// covered by any triangle. Point(3, 3) is covered by the top-left triangle. +TEST_P(ViewportTest, EnlargeViewportAndShiftToBottomRight) { + ViewportParams viewport = {2.0, 2.0, 8.0, 8.0, 0.0, 1.0}; + TestInfo info = {viewport, BackgroundColor, TopLeftTriangleColor}; + DoTest(info); +} + +// Enlarge the viewport by 2 times. So the entire back buffer tend to be covered by the top-left +// triangle. However, depth values of the top-left triangle are >= 0.25. They are greater than the +// depth values in depth buffer, so the top-left triangle is not drawn at all. As a result, neither +// point(0, 0) nor point(3, 3) is covered by any triangles. +TEST_P(ViewportTest, EnlargeViewportAndApplyDepth) { + ViewportParams viewport = {0.0, 0.0, 8.0, 8.0, 0.0, 0.5}; + TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25}; + DoTest(info); +} + +// Enlarge the viewport by 2 times and shift toward top-left by (2, 2). The back buffer sits exactly +// at the center of the whole viewport. However, depth values of the top-left triangle are >= 0.25. +// They are greater than the depth values in depth buffer, so the top-left triangle is not drawn at +// all. As a result, point(0, 0) is not covered by it. The bottom-right triangle is drawn because +// its depth values are < 0.25. So point(3, 3) is covered by it as usual. +TEST_P(ViewportTest, EnlargeViewportAndShiftToTopLeftAndApplyDepth) { + // Test failing on Linux Vulkan Intel. + // See https://bugs.chromium.org/p/dawn/issues/detail?id=187 + DAWN_SKIP_TEST_IF(IsLinux() && IsVulkan() && IsIntel()); + + ViewportParams viewport = {-2.0, -2.0, 8.0, 8.0, 0.0, 0.5}; + TestInfo info = {viewport, BackgroundColor, BottomRightTriangleColor, 0.25}; + DoTest(info); +} + +// Enlarge the viewport by 2 times and shift toward bottom-right by (2, 2). Point(0, 0) is not +// covered by any triangle. The point(3, 3) tend to be covered by the top-left triangle. However, +// depth values of the top-left triangle are >= 0.25. They are greater than the depth values in +// depth buffer, so the top-left triangle is not drawn at all. As a result, point(3, 3) is not +// covered by any triangle, either. +TEST_P(ViewportTest, EnlargeViewportAndShiftToBottomRightAndApplyDepth) { + ViewportParams viewport = {2.0, 2.0, 8.0, 8.0, 0.0, 0.5}; + TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25}; + DoTest(info); +} + +// Shrink the viewport to its half. So point(0, 0) is covered by the top-left triangle, while +// point(3, 3) is not covered by any triangles because the drawing area is too small to cover the +// entire back buffer. +TEST_P(ViewportTest, ShrinkViewport) { + ViewportParams viewport = {0.0, 0.0, 2.0, 2.0, 0.0, 1.0}; + TestInfo info = {viewport, TopLeftTriangleColor, BackgroundColor}; + DoTest(info); +} + +// Shrink the viewport to its half and move toward top-left by (1, 1), So point(0, 0) is covered by +// bottom-right triangle, while point(3, 3) is not covered by any triangles. +TEST_P(ViewportTest, ShrinkViewportAndShiftToTopLeft) { + ViewportParams viewport = {-1.0, -1.0, 2.0, 2.0, 0.0, 1.0}; + TestInfo info = {viewport, BottomRightTriangleColor, BackgroundColor}; + DoTest(info); +} + +// Shrink the viewport to its half and move toward bottom-right by (3, 3), So point(0, 0) is not +// covered by any triangles, and point(3, 3) is covered by the bottom-right triangle. +TEST_P(ViewportTest, ShrinkViewportAndShiftToBottomRight) { + ViewportParams viewport = {3.0, 3.0, 2.0, 2.0, 0.0, 1.0}; + TestInfo info = {viewport, BackgroundColor, TopLeftTriangleColor}; + DoTest(info); +} + +// Shrink the viewport to its half. So point(0, 0) is tend to be covered by top-left triangle. +// However, depth values of the top-left triangle are >= 0.25. They are greater than the depth +// values in depth buffer, so the top-left triangle is not drawn at all. As a result, point(0, 0) +// is not covered by any triangle. Point(3, 3) is not covered by any triangles, either. Because the +// drawing area is too small to cover the entire back buffer. +TEST_P(ViewportTest, ShrinkViewportAndApplyDepth) { + ViewportParams viewport = {0.0, 0.0, 2.0, 2.0, 0.0, 0.5}; + TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25}; + DoTest(info); +} + +// Shrink the viewport to its half and move toward top-left by (1, 1), So point(0, 0) is covered by +// the bottom-right triangle, while point(3, 3) is not covered by any triangles. +TEST_P(ViewportTest, ShrinkViewportAndShiftToTopLeftAndApplyDepth) { + // Test failing on Linux Vulkan Intel. + // See https://bugs.chromium.org/p/dawn/issues/detail?id=187 + DAWN_SKIP_TEST_IF(IsLinux() && IsVulkan() && IsIntel()); + + ViewportParams viewport = {-1.0, -1.0, 2.0, 2.0, 0.0, 0.5}; + TestInfo info = {viewport, BottomRightTriangleColor, BackgroundColor, 0.25}; + DoTest(info); +} + +// Shrink the viewport to its half and move toward bottom-right by (3, 3), So point(0, 0) is not +// covered by any triangle. Point(3, 3) is tend to be covered by the top-left triangle. However, +// depth values of the top-left triangle are >= 0.25. They are greater than the depth values in +// depth buffer, so the top-left triangle is not drawn at all. As a result, point(3, 3) is not +// covered by any triangle, either. +TEST_P(ViewportTest, ShrinkViewportAndShiftToBottomRightAndApplyDepth) { + ViewportParams viewport = {3.0, 3.0, 2.0, 2.0, 0.0, 0.5}; + TestInfo info = {viewport, BackgroundColor, BackgroundColor, 0.25}; + DoTest(info); +} + +// X and y have fractions and they are smaller than 0.5, which is the center of point(0, 0). So +// point(0, 0) is covered by the top left triangle as usual. +TEST_P(ViewportTest, DoNotTruncateXAndY) { + // Swiftshader seems to be using 4 bits of subpixel precision for viewport computations but + // advertises 0 bits of precision. This is within the allowed Vulkan behaviors so this test + // should likely be revisited. + DAWN_SKIP_TEST_IF(IsVulkan() && IsSwiftshader()); + + ViewportParams viewport = {0.49, 0.49, 4.0, 4.0, 0.0, 1.0}; + TestInfo info = {viewport, TopLeftTriangleColor, BottomRightTriangleColor}; + DoTest(info); +} + +// X and y have fractions and they are not smaller than 0.5, which is the center of point(0, 0). So +// point(0, 0) is not covered by any trinagle. +TEST_P(ViewportTest, DoNotTruncateXAndY2) { + ViewportParams viewport = {0.5, 0.5, 4.0, 4.0, 0.0, 1.0}; + TestInfo info = {viewport, BackgroundColor, BottomRightTriangleColor}; + DoTest(info); +} + +// Width and height have fractions and they are greater than 3.5, which is the center of +// point(3, 3). So point(3, 3) is covered by the bottom right triangle as usual. +TEST_P(ViewportTest, DoNotTruncateWidthAndHeight) { + // Test failing on many D3D12 backend and Intel devices. + // It also fails on Vulkan and GL backend on some devices. + // See https://bugs.chromium.org/p/dawn/issues/detail?id=205 + // See https://bugs.chromium.org/p/dawn/issues/detail?id=257 + DAWN_SKIP_TEST_IF(IsIntel() || !IsMetal()); + ViewportParams viewport = {0.0, 0.0, 3.51, 3.51, 0.0, 1.0}; + TestInfo info = {viewport, TopLeftTriangleColor, BottomRightTriangleColor}; + DoTest(info); +} + +// Width and height have fractions and they are not greater than 3.5, which is the center of +// point(3, 3). So point(3, 3) is not covered by any triangle. +TEST_P(ViewportTest, DoNotTruncateWidthAndHeight2) { + ViewportParams viewport = {0.0, 0.0, 3.5, 3.5, 0.0, 1.0}; + TestInfo info = {viewport, TopLeftTriangleColor, BackgroundColor}; + DoTest(info); +} + +DAWN_INSTANTIATE_TEST(ViewportTest, + D3D12Backend(), + MetalBackend(), + OpenGLBackend(), + VulkanBackend()); diff --git a/third_party/dawn/src/tests/end2end/WindowSurfaceTests.cpp b/third_party/dawn/src/tests/end2end/WindowSurfaceTests.cpp new file mode 100644 index 00000000000..8aaa8ccc23e --- /dev/null +++ b/third_party/dawn/src/tests/end2end/WindowSurfaceTests.cpp @@ -0,0 +1,252 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Log.h" +#include "common/Platform.h" +#include "dawn/dawn_proc.h" +#include "dawn_native/DawnNative.h" +#include "tests/DawnTest.h" +#include "utils/GLFWUtils.h" + +#include + +#include + +// Include windows.h before GLFW so GLFW's APIENTRY macro doesn't conflict with windows.h's. +#if defined(DAWN_PLATFORM_WINDOWS) +# include "common/windows_with_undefs.h" +#endif // defined(DAWN_PLATFORM_WINDOWS) + +#include "GLFW/glfw3.h" + +#if defined(DAWN_USE_X11) +# include "common/xlib_with_undefs.h" +#endif // defined(DAWN_USE_X11) + +#if defined(DAWN_ENABLE_BACKEND_METAL) +# include "utils/ObjCUtils.h" +#endif // defined(DAWN_ENABLE_BACKEND_METAL) + +#include "GLFW/glfw3native.h" + +// Test for wgpu::Surface creation that only need an instance (no devices) and don't need all the +// complexity of DawnTest. +class WindowSurfaceInstanceTests : public testing::Test { + public: + void SetUp() override { + glfwSetErrorCallback([](int code, const char* message) { + dawn::ErrorLog() << "GLFW error " << code << " " << message; + }); + DAWN_SKIP_TEST_IF(!glfwInit()); + + DawnProcTable procs = dawn_native::GetProcs(); + dawnProcSetProcs(&procs); + + mInstance = wgpu::CreateInstance(); + } + + void TearDown() override { + if (mWindow != nullptr) { + glfwDestroyWindow(mWindow); + mWindow = nullptr; + } + } + + void AssertSurfaceCreation(const wgpu::SurfaceDescriptor* descriptor, bool succeeds) { + ASSERT_EQ(mInstance.CreateSurface(descriptor).Get() != nullptr, succeeds); + } + + GLFWwindow* CreateWindow() { + // The WindowSurfaceInstance tests don't create devices so we don't need to call + // SetupGLFWWindowHintsForBackend. Set GLFW_NO_API anyway to avoid GLFW bringing up a GL + // context that we won't use. + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + mWindow = glfwCreateWindow(400, 400, "WindowSurfaceInstanceTests window", nullptr, nullptr); + return mWindow; + } + + private: + wgpu::Instance mInstance; + GLFWwindow* mWindow = nullptr; +}; + +// Test that a valid chained descriptor works (and that GLFWUtils creates a valid chained +// descriptor). +TEST_F(WindowSurfaceInstanceTests, ControlCase) { + GLFWwindow* window = CreateWindow(); + std::unique_ptr chainedDescriptor = + utils::SetupWindowAndGetSurfaceDescriptorForTesting(window); + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = chainedDescriptor.get(); + + AssertSurfaceCreation(&descriptor, true); +} + +// Test that just wgpu::SurfaceDescriptor isn't enough and needs a chained descriptor. +TEST_F(WindowSurfaceInstanceTests, NoChainedDescriptors) { + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = nullptr; // That's the default value but we set it for clarity. + + AssertSurfaceCreation(&descriptor, false); +} + +// Test that a chained descriptor with a garbage sType produces an error. +TEST_F(WindowSurfaceInstanceTests, BadChainedDescriptors) { + wgpu::ChainedStruct chainedDescriptor; + chainedDescriptor.sType = wgpu::SType::Invalid; // The default but we set it for clarity. + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = &chainedDescriptor; + + AssertSurfaceCreation(&descriptor, false); +} + +// Test that a chained descriptor with HTMLCanvas produces an error. +TEST_F(WindowSurfaceInstanceTests, HTMLCanvasDescriptor) { + wgpu::SurfaceDescriptorFromCanvasHTMLSelector chainedDescriptor; + chainedDescriptor.selector = "#myCanvas"; + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = &chainedDescriptor; + + AssertSurfaceCreation(&descriptor, false); +} + +// Test that it is invalid to give two valid chained descriptors +TEST_F(WindowSurfaceInstanceTests, TwoChainedDescriptors) { + GLFWwindow* window = CreateWindow(); + std::unique_ptr chainedDescriptor1 = + utils::SetupWindowAndGetSurfaceDescriptorForTesting(window); + std::unique_ptr chainedDescriptor2 = + utils::SetupWindowAndGetSurfaceDescriptorForTesting(window); + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = chainedDescriptor1.get(); + chainedDescriptor1->nextInChain = chainedDescriptor2.get(); + + AssertSurfaceCreation(&descriptor, false); +} + +#if defined(DAWN_PLATFORM_WINDOWS) + +// Tests that GLFWUtils returns a descriptor of HWND type +TEST_F(WindowSurfaceInstanceTests, CorrectSTypeHWND) { + GLFWwindow* window = CreateWindow(); + std::unique_ptr chainedDescriptor = + utils::SetupWindowAndGetSurfaceDescriptorForTesting(window); + ASSERT_EQ(chainedDescriptor->sType, wgpu::SType::SurfaceDescriptorFromWindowsHWND); +} + +// Test with setting an invalid hwnd +TEST_F(WindowSurfaceInstanceTests, InvalidHWND) { + wgpu::SurfaceDescriptorFromWindowsHWND chainedDescriptor; + chainedDescriptor.hinstance = GetModuleHandle(nullptr); + chainedDescriptor.hwnd = 0; // This always is an invalid HWND value. + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = &chainedDescriptor; + AssertSurfaceCreation(&descriptor, false); +} + +#else // defined(DAWN_PLATFORM_WINDOWS) + +// Test using HWND when it is not supported +TEST_F(WindowSurfaceInstanceTests, HWNDSurfacesAreInvalid) { + wgpu::SurfaceDescriptorFromWindowsHWND chainedDescriptor; + chainedDescriptor.hinstance = nullptr; + chainedDescriptor.hwnd = 0; + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = &chainedDescriptor; + AssertSurfaceCreation(&descriptor, false); +} + +#endif // defined(DAWN_PLATFORM_WINDOWS) + +#if defined(DAWN_USE_X11) + +// Tests that GLFWUtils returns a descriptor of Xlib type +TEST_F(WindowSurfaceInstanceTests, CorrectSTypeXlib) { + GLFWwindow* window = CreateWindow(); + std::unique_ptr chainedDescriptor = + utils::SetupWindowAndGetSurfaceDescriptorForTesting(window); + ASSERT_EQ(chainedDescriptor->sType, wgpu::SType::SurfaceDescriptorFromXlib); +} + +// Test with setting an invalid window +TEST_F(WindowSurfaceInstanceTests, InvalidXWindow) { + wgpu::SurfaceDescriptorFromXlib chainedDescriptor; + chainedDescriptor.display = XOpenDisplay(nullptr); + // From the "X Window System Protocol" "X Version 11, Release 6.8" page 2 at + // https://www.x.org/releases/X11R7.5/doc/x11proto/proto.pdf + // WINDOW 32-bit value (top three bits guaranteed to be zero. + // So UINT32_MAX should be an invalid window. + chainedDescriptor.window = 0xFFFFFFFF; + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = &chainedDescriptor; + AssertSurfaceCreation(&descriptor, false); +} + +#else // defined(DAWN_USE_X11) + +// Test using Xlib when it is not supported +TEST_F(WindowSurfaceInstanceTests, XlibSurfacesAreInvalid) { + wgpu::SurfaceDescriptorFromXlib chainedDescriptor; + chainedDescriptor.display = nullptr; + chainedDescriptor.window = 0; + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = &chainedDescriptor; + AssertSurfaceCreation(&descriptor, false); +} + +#endif // defined(DAWN_USE_X11) + +#if defined(DAWN_ENABLE_BACKEND_METAL) + +// Tests that GLFWUtils returns a descriptor of Metal type +TEST_F(WindowSurfaceInstanceTests, CorrectSTypeMetal) { + GLFWwindow* window = CreateWindow(); + std::unique_ptr chainedDescriptor = + utils::SetupWindowAndGetSurfaceDescriptorForTesting(window); + ASSERT_EQ(chainedDescriptor->sType, wgpu::SType::SurfaceDescriptorFromMetalLayer); +} + +// Test with setting an invalid layer +TEST_F(WindowSurfaceInstanceTests, InvalidMetalLayer) { + wgpu::SurfaceDescriptorFromMetalLayer chainedDescriptor; + // The CALayer is autoreleased. Releasing it causes a test failure when the Chromium GTest + // autoreleasepool is emptied. + chainedDescriptor.layer = utils::CreateDummyCALayer(); + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = &chainedDescriptor; + AssertSurfaceCreation(&descriptor, false); +} + +#else // defined(DAWN_ENABLE_BACKEND_METAL) + +// Test using Metal when it is not supported +TEST_F(WindowSurfaceInstanceTests, MetalSurfacesAreInvalid) { + wgpu::SurfaceDescriptorFromMetalLayer chainedDescriptor; + chainedDescriptor.layer = nullptr; + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = &chainedDescriptor; + AssertSurfaceCreation(&descriptor, false); +} + +#endif // defined(DAWN_ENABLE_BACKEND_METAL) diff --git a/third_party/dawn/src/tests/perf_tests/BufferUploadPerf.cpp b/third_party/dawn/src/tests/perf_tests/BufferUploadPerf.cpp new file mode 100644 index 00000000000..49e52f5c095 --- /dev/null +++ b/third_party/dawn/src/tests/perf_tests/BufferUploadPerf.cpp @@ -0,0 +1,156 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/perf_tests/DawnPerfTest.h" + +#include "tests/ParamGenerator.h" +#include "utils/WGPUHelpers.h" + +namespace { + + constexpr unsigned int kNumIterations = 50; + + enum class UploadMethod { + WriteBuffer, + CreateBufferMapped, + }; + + // Perf delta exists between ranges [0, 1MB] vs [1MB, MAX_SIZE). + // These are sample buffer sizes within each range. + enum class UploadSize { + BufferSize_1KB = 1 * 1024, + BufferSize_64KB = 64 * 1024, + BufferSize_1MB = 1 * 1024 * 1024, + + BufferSize_4MB = 4 * 1024 * 1024, + BufferSize_16MB = 16 * 1024 * 1024, + }; + + struct BufferUploadParams : AdapterTestParam { + BufferUploadParams(const AdapterTestParam& param, + UploadMethod uploadMethod, + UploadSize uploadSize) + : AdapterTestParam(param), uploadMethod(uploadMethod), uploadSize(uploadSize) { + } + + UploadMethod uploadMethod; + UploadSize uploadSize; + }; + + std::ostream& operator<<(std::ostream& ostream, const BufferUploadParams& param) { + ostream << static_cast(param); + + switch (param.uploadMethod) { + case UploadMethod::WriteBuffer: + ostream << "_WriteBuffer"; + break; + case UploadMethod::CreateBufferMapped: + ostream << "_CreateBufferMapped"; + break; + } + + switch (param.uploadSize) { + case UploadSize::BufferSize_1KB: + ostream << "_BufferSize_1KB"; + break; + case UploadSize::BufferSize_64KB: + ostream << "_BufferSize_64KB"; + break; + case UploadSize::BufferSize_1MB: + ostream << "_BufferSize_1MB"; + break; + case UploadSize::BufferSize_4MB: + ostream << "_BufferSize_4MB"; + break; + case UploadSize::BufferSize_16MB: + ostream << "_BufferSize_16MB"; + break; + } + + return ostream; + } + +} // namespace + +// Test uploading |kBufferSize| bytes of data |kNumIterations| times. +class BufferUploadPerf : public DawnPerfTestWithParams { + public: + BufferUploadPerf() + : DawnPerfTestWithParams(kNumIterations, 1), + data(static_cast(GetParam().uploadSize)) { + } + ~BufferUploadPerf() override = default; + + void SetUp() override; + + private: + void Step() override; + + wgpu::Buffer dst; + std::vector data; +}; + +void BufferUploadPerf::SetUp() { + DawnPerfTestWithParams::SetUp(); + + wgpu::BufferDescriptor desc = {}; + desc.size = data.size(); + desc.usage = wgpu::BufferUsage::CopyDst; + + dst = device.CreateBuffer(&desc); +} + +void BufferUploadPerf::Step() { + switch (GetParam().uploadMethod) { + case UploadMethod::WriteBuffer: { + for (unsigned int i = 0; i < kNumIterations; ++i) { + queue.WriteBuffer(dst, 0, data.data(), data.size()); + } + // Make sure all WriteBuffer's are flushed. + queue.Submit(0, nullptr); + break; + } + + case UploadMethod::CreateBufferMapped: { + wgpu::BufferDescriptor desc = {}; + desc.size = data.size(); + desc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + for (unsigned int i = 0; i < kNumIterations; ++i) { + auto result = device.CreateBufferMapped(&desc); + memcpy(result.data, data.data(), data.size()); + result.buffer.Unmap(); + encoder.CopyBufferToBuffer(result.buffer, 0, dst, 0, data.size()); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + break; + } + } +} + +TEST_P(BufferUploadPerf, Run) { + RunTest(); +} + +DAWN_INSTANTIATE_PERF_TEST_SUITE_P(BufferUploadPerf, + {D3D12Backend(), MetalBackend(), OpenGLBackend(), + VulkanBackend()}, + {UploadMethod::WriteBuffer, UploadMethod::CreateBufferMapped}, + {UploadSize::BufferSize_1KB, UploadSize::BufferSize_64KB, + UploadSize::BufferSize_1MB, UploadSize::BufferSize_4MB, + UploadSize::BufferSize_16MB}); diff --git a/third_party/dawn/src/tests/perf_tests/DawnPerfTest.cpp b/third_party/dawn/src/tests/perf_tests/DawnPerfTest.cpp new file mode 100644 index 00000000000..58a0cbe6daa --- /dev/null +++ b/third_party/dawn/src/tests/perf_tests/DawnPerfTest.cpp @@ -0,0 +1,403 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/perf_tests/DawnPerfTest.h" + +#include +#include +#include + +#include "common/Assert.h" +#include "common/Log.h" +#include "dawn_platform/tracing/TraceEvent.h" +#include "tests/perf_tests/DawnPerfTestPlatform.h" +#include "utils/Timer.h" + +namespace { + + DawnPerfTestEnvironment* gTestEnv = nullptr; + + void DumpTraceEventsToJSONFile( + const std::vector& traceEventBuffer, + const char* traceFile) { + std::ofstream outFile; + outFile.open(traceFile, std::ios_base::app); + + for (const DawnPerfTestPlatform::TraceEvent& traceEvent : traceEventBuffer) { + const char* category = nullptr; + switch (traceEvent.category) { + case dawn_platform::TraceCategory::General: + category = "general"; + break; + case dawn_platform::TraceCategory::Validation: + category = "validation"; + break; + case dawn_platform::TraceCategory::Recording: + category = "recording"; + break; + case dawn_platform::TraceCategory::GPUWork: + category = "gpu"; + break; + default: + UNREACHABLE(); + } + + uint64_t microseconds = static_cast(traceEvent.timestamp * 1000.0 * 1000.0); + + outFile << ", { " + << "\"name\": \"" << traceEvent.name << "\", " + << "\"cat\": \"" << category << "\", " + << "\"ph\": \"" << traceEvent.phase << "\", " + << "\"id\": " << traceEvent.id << ", " + << "\"tid\": " << traceEvent.threadId << ", " + << "\"ts\": " << microseconds << ", " + << "\"pid\": \"Dawn\"" + << " }"; + } + outFile.close(); + } + +} // namespace + +void InitDawnPerfTestEnvironment(int argc, char** argv) { + gTestEnv = new DawnPerfTestEnvironment(argc, argv); + DawnTestEnvironment::SetEnvironment(gTestEnv); + testing::AddGlobalTestEnvironment(gTestEnv); +} + +DawnPerfTestEnvironment::DawnPerfTestEnvironment(int argc, char** argv) + : DawnTestEnvironment(argc, argv) { + size_t argLen = 0; // Set when parsing --arg=X arguments + for (int i = 1; i < argc; ++i) { + if (strcmp("--calibration", argv[i]) == 0) { + mIsCalibrating = true; + continue; + } + + constexpr const char kOverrideStepsArg[] = "--override-steps="; + argLen = sizeof(kOverrideStepsArg) - 1; + if (strncmp(argv[i], kOverrideStepsArg, argLen) == 0) { + const char* overrideSteps = argv[i] + argLen; + if (overrideSteps[0] != '\0') { + mOverrideStepsToRun = strtoul(overrideSteps, nullptr, 0); + } + continue; + } + + constexpr const char kTraceFileArg[] = "--trace-file="; + argLen = sizeof(kTraceFileArg) - 1; + if (strncmp(argv[i], kTraceFileArg, argLen) == 0) { + const char* traceFile = argv[i] + argLen; + if (traceFile[0] != '\0') { + mTraceFile = traceFile; + } + continue; + } + + if (strcmp("-h", argv[i]) == 0 || strcmp("--help", argv[i]) == 0) { + dawn::InfoLog() + << "Additional flags:" + << " [--calibration] [--override-steps=x] [--trace-file=file]\n" + << " --calibration: Only run calibration. Calibration allows the perf test" + " runner script to save some time.\n" + << " --override-steps: Set a fixed number of steps to run for each test\n" + << " --trace-file: The file to dump trace results.\n"; + continue; + } + } +} + +DawnPerfTestEnvironment::~DawnPerfTestEnvironment() = default; + +void DawnPerfTestEnvironment::SetUp() { + DawnTestEnvironment::SetUp(); + + mPlatform = std::make_unique(); + mInstance->SetPlatform(mPlatform.get()); + + // Begin writing the trace event array. + if (mTraceFile != nullptr) { + std::ofstream outFile; + outFile.open(mTraceFile); + outFile << "{ \"traceEvents\": ["; + outFile << "{}"; // Dummy object so trace events can always prepend a comma + outFile.flush(); + outFile.close(); + } +} + +void DawnPerfTestEnvironment::TearDown() { + // End writing the trace event array. + if (mTraceFile != nullptr) { + std::vector traceEventBuffer = + mPlatform->AcquireTraceEventBuffer(); + + // Write remaining trace events. + DumpTraceEventsToJSONFile(traceEventBuffer, mTraceFile); + + std::ofstream outFile; + outFile.open(mTraceFile, std::ios_base::app); + outFile << "]}"; + outFile << std::endl; + outFile.close(); + } + + DawnTestEnvironment::TearDown(); +} + +bool DawnPerfTestEnvironment::IsCalibrating() const { + return mIsCalibrating; +} + +unsigned int DawnPerfTestEnvironment::OverrideStepsToRun() const { + return mOverrideStepsToRun; +} + +const char* DawnPerfTestEnvironment::GetTraceFile() const { + return mTraceFile; +} + +DawnPerfTestPlatform* DawnPerfTestEnvironment::GetPlatform() const { + return mPlatform.get(); +} + +DawnPerfTestBase::DawnPerfTestBase(DawnTestBase* test, + unsigned int iterationsPerStep, + unsigned int maxStepsInFlight) + : mTest(test), + mIterationsPerStep(iterationsPerStep), + mMaxStepsInFlight(maxStepsInFlight), + mTimer(utils::CreateTimer()) { +} + +DawnPerfTestBase::~DawnPerfTestBase() = default; + +void DawnPerfTestBase::AbortTest() { + mRunning = false; +} + +void DawnPerfTestBase::RunTest() { + if (gTestEnv->OverrideStepsToRun() == 0) { + // Run to compute the approximate number of steps to perform. + mStepsToRun = std::numeric_limits::max(); + + // Do a warmup run for calibration. + DoRunLoop(kCalibrationRunTimeSeconds); + DoRunLoop(kCalibrationRunTimeSeconds); + + // Scale steps down according to the time that exceeded one second. + double scale = kCalibrationRunTimeSeconds / mTimer->GetElapsedTime(); + mStepsToRun = static_cast(static_cast(mNumStepsPerformed) * scale); + + // Calibration allows the perf test runner script to save some time. + if (gTestEnv->IsCalibrating()) { + PrintResult("steps", mStepsToRun, "count", false); + return; + } + } else { + mStepsToRun = gTestEnv->OverrideStepsToRun(); + } + + // Do another warmup run. Seems to consistently improve results. + DoRunLoop(kMaximumRunTimeSeconds); + + DawnPerfTestPlatform* platform = + reinterpret_cast(gTestEnv->GetPlatform()); + const char* testName = ::testing::UnitTest::GetInstance()->current_test_info()->name(); + + // Only enable trace event recording in this section. + // We don't care about trace events during warmup and calibration. + platform->EnableTraceEventRecording(true); + { + TRACE_EVENT0(platform, General, testName); + for (unsigned int trial = 0; trial < kNumTrials; ++trial) { + TRACE_EVENT0(platform, General, "Trial"); + DoRunLoop(kMaximumRunTimeSeconds); + OutputResults(); + } + } + platform->EnableTraceEventRecording(false); +} + +void DawnPerfTestBase::DoRunLoop(double maxRunTime) { + dawn_platform::Platform* platform = gTestEnv->GetPlatform(); + + mNumStepsPerformed = 0; + cpuTime = 0; + mRunning = true; + + wgpu::FenceDescriptor desc = {}; + uint64_t signaledFenceValue = 0; + wgpu::Fence fence = mTest->queue.CreateFence(&desc); + + mTimer->Start(); + + // This loop can be canceled by calling AbortTest(). + while (mRunning) { + // Wait if there are too many steps in flight on the GPU. + while (signaledFenceValue - fence.GetCompletedValue() >= mMaxStepsInFlight) { + mTest->WaitABit(); + } + TRACE_EVENT0(platform, General, "Step"); + double stepStart = mTimer->GetElapsedTime(); + Step(); + cpuTime += mTimer->GetElapsedTime() - stepStart; + + mTest->queue.Signal(fence, ++signaledFenceValue); + + if (mRunning) { + ++mNumStepsPerformed; + if (mTimer->GetElapsedTime() > maxRunTime) { + mRunning = false; + } else if (mNumStepsPerformed >= mStepsToRun) { + mRunning = false; + } + } + } + + // Wait for all GPU commands to complete. + // TODO(enga): When Dawn has multiple backgrounds threads, add a Device::WaitForIdleForTesting() + // which waits for all threads to stop doing work. When we output results, there should + // be no additional incoming trace events. + while (signaledFenceValue != fence.GetCompletedValue()) { + mTest->WaitABit(); + } + + mTimer->Stop(); +} + +void DawnPerfTestBase::OutputResults() { + // TODO(enga): When Dawn has multiple backgrounds threads, add a Device::WaitForIdleForTesting() + // which waits for all threads to stop doing work. When we output results, there should + // be no additional incoming trace events. + DawnPerfTestPlatform* platform = + reinterpret_cast(gTestEnv->GetPlatform()); + + std::vector traceEventBuffer = + platform->AcquireTraceEventBuffer(); + + struct EventTracker { + double start = std::numeric_limits::max(); + double end = 0; + uint32_t count = 0; + }; + + EventTracker validationTracker = {}; + EventTracker recordingTracker = {}; + + double totalValidationTime = 0; + double totalRecordingTime = 0; + + // Note: We assume END timestamps always come after their corresponding BEGIN timestamps. + // TODO(enga): When Dawn has multiple threads, stratify by thread id. + for (const DawnPerfTestPlatform::TraceEvent& traceEvent : traceEventBuffer) { + EventTracker* tracker = nullptr; + double* totalTime = nullptr; + + switch (traceEvent.category) { + case dawn_platform::TraceCategory::Validation: + tracker = &validationTracker; + totalTime = &totalValidationTime; + break; + case dawn_platform::TraceCategory::Recording: + tracker = &recordingTracker; + totalTime = &totalRecordingTime; + break; + default: + break; + } + + if (tracker == nullptr) { + continue; + } + + if (traceEvent.phase == TRACE_EVENT_PHASE_BEGIN) { + tracker->start = std::min(tracker->start, traceEvent.timestamp); + tracker->count++; + } + + if (traceEvent.phase == TRACE_EVENT_PHASE_END) { + tracker->end = std::max(tracker->end, traceEvent.timestamp); + ASSERT(tracker->count > 0); + tracker->count--; + + if (tracker->count == 0) { + *totalTime += (tracker->end - tracker->start); + *tracker = {}; + } + } + } + + PrintPerIterationResultFromSeconds("wall_time", mTimer->GetElapsedTime(), true); + PrintPerIterationResultFromSeconds("cpu_time", cpuTime, true); + PrintPerIterationResultFromSeconds("validation_time", totalValidationTime, true); + PrintPerIterationResultFromSeconds("recording_time", totalRecordingTime, true); + + const char* traceFile = gTestEnv->GetTraceFile(); + if (traceFile != nullptr) { + DumpTraceEventsToJSONFile(traceEventBuffer, traceFile); + } +} + +void DawnPerfTestBase::PrintPerIterationResultFromSeconds(const std::string& trace, + double valueInSeconds, + bool important) const { + if (valueInSeconds == 0) { + return; + } + + double secondsPerIteration = + valueInSeconds / static_cast(mNumStepsPerformed * mIterationsPerStep); + + // Give the result a different name to ensure separate graphs if we transition. + if (secondsPerIteration > 1) { + PrintResult(trace, secondsPerIteration * 1e3, "ms", important); + } else if (secondsPerIteration > 1e-3) { + PrintResult(trace, secondsPerIteration * 1e6, "us", important); + } else { + PrintResult(trace, secondsPerIteration * 1e9, "ns", important); + } +} + +void DawnPerfTestBase::PrintResult(const std::string& trace, + double value, + const std::string& units, + bool important) const { + PrintResultImpl(trace, std::to_string(value), units, important); +} + +void DawnPerfTestBase::PrintResult(const std::string& trace, + unsigned int value, + const std::string& units, + bool important) const { + PrintResultImpl(trace, std::to_string(value), units, important); +} + +void DawnPerfTestBase::PrintResultImpl(const std::string& trace, + const std::string& value, + const std::string& units, + bool important) const { + const ::testing::TestInfo* const testInfo = + ::testing::UnitTest::GetInstance()->current_test_info(); + + std::string metric = std::string(testInfo->test_suite_name()) + "." + trace; + + std::string story = testInfo->name(); + std::replace(story.begin(), story.end(), '/', '_'); + + // The results are printed according to the format specified at + // [chromium]//src/tools/perf/generate_legacy_perf_dashboard_json.py + dawn::InfoLog() << (important ? "*" : "") << "RESULT " << metric << ": " << story << "= " + << value << " " << units; +} diff --git a/third_party/dawn/src/tests/perf_tests/DawnPerfTest.h b/third_party/dawn/src/tests/perf_tests/DawnPerfTest.h new file mode 100644 index 00000000000..57000f89c7c --- /dev/null +++ b/third_party/dawn/src/tests/perf_tests/DawnPerfTest.h @@ -0,0 +1,128 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TESTS_PERFTESTS_DAWNPERFTEST_H_ +#define TESTS_PERFTESTS_DAWNPERFTEST_H_ + +#include "tests/DawnTest.h" + +namespace utils { + class Timer; +} + +class DawnPerfTestPlatform; + +void InitDawnPerfTestEnvironment(int argc, char** argv); + +class DawnPerfTestEnvironment : public DawnTestEnvironment { + public: + DawnPerfTestEnvironment(int argc, char** argv); + ~DawnPerfTestEnvironment() override; + + void SetUp() override; + void TearDown() override; + + bool IsCalibrating() const; + unsigned int OverrideStepsToRun() const; + + // Returns the path to the trace file, or nullptr if traces should + // not be written to a json file. + const char* GetTraceFile() const; + + DawnPerfTestPlatform* GetPlatform() const; + + private: + // Only run calibration which allows the perf test runner to save time. + bool mIsCalibrating = false; + + // If non-zero, overrides the number of steps. + unsigned int mOverrideStepsToRun = 0; + + const char* mTraceFile = nullptr; + + std::unique_ptr mPlatform; +}; + +class DawnPerfTestBase { + static constexpr double kCalibrationRunTimeSeconds = 1.0; + static constexpr double kMaximumRunTimeSeconds = 10.0; + static constexpr unsigned int kNumTrials = 3; + + public: + // Perf test results are reported as the amortized time of |mStepsToRun| * |mIterationsPerStep|. + // A test deriving from |DawnPerfTestBase| must call the base contructor with + // |iterationsPerStep| appropriately to reflect the amount of work performed. + // |maxStepsInFlight| may be used to mimic having multiple frames or workloads in flight which + // is common with double or triple buffered applications. + DawnPerfTestBase(DawnTestBase* test, + unsigned int iterationsPerStep, + unsigned int maxStepsInFlight); + virtual ~DawnPerfTestBase(); + + protected: + // Call if the test step was aborted and the test should stop running. + void AbortTest(); + + void RunTest(); + void PrintPerIterationResultFromSeconds(const std::string& trace, + double valueInSeconds, + bool important) const; + void PrintResult(const std::string& trace, + double value, + const std::string& units, + bool important) const; + void PrintResult(const std::string& trace, + unsigned int value, + const std::string& units, + bool important) const; + + private: + void DoRunLoop(double maxRunTime); + void OutputResults(); + + void PrintResultImpl(const std::string& trace, + const std::string& value, + const std::string& units, + bool important) const; + + virtual void Step() = 0; + + DawnTestBase* mTest; + bool mRunning = false; + const unsigned int mIterationsPerStep; + const unsigned int mMaxStepsInFlight; + unsigned int mStepsToRun = 0; + unsigned int mNumStepsPerformed = 0; + double cpuTime; + std::unique_ptr mTimer; +}; + +template +class DawnPerfTestWithParams : public DawnTestWithParams, public DawnPerfTestBase { + protected: + DawnPerfTestWithParams(unsigned int iterationsPerStep, unsigned int maxStepsInFlight) + : DawnTestWithParams(), + DawnPerfTestBase(this, iterationsPerStep, maxStepsInFlight) { + } + ~DawnPerfTestWithParams() override = default; +}; + +using DawnPerfTest = DawnPerfTestWithParams<>; + +#define DAWN_INSTANTIATE_PERF_TEST_SUITE_P(testName, ...) \ + INSTANTIATE_TEST_SUITE_P( \ + , testName, ::testing::ValuesIn(MakeParamGenerator(__VA_ARGS__)), \ + testing::PrintToStringParamName()) + +#endif // TESTS_PERFTESTS_DAWNPERFTEST_H_ diff --git a/third_party/dawn/src/tests/perf_tests/DawnPerfTestPlatform.cpp b/third_party/dawn/src/tests/perf_tests/DawnPerfTestPlatform.cpp new file mode 100644 index 00000000000..29330352278 --- /dev/null +++ b/third_party/dawn/src/tests/perf_tests/DawnPerfTestPlatform.cpp @@ -0,0 +1,148 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/perf_tests/DawnPerfTestPlatform.h" + +#include + +#include "common/Assert.h" +#include "common/HashUtils.h" +#include "dawn_platform/tracing/TraceEvent.h" +#include "tests/perf_tests/DawnPerfTest.h" +#include "utils/Timer.h" +namespace { + + struct TraceCategoryInfo { + unsigned char enabled; + dawn_platform::TraceCategory category; + }; + + constexpr TraceCategoryInfo gTraceCategories[4] = { + {1, dawn_platform::TraceCategory::General}, + {1, dawn_platform::TraceCategory::Validation}, + {1, dawn_platform::TraceCategory::Recording}, + {1, dawn_platform::TraceCategory::GPUWork}, + }; + + static_assert(static_cast(dawn_platform::TraceCategory::General) == 0, ""); + static_assert(static_cast(dawn_platform::TraceCategory::Validation) == 1, ""); + static_assert(static_cast(dawn_platform::TraceCategory::Recording) == 2, ""); + static_assert(static_cast(dawn_platform::TraceCategory::GPUWork) == 3, ""); + +} // anonymous namespace + +DawnPerfTestPlatform::DawnPerfTestPlatform() + : dawn_platform::Platform(), mTimer(utils::CreateTimer()) { +} + +DawnPerfTestPlatform::~DawnPerfTestPlatform() = default; + +const unsigned char* DawnPerfTestPlatform::GetTraceCategoryEnabledFlag( + dawn_platform::TraceCategory category) { + switch (category) { + case dawn_platform::TraceCategory::General: + case dawn_platform::TraceCategory::Validation: + case dawn_platform::TraceCategory::Recording: + case dawn_platform::TraceCategory::GPUWork: + break; + default: + UNREACHABLE(); + } + return &gTraceCategories[static_cast(category)].enabled; +} + +double DawnPerfTestPlatform::MonotonicallyIncreasingTime() { + // Move the time origin to the first call to this function, to avoid generating + // unnecessarily large timestamps. + static double origin = mTimer->GetAbsoluteTime(); + return mTimer->GetAbsoluteTime() - origin; +} + +std::vector* DawnPerfTestPlatform::GetLocalTraceEventBuffer() { + // Cache the pointer to the vector in thread_local storage + thread_local std::vector* traceEventBuffer = nullptr; + + if (traceEventBuffer == nullptr) { + auto buffer = std::make_unique>(); + traceEventBuffer = buffer.get(); + + // Add a new buffer to the map + std::lock_guard guard(mTraceEventBufferMapMutex); + mTraceEventBuffers[std::this_thread::get_id()] = std::move(buffer); + } + + return traceEventBuffer; +} + +// TODO(enga): Simplify this API. +uint64_t DawnPerfTestPlatform::AddTraceEvent(char phase, + const unsigned char* categoryGroupEnabled, + const char* name, + uint64_t id, + double timestamp, + int numArgs, + const char** argNames, + const unsigned char* argTypes, + const uint64_t* argValues, + unsigned char flags) { + if (!mRecordTraceEvents) { + return 0; + } + + // Discover the category name based on categoryGroupEnabled. This flag comes from the first + // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories. + static_assert(offsetof(TraceCategoryInfo, enabled) == 0, + "|enabled| must be the first field of the TraceCategoryInfo class."); + + const TraceCategoryInfo* info = + reinterpret_cast(categoryGroupEnabled); + + std::vector* buffer = GetLocalTraceEventBuffer(); + buffer->emplace_back(phase, info->category, name, id, timestamp); + + size_t hash = 0; + HashCombine(&hash, buffer->size()); + HashCombine(&hash, std::this_thread::get_id()); + return static_cast(hash); +} + +void DawnPerfTestPlatform::EnableTraceEventRecording(bool enable) { + mRecordTraceEvents = enable; +} + +std::vector DawnPerfTestPlatform::AcquireTraceEventBuffer() { + std::vector traceEventBuffer; + { + // AcquireTraceEventBuffer should only be called when Dawn is completely idle. There should + // be no threads inserting trace events. + // Right now, this is safe because AcquireTraceEventBuffer is called after waiting on a + // fence for all GPU commands to finish executing. When Dawn has multiple background threads + // for other work (creation, validation, submission, residency, etc), we will need to ensure + // all work on those threads is stopped as well. + std::lock_guard guard(mTraceEventBufferMapMutex); + for (auto it = mTraceEventBuffers.begin(); it != mTraceEventBuffers.end(); ++it) { + std::ostringstream stream; + stream << it->first; + std::string threadId = stream.str(); + + std::transform(it->second->begin(), it->second->end(), + std::back_inserter(traceEventBuffer), [&threadId](TraceEvent ev) { + ev.threadId = threadId; + return ev; + }); + it->second->clear(); + } + } + return traceEventBuffer; +} diff --git a/third_party/dawn/src/tests/perf_tests/DawnPerfTestPlatform.h b/third_party/dawn/src/tests/perf_tests/DawnPerfTestPlatform.h new file mode 100644 index 00000000000..3fef9e92b66 --- /dev/null +++ b/third_party/dawn/src/tests/perf_tests/DawnPerfTestPlatform.h @@ -0,0 +1,91 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TESTS_PERFTESTS_DAWNPERFTESTPLATFORM_H_ +#define TESTS_PERFTESTS_DAWNPERFTESTPLATFORM_H_ + +#include + +#include +#include +#include +#include +#include + +namespace utils { + class Timer; +} + +class DawnPerfTestPlatform : public dawn_platform::Platform { + public: + // These are trace events according to Google's "Trace Event Format". + // See https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU + // Only a subset of the properties are implemented. + struct TraceEvent final { + TraceEvent() { + } + TraceEvent(char phaseIn, + dawn_platform::TraceCategory categoryIn, + const char* nameIn, + uint64_t idIn, + double timestampIn) + : phase(phaseIn), category(categoryIn), name(nameIn), id(idIn), timestamp(timestampIn) { + } + + char phase = 0; + dawn_platform::TraceCategory category; + const char* name = nullptr; + uint64_t id = 0; + std::string threadId; + double timestamp = 0; + }; + + DawnPerfTestPlatform(); + ~DawnPerfTestPlatform() override; + + void EnableTraceEventRecording(bool enable); + std::vector AcquireTraceEventBuffer(); + + private: + const unsigned char* GetTraceCategoryEnabledFlag( + dawn_platform::TraceCategory category) override; + + double MonotonicallyIncreasingTime() override; + + std::vector* GetLocalTraceEventBuffer(); + + uint64_t AddTraceEvent(char phase, + const unsigned char* categoryGroupEnabled, + const char* name, + uint64_t id, + double timestamp, + int numArgs, + const char** argNames, + const unsigned char* argTypes, + const uint64_t* argValues, + unsigned char flags) override; + + bool mRecordTraceEvents = false; + std::unique_ptr mTimer; + + // Trace event record. + // Each uses their own trace event buffer, but the PerfTestPlatform owns all of them in + // this map. The map stores all of them so we can iterate through them and flush when + // AcquireTraceEventBuffer is called. + std::unordered_map>> + mTraceEventBuffers; + std::mutex mTraceEventBufferMapMutex; +}; + +#endif // TESTS_PERFTESTS_DAWNPERFTESTPLATFORM_H_ diff --git a/third_party/dawn/src/tests/perf_tests/DrawCallPerf.cpp b/third_party/dawn/src/tests/perf_tests/DrawCallPerf.cpp new file mode 100644 index 00000000000..1b1fe5899f8 --- /dev/null +++ b/third_party/dawn/src/tests/perf_tests/DrawCallPerf.cpp @@ -0,0 +1,668 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/perf_tests/DawnPerfTest.h" + +#include "common/Assert.h" +#include "common/Constants.h" +#include "common/Math.h" +#include "tests/ParamGenerator.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +namespace { + + constexpr unsigned int kNumDraws = 2000; + + constexpr uint32_t kTextureSize = 64; + constexpr size_t kUniformSize = 3 * sizeof(float); + + constexpr float kVertexData[12] = { + 0.0f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, 0.0f, 1.0f, + }; + + constexpr char kVertexShader[] = R"( + #version 450 + layout(location = 0) in vec4 pos; + void main() { + gl_Position = pos; + })"; + + constexpr char kFragmentShaderA[] = R"( + #version 450 + layout (std140, set = 0, binding = 0) uniform Uniforms { + vec3 color; + }; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(color / 5000., 1.0); + })"; + + constexpr char kFragmentShaderB[] = R"( + #version 450 + layout (std140, set = 0, binding = 0) uniform Constants { + vec3 colorA; + }; + layout (std140, set = 1, binding = 0) uniform Uniforms { + vec3 colorB; + }; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4((colorA + colorB) / 5000., 1.0); + })"; + + enum class Pipeline { + Static, // Keep the same pipeline for all draws. + Redundant, // Use the same pipeline, but redundantly set it. + Dynamic, // Change the pipeline between draws. + }; + + enum class UniformData { + Static, // Don't update per-draw uniform data. + Dynamic, // Update the per-draw uniform data once per frame. + }; + + enum class BindGroup { + NoChange, // Use one bind group for all draws. + Redundant, // Use the same bind group, but redundantly set it. + NoReuse, // Create a new bind group every time. + Multiple, // Use multiple static bind groups. + Dynamic, // Use bind groups with dynamic offsets. + }; + + enum class VertexBuffer { + NoChange, // Use one vertex buffer for all draws. + Multiple, // Use multiple static vertex buffers. + Dynamic, // Switch vertex buffers between draws. + }; + + enum class RenderBundle { + No, // Record commands in a render pass + Yes, // Record commands in a render bundle + }; + + struct DrawCallParam { + Pipeline pipelineType; + VertexBuffer vertexBufferType; + BindGroup bindGroupType; + UniformData uniformDataType; + RenderBundle withRenderBundle; + }; + + using DrawCallParamTuple = + std::tuple; + + template + unsigned int AssignParam(T& lhs, T rhs) { + lhs = rhs; + return 0u; + } + + // This helper function allows creating a DrawCallParam from a list of arguments + // without specifying all of the members. Provided members can be passed once in an arbitrary + // order. Unspecified members default to: + // - Pipeline::Static + // - VertexBuffer::NoChange + // - BindGroup::NoChange + // - UniformData::Static + // - RenderBundle::No + template + DrawCallParam MakeParam(Ts... args) { + // Baseline param + DrawCallParamTuple paramTuple{Pipeline::Static, VertexBuffer::NoChange, BindGroup::NoChange, + UniformData::Static, RenderBundle::No}; + + unsigned int unused[] = { + 0, // Avoid making a 0-sized array. + AssignParam(std::get(paramTuple), args)..., + }; + DAWN_UNUSED(unused); + + return DrawCallParam{ + std::get(paramTuple), std::get(paramTuple), + std::get(paramTuple), std::get(paramTuple), + std::get(paramTuple), + }; + } + + struct DrawCallParamForTest : AdapterTestParam { + DrawCallParamForTest(const AdapterTestParam& backendParam, DrawCallParam param) + : AdapterTestParam(backendParam), param(param) { + } + DrawCallParam param; + }; + + std::ostream& operator<<(std::ostream& ostream, const DrawCallParamForTest& testParams) { + ostream << static_cast(testParams); + + const DrawCallParam& param = testParams.param; + + switch (param.pipelineType) { + case Pipeline::Static: + break; + case Pipeline::Redundant: + ostream << "_RedundantPipeline"; + break; + case Pipeline::Dynamic: + ostream << "_DynamicPipeline"; + break; + } + + switch (param.vertexBufferType) { + case VertexBuffer::NoChange: + break; + case VertexBuffer::Multiple: + ostream << "_MultipleVertexBuffers"; + break; + case VertexBuffer::Dynamic: + ostream << "_DynamicVertexBuffer"; + } + + switch (param.bindGroupType) { + case BindGroup::NoChange: + break; + case BindGroup::Redundant: + ostream << "_RedundantBindGroups"; + break; + case BindGroup::NoReuse: + ostream << "_NoReuseBindGroups"; + break; + case BindGroup::Multiple: + ostream << "_MultipleBindGroups"; + break; + case BindGroup::Dynamic: + ostream << "_DynamicBindGroup"; + break; + } + + switch (param.uniformDataType) { + case UniformData::Static: + break; + case UniformData::Dynamic: + ostream << "_DynamicData"; + break; + } + + switch (param.withRenderBundle) { + case RenderBundle::No: + break; + case RenderBundle::Yes: + ostream << "_RenderBundle"; + break; + } + + return ostream; + } + +} // anonymous namespace + +// DrawCallPerf is an uber-benchmark with supports many parameterizations. +// The specific parameterizations we care about are explicitly instantiated at the bottom +// of this test file. +// DrawCallPerf tests drawing a simple triangle with many ways of encoding commands, +// binding, and uploading data to the GPU. The rationale for this is the following: +// - Static/Multiple/Dynamic vertex buffers: Tests switching buffer bindings. This has +// a state tracking cost as well as a GPU driver cost. +// - Static/Multiple/Dynamic bind groups: Same rationale as vertex buffers +// - Static/Dynamic pipelines: In addition to a change to GPU state, changing the pipeline +// layout incurs additional state tracking costs in Dawn. +// - With/Without render bundles: All of the above can have lower validation costs if +// precomputed in a render bundle. +// - Static/Dynamic data: Updating data for each draw is a common use case. It also tests +// the efficiency of resource transitions. +class DrawCallPerf : public DawnPerfTestWithParams { + public: + DrawCallPerf() : DawnPerfTestWithParams(kNumDraws, 3) { + } + ~DrawCallPerf() override = default; + + void SetUp() override; + + protected: + DrawCallParam GetParam() const { + return DawnPerfTestWithParams::GetParam().param; + } + + template + void RecordRenderCommands(Encoder encoder); + + private: + void Step() override; + + // One large dynamic vertex buffer, or multiple separate vertex buffers. + wgpu::Buffer mVertexBuffers[kNumDraws]; + size_t mAlignedVertexDataSize; + + std::vector mUniformBufferData; + // One large dynamic uniform buffer, or multiple separate uniform buffers. + wgpu::Buffer mUniformBuffers[kNumDraws]; + + wgpu::BindGroupLayout mUniformBindGroupLayout; + // One dynamic bind group or multiple bind groups. + wgpu::BindGroup mUniformBindGroups[kNumDraws]; + size_t mAlignedUniformSize; + size_t mNumUniformFloats; + + wgpu::BindGroupLayout mConstantBindGroupLayout; + wgpu::BindGroup mConstantBindGroup; + + // If the pipeline is static, only the first is used. + // Otherwise, the test alternates between two pipelines for each draw. + wgpu::RenderPipeline mPipelines[2]; + + wgpu::TextureView mColorAttachment; + wgpu::TextureView mDepthStencilAttachment; + + wgpu::RenderBundle mRenderBundle; +}; + +void DrawCallPerf::SetUp() { + DawnPerfTestWithParams::SetUp(); + + // Compute aligned uniform / vertex data sizes. + mAlignedUniformSize = Align(kUniformSize, kMinDynamicBufferOffsetAlignment); + mAlignedVertexDataSize = Align(sizeof(kVertexData), 4); + + // Initialize uniform buffer data. + mNumUniformFloats = mAlignedUniformSize / sizeof(float); + mUniformBufferData = std::vector(kNumDraws * mNumUniformFloats, 0.0); + + // Create the color / depth stencil attachments. + { + wgpu::TextureDescriptor descriptor = {}; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kTextureSize; + descriptor.size.height = kTextureSize; + descriptor.size.depth = 1; + descriptor.usage = wgpu::TextureUsage::OutputAttachment; + + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + mColorAttachment = device.CreateTexture(&descriptor).CreateView(); + + descriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; + mDepthStencilAttachment = device.CreateTexture(&descriptor).CreateView(); + } + + // Create vertex buffer(s) + switch (GetParam().vertexBufferType) { + case VertexBuffer::NoChange: + mVertexBuffers[0] = utils::CreateBufferFromData( + device, kVertexData, sizeof(kVertexData), wgpu::BufferUsage::Vertex); + break; + + case VertexBuffer::Multiple: { + for (uint32_t i = 0; i < kNumDraws; ++i) { + mVertexBuffers[i] = utils::CreateBufferFromData( + device, kVertexData, sizeof(kVertexData), wgpu::BufferUsage::Vertex); + } + break; + } + + case VertexBuffer::Dynamic: { + std::vector data(mAlignedVertexDataSize * kNumDraws); + for (uint32_t i = 0; i < kNumDraws; ++i) { + memcpy(data.data() + mAlignedVertexDataSize * i, kVertexData, sizeof(kVertexData)); + } + + mVertexBuffers[0] = utils::CreateBufferFromData(device, data.data(), data.size(), + wgpu::BufferUsage::Vertex); + break; + } + } + + // Create the bind group layout. + switch (GetParam().bindGroupType) { + case BindGroup::NoChange: + case BindGroup::Redundant: + case BindGroup::NoReuse: + case BindGroup::Multiple: + mUniformBindGroupLayout = utils::MakeBindGroupLayout( + device, + { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, false}, + }); + break; + + case BindGroup::Dynamic: + mUniformBindGroupLayout = utils::MakeBindGroupLayout( + device, + { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, true}, + }); + break; + + default: + UNREACHABLE(); + break; + } + + // Setup the base render pipeline descriptor. + utils::ComboRenderPipelineDescriptor renderPipelineDesc(device); + renderPipelineDesc.cVertexState.vertexBufferCount = 1; + renderPipelineDesc.cVertexState.cVertexBuffers[0].arrayStride = 4 * sizeof(float); + renderPipelineDesc.cVertexState.cVertexBuffers[0].attributeCount = 1; + renderPipelineDesc.cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float4; + renderPipelineDesc.depthStencilState = &renderPipelineDesc.cDepthStencilState; + renderPipelineDesc.cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; + renderPipelineDesc.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; + + // Create the pipeline layout for the first pipeline. + wgpu::PipelineLayoutDescriptor pipelineLayoutDesc = {}; + pipelineLayoutDesc.bindGroupLayouts = &mUniformBindGroupLayout; + pipelineLayoutDesc.bindGroupLayoutCount = 1; + wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc); + + // Create the shaders for the first pipeline. + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, kVertexShader); + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, kFragmentShaderA); + + // Create the first pipeline. + renderPipelineDesc.layout = pipelineLayout; + renderPipelineDesc.vertexStage.module = vsModule; + renderPipelineDesc.cFragmentStage.module = fsModule; + mPipelines[0] = device.CreateRenderPipeline(&renderPipelineDesc); + + // If the test is using a dynamic pipeline, create the second pipeline. + if (GetParam().pipelineType == Pipeline::Dynamic) { + // Create another bind group layout. The data for this binding point will be the same for + // all draws. + mConstantBindGroupLayout = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, false}, + }); + + // Create the pipeline layout. + wgpu::BindGroupLayout bindGroupLayouts[2] = { + mConstantBindGroupLayout, + mUniformBindGroupLayout, + }; + pipelineLayoutDesc.bindGroupLayouts = bindGroupLayouts, + pipelineLayoutDesc.bindGroupLayoutCount = 2; + wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc); + + // Create the fragment shader module. This shader matches the pipeline layout described + // above. + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, kFragmentShaderB); + + // Create the pipeline. + renderPipelineDesc.layout = pipelineLayout; + renderPipelineDesc.cFragmentStage.module = fsModule; + mPipelines[1] = device.CreateRenderPipeline(&renderPipelineDesc); + + // Create the buffer and bind group to bind to the constant bind group layout slot. + constexpr float kConstantData[] = {0.01, 0.02, 0.03}; + wgpu::Buffer constantBuffer = utils::CreateBufferFromData( + device, kConstantData, sizeof(kConstantData), wgpu::BufferUsage::Uniform); + mConstantBindGroup = utils::MakeBindGroup(device, mConstantBindGroupLayout, + {{0, constantBuffer, 0, sizeof(kConstantData)}}); + } + + // Create the buffers and bind groups for the per-draw uniform data. + switch (GetParam().bindGroupType) { + case BindGroup::NoChange: + case BindGroup::Redundant: + mUniformBuffers[0] = utils::CreateBufferFromData( + device, mUniformBufferData.data(), 3 * sizeof(float), wgpu::BufferUsage::Uniform); + + mUniformBindGroups[0] = utils::MakeBindGroup( + device, mUniformBindGroupLayout, {{0, mUniformBuffers[0], 0, kUniformSize}}); + break; + + case BindGroup::NoReuse: + for (uint32_t i = 0; i < kNumDraws; ++i) { + mUniformBuffers[i] = utils::CreateBufferFromData( + device, mUniformBufferData.data() + i * mNumUniformFloats, 3 * sizeof(float), + wgpu::BufferUsage::Uniform); + } + // Bind groups are created on-the-fly. + break; + + case BindGroup::Multiple: + for (uint32_t i = 0; i < kNumDraws; ++i) { + mUniformBuffers[i] = utils::CreateBufferFromData( + device, mUniformBufferData.data() + i * mNumUniformFloats, 3 * sizeof(float), + wgpu::BufferUsage::Uniform); + + mUniformBindGroups[i] = utils::MakeBindGroup( + device, mUniformBindGroupLayout, {{0, mUniformBuffers[i], 0, kUniformSize}}); + } + break; + + case BindGroup::Dynamic: + mUniformBuffers[0] = utils::CreateBufferFromData( + device, mUniformBufferData.data(), mUniformBufferData.size() * sizeof(float), + wgpu::BufferUsage::Uniform); + + mUniformBindGroups[0] = utils::MakeBindGroup( + device, mUniformBindGroupLayout, {{0, mUniformBuffers[0], 0, kUniformSize}}); + break; + default: + UNREACHABLE(); + break; + } + + // If using render bundles, record the render commands now. + if (GetParam().withRenderBundle == RenderBundle::Yes) { + wgpu::RenderBundleEncoderDescriptor descriptor = {}; + descriptor.colorFormatsCount = 1; + descriptor.colorFormats = &renderPipelineDesc.cColorStates[0].format; + descriptor.depthStencilFormat = renderPipelineDesc.cDepthStencilState.format; + + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&descriptor); + RecordRenderCommands(encoder); + mRenderBundle = encoder.Finish(); + } +} + +template +void DrawCallPerf::RecordRenderCommands(Encoder pass) { + uint32_t uniformBindGroupIndex = 0; + + if (GetParam().pipelineType == Pipeline::Static) { + // Static pipeline can be set now. + pass.SetPipeline(mPipelines[0]); + } + + if (GetParam().vertexBufferType == VertexBuffer::NoChange) { + // Static vertex buffer can be set now. + pass.SetVertexBuffer(0, mVertexBuffers[0]); + } + + if (GetParam().bindGroupType == BindGroup::NoChange) { + // Incompatible. Can't change pipeline without changing bind groups. + ASSERT(GetParam().pipelineType == Pipeline::Static); + + // Static bind group can be set now. + pass.SetBindGroup(uniformBindGroupIndex, mUniformBindGroups[0]); + } + + for (unsigned int i = 0; i < kNumDraws; ++i) { + switch (GetParam().pipelineType) { + case Pipeline::Static: + break; + case Pipeline::Redundant: + pass.SetPipeline(mPipelines[0]); + break; + case Pipeline::Dynamic: { + // If the pipeline is dynamic, ping pong between two pipelines. + pass.SetPipeline(mPipelines[i % 2]); + + // The pipelines have different layouts so we change the binding index here. + uniformBindGroupIndex = i % 2; + if (uniformBindGroupIndex == 1) { + // Because of the pipeline layout change, we need to rebind bind group index 0. + pass.SetBindGroup(0, mConstantBindGroup); + } + break; + } + } + + // Set the vertex buffer, if it changes. + switch (GetParam().vertexBufferType) { + case VertexBuffer::NoChange: + break; + + case VertexBuffer::Multiple: + pass.SetVertexBuffer(0, mVertexBuffers[i]); + break; + + case VertexBuffer::Dynamic: + pass.SetVertexBuffer(0, mVertexBuffers[0], i * mAlignedVertexDataSize); + break; + } + + // Set the bind group, if it changes. + switch (GetParam().bindGroupType) { + case BindGroup::NoChange: + break; + + case BindGroup::Redundant: + pass.SetBindGroup(uniformBindGroupIndex, mUniformBindGroups[0]); + break; + + case BindGroup::NoReuse: { + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, mUniformBindGroupLayout, {{0, mUniformBuffers[i], 0, kUniformSize}}); + pass.SetBindGroup(uniformBindGroupIndex, bindGroup); + break; + } + + case BindGroup::Multiple: + pass.SetBindGroup(uniformBindGroupIndex, mUniformBindGroups[i]); + break; + + case BindGroup::Dynamic: { + uint32_t dynamicOffset = static_cast(i * mAlignedUniformSize); + pass.SetBindGroup(uniformBindGroupIndex, mUniformBindGroups[0], 1, &dynamicOffset); + break; + } + + default: + UNREACHABLE(); + break; + } + pass.Draw(3); + } +} + +void DrawCallPerf::Step() { + if (GetParam().uniformDataType == UniformData::Dynamic) { + // Update uniform data if it's dynamic. + std::fill(mUniformBufferData.begin(), mUniformBufferData.end(), + mUniformBufferData[0] + 1.0); + + switch (GetParam().bindGroupType) { + case BindGroup::NoChange: + case BindGroup::Redundant: + queue.WriteBuffer(mUniformBuffers[0], 0, mUniformBufferData.data(), + 3 * sizeof(float)); + break; + case BindGroup::NoReuse: + case BindGroup::Multiple: + for (uint32_t i = 0; i < kNumDraws; ++i) { + queue.WriteBuffer(mUniformBuffers[i], 0, + mUniformBufferData.data() + i * mNumUniformFloats, + 3 * sizeof(float)); + } + break; + case BindGroup::Dynamic: + queue.WriteBuffer(mUniformBuffers[0], 0, mUniformBufferData.data(), + mUniformBufferData.size() * sizeof(float)); + break; + } + } + + wgpu::CommandEncoder commands = device.CreateCommandEncoder(); + utils::ComboRenderPassDescriptor renderPass({mColorAttachment}, mDepthStencilAttachment); + wgpu::RenderPassEncoder pass = commands.BeginRenderPass(&renderPass); + + switch (GetParam().withRenderBundle) { + case RenderBundle::No: + RecordRenderCommands(pass); + break; + case RenderBundle::Yes: + pass.ExecuteBundles(1, &mRenderBundle); + break; + default: + UNREACHABLE(); + break; + } + + pass.EndPass(); + wgpu::CommandBuffer commandBuffer = commands.Finish(); + queue.Submit(1, &commandBuffer); +} + +TEST_P(DrawCallPerf, Run) { + RunTest(); +} + +DAWN_INSTANTIATE_PERF_TEST_SUITE_P( + DrawCallPerf, + {D3D12Backend(), MetalBackend(), OpenGLBackend(), VulkanBackend(), + VulkanBackend({"skip_validation"})}, + { + // Baseline + MakeParam(), + + // Change vertex buffer binding + MakeParam(VertexBuffer::Multiple), // Multiple vertex buffers + MakeParam(VertexBuffer::Dynamic), // Dynamic vertex buffer + + // Change bind group binding + MakeParam(BindGroup::Multiple), // Multiple bind groups + MakeParam(BindGroup::Dynamic), // Dynamic bind groups + MakeParam(BindGroup::NoReuse), // New bind group per-draw + + // Redundantly set pipeline / bind groups + MakeParam(Pipeline::Redundant, BindGroup::Redundant), + + // Switch the pipeline every draw to test state tracking and updates to binding points + MakeParam(Pipeline::Dynamic, + BindGroup::Multiple), // Multiple bind groups w/ dynamic pipeline + MakeParam(Pipeline::Dynamic, + BindGroup::Dynamic), // Dynamic bind groups w/ dynamic pipeline + + // ----------- Render Bundles ----------- + // Command validation / state tracking can be futher optimized / precomputed. + // Use render bundles with varying vertex buffer binding + MakeParam(VertexBuffer::Multiple, + RenderBundle::Yes), // Multiple vertex buffers w/ render bundle + MakeParam(VertexBuffer::Dynamic, + RenderBundle::Yes), // Dynamic vertex buffer w/ render bundle + + // Use render bundles with varying bind group binding + MakeParam(BindGroup::Multiple, RenderBundle::Yes), // Multiple bind groups w/ render bundle + MakeParam(BindGroup::Dynamic, RenderBundle::Yes), // Dynamic bind groups w/ render bundle + + // Use render bundles with dynamic pipeline + MakeParam(Pipeline::Dynamic, + BindGroup::Multiple, + RenderBundle::Yes), // Multiple bind groups w/ dynamic pipeline w/ render bundle + MakeParam(Pipeline::Dynamic, + BindGroup::Dynamic, + RenderBundle::Yes), // Dynamic bind groups w/ dynamic pipeline w/ render bundle + + // ----------- Render Bundles (end)------- + + // Update per-draw data in the bind group(s). This will cause resource transitions between + // updating and drawing. + MakeParam(BindGroup::Multiple, + UniformData::Dynamic), // Update per-draw data: Multiple bind groups + MakeParam(BindGroup::Dynamic, + UniformData::Dynamic), // Update per-draw data: Dynamic bind groups + }); diff --git a/third_party/dawn/src/tests/perf_tests/README.md b/third_party/dawn/src/tests/perf_tests/README.md new file mode 100644 index 00000000000..e2172c7ba2e --- /dev/null +++ b/third_party/dawn/src/tests/perf_tests/README.md @@ -0,0 +1,2 @@ +# Dawn Perf Tests +Moved to [`//src/docs/testing.md`](https://dawn.googlesource.com/dawn/+/refs/heads/master/docs/testing.md). diff --git a/third_party/dawn/src/tests/unittests/BitSetIteratorTests.cpp b/third_party/dawn/src/tests/unittests/BitSetIteratorTests.cpp index cc6c3edf055..7bed713a544 100644 --- a/third_party/dawn/src/tests/unittests/BitSetIteratorTests.cpp +++ b/third_party/dawn/src/tests/unittests/BitSetIteratorTests.cpp @@ -15,12 +15,13 @@ #include #include "common/BitSetIterator.h" +#include "common/ityp_bitset.h" // This is ANGLE's BitSetIterator_unittests.cpp file. class BitSetIteratorTest : public testing::Test { - protected: - std::bitset<40> mStateBits; + protected: + std::bitset<40> mStateBits; }; // Simple iterator test. @@ -48,7 +49,7 @@ TEST_F(BitSetIteratorTest, Iterator) { // Test an empty iterator. TEST_F(BitSetIteratorTest, EmptySet) { // We don't use the FAIL gtest macro here since it returns immediately, - // causing an unreachable code warning in MSVS + // causing an unreachable code warning in MSVC bool sawBit = false; for (unsigned long bit : IterateBitSet(mStateBits)) { DAWN_UNUSED(bit); @@ -82,3 +83,137 @@ TEST_F(BitSetIteratorTest, NonLValueBitset) { EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size()); } + +class EnumBitSetIteratorTest : public testing::Test { + protected: + enum class TestEnum { A, B, C, D, E, F, G, H, I, J, EnumCount }; + + static constexpr size_t kEnumCount = static_cast(TestEnum::EnumCount); + ityp::bitset mStateBits; +}; + +// Simple iterator test. +TEST_F(EnumBitSetIteratorTest, Iterator) { + std::set originalValues; + originalValues.insert(TestEnum::B); + originalValues.insert(TestEnum::F); + originalValues.insert(TestEnum::C); + originalValues.insert(TestEnum::I); + + for (TestEnum value : originalValues) { + mStateBits.set(value); + } + + std::set readValues; + for (TestEnum bit : IterateBitSet(mStateBits)) { + EXPECT_EQ(1u, originalValues.count(bit)); + EXPECT_EQ(0u, readValues.count(bit)); + readValues.insert(bit); + } + + EXPECT_EQ(originalValues.size(), readValues.size()); +} + +// Test an empty iterator. +TEST_F(EnumBitSetIteratorTest, EmptySet) { + // We don't use the FAIL gtest macro here since it returns immediately, + // causing an unreachable code warning in MSVC + bool sawBit = false; + for (TestEnum bit : IterateBitSet(mStateBits)) { + DAWN_UNUSED(bit); + sawBit = true; + } + EXPECT_FALSE(sawBit); +} + +// Test iterating a result of combining two bitsets. +TEST_F(EnumBitSetIteratorTest, NonLValueBitset) { + ityp::bitset otherBits; + + mStateBits.set(TestEnum::B); + mStateBits.set(TestEnum::C); + mStateBits.set(TestEnum::D); + mStateBits.set(TestEnum::E); + + otherBits.set(TestEnum::A); + otherBits.set(TestEnum::B); + otherBits.set(TestEnum::D); + otherBits.set(TestEnum::F); + + std::set seenBits; + + for (TestEnum bit : IterateBitSet(mStateBits & otherBits)) { + EXPECT_EQ(0u, seenBits.count(bit)); + seenBits.insert(bit); + EXPECT_TRUE(mStateBits[bit]); + EXPECT_TRUE(otherBits[bit]); + } + + EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size()); +} + +class ITypBitsetIteratorTest : public testing::Test { + protected: + using IntegerT = TypedInteger; + ityp::bitset mStateBits; +}; + +// Simple iterator test. +TEST_F(ITypBitsetIteratorTest, Iterator) { + std::set originalValues; + originalValues.insert(IntegerT(2)); + originalValues.insert(IntegerT(6)); + originalValues.insert(IntegerT(8)); + originalValues.insert(IntegerT(35)); + + for (IntegerT value : originalValues) { + mStateBits.set(value); + } + + std::set readValues; + for (IntegerT bit : IterateBitSet(mStateBits)) { + EXPECT_EQ(1u, originalValues.count(bit)); + EXPECT_EQ(0u, readValues.count(bit)); + readValues.insert(bit); + } + + EXPECT_EQ(originalValues.size(), readValues.size()); +} + +// Test an empty iterator. +TEST_F(ITypBitsetIteratorTest, EmptySet) { + // We don't use the FAIL gtest macro here since it returns immediately, + // causing an unreachable code warning in MSVC + bool sawBit = false; + for (IntegerT bit : IterateBitSet(mStateBits)) { + DAWN_UNUSED(bit); + sawBit = true; + } + EXPECT_FALSE(sawBit); +} + +// Test iterating a result of combining two bitsets. +TEST_F(ITypBitsetIteratorTest, NonLValueBitset) { + ityp::bitset otherBits; + + mStateBits.set(IntegerT(1)); + mStateBits.set(IntegerT(2)); + mStateBits.set(IntegerT(3)); + mStateBits.set(IntegerT(4)); + + otherBits.set(IntegerT(0)); + otherBits.set(IntegerT(1)); + otherBits.set(IntegerT(3)); + otherBits.set(IntegerT(5)); + + std::set seenBits; + + for (IntegerT bit : IterateBitSet(mStateBits & otherBits)) { + EXPECT_EQ(0u, seenBits.count(bit)); + seenBits.insert(bit); + EXPECT_TRUE(mStateBits[bit]); + EXPECT_TRUE(otherBits[bit]); + } + + EXPECT_EQ((mStateBits & otherBits).count(), seenBits.size()); +} diff --git a/third_party/dawn/src/tests/unittests/BuddyAllocatorTests.cpp b/third_party/dawn/src/tests/unittests/BuddyAllocatorTests.cpp new file mode 100644 index 00000000000..5d46630372a --- /dev/null +++ b/third_party/dawn/src/tests/unittests/BuddyAllocatorTests.cpp @@ -0,0 +1,327 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include "dawn_native/BuddyAllocator.h" + +using namespace dawn_native; + +constexpr uint64_t BuddyAllocator::kInvalidOffset; + +// Verify the buddy allocator with a basic test. +TEST(BuddyAllocatorTests, SingleBlock) { + // After one 32 byte allocation: + // + // Level -------------------------------- + // 0 32 | A | + // -------------------------------- + // + constexpr uint64_t maxBlockSize = 32; + BuddyAllocator allocator(maxBlockSize); + + // Check that we cannot allocate a oversized block. + ASSERT_EQ(allocator.Allocate(maxBlockSize * 2), BuddyAllocator::kInvalidOffset); + + // Check that we cannot allocate a zero sized block. + ASSERT_EQ(allocator.Allocate(0u), BuddyAllocator::kInvalidOffset); + + // Allocate the block. + uint64_t blockOffset = allocator.Allocate(maxBlockSize); + ASSERT_EQ(blockOffset, 0u); + + // Check that we are full. + ASSERT_EQ(allocator.Allocate(maxBlockSize), BuddyAllocator::kInvalidOffset); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 0u); + + // Deallocate the block. + allocator.Deallocate(blockOffset); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); +} + +// Verify multiple allocations succeeds using a buddy allocator. +TEST(BuddyAllocatorTests, MultipleBlocks) { + // Fill every level in the allocator (order-n = 2^n) + const uint64_t maxBlockSize = (1ull << 16); + for (uint64_t order = 1; (1ull << order) <= maxBlockSize; order++) { + BuddyAllocator allocator(maxBlockSize); + + uint64_t blockSize = (1ull << order); + for (uint32_t blocki = 0; blocki < (maxBlockSize / blockSize); blocki++) { + ASSERT_EQ(allocator.Allocate(blockSize), blockSize * blocki); + } + } +} + +// Verify that a single allocation succeeds using a buddy allocator. +TEST(BuddyAllocatorTests, SingleSplitBlock) { + // After one 8 byte allocation: + // + // Level -------------------------------- + // 0 32 | S | + // -------------------------------- + // 1 16 | S | F | S - split + // -------------------------------- F - free + // 2 8 | A | F | | | A - allocated + // -------------------------------- + // + constexpr uint64_t maxBlockSize = 32; + BuddyAllocator allocator(maxBlockSize); + + // Allocate block (splits two blocks). + uint64_t blockOffset = allocator.Allocate(8); + ASSERT_EQ(blockOffset, 0u); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 2u); + + // Deallocate block (merges two blocks). + allocator.Deallocate(blockOffset); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); + + // Check that we cannot allocate a block that is oversized. + ASSERT_EQ(allocator.Allocate(maxBlockSize * 2), BuddyAllocator::kInvalidOffset); + + // Re-allocate the largest block allowed after merging. + blockOffset = allocator.Allocate(maxBlockSize); + ASSERT_EQ(blockOffset, 0u); + + allocator.Deallocate(blockOffset); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); +} + +// Verify that a multiple allocated blocks can be removed in the free-list. +TEST(BuddyAllocatorTests, MultipleSplitBlocks) { + // After four 16 byte allocations: + // + // Level -------------------------------- + // 0 32 | S | + // -------------------------------- + // 1 16 | S | S | S - split + // -------------------------------- F - free + // 2 8 | Aa | Ab | Ac | Ad | A - allocated + // -------------------------------- + // + constexpr uint64_t maxBlockSize = 32; + BuddyAllocator allocator(maxBlockSize); + + // Populates the free-list with four blocks at Level2. + + // Allocate "a" block (two splits). + constexpr uint64_t blockSizeInBytes = 8; + uint64_t blockOffsetA = allocator.Allocate(blockSizeInBytes); + ASSERT_EQ(blockOffsetA, 0u); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 2u); + + // Allocate "b" block. + uint64_t blockOffsetB = allocator.Allocate(blockSizeInBytes); + ASSERT_EQ(blockOffsetB, blockSizeInBytes); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); + + // Allocate "c" block (three splits). + uint64_t blockOffsetC = allocator.Allocate(blockSizeInBytes); + ASSERT_EQ(blockOffsetC, blockOffsetB + blockSizeInBytes); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); + + // Allocate "d" block. + uint64_t blockOffsetD = allocator.Allocate(blockSizeInBytes); + ASSERT_EQ(blockOffsetD, blockOffsetC + blockSizeInBytes); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 0u); + + // Deallocate "d" block. + // FreeList[Level2] = [BlockD] -> x + allocator.Deallocate(blockOffsetD); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); + + // Deallocate "b" block. + // FreeList[Level2] = [BlockB] -> [BlockD] -> x + allocator.Deallocate(blockOffsetB); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 2u); + + // Deallocate "c" block (one merges). + // FreeList[Level1] = [BlockCD] -> x + // FreeList[Level2] = [BlockB] -> x + allocator.Deallocate(blockOffsetC); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 2u); + + // Deallocate "a" block (two merges). + // FreeList[Level0] = [BlockABCD] -> x + allocator.Deallocate(blockOffsetA); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); +} + +// Verify the buddy allocator can handle allocations of various sizes. +TEST(BuddyAllocatorTests, MultipleSplitBlockIncreasingSize) { + // After four Level4-to-Level1 byte then one L4 block allocations: + // + // Level ----------------------------------------------------------------- + // 0 512 | S | + // ----------------------------------------------------------------- + // 1 256 | S | A | + // ----------------------------------------------------------------- + // 2 128 | S | A | | | + // ----------------------------------------------------------------- + // 3 64 | S | A | | | | | | | + // ----------------------------------------------------------------- + // 4 32 | A | F | | | | | | | | | | | | | | | + // ----------------------------------------------------------------- + // + constexpr uint64_t maxBlockSize = 512; + BuddyAllocator allocator(maxBlockSize); + + ASSERT_EQ(allocator.Allocate(32), 0ull); + ASSERT_EQ(allocator.Allocate(64), 64ull); + ASSERT_EQ(allocator.Allocate(128), 128ull); + ASSERT_EQ(allocator.Allocate(256), 256ull); + + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); + + // Fill in the last free block. + ASSERT_EQ(allocator.Allocate(32), 32ull); + + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 0u); + + // Check if we're full. + ASSERT_EQ(allocator.Allocate(32), BuddyAllocator::kInvalidOffset); +} + +// Verify very small allocations using a larger allocator works correctly. +TEST(BuddyAllocatorTests, MultipleSplitBlocksVariableSizes) { + // After allocating four pairs of one 64 byte block and one 32 byte block. + // + // Level ----------------------------------------------------------------- + // 0 512 | S | + // ----------------------------------------------------------------- + // 1 256 | S | S | + // ----------------------------------------------------------------- + // 2 128 | S | S | S | F | + // ----------------------------------------------------------------- + // 3 64 | A | S | A | A | S | A | | | + // ----------------------------------------------------------------- + // 4 32 | | | A | A | | | | | A | A | | | | | | | + // ----------------------------------------------------------------- + // + constexpr uint64_t maxBlockSize = 512; + BuddyAllocator allocator(maxBlockSize); + + ASSERT_EQ(allocator.Allocate(64), 0ull); + ASSERT_EQ(allocator.Allocate(32), 64ull); + + ASSERT_EQ(allocator.Allocate(64), 128ull); + ASSERT_EQ(allocator.Allocate(32), 96ull); + + ASSERT_EQ(allocator.Allocate(64), 192ull); + ASSERT_EQ(allocator.Allocate(32), 256ull); + + ASSERT_EQ(allocator.Allocate(64), 320ull); + ASSERT_EQ(allocator.Allocate(32), 288ull); + + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); +} + +// Verify the buddy allocator can deal with bad fragmentation. +TEST(BuddyAllocatorTests, MultipleSplitBlocksInterleaved) { + // Allocate every leaf then de-allocate every other of those allocations. + // + // Level ----------------------------------------------------------------- + // 0 512 | S | + // ----------------------------------------------------------------- + // 1 256 | S | S | + // ----------------------------------------------------------------- + // 2 128 | S | S | S | S | + // ----------------------------------------------------------------- + // 3 64 | S | S | S | S | S | S | S | S | + // ----------------------------------------------------------------- + // 4 32 | A | F | A | F | A | F | A | F | A | F | A | F | A | F | A | F | + // ----------------------------------------------------------------- + // + constexpr uint64_t maxBlockSize = 512; + BuddyAllocator allocator(maxBlockSize); + + // Allocate leaf blocks + constexpr uint64_t minBlockSizeInBytes = 32; + std::vector blockOffsets; + for (uint64_t i = 0; i < maxBlockSize / minBlockSizeInBytes; i++) { + blockOffsets.push_back(allocator.Allocate(minBlockSizeInBytes)); + } + + // Free every other leaf block. + for (size_t count = 1; count < blockOffsets.size(); count += 2) { + allocator.Deallocate(blockOffsets[count]); + } + + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 8u); +} + +// Verify the buddy allocator can deal with multiple allocations with mixed alignments. +TEST(BuddyAllocatorTests, SameSizeVariousAlignment) { + // After two 8 byte allocations with 16 byte alignment then one 8 byte allocation with 8 byte + // alignment. + // + // Level -------------------------------- + // 0 32 | S | + // -------------------------------- + // 1 16 | S | S | S - split + // -------------------------------- F - free + // 2 8 | Aa | F | Ab | Ac | A - allocated + // -------------------------------- + // + BuddyAllocator allocator(32); + + // Allocate Aa (two splits). + ASSERT_EQ(allocator.Allocate(8, 16), 0u); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 2u); + + // Allocate Ab (skip Aa buddy due to alignment and perform another split). + ASSERT_EQ(allocator.Allocate(8, 16), 16u); + + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 2u); + + // Check that we cannot fit another. + ASSERT_EQ(allocator.Allocate(8, 16), BuddyAllocator::kInvalidOffset); + + // Allocate Ac (zero splits and Ab's buddy is now the first free block). + ASSERT_EQ(allocator.Allocate(8, 8), 24u); + + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); +} + +// Verify the buddy allocator can deal with multiple allocations with equal alignments. +TEST(BuddyAllocatorTests, VariousSizeSameAlignment) { + // After two 8 byte allocations with 4 byte alignment then one 16 byte allocation with 4 byte + // alignment. + // + // Level -------------------------------- + // 0 32 | S | + // -------------------------------- + // 1 16 | S | Ac | S - split + // -------------------------------- F - free + // 2 8 | Aa | Ab | | A - allocated + // -------------------------------- + // + constexpr uint64_t maxBlockSize = 32; + constexpr uint64_t alignment = 4; + BuddyAllocator allocator(maxBlockSize); + + // Allocate block Aa (two splits) + ASSERT_EQ(allocator.Allocate(8, alignment), 0u); + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 2u); + + // Allocate block Ab (Aa's buddy) + ASSERT_EQ(allocator.Allocate(8, alignment), 8u); + + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 1u); + + // Check that we can still allocate Ac. + ASSERT_EQ(allocator.Allocate(16, alignment), 16ull); + + ASSERT_EQ(allocator.ComputeTotalNumOfFreeBlocksForTesting(), 0u); +} diff --git a/third_party/dawn/src/tests/unittests/BuddyMemoryAllocatorTests.cpp b/third_party/dawn/src/tests/unittests/BuddyMemoryAllocatorTests.cpp new file mode 100644 index 00000000000..55da40175e5 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/BuddyMemoryAllocatorTests.cpp @@ -0,0 +1,358 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "dawn_native/BuddyMemoryAllocator.h" +#include "dawn_native/ResourceHeapAllocator.h" + +using namespace dawn_native; + +class DummyResourceHeapAllocator : public ResourceHeapAllocator { + public: + ResultOrError> AllocateResourceHeap(uint64_t size) override { + return std::make_unique(); + } + void DeallocateResourceHeap(std::unique_ptr allocation) override { + } +}; + +class DummyBuddyResourceAllocator { + public: + DummyBuddyResourceAllocator(uint64_t maxBlockSize, uint64_t memorySize) + : mAllocator(maxBlockSize, memorySize, &mHeapAllocator) { + } + + ResourceMemoryAllocation Allocate(uint64_t allocationSize, uint64_t alignment = 1) { + ResultOrError result = + mAllocator.Allocate(allocationSize, alignment); + return (result.IsSuccess()) ? result.AcquireSuccess() : ResourceMemoryAllocation{}; + } + + void Deallocate(ResourceMemoryAllocation& allocation) { + mAllocator.Deallocate(allocation); + } + + uint64_t ComputeTotalNumOfHeapsForTesting() const { + return mAllocator.ComputeTotalNumOfHeapsForTesting(); + } + + private: + DummyResourceHeapAllocator mHeapAllocator; + BuddyMemoryAllocator mAllocator; +}; + +// Verify a single resource allocation in a single heap. +TEST(BuddyMemoryAllocatorTests, SingleHeap) { + // After one 128 byte resource allocation: + // + // max block size -> --------------------------- + // | A1/H0 | Hi - Heap at index i + // max heap size -> --------------------------- An - Resource allocation n + // + constexpr uint64_t heapSize = 128; + constexpr uint64_t maxBlockSize = heapSize; + DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize); + + // Cannot allocate greater than heap size. + ResourceMemoryAllocation invalidAllocation = allocator.Allocate(heapSize * 2); + ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid); + + // Allocate one 128 byte allocation (same size as heap). + ResourceMemoryAllocation allocation1 = allocator.Allocate(128); + ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u); + ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); + + // Cannot allocate when allocator is full. + invalidAllocation = allocator.Allocate(128); + ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid); + + allocator.Deallocate(allocation1); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 0u); +} + +// Verify that multiple allocation are created in separate heaps. +TEST(BuddyMemoryAllocatorTests, MultipleHeaps) { + // After two 128 byte resource allocations: + // + // max block size -> --------------------------- + // | | Hi - Heap at index i + // max heap size -> --------------------------- An - Resource allocation n + // | A1/H0 | A2/H1 | + // --------------------------- + // + constexpr uint64_t maxBlockSize = 256; + constexpr uint64_t heapSize = 128; + DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize); + + // Cannot allocate greater than heap size. + ResourceMemoryAllocation invalidAllocation = allocator.Allocate(heapSize * 2); + ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid); + + // Cannot allocate greater than max block size. + invalidAllocation = allocator.Allocate(maxBlockSize * 2); + ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid); + + // Allocate two 128 byte allocations. + ResourceMemoryAllocation allocation1 = allocator.Allocate(heapSize); + ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u); + ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + // First allocation creates first heap. + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); + + ResourceMemoryAllocation allocation2 = allocator.Allocate(heapSize); + ASSERT_EQ(allocation2.GetInfo().mBlockOffset, heapSize); + ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + // Second allocation creates second heap. + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u); + + // Deallocate both allocations + allocator.Deallocate(allocation1); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); // Released H0 + + allocator.Deallocate(allocation2); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 0u); // Released H1 +} + +// Verify multiple sub-allocations can re-use heaps. +TEST(BuddyMemoryAllocatorTests, MultipleSplitHeaps) { + // After two 64 byte allocations with 128 byte heaps. + // + // max block size -> --------------------------- + // | | Hi - Heap at index i + // max heap size -> --------------------------- An - Resource allocation n + // | H0 | H1 | + // --------------------------- + // | A1 | A2 | A3 | | + // --------------------------- + // + constexpr uint64_t maxBlockSize = 256; + constexpr uint64_t heapSize = 128; + DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize); + + // Allocate two 64 byte sub-allocations. + ResourceMemoryAllocation allocation1 = allocator.Allocate(heapSize / 2); + ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u); + ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + // First sub-allocation creates first heap. + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); + + ResourceMemoryAllocation allocation2 = allocator.Allocate(heapSize / 2); + ASSERT_EQ(allocation2.GetInfo().mBlockOffset, heapSize / 2); + ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + // Second allocation re-uses first heap. + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); + + ResourceMemoryAllocation allocation3 = allocator.Allocate(heapSize / 2); + ASSERT_EQ(allocation3.GetInfo().mBlockOffset, heapSize); + ASSERT_EQ(allocation3.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + // Third allocation creates second heap. + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u); + + // Deallocate all allocations in reverse order. + allocator.Deallocate(allocation1); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), + 2u); // A2 pins H0. + + allocator.Deallocate(allocation2); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); // Released H0 + + allocator.Deallocate(allocation3); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 0u); // Released H1 +} + +// Verify resource sub-allocation of various sizes over multiple heaps. +TEST(BuddyMemoryAllocatorTests, MultiplSplitHeapsVariableSizes) { + // After three 64 byte allocations and two 128 byte allocations. + // + // max block size -> ------------------------------------------------------- + // | | + // ------------------------------------------------------- + // | | | + // max heap size -> ------------------------------------------------------- + // | H0 | A3/H1 | H2 | A5/H3 | + // ------------------------------------------------------- + // | A1 | A2 | | A4 | | | + // ------------------------------------------------------- + // + constexpr uint64_t heapSize = 128; + constexpr uint64_t maxBlockSize = 512; + DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize); + + // Allocate two 64-byte allocations. + ResourceMemoryAllocation allocation1 = allocator.Allocate(64); + ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u); + ASSERT_EQ(allocation1.GetOffset(), 0u); + ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ResourceMemoryAllocation allocation2 = allocator.Allocate(64); + ASSERT_EQ(allocation2.GetInfo().mBlockOffset, 64u); + ASSERT_EQ(allocation2.GetOffset(), 64u); + ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + // A1 and A2 share H0 + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); + + ResourceMemoryAllocation allocation3 = allocator.Allocate(128); + ASSERT_EQ(allocation3.GetInfo().mBlockOffset, 128u); + ASSERT_EQ(allocation3.GetOffset(), 0u); + ASSERT_EQ(allocation3.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + // A3 creates and fully occupies a new heap. + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u); + + ResourceMemoryAllocation allocation4 = allocator.Allocate(64); + ASSERT_EQ(allocation4.GetInfo().mBlockOffset, 256u); + ASSERT_EQ(allocation4.GetOffset(), 0u); + ASSERT_EQ(allocation4.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u); + + // R5 size forms 64 byte hole after R4. + ResourceMemoryAllocation allocation5 = allocator.Allocate(128); + ASSERT_EQ(allocation5.GetInfo().mBlockOffset, 384u); + ASSERT_EQ(allocation5.GetOffset(), 0u); + ASSERT_EQ(allocation5.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 4u); + + // Deallocate allocations in staggered order. + allocator.Deallocate(allocation1); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 4u); // A2 pins H0 + + allocator.Deallocate(allocation5); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u); // Released H3 + + allocator.Deallocate(allocation2); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u); // Released H0 + + allocator.Deallocate(allocation4); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); // Released H2 + + allocator.Deallocate(allocation3); + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 0u); // Released H1 +} + +// Verify resource sub-allocation of same sizes with various alignments. +TEST(BuddyMemoryAllocatorTests, SameSizeVariousAlignment) { + // After three 64 byte and one 128 byte resource allocations. + // + // max block size -> ------------------------------------------------------- + // | | + // ------------------------------------------------------- + // | | | + // max heap size -> ------------------------------------------------------- + // | H0 | H1 | H2 | | + // ------------------------------------------------------- + // | A1 | | A2 | | A3 | A4 | | + // ------------------------------------------------------- + // + constexpr uint64_t heapSize = 128; + constexpr uint64_t maxBlockSize = 512; + DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize); + + ResourceMemoryAllocation allocation1 = allocator.Allocate(64, 128); + ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u); + ASSERT_EQ(allocation1.GetOffset(), 0u); + ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); + + ResourceMemoryAllocation allocation2 = allocator.Allocate(64, 128); + ASSERT_EQ(allocation2.GetInfo().mBlockOffset, 128u); + ASSERT_EQ(allocation2.GetOffset(), 0u); + ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u); + + ResourceMemoryAllocation allocation3 = allocator.Allocate(64, 128); + ASSERT_EQ(allocation3.GetInfo().mBlockOffset, 256u); + ASSERT_EQ(allocation3.GetOffset(), 0u); + ASSERT_EQ(allocation3.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u); + + ResourceMemoryAllocation allocation4 = allocator.Allocate(64, 64); + ASSERT_EQ(allocation4.GetInfo().mBlockOffset, 320u); + ASSERT_EQ(allocation4.GetOffset(), 64u); + ASSERT_EQ(allocation4.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u); +} + +// Verify resource sub-allocation of various sizes with same alignments. +TEST(BuddyMemoryAllocatorTests, VariousSizeSameAlignment) { + // After two 64 byte and two 128 byte resource allocations: + // + // max block size -> ------------------------------------------------------- + // | | + // ------------------------------------------------------- + // | | | + // max heap size -> ------------------------------------------------------- + // | H0 | A3/H1 | A4/H2 | | + // ------------------------------------------------------- + // | A1 | A2 | | | | + // ------------------------------------------------------- + // + constexpr uint64_t heapSize = 128; + constexpr uint64_t maxBlockSize = 512; + DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize); + + constexpr uint64_t alignment = 64; + + ResourceMemoryAllocation allocation1 = allocator.Allocate(64, alignment); + ASSERT_EQ(allocation1.GetInfo().mBlockOffset, 0u); + ASSERT_EQ(allocation1.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); + + ResourceMemoryAllocation allocation2 = allocator.Allocate(64, alignment); + ASSERT_EQ(allocation2.GetInfo().mBlockOffset, 64u); + ASSERT_EQ(allocation2.GetOffset(), 64u); + ASSERT_EQ(allocation2.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 1u); // Reuses H0 + + ResourceMemoryAllocation allocation3 = allocator.Allocate(128, alignment); + ASSERT_EQ(allocation3.GetInfo().mBlockOffset, 128u); + ASSERT_EQ(allocation3.GetOffset(), 0u); + ASSERT_EQ(allocation3.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 2u); + + ResourceMemoryAllocation allocation4 = allocator.Allocate(128, alignment); + ASSERT_EQ(allocation4.GetInfo().mBlockOffset, 256u); + ASSERT_EQ(allocation4.GetOffset(), 0u); + ASSERT_EQ(allocation4.GetInfo().mMethod, AllocationMethod::kSubAllocated); + + ASSERT_EQ(allocator.ComputeTotalNumOfHeapsForTesting(), 3u); +} + +// Verify allocating a very large resource does not overflow. +TEST(BuddyMemoryAllocatorTests, AllocationOverflow) { + constexpr uint64_t heapSize = 128; + constexpr uint64_t maxBlockSize = 512; + DummyBuddyResourceAllocator allocator(maxBlockSize, heapSize); + + constexpr uint64_t largeBlock = (1ull << 63) + 1; + ResourceMemoryAllocation invalidAllocation = allocator.Allocate(largeBlock); + ASSERT_EQ(invalidAllocation.GetInfo().mMethod, AllocationMethod::kInvalid); +} diff --git a/third_party/dawn/src/tests/unittests/CommandAllocatorTests.cpp b/third_party/dawn/src/tests/unittests/CommandAllocatorTests.cpp index 206261fc0e3..bc1d736cd8d 100644 --- a/third_party/dawn/src/tests/unittests/CommandAllocatorTests.cpp +++ b/third_party/dawn/src/tests/unittests/CommandAllocatorTests.cpp @@ -121,7 +121,8 @@ TEST(CommandAllocator, BasicWithData) { uint32_t myValues[5] = {6, 42, 0xFFFFFFFF, 0, 54}; { - CommandPushConstants* pushConstants = allocator.Allocate(CommandType::PushConstants); + CommandPushConstants* pushConstants = + allocator.Allocate(CommandType::PushConstants); pushConstants->size = mySize; pushConstants->offset = myOffset; @@ -209,7 +210,7 @@ TEST(CommandAllocator, LargeCommands) { for (int i = 0; i < kCommandCount; i++) { CommandBig* big = allocator.Allocate(CommandType::Big); for (int j = 0; j < kBigBufferSize; j++) { - big->buffer[j] = count ++; + big->buffer[j] = count++; } } @@ -223,9 +224,9 @@ TEST(CommandAllocator, LargeCommands) { CommandBig* big = iterator.NextCommand(); for (int i = 0; i < kBigBufferSize; i++) { ASSERT_EQ(big->buffer[i], count); - count ++; + count++; } - numCommands ++; + numCommands++; } ASSERT_EQ(numCommands, kCommandCount); @@ -242,7 +243,7 @@ TEST(CommandAllocator, ManySmallCommands) { uint16_t count = 0; for (int i = 0; i < kCommandCount; i++) { CommandSmall* small = allocator.Allocate(CommandType::Small); - small->data = count ++; + small->data = count++; } CommandIterator iterator(std::move(allocator)); @@ -254,8 +255,8 @@ TEST(CommandAllocator, ManySmallCommands) { CommandSmall* small = iterator.NextCommand(); ASSERT_EQ(small->data, count); - count ++; - numCommands ++; + count++; + numCommands++; } ASSERT_EQ(numCommands, kCommandCount); diff --git a/third_party/dawn/src/tests/unittests/EnumClassBitmasksTests.cpp b/third_party/dawn/src/tests/unittests/EnumClassBitmasksTests.cpp index ad7f708a04d..c87f09f1e8d 100644 --- a/third_party/dawn/src/tests/unittests/EnumClassBitmasksTests.cpp +++ b/third_party/dawn/src/tests/unittests/EnumClassBitmasksTests.cpp @@ -16,7 +16,7 @@ #include "dawn/EnumClassBitmasks.h" -namespace dawn { +namespace wgpu { enum class Color : uint32_t { R = 1, @@ -25,7 +25,7 @@ namespace dawn { A = 8, }; - template<> + template <> struct IsDawnBitmask { static constexpr bool enable = true; }; @@ -80,14 +80,14 @@ namespace dawn { TEST(BitmaskTests, ZeroOrOneBits) { Color zero = static_cast(0); - ASSERT_TRUE(HasZeroOrOneBits(zero)); - ASSERT_TRUE(HasZeroOrOneBits(Color::R)); - ASSERT_TRUE(HasZeroOrOneBits(Color::G)); - ASSERT_TRUE(HasZeroOrOneBits(Color::B)); - ASSERT_TRUE(HasZeroOrOneBits(Color::A)); - ASSERT_FALSE(HasZeroOrOneBits(static_cast(Color::R | Color::G))); - ASSERT_FALSE(HasZeroOrOneBits(static_cast(Color::G | Color::B))); - ASSERT_FALSE(HasZeroOrOneBits(static_cast(Color::B | Color::A))); + ASSERT_TRUE(wgpu::HasZeroOrOneBits(zero)); + ASSERT_TRUE(wgpu::HasZeroOrOneBits(Color::R)); + ASSERT_TRUE(wgpu::HasZeroOrOneBits(Color::G)); + ASSERT_TRUE(wgpu::HasZeroOrOneBits(Color::B)); + ASSERT_TRUE(wgpu::HasZeroOrOneBits(Color::A)); + ASSERT_FALSE(wgpu::HasZeroOrOneBits(static_cast(Color::R | Color::G))); + ASSERT_FALSE(wgpu::HasZeroOrOneBits(static_cast(Color::G | Color::B))); + ASSERT_FALSE(wgpu::HasZeroOrOneBits(static_cast(Color::B | Color::A))); } -} // namespace dawn +} // namespace wgpu diff --git a/third_party/dawn/src/tests/unittests/ErrorTests.cpp b/third_party/dawn/src/tests/unittests/ErrorTests.cpp index f7d5bc82ea6..042784db6d8 100644 --- a/third_party/dawn/src/tests/unittests/ErrorTests.cpp +++ b/third_party/dawn/src/tests/unittests/ErrorTests.cpp @@ -21,292 +21,260 @@ using namespace dawn_native; namespace { -int dummySuccess = 0xbeef; -const char* dummyErrorMessage = "I am an error message :3"; - -// Check returning a success MaybeError with {}; -TEST(ErrorTests, Error_Success) { - auto ReturnSuccess = []() -> MaybeError { - return {}; - }; - - MaybeError result = ReturnSuccess(); - ASSERT_TRUE(result.IsSuccess()); -} - -// Check returning an error MaybeError with "return DAWN_VALIDATION_ERROR" -TEST(ErrorTests, Error_Error) { - auto ReturnError = []() -> MaybeError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - MaybeError result = ReturnError(); - ASSERT_TRUE(result.IsError()); - - ErrorData* errorData = result.AcquireError(); - ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); - delete errorData; -} - -// Check returning a success ResultOrError with an implicit conversion -TEST(ErrorTests, ResultOrError_Success) { - auto ReturnSuccess = []() -> ResultOrError { - return &dummySuccess; - }; - - ResultOrError result = ReturnSuccess(); - ASSERT_TRUE(result.IsSuccess()); - ASSERT_EQ(result.AcquireSuccess(), &dummySuccess); -} - -// Check returning an error ResultOrError with "return DAWN_VALIDATION_ERROR" -TEST(ErrorTests, ResultOrError_Error) { - auto ReturnError = []() -> ResultOrError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - ResultOrError result = ReturnError(); - ASSERT_TRUE(result.IsError()); - - ErrorData* errorData = result.AcquireError(); - ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); - delete errorData; -} - -// Check DAWN_TRY handles successes correctly. -TEST(ErrorTests, TRY_Success) { - auto ReturnSuccess = []() -> MaybeError { - return {}; - }; - - // We need to check that DAWN_TRY doesn't return on successes - bool tryReturned = true; - - auto Try = [ReturnSuccess, &tryReturned]() -> MaybeError { - DAWN_TRY(ReturnSuccess()); - tryReturned = false; - return {}; - }; - - MaybeError result = Try(); - ASSERT_TRUE(result.IsSuccess()); - ASSERT_FALSE(tryReturned); -} - -// Check DAWN_TRY handles errors correctly. -TEST(ErrorTests, TRY_Error) { - auto ReturnError = []() -> MaybeError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - auto Try = [ReturnError]() -> MaybeError { - DAWN_TRY(ReturnError()); - // DAWN_TRY should return before this point - EXPECT_FALSE(true); - return {}; - }; - - MaybeError result = Try(); - ASSERT_TRUE(result.IsError()); - - ErrorData* errorData = result.AcquireError(); - ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); - delete errorData; -} - -// Check DAWN_TRY adds to the backtrace. -TEST(ErrorTests, TRY_AddsToBacktrace) { - auto ReturnError = []() -> MaybeError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - auto SingleTry = [ReturnError]() -> MaybeError { - DAWN_TRY(ReturnError()); - return {}; - }; - - auto DoubleTry = [SingleTry]() -> MaybeError { - DAWN_TRY(SingleTry()); - return {}; - }; - - MaybeError singleResult = SingleTry(); - ASSERT_TRUE(singleResult.IsError()); - - MaybeError doubleResult = DoubleTry(); - ASSERT_TRUE(doubleResult.IsError()); - - ErrorData* singleData = singleResult.AcquireError(); - ErrorData* doubleData = doubleResult.AcquireError(); - - ASSERT_EQ(singleData->GetBacktrace().size() + 1, doubleData->GetBacktrace().size()); - - delete singleData; - delete doubleData; -} - -// Check DAWN_TRY_ASSIGN handles successes correctly. -TEST(ErrorTests, TRY_RESULT_Success) { - auto ReturnSuccess = []() -> ResultOrError { - return &dummySuccess; - }; - - // We need to check that DAWN_TRY doesn't return on successes - bool tryReturned = true; - - auto Try = [ReturnSuccess, &tryReturned]() -> ResultOrError { - int* result = nullptr; - DAWN_TRY_ASSIGN(result, ReturnSuccess()); - tryReturned = false; - - EXPECT_EQ(result, &dummySuccess); - return result; - }; - - ResultOrError result = Try(); - ASSERT_TRUE(result.IsSuccess()); - ASSERT_FALSE(tryReturned); - ASSERT_EQ(result.AcquireSuccess(), &dummySuccess); -} - -// Check DAWN_TRY_ASSIGN handles errors correctly. -TEST(ErrorTests, TRY_RESULT_Error) { - auto ReturnError = []() -> ResultOrError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - auto Try = [ReturnError]() -> ResultOrError { - int* result = nullptr; - DAWN_TRY_ASSIGN(result, ReturnError()); - DAWN_UNUSED(result); - - // DAWN_TRY should return before this point - EXPECT_FALSE(true); - return &dummySuccess; - }; - - ResultOrError result = Try(); - ASSERT_TRUE(result.IsError()); - - ErrorData* errorData = result.AcquireError(); - ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); - delete errorData; -} - -// Check DAWN_TRY_ASSIGN adds to the backtrace. -TEST(ErrorTests, TRY_RESULT_AddsToBacktrace) { - auto ReturnError = []() -> ResultOrError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - auto SingleTry = [ReturnError]() -> ResultOrError { - DAWN_TRY(ReturnError()); - return &dummySuccess; - }; - - auto DoubleTry = [SingleTry]() -> ResultOrError { - DAWN_TRY(SingleTry()); - return &dummySuccess; - }; - - ResultOrError singleResult = SingleTry(); - ASSERT_TRUE(singleResult.IsError()); - - ResultOrError doubleResult = DoubleTry(); - ASSERT_TRUE(doubleResult.IsError()); - - ErrorData* singleData = singleResult.AcquireError(); - ErrorData* doubleData = doubleResult.AcquireError(); - - ASSERT_EQ(singleData->GetBacktrace().size() + 1, doubleData->GetBacktrace().size()); - - delete singleData; - delete doubleData; -} - -// Check a ResultOrError can be DAWN_TRY_ASSIGNED in a function that returns an Error -TEST(ErrorTests, TRY_RESULT_ConversionToError) { - auto ReturnError = []() -> ResultOrError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - auto Try = [ReturnError]() -> MaybeError { - int* result = nullptr; - DAWN_TRY_ASSIGN(result, ReturnError()); - DAWN_UNUSED(result); - - return {}; - }; - - MaybeError result = Try(); - ASSERT_TRUE(result.IsError()); - - ErrorData* errorData = result.AcquireError(); - ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); - delete errorData; -} - -// Check a ResultOrError can be DAWN_TRY_ASSIGNED in a function that returns an Error -// Version without Result -TEST(ErrorTests, TRY_RESULT_ConversionToErrorNonPointer) { - auto ReturnError = []() -> ResultOrError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - auto Try = [ReturnError]() -> MaybeError { - int result = 0; - DAWN_TRY_ASSIGN(result, ReturnError()); - DAWN_UNUSED(result); - - return {}; - }; - - MaybeError result = Try(); - ASSERT_TRUE(result.IsError()); - - ErrorData* errorData = result.AcquireError(); - ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); - delete errorData; -} - -// Check a MaybeError can be DAWN_TRIED in a function that returns an ResultOrError -// Check DAWN_TRY handles errors correctly. -TEST(ErrorTests, TRY_ConversionToErrorOrResult) { - auto ReturnError = []() -> MaybeError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - auto Try = [ReturnError]() -> ResultOrError{ - DAWN_TRY(ReturnError()); - return &dummySuccess; - }; - - ResultOrError result = Try(); - ASSERT_TRUE(result.IsError()); - - ErrorData* errorData = result.AcquireError(); - ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); - delete errorData; -} - -// Check a MaybeError can be DAWN_TRIED in a function that returns an ResultOrError -// Check DAWN_TRY handles errors correctly. Version without Result -TEST(ErrorTests, TRY_ConversionToErrorOrResultNonPointer) { - auto ReturnError = []() -> MaybeError { - return DAWN_VALIDATION_ERROR(dummyErrorMessage); - }; - - auto Try = [ReturnError]() -> ResultOrError{ - DAWN_TRY(ReturnError()); - return 42; - }; - - ResultOrError result = Try(); - ASSERT_TRUE(result.IsError()); - - ErrorData* errorData = result.AcquireError(); - ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); - delete errorData; -} + int dummySuccess = 0xbeef; + const char* dummyErrorMessage = "I am an error message :3"; + + // Check returning a success MaybeError with {}; + TEST(ErrorTests, Error_Success) { + auto ReturnSuccess = []() -> MaybeError { return {}; }; + + MaybeError result = ReturnSuccess(); + ASSERT_TRUE(result.IsSuccess()); + } + + // Check returning an error MaybeError with "return DAWN_VALIDATION_ERROR" + TEST(ErrorTests, Error_Error) { + auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); }; + + MaybeError result = ReturnError(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr errorData = result.AcquireError(); + ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); + } + + // Check returning a success ResultOrError with an implicit conversion + TEST(ErrorTests, ResultOrError_Success) { + auto ReturnSuccess = []() -> ResultOrError { return &dummySuccess; }; + + ResultOrError result = ReturnSuccess(); + ASSERT_TRUE(result.IsSuccess()); + ASSERT_EQ(result.AcquireSuccess(), &dummySuccess); + } + + // Check returning an error ResultOrError with "return DAWN_VALIDATION_ERROR" + TEST(ErrorTests, ResultOrError_Error) { + auto ReturnError = []() -> ResultOrError { + return DAWN_VALIDATION_ERROR(dummyErrorMessage); + }; + + ResultOrError result = ReturnError(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr errorData = result.AcquireError(); + ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); + } + + // Check DAWN_TRY handles successes correctly. + TEST(ErrorTests, TRY_Success) { + auto ReturnSuccess = []() -> MaybeError { return {}; }; + + // We need to check that DAWN_TRY doesn't return on successes + bool tryReturned = true; + + auto Try = [ReturnSuccess, &tryReturned]() -> MaybeError { + DAWN_TRY(ReturnSuccess()); + tryReturned = false; + return {}; + }; + + MaybeError result = Try(); + ASSERT_TRUE(result.IsSuccess()); + ASSERT_FALSE(tryReturned); + } + + // Check DAWN_TRY handles errors correctly. + TEST(ErrorTests, TRY_Error) { + auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); }; + + auto Try = [ReturnError]() -> MaybeError { + DAWN_TRY(ReturnError()); + // DAWN_TRY should return before this point + EXPECT_FALSE(true); + return {}; + }; + + MaybeError result = Try(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr errorData = result.AcquireError(); + ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); + } + + // Check DAWN_TRY adds to the backtrace. + TEST(ErrorTests, TRY_AddsToBacktrace) { + auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); }; + + auto SingleTry = [ReturnError]() -> MaybeError { + DAWN_TRY(ReturnError()); + return {}; + }; + + auto DoubleTry = [SingleTry]() -> MaybeError { + DAWN_TRY(SingleTry()); + return {}; + }; + + MaybeError singleResult = SingleTry(); + ASSERT_TRUE(singleResult.IsError()); + + MaybeError doubleResult = DoubleTry(); + ASSERT_TRUE(doubleResult.IsError()); + + std::unique_ptr singleData = singleResult.AcquireError(); + std::unique_ptr doubleData = doubleResult.AcquireError(); + + ASSERT_EQ(singleData->GetBacktrace().size() + 1, doubleData->GetBacktrace().size()); + } + + // Check DAWN_TRY_ASSIGN handles successes correctly. + TEST(ErrorTests, TRY_RESULT_Success) { + auto ReturnSuccess = []() -> ResultOrError { return &dummySuccess; }; + + // We need to check that DAWN_TRY doesn't return on successes + bool tryReturned = true; + + auto Try = [ReturnSuccess, &tryReturned]() -> ResultOrError { + int* result = nullptr; + DAWN_TRY_ASSIGN(result, ReturnSuccess()); + tryReturned = false; + + EXPECT_EQ(result, &dummySuccess); + return result; + }; + + ResultOrError result = Try(); + ASSERT_TRUE(result.IsSuccess()); + ASSERT_FALSE(tryReturned); + ASSERT_EQ(result.AcquireSuccess(), &dummySuccess); + } + + // Check DAWN_TRY_ASSIGN handles errors correctly. + TEST(ErrorTests, TRY_RESULT_Error) { + auto ReturnError = []() -> ResultOrError { + return DAWN_VALIDATION_ERROR(dummyErrorMessage); + }; + + auto Try = [ReturnError]() -> ResultOrError { + int* result = nullptr; + DAWN_TRY_ASSIGN(result, ReturnError()); + DAWN_UNUSED(result); + + // DAWN_TRY should return before this point + EXPECT_FALSE(true); + return &dummySuccess; + }; + + ResultOrError result = Try(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr errorData = result.AcquireError(); + ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); + } + + // Check DAWN_TRY_ASSIGN adds to the backtrace. + TEST(ErrorTests, TRY_RESULT_AddsToBacktrace) { + auto ReturnError = []() -> ResultOrError { + return DAWN_VALIDATION_ERROR(dummyErrorMessage); + }; + + auto SingleTry = [ReturnError]() -> ResultOrError { + DAWN_TRY(ReturnError()); + return &dummySuccess; + }; + + auto DoubleTry = [SingleTry]() -> ResultOrError { + DAWN_TRY(SingleTry()); + return &dummySuccess; + }; + + ResultOrError singleResult = SingleTry(); + ASSERT_TRUE(singleResult.IsError()); + + ResultOrError doubleResult = DoubleTry(); + ASSERT_TRUE(doubleResult.IsError()); + + std::unique_ptr singleData = singleResult.AcquireError(); + std::unique_ptr doubleData = doubleResult.AcquireError(); + + ASSERT_EQ(singleData->GetBacktrace().size() + 1, doubleData->GetBacktrace().size()); + } + + // Check a ResultOrError can be DAWN_TRY_ASSIGNED in a function that returns an Error + TEST(ErrorTests, TRY_RESULT_ConversionToError) { + auto ReturnError = []() -> ResultOrError { + return DAWN_VALIDATION_ERROR(dummyErrorMessage); + }; + + auto Try = [ReturnError]() -> MaybeError { + int* result = nullptr; + DAWN_TRY_ASSIGN(result, ReturnError()); + DAWN_UNUSED(result); + + return {}; + }; + + MaybeError result = Try(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr errorData = result.AcquireError(); + ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); + } + + // Check a ResultOrError can be DAWN_TRY_ASSIGNED in a function that returns an Error + // Version without Result + TEST(ErrorTests, TRY_RESULT_ConversionToErrorNonPointer) { + auto ReturnError = []() -> ResultOrError { + return DAWN_VALIDATION_ERROR(dummyErrorMessage); + }; + + auto Try = [ReturnError]() -> MaybeError { + int result = 0; + DAWN_TRY_ASSIGN(result, ReturnError()); + DAWN_UNUSED(result); + + return {}; + }; + + MaybeError result = Try(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr errorData = result.AcquireError(); + ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); + } + + // Check a MaybeError can be DAWN_TRIED in a function that returns an ResultOrError + // Check DAWN_TRY handles errors correctly. + TEST(ErrorTests, TRY_ConversionToErrorOrResult) { + auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); }; + + auto Try = [ReturnError]() -> ResultOrError { + DAWN_TRY(ReturnError()); + return &dummySuccess; + }; + + ResultOrError result = Try(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr errorData = result.AcquireError(); + ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); + } + + // Check a MaybeError can be DAWN_TRIED in a function that returns an ResultOrError + // Check DAWN_TRY handles errors correctly. Version without Result + TEST(ErrorTests, TRY_ConversionToErrorOrResultNonPointer) { + auto ReturnError = []() -> MaybeError { return DAWN_VALIDATION_ERROR(dummyErrorMessage); }; + + auto Try = [ReturnError]() -> ResultOrError { + DAWN_TRY(ReturnError()); + return 42; + }; + + ResultOrError result = Try(); + ASSERT_TRUE(result.IsError()); + + std::unique_ptr errorData = result.AcquireError(); + ASSERT_EQ(errorData->GetMessage(), dummyErrorMessage); + } } // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/ExtensionTests.cpp b/third_party/dawn/src/tests/unittests/ExtensionTests.cpp new file mode 100644 index 00000000000..4e74f8e946f --- /dev/null +++ b/third_party/dawn/src/tests/unittests/ExtensionTests.cpp @@ -0,0 +1,81 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "dawn_native/Extensions.h" +#include "dawn_native/Instance.h" +#include "dawn_native/null/DeviceNull.h" + +class ExtensionTests : public testing::Test { + public: + ExtensionTests() + : testing::Test(), + mInstanceBase(dawn_native::InstanceBase::Create()), + mAdapterBase(mInstanceBase.Get()) { + } + + std::vector GetAllExtensionNames() { + std::vector allExtensionNames(kTotalExtensionsCount); + for (size_t i = 0; i < kTotalExtensionsCount; ++i) { + allExtensionNames[i] = ExtensionEnumToName(static_cast(i)); + } + return allExtensionNames; + } + + static constexpr size_t kTotalExtensionsCount = + static_cast(dawn_native::Extension::EnumCount); + + protected: + Ref mInstanceBase; + dawn_native::null::Adapter mAdapterBase; +}; + +// Test the creation of a device will fail if the requested extension is not supported on the +// Adapter. +TEST_F(ExtensionTests, AdapterWithRequiredExtensionDisabled) { + const std::vector kAllExtensionNames = GetAllExtensionNames(); + for (size_t i = 0; i < kTotalExtensionsCount; ++i) { + dawn_native::Extension notSupportedExtension = static_cast(i); + + std::vector extensionNamesWithoutOne = kAllExtensionNames; + extensionNamesWithoutOne.erase(extensionNamesWithoutOne.begin() + i); + + mAdapterBase.SetSupportedExtensions(extensionNamesWithoutOne); + dawn_native::Adapter adapterWithoutExtension(&mAdapterBase); + + dawn_native::DeviceDescriptor deviceDescriptor; + const char* extensionName = ExtensionEnumToName(notSupportedExtension); + deviceDescriptor.requiredExtensions = std::vector(1, extensionName); + WGPUDevice deviceWithExtension = adapterWithoutExtension.CreateDevice(&deviceDescriptor); + ASSERT_EQ(nullptr, deviceWithExtension); + } +} + +// Test Device.GetEnabledExtensions() can return the names of the enabled extensions correctly. +TEST_F(ExtensionTests, GetEnabledExtensions) { + dawn_native::Adapter adapter(&mAdapterBase); + for (size_t i = 0; i < kTotalExtensionsCount; ++i) { + dawn_native::Extension extension = static_cast(i); + const char* extensionName = ExtensionEnumToName(extension); + + dawn_native::DeviceDescriptor deviceDescriptor; + deviceDescriptor.requiredExtensions = {extensionName}; + dawn_native::DeviceBase* deviceBase = + reinterpret_cast(adapter.CreateDevice(&deviceDescriptor)); + std::vector enabledExtensions = deviceBase->GetEnabledExtensions(); + ASSERT_EQ(1u, enabledExtensions.size()); + ASSERT_EQ(0, std::strcmp(extensionName, enabledExtensions[0])); + } +} diff --git a/third_party/dawn/src/tests/unittests/GetProcAddressTests.cpp b/third_party/dawn/src/tests/unittests/GetProcAddressTests.cpp new file mode 100644 index 00000000000..f5ac8c699a7 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/GetProcAddressTests.cpp @@ -0,0 +1,170 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "dawn/dawn_proc.h" +#include "dawn_native/Instance.h" +#include "dawn_native/null/DeviceNull.h" +#include "dawn_wire/WireClient.h" +#include "utils/TerribleCommandBuffer.h" + +namespace { + + // dawn_wire and dawn_native contain duplicated code for the handling of GetProcAddress + // so we run the tests against both implementations. This enum is used as a test parameters to + // know which implementation to test. + enum class DawnFlavor { + Native, + Wire, + }; + + std::ostream& operator<<(std::ostream& stream, DawnFlavor flavor) { + switch (flavor) { + case DawnFlavor::Native: + stream << "dawn_native"; + break; + + case DawnFlavor::Wire: + stream << "dawn_wire"; + break; + + default: + UNREACHABLE(); + break; + } + return stream; + } + + class GetProcAddressTests : public testing::TestWithParam { + public: + GetProcAddressTests() + : testing::TestWithParam(), + mNativeInstance(dawn_native::InstanceBase::Create()), + mNativeAdapter(mNativeInstance.Get()) { + } + + void SetUp() override { + switch (GetParam()) { + case DawnFlavor::Native: { + mDevice = wgpu::Device::Acquire( + reinterpret_cast(mNativeAdapter.CreateDevice(nullptr))); + mProcs = dawn_native::GetProcs(); + break; + } + + case DawnFlavor::Wire: { + mC2sBuf = std::make_unique(); + + dawn_wire::WireClientDescriptor clientDesc = {}; + clientDesc.serializer = mC2sBuf.get(); + mWireClient = std::make_unique(clientDesc); + + mDevice = wgpu::Device::Acquire(mWireClient->GetDevice()); + mProcs = dawn_wire::WireClient::GetProcs(); + break; + } + + default: + UNREACHABLE(); + break; + } + + dawnProcSetProcs(&mProcs); + } + + void TearDown() override { + // Destroy the device before freeing the instance or the wire client in the destructor + mDevice = wgpu::Device(); + } + + protected: + Ref mNativeInstance; + dawn_native::null::Adapter mNativeAdapter; + + std::unique_ptr mC2sBuf; + std::unique_ptr mWireClient; + + wgpu::Device mDevice; + DawnProcTable mProcs; + }; + + // Test GetProcAddress with and without devices on some valid examples + TEST_P(GetProcAddressTests, ValidExamples) { + ASSERT_EQ(mProcs.getProcAddress(nullptr, "wgpuDeviceCreateBuffer"), + reinterpret_cast(mProcs.deviceCreateBuffer)); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), "wgpuDeviceCreateBuffer"), + reinterpret_cast(mProcs.deviceCreateBuffer)); + ASSERT_EQ(mProcs.getProcAddress(nullptr, "wgpuQueueSubmit"), + reinterpret_cast(mProcs.queueSubmit)); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), "wgpuQueueSubmit"), + reinterpret_cast(mProcs.queueSubmit)); + } + + // Test GetProcAddress with and without devices on nullptr procName + TEST_P(GetProcAddressTests, Nullptr) { + ASSERT_EQ(mProcs.getProcAddress(nullptr, nullptr), nullptr); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), nullptr), nullptr); + } + + // Test GetProcAddress with and without devices on some invalid + TEST_P(GetProcAddressTests, InvalidExamples) { + ASSERT_EQ(mProcs.getProcAddress(nullptr, "wgpuDeviceDoSomething"), nullptr); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), "wgpuDeviceDoSomething"), nullptr); + + // Trigger the condition where lower_bound will return the end of the procMap. + ASSERT_EQ(mProcs.getProcAddress(nullptr, "zzzzzzz"), nullptr); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), "zzzzzzz"), nullptr); + ASSERT_EQ(mProcs.getProcAddress(nullptr, "ZZ"), nullptr); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), "ZZ"), nullptr); + + // Some more potential corner cases. + ASSERT_EQ(mProcs.getProcAddress(nullptr, ""), nullptr); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), ""), nullptr); + ASSERT_EQ(mProcs.getProcAddress(nullptr, "0"), nullptr); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), "0"), nullptr); + } + + // Test that GetProcAddress supports freestanding function that are handled specially + TEST_P(GetProcAddressTests, FreeStandingFunctions) { + ASSERT_EQ(mProcs.getProcAddress(nullptr, "wgpuGetProcAddress"), + reinterpret_cast(mProcs.getProcAddress)); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), "wgpuGetProcAddress"), + reinterpret_cast(mProcs.getProcAddress)); + + ASSERT_EQ(mProcs.getProcAddress(nullptr, "wgpuCreateInstance"), + reinterpret_cast(mProcs.createInstance)); + ASSERT_EQ(mProcs.getProcAddress(mDevice.Get(), "wgpuCreateInstance"), + reinterpret_cast(mProcs.createInstance)); + } + + INSTANTIATE_TEST_SUITE_P(, + GetProcAddressTests, + testing::Values(DawnFlavor::Native, DawnFlavor::Wire), + testing::PrintToStringParamName()); + + TEST(GetProcAddressInternalTests, CheckDawnNativeProcMapOrder) { + std::vector names = dawn_native::GetProcMapNamesForTesting(); + for (size_t i = 1; i < names.size(); i++) { + ASSERT_LT(std::string(names[i - 1]), std::string(names[i])); + } + } + + TEST(GetProcAddressInternalTests, CheckDawnWireClientProcMapOrder) { + std::vector names = dawn_wire::client::GetProcMapNamesForTesting(); + for (size_t i = 1; i < names.size(); i++) { + ASSERT_LT(std::string(names[i - 1]), std::string(names[i])); + } + } +} // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/ITypArrayTests.cpp b/third_party/dawn/src/tests/unittests/ITypArrayTests.cpp new file mode 100644 index 00000000000..ce71cda5a89 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/ITypArrayTests.cpp @@ -0,0 +1,91 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/TypedInteger.h" +#include "common/ityp_array.h" + +class ITypArrayTest : public testing::Test { + protected: + using Key = TypedInteger; + using Val = TypedInteger; + using Array = ityp::array; + + // Test that the expected array methods can be constexpr + struct ConstexprTest { + static constexpr Array kArr = {Val(0), Val(1), Val(2), Val(3), Val(4), + Val(5), Val(6), Val(7), Val(8), Val(9)}; + + static_assert(kArr[Key(3)] == Val(3), ""); + static_assert(kArr.at(Key(7)) == Val(7), ""); + static_assert(kArr.size() == Key(10), ""); + }; +}; + +// Test that values can be set at an index and retrieved from the same index. +TEST_F(ITypArrayTest, Indexing) { + Array arr; + { + arr[Key(2)] = Val(5); + arr[Key(1)] = Val(9); + arr[Key(9)] = Val(2); + + ASSERT_EQ(arr[Key(2)], Val(5)); + ASSERT_EQ(arr[Key(1)], Val(9)); + ASSERT_EQ(arr[Key(9)], Val(2)); + } + { + arr.at(Key(4)) = Val(5); + arr.at(Key(3)) = Val(8); + arr.at(Key(1)) = Val(7); + + ASSERT_EQ(arr.at(Key(4)), Val(5)); + ASSERT_EQ(arr.at(Key(3)), Val(8)); + ASSERT_EQ(arr.at(Key(1)), Val(7)); + } +} + +// Test that the array can be iterated in order with a range-based for loop +TEST_F(ITypArrayTest, RangeBasedIteration) { + Array arr; + + // Assign in a non-const range-based for loop + uint32_t i = 0; + for (Val& val : arr) { + val = Val(i); + } + + // Check values in a const range-based for loop + i = 0; + for (Val val : static_cast(arr)) { + ASSERT_EQ(val, arr[Key(i++)]); + } +} + +// Test that begin/end/front/back/data return pointers/references to the correct elements. +TEST_F(ITypArrayTest, BeginEndFrontBackData) { + Array arr; + + // non-const versions + ASSERT_EQ(&arr.front(), &arr[Key(0)]); + ASSERT_EQ(&arr.back(), &arr[Key(9)]); + ASSERT_EQ(arr.data(), &arr[Key(0)]); + + // const versions + const Array& constArr = arr; + ASSERT_EQ(&constArr.front(), &constArr[Key(0)]); + ASSERT_EQ(&constArr.back(), &constArr[Key(9)]); + ASSERT_EQ(constArr.data(), &constArr[Key(0)]); +} diff --git a/third_party/dawn/src/tests/unittests/ITypBitsetTests.cpp b/third_party/dawn/src/tests/unittests/ITypBitsetTests.cpp new file mode 100644 index 00000000000..88390a51dee --- /dev/null +++ b/third_party/dawn/src/tests/unittests/ITypBitsetTests.cpp @@ -0,0 +1,178 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/TypedInteger.h" +#include "common/ityp_bitset.h" + +#include + +class ITypBitsetTest : public testing::Test { + protected: + using Key = TypedInteger; + using Bitset = ityp::bitset; + + // Test that the expected bitset methods can be constexpr + struct ConstexprTest { + static constexpr Bitset kBitset = {1 << 0 | 1 << 3 | 1 << 7 | 1 << 8}; + + static_assert(kBitset[Key(0)] == true, ""); + static_assert(kBitset[Key(1)] == false, ""); + static_assert(kBitset[Key(2)] == false, ""); + static_assert(kBitset[Key(3)] == true, ""); + static_assert(kBitset[Key(4)] == false, ""); + static_assert(kBitset[Key(5)] == false, ""); + static_assert(kBitset[Key(6)] == false, ""); + static_assert(kBitset[Key(7)] == true, ""); + static_assert(kBitset[Key(8)] == true, ""); + + static_assert(kBitset.size() == 9, ""); + }; + + void ExpectBits(const Bitset& bits, std::set indices) { + size_t mask = 0; + + for (size_t i = 0; i < bits.size(); ++i) { + if (indices.count(i) == 0) { + ASSERT_FALSE(bits[Key(i)]) << i; + ASSERT_FALSE(bits.test(Key(i))) << i; + } else { + mask |= (size_t(1) << i); + ASSERT_TRUE(bits[Key(i)]) << i; + ASSERT_TRUE(bits.test(Key(i))) << i; + } + } + + ASSERT_EQ(bits.to_ullong(), mask); + ASSERT_EQ(bits.to_ulong(), mask); + ASSERT_EQ(bits.count(), indices.size()); + ASSERT_EQ(bits.all(), indices.size() == bits.size()); + ASSERT_EQ(bits.any(), indices.size() != 0); + ASSERT_EQ(bits.none(), indices.size() == 0); + } +}; + +// Test that by default no bits are set +TEST_F(ITypBitsetTest, DefaultZero) { + Bitset bits; + ExpectBits(bits, {}); +} + +// Test the bitset can be initialized with a bitmask +TEST_F(ITypBitsetTest, InitializeByBits) { + Bitset bits = {1 << 1 | 1 << 2 | 1 << 7}; + ExpectBits(bits, {1, 2, 7}); +} + +// Test that bits can be set at an index and retrieved from the same index. +TEST_F(ITypBitsetTest, Indexing) { + Bitset bits; + ExpectBits(bits, {}); + + bits[Key(2)] = true; + bits[Key(4)] = false; + bits.set(Key(1)); + bits.set(Key(7), true); + bits.set(Key(8), false); + + ExpectBits(bits, {1, 2, 7}); + + bits.reset(Key(2)); + bits.reset(Key(7)); + ExpectBits(bits, {1}); +} + +// Test that bits can be flipped +TEST_F(ITypBitsetTest, Flip) { + Bitset bits = {1 << 1 | 1 << 2 | 1 << 7}; + ExpectBits(bits, {1, 2, 7}); + + bits.flip(Key(4)); + bits.flip(Key(1)); // false + bits.flip(Key(6)); + bits.flip(Key(5)); + ExpectBits(bits, {2, 4, 5, 6, 7}); + + bits.flip(); + ExpectBits(bits, {0, 1, 3, 8}); + + ExpectBits(~bits, {2, 4, 5, 6, 7}); +} + +// Test that all the bits can be set/reset. +TEST_F(ITypBitsetTest, SetResetAll) { + Bitset bits; + + bits.set(); + + ASSERT_EQ(bits.count(), 9u); + ASSERT_TRUE(bits.all()); + ASSERT_TRUE(bits.any()); + ASSERT_FALSE(bits.none()); + + for (Key i(0); i < Key(9); ++i) { + ASSERT_TRUE(bits[i]); + } + + bits.reset(); + + ASSERT_EQ(bits.count(), 0u); + ASSERT_FALSE(bits.all()); + ASSERT_FALSE(bits.any()); + ASSERT_TRUE(bits.none()); + + for (Key i(0); i < Key(9); ++i) { + ASSERT_FALSE(bits[i]); + } +} + +// Test And operations +TEST_F(ITypBitsetTest, And) { + Bitset bits = {1 << 1 | 1 << 2 | 1 << 7}; + ExpectBits(bits, {1, 2, 7}); + + Bitset bits2 = bits & Bitset{1 << 0 | 1 << 3 | 1 << 7}; + ExpectBits(bits2, {7}); + ExpectBits(bits, {1, 2, 7}); + + bits &= Bitset{1 << 1 | 1 << 6}; + ExpectBits(bits, {1}); +} + +// Test Or operations +TEST_F(ITypBitsetTest, Or) { + Bitset bits = {1 << 1 | 1 << 2 | 1 << 7}; + ExpectBits(bits, {1, 2, 7}); + + Bitset bits2 = bits | Bitset{1 << 0 | 1 << 3 | 1 << 7}; + ExpectBits(bits2, {0, 1, 2, 3, 7}); + ExpectBits(bits, {1, 2, 7}); + + bits |= Bitset{1 << 1 | 1 << 6}; + ExpectBits(bits, {1, 2, 6, 7}); +} + +// Test xor operations +TEST_F(ITypBitsetTest, Xor) { + Bitset bits = {1 << 1 | 1 << 2 | 1 << 7}; + ExpectBits(bits, {1, 2, 7}); + + Bitset bits2 = bits ^ Bitset { 1 << 0 | 1 << 3 | 1 << 7 }; + ExpectBits(bits2, {0, 1, 2, 3}); + ExpectBits(bits, {1, 2, 7}); + + bits ^= Bitset{1 << 1 | 1 << 6}; + ExpectBits(bits, {2, 6, 7}); +} diff --git a/third_party/dawn/src/tests/unittests/ITypSpanTests.cpp b/third_party/dawn/src/tests/unittests/ITypSpanTests.cpp new file mode 100644 index 00000000000..16942ac9786 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/ITypSpanTests.cpp @@ -0,0 +1,81 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/TypedInteger.h" +#include "common/ityp_span.h" + +#include + +class ITypSpanTest : public testing::Test { + protected: + using Key = TypedInteger; + using Val = TypedInteger; + using Span = ityp::span; +}; + +// Test that values can be set at an index and retrieved from the same index. +TEST_F(ITypSpanTest, Indexing) { + std::array arr; + Span span(arr.data(), Key(arr.size())); + { + span[Key(2)] = Val(5); + span[Key(1)] = Val(9); + span[Key(9)] = Val(2); + + ASSERT_EQ(span[Key(2)], Val(5)); + ASSERT_EQ(span[Key(1)], Val(9)); + ASSERT_EQ(span[Key(9)], Val(2)); + } +} + +// Test that the span can be is iterated in order with a range-based for loop +TEST_F(ITypSpanTest, RangeBasedIteration) { + std::array arr; + Span span(arr.data(), Key(arr.size())); + + // Assign in a non-const range-based for loop + uint32_t i = 0; + for (Val& val : span) { + val = Val(i); + } + + // Check values in a const range-based for loop + i = 0; + for (Val val : static_cast(span)) { + ASSERT_EQ(val, span[Key(i++)]); + } +} + +// Test that begin/end/front/back/data return pointers/references to the correct elements. +TEST_F(ITypSpanTest, BeginEndFrontBackData) { + std::array arr; + Span span(arr.data(), Key(arr.size())); + + // non-const versions + ASSERT_EQ(span.begin(), &span[Key(0)]); + ASSERT_EQ(span.end(), &span[Key(0)] + static_cast(span.size())); + ASSERT_EQ(&span.front(), &span[Key(0)]); + ASSERT_EQ(&span.back(), &span[Key(9)]); + ASSERT_EQ(span.data(), &span[Key(0)]); + + // const versions + const Span& constSpan = span; + ASSERT_EQ(constSpan.begin(), &constSpan[Key(0)]); + ASSERT_EQ(constSpan.end(), &constSpan[Key(0)] + static_cast(constSpan.size())); + ASSERT_EQ(&constSpan.front(), &constSpan[Key(0)]); + ASSERT_EQ(&constSpan.back(), &constSpan[Key(9)]); + ASSERT_EQ(constSpan.data(), &constSpan[Key(0)]); +} diff --git a/third_party/dawn/src/tests/unittests/ITypVectorTests.cpp b/third_party/dawn/src/tests/unittests/ITypVectorTests.cpp new file mode 100644 index 00000000000..acc92b5deba --- /dev/null +++ b/third_party/dawn/src/tests/unittests/ITypVectorTests.cpp @@ -0,0 +1,184 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WvecANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/TypedInteger.h" +#include "common/ityp_vector.h" + +class ITypVectorTest : public testing::Test { + protected: + using Key = TypedInteger; + using Val = TypedInteger; + + using Vector = ityp::vector; +}; + +// Test creation and initialization of the vector. +TEST_F(ITypVectorTest, Creation) { + // Default constructor initializes to 0 + { + Vector vec; + ASSERT_EQ(vec.size(), Key(0)); + } + + // Size constructor initializes contents to 0 + { + Vector vec(Key(10)); + ASSERT_EQ(vec.size(), Key(10)); + + for (Key i(0); i < Key(10); ++i) { + ASSERT_EQ(vec[i], Val(0)); + } + } + + // Size and initial value constructor initializes contents to the inital value + { + Vector vec(Key(10), Val(7)); + ASSERT_EQ(vec.size(), Key(10)); + + for (Key i(0); i < Key(10); ++i) { + ASSERT_EQ(vec[i], Val(7)); + } + } + + // Initializer list constructor + { + Vector vec = {Val(2), Val(8), Val(1)}; + ASSERT_EQ(vec.size(), Key(3)); + ASSERT_EQ(vec[Key(0)], Val(2)); + ASSERT_EQ(vec[Key(1)], Val(8)); + ASSERT_EQ(vec[Key(2)], Val(1)); + } +} + +// Test copy construction/assignment +TEST_F(ITypVectorTest, CopyConstructAssign) { + // Test the copy constructor + { + Vector rhs = {Val(2), Val(8), Val(1)}; + + Vector vec(rhs); + ASSERT_EQ(vec.size(), Key(3)); + ASSERT_EQ(vec[Key(0)], Val(2)); + ASSERT_EQ(vec[Key(1)], Val(8)); + ASSERT_EQ(vec[Key(2)], Val(1)); + + ASSERT_EQ(rhs.size(), Key(3)); + ASSERT_EQ(rhs[Key(0)], Val(2)); + ASSERT_EQ(rhs[Key(1)], Val(8)); + ASSERT_EQ(rhs[Key(2)], Val(1)); + } + + // Test the copy assignment + { + Vector rhs = {Val(2), Val(8), Val(1)}; + + Vector vec = rhs; + ASSERT_EQ(vec.size(), Key(3)); + ASSERT_EQ(vec[Key(0)], Val(2)); + ASSERT_EQ(vec[Key(1)], Val(8)); + ASSERT_EQ(vec[Key(2)], Val(1)); + + ASSERT_EQ(rhs.size(), Key(3)); + ASSERT_EQ(rhs[Key(0)], Val(2)); + ASSERT_EQ(rhs[Key(1)], Val(8)); + ASSERT_EQ(rhs[Key(2)], Val(1)); + } +} + +// Test move construction/assignment +TEST_F(ITypVectorTest, MoveConstructAssign) { + // Test the move constructor + { + Vector rhs = {Val(2), Val(8), Val(1)}; + + Vector vec(std::move(rhs)); + ASSERT_EQ(vec.size(), Key(3)); + ASSERT_EQ(vec[Key(0)], Val(2)); + ASSERT_EQ(vec[Key(1)], Val(8)); + ASSERT_EQ(vec[Key(2)], Val(1)); + + ASSERT_EQ(rhs.size(), Key(0)); + } + + // Test the move assignment + { + Vector rhs = {Val(2), Val(8), Val(1)}; + + Vector vec = std::move(rhs); + ASSERT_EQ(vec.size(), Key(3)); + ASSERT_EQ(vec[Key(0)], Val(2)); + ASSERT_EQ(vec[Key(1)], Val(8)); + ASSERT_EQ(vec[Key(2)], Val(1)); + + ASSERT_EQ(rhs.size(), Key(0)); + } +} + +// Test that values can be set at an index and retrieved from the same index. +TEST_F(ITypVectorTest, Indexing) { + Vector vec(Key(10)); + { + vec[Key(2)] = Val(5); + vec[Key(1)] = Val(9); + vec[Key(9)] = Val(2); + + ASSERT_EQ(vec[Key(2)], Val(5)); + ASSERT_EQ(vec[Key(1)], Val(9)); + ASSERT_EQ(vec[Key(9)], Val(2)); + } + { + vec.at(Key(4)) = Val(5); + vec.at(Key(3)) = Val(8); + vec.at(Key(1)) = Val(7); + + ASSERT_EQ(vec.at(Key(4)), Val(5)); + ASSERT_EQ(vec.at(Key(3)), Val(8)); + ASSERT_EQ(vec.at(Key(1)), Val(7)); + } +} + +// Test that the vector can be iterated in order with a range-based for loop +TEST_F(ITypVectorTest, RangeBasedIteration) { + Vector vec(Key(10)); + + // Assign in a non-const range-based for loop + uint32_t i = 0; + for (Val& val : vec) { + val = Val(i); + } + + // Check values in a const range-based for loop + i = 0; + for (Val val : static_cast(vec)) { + ASSERT_EQ(val, vec[Key(i++)]); + } +} + +// Test that begin/end/front/back/data return pointers/references to the correct elements. +TEST_F(ITypVectorTest, BeginEndFrontBackData) { + Vector vec(Key(10)); + + // non-const versions + ASSERT_EQ(&vec.front(), &vec[Key(0)]); + ASSERT_EQ(&vec.back(), &vec[Key(9)]); + ASSERT_EQ(vec.data(), &vec[Key(0)]); + + // const versions + const Vector& constVec = vec; + ASSERT_EQ(&constVec.front(), &constVec[Key(0)]); + ASSERT_EQ(&constVec.back(), &constVec[Key(9)]); + ASSERT_EQ(constVec.data(), &constVec[Key(0)]); +} diff --git a/third_party/dawn/src/tests/unittests/LinkedListTests.cpp b/third_party/dawn/src/tests/unittests/LinkedListTests.cpp new file mode 100644 index 00000000000..7dcb37f009f --- /dev/null +++ b/third_party/dawn/src/tests/unittests/LinkedListTests.cpp @@ -0,0 +1,365 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is a copy of Chromium's /src/base/containers/linked_list_unittest.cc + +#include + +#include "common/LinkedList.h" + +#include + +class Node : public LinkNode { + public: + explicit Node(int id) : id_(id) { + } + + int id() const { + return id_; + } + + private: + int id_; +}; + +class MultipleInheritanceNodeBase { + public: + MultipleInheritanceNodeBase() : field_taking_up_space_(0) { + } + int field_taking_up_space_; +}; + +class MultipleInheritanceNode : public MultipleInheritanceNodeBase, + public LinkNode { + public: + MultipleInheritanceNode() = default; +}; + +class MovableNode : public LinkNode { + public: + explicit MovableNode(int id) : id_(id) { + } + + MovableNode(MovableNode&&) = default; + + int id() const { + return id_; + } + + private: + int id_; +}; + +// Checks that when iterating |list| (either from head to tail, or from +// tail to head, as determined by |forward|), we get back |node_ids|, +// which is an array of size |num_nodes|. +void ExpectListContentsForDirection(const LinkedList& list, + int num_nodes, + const int* node_ids, + bool forward) { + int i = 0; + for (const LinkNode* node = (forward ? list.head() : list.tail()); node != list.end(); + node = (forward ? node->next() : node->previous())) { + ASSERT_LT(i, num_nodes); + int index_of_id = forward ? i : num_nodes - i - 1; + EXPECT_EQ(node_ids[index_of_id], node->value()->id()); + ++i; + } + EXPECT_EQ(num_nodes, i); +} + +void ExpectListContents(const LinkedList& list, int num_nodes, const int* node_ids) { + { + SCOPED_TRACE("Iterating forward (from head to tail)"); + ExpectListContentsForDirection(list, num_nodes, node_ids, true); + } + { + SCOPED_TRACE("Iterating backward (from tail to head)"); + ExpectListContentsForDirection(list, num_nodes, node_ids, false); + } +} + +TEST(LinkedList, Empty) { + LinkedList list; + EXPECT_EQ(list.end(), list.head()); + EXPECT_EQ(list.end(), list.tail()); + ExpectListContents(list, 0, nullptr); +} + +TEST(LinkedList, Append) { + LinkedList list; + ExpectListContents(list, 0, nullptr); + + Node n1(1); + list.Append(&n1); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n1, list.tail()); + { + const int expected[] = {1}; + ExpectListContents(list, 1, expected); + } + + Node n2(2); + list.Append(&n2); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n2, list.tail()); + { + const int expected[] = {1, 2}; + ExpectListContents(list, 2, expected); + } + + Node n3(3); + list.Append(&n3); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n3, list.tail()); + { + const int expected[] = {1, 2, 3}; + ExpectListContents(list, 3, expected); + } +} + +TEST(LinkedList, RemoveFromList) { + LinkedList list; + + Node n1(1); + Node n2(2); + Node n3(3); + Node n4(4); + Node n5(5); + + list.Append(&n1); + list.Append(&n2); + list.Append(&n3); + list.Append(&n4); + list.Append(&n5); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n5, list.tail()); + { + const int expected[] = {1, 2, 3, 4, 5}; + ExpectListContents(list, 5, expected); + } + + // Remove from the middle. + n3.RemoveFromList(); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n5, list.tail()); + { + const int expected[] = {1, 2, 4, 5}; + ExpectListContents(list, 4, expected); + } + + // Remove from the tail. + n5.RemoveFromList(); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n4, list.tail()); + { + const int expected[] = {1, 2, 4}; + ExpectListContents(list, 3, expected); + } + + // Remove from the head. + n1.RemoveFromList(); + + EXPECT_EQ(&n2, list.head()); + EXPECT_EQ(&n4, list.tail()); + { + const int expected[] = {2, 4}; + ExpectListContents(list, 2, expected); + } + + // Empty the list. + n2.RemoveFromList(); + n4.RemoveFromList(); + + ExpectListContents(list, 0, nullptr); + EXPECT_EQ(list.end(), list.head()); + EXPECT_EQ(list.end(), list.tail()); + + // Fill the list once again. + list.Append(&n1); + list.Append(&n2); + list.Append(&n3); + list.Append(&n4); + list.Append(&n5); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n5, list.tail()); + { + const int expected[] = {1, 2, 3, 4, 5}; + ExpectListContents(list, 5, expected); + } +} + +TEST(LinkedList, InsertBefore) { + LinkedList list; + + Node n1(1); + Node n2(2); + Node n3(3); + Node n4(4); + + list.Append(&n1); + list.Append(&n2); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n2, list.tail()); + { + const int expected[] = {1, 2}; + ExpectListContents(list, 2, expected); + } + + n3.InsertBefore(&n2); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n2, list.tail()); + { + const int expected[] = {1, 3, 2}; + ExpectListContents(list, 3, expected); + } + + n4.InsertBefore(&n1); + + EXPECT_EQ(&n4, list.head()); + EXPECT_EQ(&n2, list.tail()); + { + const int expected[] = {4, 1, 3, 2}; + ExpectListContents(list, 4, expected); + } +} + +TEST(LinkedList, InsertAfter) { + LinkedList list; + + Node n1(1); + Node n2(2); + Node n3(3); + Node n4(4); + + list.Append(&n1); + list.Append(&n2); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n2, list.tail()); + { + const int expected[] = {1, 2}; + ExpectListContents(list, 2, expected); + } + + n3.InsertAfter(&n2); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n3, list.tail()); + { + const int expected[] = {1, 2, 3}; + ExpectListContents(list, 3, expected); + } + + n4.InsertAfter(&n1); + + EXPECT_EQ(&n1, list.head()); + EXPECT_EQ(&n3, list.tail()); + { + const int expected[] = {1, 4, 2, 3}; + ExpectListContents(list, 4, expected); + } +} + +TEST(LinkedList, MultipleInheritanceNode) { + MultipleInheritanceNode node; + EXPECT_EQ(&node, node.value()); +} + +TEST(LinkedList, EmptyListIsEmpty) { + LinkedList list; + EXPECT_TRUE(list.empty()); +} + +TEST(LinkedList, NonEmptyListIsNotEmpty) { + LinkedList list; + + Node n(1); + list.Append(&n); + + EXPECT_FALSE(list.empty()); +} + +TEST(LinkedList, EmptiedListIsEmptyAgain) { + LinkedList list; + + Node n(1); + list.Append(&n); + n.RemoveFromList(); + + EXPECT_TRUE(list.empty()); +} + +TEST(LinkedList, NodesCanBeReused) { + LinkedList list1; + LinkedList list2; + + Node n(1); + list1.Append(&n); + n.RemoveFromList(); + list2.Append(&n); + + EXPECT_EQ(list2.head()->value(), &n); +} + +TEST(LinkedList, RemovedNodeHasNullNextPrevious) { + LinkedList list; + + Node n(1); + list.Append(&n); + n.RemoveFromList(); + + EXPECT_EQ(nullptr, n.next()); + EXPECT_EQ(nullptr, n.previous()); +} + +TEST(LinkedList, NodeMoveConstructor) { + LinkedList list; + + MovableNode n1(1); + MovableNode n2(2); + MovableNode n3(3); + + list.Append(&n1); + list.Append(&n2); + list.Append(&n3); + + EXPECT_EQ(&n1, n2.previous()); + EXPECT_EQ(&n2, n1.next()); + EXPECT_EQ(&n3, n2.next()); + EXPECT_EQ(&n2, n3.previous()); + EXPECT_EQ(2, n2.id()); + + MovableNode n2_new(std::move(n2)); + + EXPECT_EQ(nullptr, n2.next()); + EXPECT_EQ(nullptr, n2.previous()); + + EXPECT_EQ(&n1, n2_new.previous()); + EXPECT_EQ(&n2_new, n1.next()); + EXPECT_EQ(&n3, n2_new.next()); + EXPECT_EQ(&n2_new, n3.previous()); + EXPECT_EQ(2, n2_new.id()); +} + +TEST(LinkedList, IsInList) { + LinkedList list; + + Node n(1); + + EXPECT_FALSE(n.IsInList()); + list.Append(&n); + EXPECT_TRUE(n.IsInList()); + n.RemoveFromList(); + EXPECT_FALSE(n.IsInList()); +} diff --git a/third_party/dawn/src/tests/unittests/MathTests.cpp b/third_party/dawn/src/tests/unittests/MathTests.cpp index 4caf9e7d0a1..2294fe1aa07 100644 --- a/third_party/dawn/src/tests/unittests/MathTests.cpp +++ b/third_party/dawn/src/tests/unittests/MathTests.cpp @@ -16,6 +16,8 @@ #include "common/Math.h" +#include + // Tests for ScanForward TEST(Math, ScanForward) { // Test extrema @@ -31,15 +33,65 @@ TEST(Math, ScanForward) { // Tests for Log2 TEST(Math, Log2) { // Test extrema - ASSERT_EQ(Log2(1), 0u); - ASSERT_EQ(Log2(0xFFFFFFFF), 31u); + ASSERT_EQ(Log2(1u), 0u); + ASSERT_EQ(Log2(0xFFFFFFFFu), 31u); + ASSERT_EQ(Log2(static_cast(0xFFFFFFFFFFFFFFFF)), 63u); + + static_assert(ConstexprLog2(1u) == 0u, ""); + static_assert(ConstexprLog2(0xFFFFFFFFu) == 31u, ""); + static_assert(ConstexprLog2(static_cast(0xFFFFFFFFFFFFFFFF)) == 63u, ""); + + // Test boundary between two logs + ASSERT_EQ(Log2(0x80000000u), 31u); + ASSERT_EQ(Log2(0x7FFFFFFFu), 30u); + ASSERT_EQ(Log2(static_cast(0x8000000000000000)), 63u); + ASSERT_EQ(Log2(static_cast(0x7FFFFFFFFFFFFFFF)), 62u); + + static_assert(ConstexprLog2(0x80000000u) == 31u, ""); + static_assert(ConstexprLog2(0x7FFFFFFFu) == 30u, ""); + static_assert(ConstexprLog2(static_cast(0x8000000000000000)) == 63u, ""); + static_assert(ConstexprLog2(static_cast(0x7FFFFFFFFFFFFFFF)) == 62u, ""); + + ASSERT_EQ(Log2(16u), 4u); + ASSERT_EQ(Log2(15u), 3u); + + static_assert(ConstexprLog2(16u) == 4u, ""); + static_assert(ConstexprLog2(15u) == 3u, ""); +} + +// Tests for Log2Ceil +TEST(Math, Log2Ceil) { + // Test extrema + ASSERT_EQ(Log2Ceil(1u), 0u); + ASSERT_EQ(Log2Ceil(0xFFFFFFFFu), 32u); + ASSERT_EQ(Log2Ceil(static_cast(0xFFFFFFFFFFFFFFFF)), 64u); + + static_assert(ConstexprLog2Ceil(1u) == 0u, ""); + static_assert(ConstexprLog2Ceil(0xFFFFFFFFu) == 32u, ""); + static_assert(ConstexprLog2Ceil(static_cast(0xFFFFFFFFFFFFFFFF)) == 64u, ""); // Test boundary between two logs - ASSERT_EQ(Log2(0x80000000), 31u); - ASSERT_EQ(Log2(0x7FFFFFFF), 30u); + ASSERT_EQ(Log2Ceil(0x80000001u), 32u); + ASSERT_EQ(Log2Ceil(0x80000000u), 31u); + ASSERT_EQ(Log2Ceil(0x7FFFFFFFu), 31u); + ASSERT_EQ(Log2Ceil(static_cast(0x8000000000000001)), 64u); + ASSERT_EQ(Log2Ceil(static_cast(0x8000000000000000)), 63u); + ASSERT_EQ(Log2Ceil(static_cast(0x7FFFFFFFFFFFFFFF)), 63u); + + static_assert(ConstexprLog2Ceil(0x80000001u) == 32u, ""); + static_assert(ConstexprLog2Ceil(0x80000000u) == 31u, ""); + static_assert(ConstexprLog2Ceil(0x7FFFFFFFu) == 31u, ""); + static_assert(ConstexprLog2Ceil(static_cast(0x8000000000000001)) == 64u, ""); + static_assert(ConstexprLog2Ceil(static_cast(0x8000000000000000)) == 63u, ""); + static_assert(ConstexprLog2Ceil(static_cast(0x7FFFFFFFFFFFFFFF)) == 63u, ""); - ASSERT_EQ(Log2(16), 4u); - ASSERT_EQ(Log2(15), 3u); + ASSERT_EQ(Log2Ceil(17u), 5u); + ASSERT_EQ(Log2Ceil(16u), 4u); + ASSERT_EQ(Log2Ceil(15u), 4u); + + static_assert(ConstexprLog2Ceil(17u) == 5u, ""); + static_assert(ConstexprLog2Ceil(16u) == 4u, ""); + static_assert(ConstexprLog2Ceil(15u) == 4u, ""); } // Tests for IsPowerOfTwo @@ -52,6 +104,19 @@ TEST(Math, IsPowerOfTwo) { ASSERT_FALSE(IsPowerOfTwo(0x8000400)); } +// Tests for NextPowerOfTwo +TEST(Math, NextPowerOfTwo) { + // Test extrema + ASSERT_EQ(NextPowerOfTwo(0), 1ull); + ASSERT_EQ(NextPowerOfTwo(0x7FFFFFFFFFFFFFFF), 0x8000000000000000); + + // Test boundary between powers-of-two. + ASSERT_EQ(NextPowerOfTwo(31), 32ull); + ASSERT_EQ(NextPowerOfTwo(33), 64ull); + + ASSERT_EQ(NextPowerOfTwo(32), 32ull); +} + // Tests for AlignPtr TEST(Math, AlignPtr) { constexpr size_t kTestAlignment = 8; @@ -64,7 +129,7 @@ TEST(Math, AlignPtr) { ASSERT_GE(aligned - unaligned, 0); ASSERT_LT(static_cast(aligned - unaligned), kTestAlignment); - ASSERT_EQ(reinterpret_cast(aligned) & (kTestAlignment -1), 0u); + ASSERT_EQ(reinterpret_cast(aligned) & (kTestAlignment - 1), 0u); } } @@ -133,3 +198,61 @@ TEST(Math, IsAligned) { ASSERT_FALSE(IsAligned(64 + i, 64)); } } + +// Tests for float32 to float16 conversion +TEST(Math, Float32ToFloat16) { + ASSERT_EQ(Float32ToFloat16(0.0f), 0x0000); + ASSERT_EQ(Float32ToFloat16(-0.0f), 0x8000); + + ASSERT_EQ(Float32ToFloat16(INFINITY), 0x7C00); + ASSERT_EQ(Float32ToFloat16(-INFINITY), 0xFC00); + + // Check that NaN is converted to a value in one of the float16 NaN ranges + uint16_t nan16 = Float32ToFloat16(NAN); + ASSERT_TRUE(nan16 > 0xFC00 || (nan16 < 0x8000 && nan16 > 0x7C00)); + + ASSERT_EQ(Float32ToFloat16(1.0f), 0x3C00); +} + +// Tests for IsFloat16NaN +TEST(Math, IsFloat16NaN) { + ASSERT_FALSE(IsFloat16NaN(0u)); + ASSERT_FALSE(IsFloat16NaN(0u)); + ASSERT_FALSE(IsFloat16NaN(Float32ToFloat16(1.0f))); + ASSERT_FALSE(IsFloat16NaN(Float32ToFloat16(INFINITY))); + ASSERT_FALSE(IsFloat16NaN(Float32ToFloat16(-INFINITY))); + + ASSERT_TRUE(IsFloat16NaN(Float32ToFloat16(INFINITY) + 1)); + ASSERT_TRUE(IsFloat16NaN(Float32ToFloat16(-INFINITY) + 1)); + ASSERT_TRUE(IsFloat16NaN(0x7FFF)); + ASSERT_TRUE(IsFloat16NaN(0xFFFF)); +} + +// Tests for SRGBToLinear +TEST(Math, SRGBToLinear) { + ASSERT_EQ(SRGBToLinear(0.0f), 0.0f); + ASSERT_EQ(SRGBToLinear(1.0f), 1.0f); + + ASSERT_EQ(SRGBToLinear(-1.0f), 0.0f); + ASSERT_EQ(SRGBToLinear(2.0f), 1.0f); + + ASSERT_FLOAT_EQ(SRGBToLinear(0.5f), 0.21404114f); +} + +// Tests for RoundUp +TEST(Math, RoundUp) { + ASSERT_EQ(RoundUp(2, 2), 2u); + ASSERT_EQ(RoundUp(2, 4), 4u); + ASSERT_EQ(RoundUp(6, 2), 6u); + ASSERT_EQ(RoundUp(8, 4), 8u); + ASSERT_EQ(RoundUp(12, 6), 12u); + + ASSERT_EQ(RoundUp(3, 3), 3u); + ASSERT_EQ(RoundUp(3, 5), 5u); + ASSERT_EQ(RoundUp(5, 3), 6u); + ASSERT_EQ(RoundUp(9, 5), 10u); + + // Test extrema + ASSERT_EQ(RoundUp(0x7FFFFFFFFFFFFFFFull, 0x8000000000000000ull), 0x8000000000000000ull); + ASSERT_EQ(RoundUp(1, 1), 1u); +} diff --git a/third_party/dawn/src/tests/unittests/ObjectBaseTests.cpp b/third_party/dawn/src/tests/unittests/ObjectBaseTests.cpp index b42920f1579..ab141d3e7a3 100644 --- a/third_party/dawn/src/tests/unittests/ObjectBaseTests.cpp +++ b/third_party/dawn/src/tests/unittests/ObjectBaseTests.cpp @@ -14,21 +14,21 @@ #include -#include "dawn/dawncpp.h" - -class Object : public dawn::ObjectBase { - public: - using ObjectBase::ObjectBase; - using ObjectBase::operator=; - - static void DawnReference(int* handle) { - ASSERT_LE(0, *handle); - *handle += 1; - } - static void DawnRelease(int* handle) { - ASSERT_LT(0, *handle); - *handle -= 1; - } +#include "dawn/webgpu_cpp.h" + +class Object : public wgpu::ObjectBase { + public: + using ObjectBase::ObjectBase; + using ObjectBase::operator=; + + static void WGPUReference(int* handle) { + ASSERT_LE(0, *handle); + *handle += 1; + } + static void WGPURelease(int* handle) { + ASSERT_LT(0, *handle); + *handle -= 1; + } }; // Test that creating an C++ object from a C object takes a ref. @@ -194,4 +194,3 @@ TEST(ObjectBase, AssignNullptr) { obj = nullptr; ASSERT_EQ(refcount, 1); } - diff --git a/third_party/dawn/src/tests/unittests/PerStageTests.cpp b/third_party/dawn/src/tests/unittests/PerStageTests.cpp index df1b2c47e0e..dcecd83dc0a 100644 --- a/third_party/dawn/src/tests/unittests/PerStageTests.cpp +++ b/third_party/dawn/src/tests/unittests/PerStageTests.cpp @@ -20,70 +20,70 @@ using namespace dawn_native; // Tests for StageBit TEST(PerStage, StageBit) { - ASSERT_EQ(StageBit(dawn::ShaderStage::Vertex), dawn::ShaderStageBit::Vertex); - ASSERT_EQ(StageBit(dawn::ShaderStage::Fragment), dawn::ShaderStageBit::Fragment); - ASSERT_EQ(StageBit(dawn::ShaderStage::Compute), dawn::ShaderStageBit::Compute); + ASSERT_EQ(StageBit(SingleShaderStage::Vertex), wgpu::ShaderStage::Vertex); + ASSERT_EQ(StageBit(SingleShaderStage::Fragment), wgpu::ShaderStage::Fragment); + ASSERT_EQ(StageBit(SingleShaderStage::Compute), wgpu::ShaderStage::Compute); } // Basic test for the PerStage container TEST(PerStage, PerStage) { PerStage data; - // Store data using dawn::ShaderStage - data[dawn::ShaderStage::Vertex] = 42; - data[dawn::ShaderStage::Fragment] = 3; - data[dawn::ShaderStage::Compute] = -1; + // Store data using wgpu::ShaderStage + data[SingleShaderStage::Vertex] = 42; + data[SingleShaderStage::Fragment] = 3; + data[SingleShaderStage::Compute] = -1; - // Load it using dawn::ShaderStageBit - ASSERT_EQ(data[dawn::ShaderStageBit::Vertex], 42); - ASSERT_EQ(data[dawn::ShaderStageBit::Fragment], 3); - ASSERT_EQ(data[dawn::ShaderStageBit::Compute], -1); + // Load it using wgpu::ShaderStage + ASSERT_EQ(data[wgpu::ShaderStage::Vertex], 42); + ASSERT_EQ(data[wgpu::ShaderStage::Fragment], 3); + ASSERT_EQ(data[wgpu::ShaderStage::Compute], -1); } // Test IterateStages with kAllStages TEST(PerStage, IterateAllStages) { PerStage counts; - counts[dawn::ShaderStage::Vertex] = 0; - counts[dawn::ShaderStage::Fragment] = 0; - counts[dawn::ShaderStage::Compute] = 0; + counts[SingleShaderStage::Vertex] = 0; + counts[SingleShaderStage::Fragment] = 0; + counts[SingleShaderStage::Compute] = 0; for (auto stage : IterateStages(kAllStages)) { - counts[stage] ++; + counts[stage]++; } - ASSERT_EQ(counts[dawn::ShaderStageBit::Vertex], 1); - ASSERT_EQ(counts[dawn::ShaderStageBit::Fragment], 1); - ASSERT_EQ(counts[dawn::ShaderStageBit::Compute], 1); + ASSERT_EQ(counts[wgpu::ShaderStage::Vertex], 1); + ASSERT_EQ(counts[wgpu::ShaderStage::Fragment], 1); + ASSERT_EQ(counts[wgpu::ShaderStage::Compute], 1); } // Test IterateStages with one stage TEST(PerStage, IterateOneStage) { PerStage counts; - counts[dawn::ShaderStage::Vertex] = 0; - counts[dawn::ShaderStage::Fragment] = 0; - counts[dawn::ShaderStage::Compute] = 0; + counts[SingleShaderStage::Vertex] = 0; + counts[SingleShaderStage::Fragment] = 0; + counts[SingleShaderStage::Compute] = 0; - for (auto stage : IterateStages(dawn::ShaderStageBit::Fragment)) { - counts[stage] ++; + for (auto stage : IterateStages(wgpu::ShaderStage::Fragment)) { + counts[stage]++; } - ASSERT_EQ(counts[dawn::ShaderStageBit::Vertex], 0); - ASSERT_EQ(counts[dawn::ShaderStageBit::Fragment], 1); - ASSERT_EQ(counts[dawn::ShaderStageBit::Compute], 0); + ASSERT_EQ(counts[wgpu::ShaderStage::Vertex], 0); + ASSERT_EQ(counts[wgpu::ShaderStage::Fragment], 1); + ASSERT_EQ(counts[wgpu::ShaderStage::Compute], 0); } // Test IterateStages with no stage TEST(PerStage, IterateNoStages) { PerStage counts; - counts[dawn::ShaderStage::Vertex] = 0; - counts[dawn::ShaderStage::Fragment] = 0; - counts[dawn::ShaderStage::Compute] = 0; + counts[SingleShaderStage::Vertex] = 0; + counts[SingleShaderStage::Fragment] = 0; + counts[SingleShaderStage::Compute] = 0; - for (auto stage : IterateStages(dawn::ShaderStageBit::Fragment & dawn::ShaderStageBit::Vertex)) { - counts[stage] ++; + for (auto stage : IterateStages(wgpu::ShaderStage::Fragment & wgpu::ShaderStage::Vertex)) { + counts[stage]++; } - ASSERT_EQ(counts[dawn::ShaderStageBit::Vertex], 0); - ASSERT_EQ(counts[dawn::ShaderStageBit::Fragment], 0); - ASSERT_EQ(counts[dawn::ShaderStageBit::Compute], 0); + ASSERT_EQ(counts[wgpu::ShaderStage::Vertex], 0); + ASSERT_EQ(counts[wgpu::ShaderStage::Fragment], 0); + ASSERT_EQ(counts[wgpu::ShaderStage::Compute], 0); } diff --git a/third_party/dawn/src/tests/unittests/PlacementAllocatedTests.cpp b/third_party/dawn/src/tests/unittests/PlacementAllocatedTests.cpp new file mode 100644 index 00000000000..9ff7d1d901f --- /dev/null +++ b/third_party/dawn/src/tests/unittests/PlacementAllocatedTests.cpp @@ -0,0 +1,115 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "common/PlacementAllocated.h" + +using namespace testing; + +namespace { + + enum class DestructedClass { + Foo, + Bar, + }; + + class MockDestructor { + public: + MOCK_METHOD(void, Call, (void*, DestructedClass)); + }; + + std::unique_ptr> mockDestructor; + + class PlacementAllocatedTests : public Test { + void SetUp() override { + mockDestructor = std::make_unique>(); + } + + void TearDown() override { + mockDestructor = nullptr; + } + }; + + struct Foo : PlacementAllocated { + virtual ~Foo() { + mockDestructor->Call(this, DestructedClass::Foo); + } + }; + + struct Bar : Foo { + ~Bar() override { + mockDestructor->Call(this, DestructedClass::Bar); + } + }; +} // namespace + +// Test that deletion calls the destructor and does not free memory. +TEST_F(PlacementAllocatedTests, DeletionDoesNotFreeMemory) { + void* ptr = malloc(sizeof(Foo)); + + Foo* foo = new (ptr) Foo(); + + EXPECT_CALL(*mockDestructor, Call(foo, DestructedClass::Foo)); + delete foo; + + // Touch the memory, this shouldn't crash. + static_assert(sizeof(Foo) >= sizeof(uint32_t), ""); + *reinterpret_cast(foo) = 42; + + free(ptr); +} + +// Test that destructing an instance of a derived class calls the derived, then base destructor, and +// does not free memory. +TEST_F(PlacementAllocatedTests, DeletingDerivedClassCallsBaseDestructor) { + void* ptr = malloc(sizeof(Bar)); + + Bar* bar = new (ptr) Bar(); + + { + InSequence s; + EXPECT_CALL(*mockDestructor, Call(bar, DestructedClass::Bar)); + EXPECT_CALL(*mockDestructor, Call(bar, DestructedClass::Foo)); + delete bar; + } + + // Touch the memory, this shouldn't crash. + static_assert(sizeof(Bar) >= sizeof(uint32_t), ""); + *reinterpret_cast(bar) = 42; + + free(ptr); +} + +// Test that destructing an instance of a base class calls the derived, then base destructor, and +// does not free memory. +TEST_F(PlacementAllocatedTests, DeletingBaseClassCallsDerivedDestructor) { + void* ptr = malloc(sizeof(Bar)); + + Foo* foo = new (ptr) Bar(); + + { + InSequence s; + EXPECT_CALL(*mockDestructor, Call(foo, DestructedClass::Bar)); + EXPECT_CALL(*mockDestructor, Call(foo, DestructedClass::Foo)); + delete foo; + } + + // Touch the memory, this shouldn't crash. + static_assert(sizeof(Bar) >= sizeof(uint32_t), ""); + *reinterpret_cast(foo) = 42; + + free(ptr); +} diff --git a/third_party/dawn/src/tests/unittests/RefCountedTests.cpp b/third_party/dawn/src/tests/unittests/RefCountedTests.cpp index e8a7f6f25c6..7e5c959c526 100644 --- a/third_party/dawn/src/tests/unittests/RefCountedTests.cpp +++ b/third_party/dawn/src/tests/unittests/RefCountedTests.cpp @@ -15,20 +15,22 @@ #include #include -#include "dawn_native/RefCounted.h" +#include "common/RefCounted.h" -using namespace dawn_native; +class RCTest : public RefCounted { + public: + RCTest() : RefCounted() { + } -struct RCTest : public RefCounted { - RCTest() { + RCTest(uint64_t payload) : RefCounted(payload) { } - RCTest(bool* deleted): deleted(deleted) { + RCTest(bool* deleted) : mDeleted(deleted) { } ~RCTest() override { - if (deleted != nullptr) { - *deleted = true; + if (mDeleted != nullptr) { + *mDeleted = true; } } @@ -36,7 +38,19 @@ struct RCTest : public RefCounted { return this; } - bool* deleted = nullptr; + private: + bool* mDeleted = nullptr; +}; + +struct RCTestDerived : public RCTest { + RCTestDerived() : RCTest() { + } + + RCTestDerived(uint64_t payload) : RCTest(payload) { + } + + RCTestDerived(bool* deleted) : RCTest(deleted) { + } }; // Test that RCs start with one ref, and removing it destroys the object. @@ -45,7 +59,7 @@ TEST(RefCounted, StartsWithOneRef) { auto test = new RCTest(&deleted); test->Release(); - ASSERT_TRUE(deleted); + EXPECT_TRUE(deleted); } // Test adding refs keep the RC alive. @@ -55,10 +69,10 @@ TEST(RefCounted, AddingRefKeepsAlive) { test->Reference(); test->Release(); - ASSERT_FALSE(deleted); + EXPECT_FALSE(deleted); test->Release(); - ASSERT_TRUE(deleted); + EXPECT_TRUE(deleted); } // Test that Reference and Release atomically change the refcount. @@ -76,7 +90,7 @@ TEST(RefCounted, RaceOnReferenceRelease) { t1.join(); t2.join(); - ASSERT_EQ(test->GetRefCount(), 200001u); + EXPECT_EQ(test->GetRefCountForTesting(), 200001u); auto releaseManyTimes = [test]() { for (uint32_t i = 0; i < 100000; ++i) { @@ -88,7 +102,10 @@ TEST(RefCounted, RaceOnReferenceRelease) { std::thread t4(releaseManyTimes); t3.join(); t4.join(); - ASSERT_EQ(test->GetRefCount(), 1u); + EXPECT_EQ(test->GetRefCountForTesting(), 1u); + + test->Release(); + EXPECT_TRUE(deleted); } // Test Ref remove reference when going out of scope @@ -98,7 +115,7 @@ TEST(Ref, EndOfScopeRemovesRef) { Ref test(new RCTest(&deleted)); test->Release(); } - ASSERT_TRUE(deleted); + EXPECT_TRUE(deleted); } // Test getting pointer out of the Ref @@ -107,18 +124,18 @@ TEST(Ref, Gets) { Ref test(original); test->Release(); - ASSERT_EQ(test.Get(), original); - ASSERT_EQ(&*test, original); - ASSERT_EQ(test->GetThis(), original); + EXPECT_EQ(test.Get(), original); + EXPECT_EQ(&*test, original); + EXPECT_EQ(test->GetThis(), original); } // Test Refs default to null TEST(Ref, DefaultsToNull) { Ref test; - ASSERT_EQ(test.Get(), nullptr); - ASSERT_EQ(&*test, nullptr); - ASSERT_EQ(test->GetThis(), nullptr); + EXPECT_EQ(test.Get(), nullptr); + EXPECT_EQ(&*test, nullptr); + EXPECT_EQ(test->GetThis(), nullptr); } // Test Refs can be used inside ifs @@ -128,7 +145,7 @@ TEST(Ref, BoolConversion) { full->Release(); if (!full || empty) { - ASSERT_TRUE(false); + EXPECT_TRUE(false); } } @@ -138,16 +155,23 @@ TEST(Ref, CopyConstructor) { RCTest* original = new RCTest(&deleted); Ref source(original); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + Ref destination(source); + EXPECT_EQ(original->GetRefCountForTesting(), 3u); + original->Release(); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); - ASSERT_EQ(source.Get(), original); - ASSERT_EQ(destination.Get(), original); + EXPECT_EQ(source.Get(), original); + EXPECT_EQ(destination.Get(), original); source = nullptr; - ASSERT_FALSE(deleted); + EXPECT_FALSE(deleted); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + destination = nullptr; - ASSERT_TRUE(deleted); + EXPECT_TRUE(deleted); } // Test Ref's copy assignment @@ -161,15 +185,15 @@ TEST(Ref, CopyAssignment) { Ref destination; destination = source; - ASSERT_EQ(source.Get(), original); - ASSERT_EQ(destination.Get(), original); + EXPECT_EQ(source.Get(), original); + EXPECT_EQ(destination.Get(), original); source = nullptr; // This fails when address sanitizer is turned on - ASSERT_FALSE(deleted); + EXPECT_FALSE(deleted); destination = nullptr; - ASSERT_TRUE(deleted); + EXPECT_TRUE(deleted); } // Test Ref's move constructor @@ -178,15 +202,20 @@ TEST(Ref, MoveConstructor) { RCTest* original = new RCTest(&deleted); Ref source(original); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + Ref destination(std::move(source)); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + original->Release(); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); - ASSERT_EQ(source.Get(), nullptr); - ASSERT_EQ(destination.Get(), original); - ASSERT_FALSE(deleted); + EXPECT_EQ(source.Get(), nullptr); + EXPECT_EQ(destination.Get(), original); + EXPECT_FALSE(deleted); destination = nullptr; - ASSERT_TRUE(deleted); + EXPECT_TRUE(deleted); } // Test Ref's move assignment @@ -195,15 +224,190 @@ TEST(Ref, MoveAssignment) { RCTest* original = new RCTest(&deleted); Ref source(original); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + original->Release(); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); Ref destination; destination = std::move(source); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + + EXPECT_EQ(source.Get(), nullptr); + EXPECT_EQ(destination.Get(), original); + EXPECT_FALSE(deleted); + + destination = nullptr; + EXPECT_TRUE(deleted); +} + +// Test move assigment where the destination and source +// point to the same underlying object. +TEST(Ref, MoveAssignmentSameObject) { + bool deleted = false; + RCTest* original = new RCTest(&deleted); + + Ref source(original); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + + original->Release(); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + + Ref& referenceToSource = source; + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + + referenceToSource = std::move(source); + + EXPECT_EQ(source.Get(), original); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + EXPECT_FALSE(deleted); + + source = nullptr; + EXPECT_TRUE(deleted); +} + +// Test the payload initial value is set correctly +TEST(Ref, InitialPayloadValue) { + RCTest* testDefaultConstructor = new RCTest(); + EXPECT_EQ(testDefaultConstructor->GetRefCountPayload(), 0u); + testDefaultConstructor->Release(); + + RCTest* testZero = new RCTest(uint64_t(0ull)); + EXPECT_EQ(testZero->GetRefCountPayload(), 0u); + testZero->Release(); + + RCTest* testOne = new RCTest(1ull); + EXPECT_EQ(testOne->GetRefCountPayload(), 1u); + testOne->Release(); +} + +// Test that the payload survives ref and release operations +TEST(Ref, PayloadUnchangedByRefCounting) { + RCTest* test = new RCTest(1ull); + EXPECT_EQ(test->GetRefCountPayload(), 1u); + + test->Reference(); + EXPECT_EQ(test->GetRefCountPayload(), 1u); + test->Release(); + EXPECT_EQ(test->GetRefCountPayload(), 1u); + + test->Release(); +} + +// Test that Detach pulls out the pointer and stops tracking it. +TEST(Ref, Detach) { + bool deleted = false; + RCTest* original = new RCTest(&deleted); + + Ref test(original); + original->Release(); + + RCTest* detached = test.Detach(); + EXPECT_EQ(detached, original); + EXPECT_EQ(detached->GetRefCountForTesting(), 1u); + EXPECT_EQ(test.Get(), nullptr); + + detached->Release(); + EXPECT_TRUE(deleted); +} + +// Test constructor passed a derived pointer +TEST(Ref, DerivedPointerConstructor) { + bool deleted = false; + { + Ref test(new RCTestDerived(&deleted)); + test->Release(); + } + EXPECT_TRUE(deleted); +} + +// Test copy constructor of derived class +TEST(Ref, DerivedCopyConstructor) { + bool deleted = false; + Ref testDerived(new RCTestDerived(&deleted)); + testDerived->Release(); + + { + Ref testBase(testDerived); + EXPECT_EQ(testBase->GetRefCountForTesting(), 2u); + EXPECT_EQ(testDerived->GetRefCountForTesting(), 2u); + } + + EXPECT_EQ(testDerived->GetRefCountForTesting(), 1u); +} + +// Test Ref constructed with nullptr +TEST(Ref, ConstructedWithNullptr) { + Ref test(nullptr); + EXPECT_EQ(test.Get(), nullptr); +} + +// Test Ref's copy assignment with derived class +TEST(Ref, CopyAssignmentDerived) { + bool deleted = false; + + RCTestDerived* original = new RCTestDerived(&deleted); + Ref source(original); + original->Release(); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + + Ref destination; + destination = source; + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + + EXPECT_EQ(source.Get(), original); + EXPECT_EQ(destination.Get(), original); + + source = nullptr; + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + EXPECT_FALSE(deleted); + + destination = nullptr; + EXPECT_TRUE(deleted); +} + +// Test Ref's move constructor with derived class +TEST(Ref, MoveConstructorDerived) { + bool deleted = false; + RCTestDerived* original = new RCTestDerived(&deleted); + + Ref source(original); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + + Ref destination(std::move(source)); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + + original->Release(); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + + EXPECT_EQ(source.Get(), nullptr); + EXPECT_EQ(destination.Get(), original); + EXPECT_FALSE(deleted); + + destination = nullptr; + EXPECT_TRUE(deleted); +} + +// Test Ref's move assignment with derived class +TEST(Ref, MoveAssignmentDerived) { + bool deleted = false; + RCTestDerived* original = new RCTestDerived(&deleted); + + Ref source(original); + EXPECT_EQ(original->GetRefCountForTesting(), 2u); + + original->Release(); + EXPECT_EQ(original->GetRefCountForTesting(), 1u); + + Ref destination; + destination = std::move(source); + + EXPECT_EQ(original->GetRefCountForTesting(), 1u); - ASSERT_EQ(source.Get(), nullptr); - ASSERT_EQ(destination.Get(), original); - ASSERT_FALSE(deleted); + EXPECT_EQ(source.Get(), nullptr); + EXPECT_EQ(destination.Get(), original); + EXPECT_FALSE(deleted); destination = nullptr; - ASSERT_TRUE(deleted); + EXPECT_TRUE(deleted); } diff --git a/third_party/dawn/src/tests/unittests/ResultTests.cpp b/third_party/dawn/src/tests/unittests/ResultTests.cpp index 46161418e88..bd48217d44d 100644 --- a/third_party/dawn/src/tests/unittests/ResultTests.cpp +++ b/third_party/dawn/src/tests/unittests/ResultTests.cpp @@ -14,176 +14,372 @@ #include +#include "common/RefCounted.h" #include "common/Result.h" namespace { -template -void TestError(Result* result, E expectedError) { - ASSERT_TRUE(result->IsError()); - ASSERT_FALSE(result->IsSuccess()); - - E storedError = result->AcquireError(); - ASSERT_EQ(storedError, expectedError); -} - -template -void TestSuccess(Result* result, T expectedSuccess) { - ASSERT_FALSE(result->IsError()); - ASSERT_TRUE(result->IsSuccess()); - - T storedSuccess = result->AcquireSuccess(); - ASSERT_EQ(storedSuccess, expectedSuccess); -} - -static int dummyError = 0xbeef; -static float dummySuccess = 42.0f; - -// Result - -// Test constructing an error Result -TEST(ResultOnlyPointerError, ConstructingError) { - Result result(&dummyError); - TestError(&result, &dummyError); -} - -// Test moving an error Result -TEST(ResultOnlyPointerError, MovingError) { - Result result(&dummyError); - Result movedResult(std::move(result)); - TestError(&movedResult, &dummyError); -} - -// Test returning an error Result -TEST(ResultOnlyPointerError, ReturningError) { - auto CreateError = []() -> Result { - return {&dummyError}; - }; + template + void TestError(Result* result, E expectedError) { + EXPECT_TRUE(result->IsError()); + EXPECT_FALSE(result->IsSuccess()); - Result result = CreateError(); - TestError(&result, &dummyError); -} - -// Test constructing a success Result -TEST(ResultOnlyPointerError, ConstructingSuccess) { - Result result; - ASSERT_TRUE(result.IsSuccess()); - ASSERT_FALSE(result.IsError()); -} - -// Test moving a success Result -TEST(ResultOnlyPointerError, MovingSuccess) { - Result result; - Result movedResult(std::move(result)); - ASSERT_TRUE(movedResult.IsSuccess()); - ASSERT_FALSE(movedResult.IsError()); -} - -// Test returning a success Result -TEST(ResultOnlyPointerError, ReturningSuccess) { - auto CreateError = []() -> Result { - return {}; - }; + std::unique_ptr storedError = result->AcquireError(); + EXPECT_EQ(*storedError, expectedError); + } - Result result = CreateError(); - ASSERT_TRUE(result.IsSuccess()); - ASSERT_FALSE(result.IsError()); -} - -// Result - -// Test constructing an error Result -TEST(ResultBothPointer, ConstructingError) { - Result result(&dummyError); - TestError(&result, &dummyError); -} - -// Test moving an error Result -TEST(ResultBothPointer, MovingError) { - Result result(&dummyError); - Result movedResult(std::move(result)); - TestError(&movedResult, &dummyError); -} - -// Test returning an error Result -TEST(ResultBothPointer, ReturningError) { - auto CreateError = []() -> Result { - return {&dummyError}; - }; + template + void TestSuccess(Result* result, T expectedSuccess) { + EXPECT_FALSE(result->IsError()); + EXPECT_TRUE(result->IsSuccess()); - Result result = CreateError(); - TestError(&result, &dummyError); -} - -// Test constructing a success Result -TEST(ResultBothPointer, ConstructingSuccess) { - Result result(&dummySuccess); - TestSuccess(&result, &dummySuccess); -} - -// Test moving a success Result -TEST(ResultBothPointer, MovingSuccess) { - Result result(&dummySuccess); - Result movedResult(std::move(result)); - TestSuccess(&movedResult, &dummySuccess); -} - -// Test returning a success Result -TEST(ResultBothPointer, ReturningSuccess) { - auto CreateSuccess = []() -> Result { - return {&dummySuccess}; - }; + const T storedSuccess = result->AcquireSuccess(); + EXPECT_EQ(storedSuccess, expectedSuccess); - Result result = CreateSuccess(); - TestSuccess(&result, &dummySuccess); -} - -// Result - -// Test constructing an error Result -TEST(ResultGeneric, ConstructingError) { - Result, int*> result(&dummyError); - TestError(&result, &dummyError); -} - -// Test moving an error Result -TEST(ResultGeneric, MovingError) { - Result, int*> result(&dummyError); - Result, int*> movedResult(std::move(result)); - TestError(&movedResult, &dummyError); -} - -// Test returning an error Result -TEST(ResultGeneric, ReturningError) { - auto CreateError = []() -> Result, int*> { - return {&dummyError}; - }; + // Once the success is acquired, result has an empty + // payload and is neither in the success nor error state. + EXPECT_FALSE(result->IsError()); + EXPECT_FALSE(result->IsSuccess()); + } + + static int dummyError = 0xbeef; + static float dummySuccess = 42.0f; + static const float dummyConstSuccess = 42.0f; - Result, int*> result = CreateError(); - TestError(&result, &dummyError); -} - -// Test constructing a success Result -TEST(ResultGeneric, ConstructingSuccess) { - Result, int*> result({1.0f}); - TestSuccess(&result, {1.0f}); -} - -// Test moving a success Result -TEST(ResultGeneric, MovingSuccess) { - Result, int*> result({1.0f}); - Result, int*> movedResult(std::move(result)); - TestSuccess(&movedResult, {1.0f}); -} - -// Test returning a success Result -TEST(ResultGeneric, ReturningSuccess) { - auto CreateSuccess = []() -> Result, int*> { - return {{1.0f}}; + class AClass : public RefCounted { + public: + int a = 0; }; - Result, int*> result = CreateSuccess(); - TestSuccess(&result, {1.0f}); -} + // Tests using the following overload of TestSuccess make + // local Ref instances to dummySuccessObj. Tests should + // ensure any local Ref objects made along the way continue + // to point to dummySuccessObj. + template + void TestSuccess(Result, E>* result, T* expectedSuccess) { + EXPECT_FALSE(result->IsError()); + EXPECT_TRUE(result->IsSuccess()); + + // AClass starts with a reference count of 1 and stored + // on the stack in the caller. The result parameter should + // hold the only other reference to the object. + EXPECT_EQ(expectedSuccess->GetRefCountForTesting(), 2u); + + const Ref storedSuccess = result->AcquireSuccess(); + EXPECT_EQ(storedSuccess.Get(), expectedSuccess); + + // Once the success is acquired, result has an empty + // payload and is neither in the success nor error state. + EXPECT_FALSE(result->IsError()); + EXPECT_FALSE(result->IsSuccess()); + + // Once we call AcquireSuccess, result no longer stores + // the object. storedSuccess should contain the only other + // reference to the object. + EXPECT_EQ(storedSuccess->GetRefCountForTesting(), 2u); + } + + // Result + + // Test constructing an error Result + TEST(ResultOnlyPointerError, ConstructingError) { + Result result(std::make_unique(dummyError)); + TestError(&result, dummyError); + } + + // Test moving an error Result + TEST(ResultOnlyPointerError, MovingError) { + Result result(std::make_unique(dummyError)); + Result movedResult(std::move(result)); + TestError(&movedResult, dummyError); + } + + // Test returning an error Result + TEST(ResultOnlyPointerError, ReturningError) { + auto CreateError = []() -> Result { + return {std::make_unique(dummyError)}; + }; + + Result result = CreateError(); + TestError(&result, dummyError); + } + + // Test constructing a success Result + TEST(ResultOnlyPointerError, ConstructingSuccess) { + Result result; + EXPECT_TRUE(result.IsSuccess()); + EXPECT_FALSE(result.IsError()); + } + + // Test moving a success Result + TEST(ResultOnlyPointerError, MovingSuccess) { + Result result; + Result movedResult(std::move(result)); + EXPECT_TRUE(movedResult.IsSuccess()); + EXPECT_FALSE(movedResult.IsError()); + } + + // Test returning a success Result + TEST(ResultOnlyPointerError, ReturningSuccess) { + auto CreateError = []() -> Result { return {}; }; + + Result result = CreateError(); + EXPECT_TRUE(result.IsSuccess()); + EXPECT_FALSE(result.IsError()); + } + + // Result + + // Test constructing an error Result + TEST(ResultBothPointer, ConstructingError) { + Result result(std::make_unique(dummyError)); + TestError(&result, dummyError); + } + + // Test moving an error Result + TEST(ResultBothPointer, MovingError) { + Result result(std::make_unique(dummyError)); + Result movedResult(std::move(result)); + TestError(&movedResult, dummyError); + } + + // Test returning an error Result + TEST(ResultBothPointer, ReturningError) { + auto CreateError = []() -> Result { + return {std::make_unique(dummyError)}; + }; + + Result result = CreateError(); + TestError(&result, dummyError); + } + + // Test constructing a success Result + TEST(ResultBothPointer, ConstructingSuccess) { + Result result(&dummySuccess); + TestSuccess(&result, &dummySuccess); + } + + // Test moving a success Result + TEST(ResultBothPointer, MovingSuccess) { + Result result(&dummySuccess); + Result movedResult(std::move(result)); + TestSuccess(&movedResult, &dummySuccess); + } + + // Test returning a success Result + TEST(ResultBothPointer, ReturningSuccess) { + auto CreateSuccess = []() -> Result { return {&dummySuccess}; }; + + Result result = CreateSuccess(); + TestSuccess(&result, &dummySuccess); + } + + // Tests converting from a Result + TEST(ResultBothPointer, ConversionFromChildClass) { + struct T { + int a; + }; + struct TChild : T {}; + + TChild child; + T* childAsT = &child; + { + Result result(&child); + TestSuccess(&result, childAsT); + } + { + Result resultChild(&child); + Result result(std::move(resultChild)); + TestSuccess(&result, childAsT); + } + { + Result resultChild(&child); + Result result = std::move(resultChild); + TestSuccess(&result, childAsT); + } + } + + // Result + + // Test constructing an error Result + TEST(ResultBothPointerWithConstResult, ConstructingError) { + Result result(std::make_unique(dummyError)); + TestError(&result, dummyError); + } + + // Test moving an error Result + TEST(ResultBothPointerWithConstResult, MovingError) { + Result result(std::make_unique(dummyError)); + Result movedResult(std::move(result)); + TestError(&movedResult, dummyError); + } + + // Test returning an error Result + TEST(ResultBothPointerWithConstResult, ReturningError) { + auto CreateError = []() -> Result { + return {std::make_unique(dummyError)}; + }; + + Result result = CreateError(); + TestError(&result, dummyError); + } + + // Test constructing a success Result + TEST(ResultBothPointerWithConstResult, ConstructingSuccess) { + Result result(&dummyConstSuccess); + TestSuccess(&result, &dummyConstSuccess); + } + + // Test moving a success Result + TEST(ResultBothPointerWithConstResult, MovingSuccess) { + Result result(&dummyConstSuccess); + Result movedResult(std::move(result)); + TestSuccess(&movedResult, &dummyConstSuccess); + } + + // Test returning a success Result + TEST(ResultBothPointerWithConstResult, ReturningSuccess) { + auto CreateSuccess = []() -> Result { return {&dummyConstSuccess}; }; + + Result result = CreateSuccess(); + TestSuccess(&result, &dummyConstSuccess); + } + + // Result, E> + + // Test constructing an error Result, E> + TEST(ResultRefT, ConstructingError) { + Result, int> result(std::make_unique(dummyError)); + TestError(&result, dummyError); + } + + // Test moving an error Result, E> + TEST(ResultRefT, MovingError) { + Result, int> result(std::make_unique(dummyError)); + Result, int> movedResult(std::move(result)); + TestError(&movedResult, dummyError); + } + + // Test returning an error Result, E> + TEST(ResultRefT, ReturningError) { + auto CreateError = []() -> Result, int> { + return {std::make_unique(dummyError)}; + }; + + Result, int> result = CreateError(); + TestError(&result, dummyError); + } + + // Test constructing a success Result, E> + TEST(ResultRefT, ConstructingSuccess) { + AClass success; + + Ref refObj(&success); + Result, int> result(std::move(refObj)); + TestSuccess(&result, &success); + } + + // Test moving a success Result, E> + TEST(ResultRefT, MovingSuccess) { + AClass success; + + Ref refObj(&success); + Result, int> result(std::move(refObj)); + Result, int> movedResult(std::move(result)); + TestSuccess(&movedResult, &success); + } + + // Test returning a success Result, E> + TEST(ResultRefT, ReturningSuccess) { + AClass success; + auto CreateSuccess = [&success]() -> Result, int> { + return Ref(&success); + }; + + Result, int> result = CreateSuccess(); + TestSuccess(&result, &success); + } + + class OtherClass { + public: + int a = 0; + }; + class Base : public RefCounted {}; + class Child : public OtherClass, public Base {}; + + // Test constructing a Result, E> + TEST(ResultRefT, ConversionFromChildConstructor) { + Child child; + Ref refChild(&child); + + Result, int> result(std::move(refChild)); + TestSuccess(&result, &child); + } + + // Test copy constructing Result, E> + TEST(ResultRefT, ConversionFromChildCopyConstructor) { + Child child; + Ref refChild(&child); + + Result, int> resultChild(std::move(refChild)); + Result, int> result(std::move(resultChild)); + TestSuccess(&result, &child); + } + + // Test assignment operator for Result, E> + TEST(ResultRefT, ConversionFromChildAssignmentOperator) { + Child child; + Ref refChild(&child); + + Result, int> resultChild(std::move(refChild)); + Result, int> result = std::move(resultChild); + TestSuccess(&result, &child); + } + + // Result + + // Test constructing an error Result + TEST(ResultGeneric, ConstructingError) { + Result, int> result(std::make_unique(dummyError)); + TestError(&result, dummyError); + } + + // Test moving an error Result + TEST(ResultGeneric, MovingError) { + Result, int> result(std::make_unique(dummyError)); + Result, int> movedResult(std::move(result)); + TestError(&movedResult, dummyError); + } + + // Test returning an error Result + TEST(ResultGeneric, ReturningError) { + auto CreateError = []() -> Result, int> { + return {std::make_unique(dummyError)}; + }; + + Result, int> result = CreateError(); + TestError(&result, dummyError); + } + + // Test constructing a success Result + TEST(ResultGeneric, ConstructingSuccess) { + Result, int> result({1.0f}); + TestSuccess(&result, {1.0f}); + } + + // Test moving a success Result + TEST(ResultGeneric, MovingSuccess) { + Result, int> result({1.0f}); + Result, int> movedResult(std::move(result)); + TestSuccess(&movedResult, {1.0f}); + } + + // Test returning a success Result + TEST(ResultGeneric, ReturningSuccess) { + auto CreateSuccess = []() -> Result, int> { return {{1.0f}}; }; + + Result, int> result = CreateSuccess(); + TestSuccess(&result, {1.0f}); + } } // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/RingBufferAllocatorTests.cpp b/third_party/dawn/src/tests/unittests/RingBufferAllocatorTests.cpp new file mode 100644 index 00000000000..f0c256b3585 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/RingBufferAllocatorTests.cpp @@ -0,0 +1,177 @@ +// Copyright 2018 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "dawn_native/RingBufferAllocator.h" + +using namespace dawn_native; + +constexpr uint64_t RingBufferAllocator::kInvalidOffset; + +// Number of basic tests for Ringbuffer +TEST(RingBufferAllocatorTests, BasicTest) { + constexpr uint64_t sizeInBytes = 64000; + RingBufferAllocator allocator(sizeInBytes); + + // Ensure no requests exist on empty buffer. + EXPECT_TRUE(allocator.Empty()); + + ASSERT_EQ(allocator.GetSize(), sizeInBytes); + + // Ensure failure upon sub-allocating an oversized request. + ASSERT_EQ(allocator.Allocate(sizeInBytes + 1, 0), RingBufferAllocator::kInvalidOffset); + + // Fill the entire buffer with two requests of equal size. + ASSERT_EQ(allocator.Allocate(sizeInBytes / 2, 1), 0u); + ASSERT_EQ(allocator.Allocate(sizeInBytes / 2, 2), 32000u); + + // Ensure the buffer is full. + ASSERT_EQ(allocator.Allocate(1, 3), RingBufferAllocator::kInvalidOffset); +} + +// Tests that several ringbuffer allocations do not fail. +TEST(RingBufferAllocatorTests, RingBufferManyAlloc) { + constexpr uint64_t maxNumOfFrames = 64000; + constexpr uint64_t frameSizeInBytes = 4; + + RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes); + + size_t offset = 0; + for (size_t i = 0; i < maxNumOfFrames; ++i) { + offset = allocator.Allocate(frameSizeInBytes, i); + ASSERT_EQ(offset, i * frameSizeInBytes); + } +} + +// Tests ringbuffer sub-allocations of the same serial are correctly tracked. +TEST(RingBufferAllocatorTests, AllocInSameFrame) { + constexpr uint64_t maxNumOfFrames = 3; + constexpr uint64_t frameSizeInBytes = 4; + + RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes); + + // F1 + // [xxxx|--------] + size_t offset = allocator.Allocate(frameSizeInBytes, 1); + + // F1 F2 + // [xxxx|xxxx|----] + + offset = allocator.Allocate(frameSizeInBytes, 2); + + // F1 F2 + // [xxxx|xxxxxxxx] + + offset = allocator.Allocate(frameSizeInBytes, 2); + + ASSERT_EQ(offset, 8u); + ASSERT_EQ(allocator.GetUsedSize(), frameSizeInBytes * 3); + + allocator.Deallocate(2); + + ASSERT_EQ(allocator.GetUsedSize(), 0u); + EXPECT_TRUE(allocator.Empty()); +} + +// Tests ringbuffer sub-allocation at various offsets. +TEST(RingBufferAllocatorTests, RingBufferSubAlloc) { + constexpr uint64_t maxNumOfFrames = 10; + constexpr uint64_t frameSizeInBytes = 4; + + RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes); + + // Sub-alloc the first eight frames. + Serial serial = 1; + for (size_t i = 0; i < 8; ++i) { + allocator.Allocate(frameSizeInBytes, serial); + serial += 1; + } + + // Each frame corrresponds to the serial number (for simplicity). + // + // F1 F2 F3 F4 F5 F6 F7 F8 + // [xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxx|--------] + // + + // Ensure an oversized allocation fails (only 8 bytes left) + ASSERT_EQ(allocator.Allocate(frameSizeInBytes * 3, serial + 1), + RingBufferAllocator::kInvalidOffset); + ASSERT_EQ(allocator.GetUsedSize(), frameSizeInBytes * 8); + + // Reclaim the first 3 frames. + allocator.Deallocate(3); + + // F4 F5 F6 F7 F8 + // [------------|xxxx|xxxx|xxxx|xxxx|xxxx|--------] + // + ASSERT_EQ(allocator.GetUsedSize(), frameSizeInBytes * 5); + + // Re-try the over-sized allocation. + size_t offset = allocator.Allocate(frameSizeInBytes * 3, serial); + + // F9 F4 F5 F6 F7 F8 + // [xxxxxxxxxxxx|xxxx|xxxx|xxxx|xxxx|xxxx|xxxxxxxx] + // ^^^^^^^^ wasted + + // In this example, Deallocate(8) could not reclaim the wasted bytes. The wasted bytes + // were added to F9's sub-allocation. + // TODO(bryan.bernhart@intel.com): Decide if Deallocate(8) should free these wasted bytes. + + ASSERT_EQ(offset, 0u); + ASSERT_EQ(allocator.GetUsedSize(), frameSizeInBytes * maxNumOfFrames); + + // Ensure we are full. + ASSERT_EQ(allocator.Allocate(frameSizeInBytes, serial + 1), + RingBufferAllocator::kInvalidOffset); + + // Reclaim the next two frames. + allocator.Deallocate(5); + + // F9 F4 F5 F6 F7 F8 + // [xxxxxxxxxxxx|----|----|xxxx|xxxx|xxxx|xxxxxxxx] + // + ASSERT_EQ(allocator.GetUsedSize(), frameSizeInBytes * 8); + + // Sub-alloc the chunk in the middle. + serial += 1; + offset = allocator.Allocate(frameSizeInBytes * 2, serial); + + ASSERT_EQ(offset, frameSizeInBytes * 3); + ASSERT_EQ(allocator.GetUsedSize(), frameSizeInBytes * maxNumOfFrames); + + // F9 F10 F6 F7 F8 + // [xxxxxxxxxxxx|xxxxxxxxx|xxxx|xxxx|xxxx|xxxxxxxx] + // + + // Ensure we are full. + ASSERT_EQ(allocator.Allocate(frameSizeInBytes, serial + 1), + RingBufferAllocator::kInvalidOffset); + + // Reclaim all. + allocator.Deallocate(maxNumOfFrames); + + EXPECT_TRUE(allocator.Empty()); +} + +// Checks if ringbuffer sub-allocation does not overflow. +TEST(RingBufferAllocatorTests, RingBufferOverflow) { + Serial serial = 1; + + RingBufferAllocator allocator(std::numeric_limits::max()); + + ASSERT_EQ(allocator.Allocate(1, serial), 0u); + ASSERT_EQ(allocator.Allocate(std::numeric_limits::max(), serial + 1), + RingBufferAllocator::kInvalidOffset); +} diff --git a/third_party/dawn/src/tests/unittests/SerialQueueTests.cpp b/third_party/dawn/src/tests/unittests/SerialQueueTests.cpp index 6f75eb78ae2..2545c51929e 100644 --- a/third_party/dawn/src/tests/unittests/SerialQueueTests.cpp +++ b/third_party/dawn/src/tests/unittests/SerialQueueTests.cpp @@ -153,4 +153,4 @@ TEST(SerialQueue, LastSerial) { queue.Enqueue({2}, 1); EXPECT_EQ(queue.LastSerial(), 1u); -} \ No newline at end of file +} diff --git a/third_party/dawn/src/tests/unittests/SlabAllocatorTests.cpp b/third_party/dawn/src/tests/unittests/SlabAllocatorTests.cpp new file mode 100644 index 00000000000..45011fc39f4 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/SlabAllocatorTests.cpp @@ -0,0 +1,180 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/Math.h" +#include "common/SlabAllocator.h" + +namespace { + + struct Foo : public PlacementAllocated { + Foo(int value) : value(value) { + } + + int value; + }; + + struct alignas(256) AlignedFoo : public Foo { + using Foo::Foo; + }; + +} // namespace + +// Test that a slab allocator of a single object works. +TEST(SlabAllocatorTests, Single) { + SlabAllocator allocator(1 * sizeof(Foo)); + + Foo* obj = allocator.Allocate(4); + EXPECT_EQ(obj->value, 4); + + allocator.Deallocate(obj); +} + +// Allocate multiple objects and check their data is correct. +TEST(SlabAllocatorTests, AllocateSequential) { + // Check small alignment + { + SlabAllocator allocator(5 * sizeof(Foo)); + + std::vector objects; + for (int i = 0; i < 10; ++i) { + auto* ptr = allocator.Allocate(i); + EXPECT_TRUE(std::find(objects.begin(), objects.end(), ptr) == objects.end()); + objects.push_back(ptr); + } + + for (int i = 0; i < 10; ++i) { + // Check that the value is correct and hasn't been trampled. + EXPECT_EQ(objects[i]->value, i); + + // Check that the alignment is correct. + EXPECT_TRUE(IsPtrAligned(objects[i], alignof(Foo))); + } + + // Deallocate all of the objects. + for (Foo* object : objects) { + allocator.Deallocate(object); + } + } + + // Check large alignment + { + SlabAllocator allocator(9 * sizeof(AlignedFoo)); + + std::vector objects; + for (int i = 0; i < 21; ++i) { + auto* ptr = allocator.Allocate(i); + EXPECT_TRUE(std::find(objects.begin(), objects.end(), ptr) == objects.end()); + objects.push_back(ptr); + } + + for (int i = 0; i < 21; ++i) { + // Check that the value is correct and hasn't been trampled. + EXPECT_EQ(objects[i]->value, i); + + // Check that the alignment is correct. + EXPECT_TRUE(IsPtrAligned(objects[i], 256)); + } + + // Deallocate all of the objects. + for (AlignedFoo* object : objects) { + allocator.Deallocate(object); + } + } +} + +// Test that when reallocating a number of objects <= pool size, all memory is reused. +TEST(SlabAllocatorTests, ReusesFreedMemory) { + SlabAllocator allocator(17 * sizeof(Foo)); + + // Allocate a number of objects. + std::set objects; + for (int i = 0; i < 17; ++i) { + EXPECT_TRUE(objects.insert(allocator.Allocate(i)).second); + } + + // Deallocate all of the objects. + for (Foo* object : objects) { + allocator.Deallocate(object); + } + + std::set reallocatedObjects; + // Allocate objects again. All of the pointers should be the same. + for (int i = 0; i < 17; ++i) { + Foo* ptr = allocator.Allocate(i); + EXPECT_TRUE(reallocatedObjects.insert(ptr).second); + EXPECT_TRUE(std::find(objects.begin(), objects.end(), ptr) != objects.end()); + } + + // Deallocate all of the objects. + for (Foo* object : objects) { + allocator.Deallocate(object); + } +} + +// Test many allocations and deallocations. Meant to catch corner cases with partially +// empty slabs. +TEST(SlabAllocatorTests, AllocateDeallocateMany) { + SlabAllocator allocator(17 * sizeof(Foo)); + + std::set objects; + std::set set3; + std::set set7; + + // Allocate many objects. + for (uint32_t i = 0; i < 800; ++i) { + Foo* object = allocator.Allocate(i); + EXPECT_TRUE(objects.insert(object).second); + + if (i % 3 == 0) { + set3.insert(object); + } else if (i % 7 == 0) { + set7.insert(object); + } + } + + // Deallocate every 3rd object. + for (Foo* object : set3) { + allocator.Deallocate(object); + objects.erase(object); + } + + // Allocate many more objects + for (uint32_t i = 0; i < 800; ++i) { + Foo* object = allocator.Allocate(i); + EXPECT_TRUE(objects.insert(object).second); + + if (i % 7 == 0) { + set7.insert(object); + } + } + + // Deallocate every 7th object from the first and second rounds of allocation. + for (Foo* object : set7) { + allocator.Deallocate(object); + objects.erase(object); + } + + // Allocate objects again + for (uint32_t i = 0; i < 800; ++i) { + Foo* object = allocator.Allocate(i); + EXPECT_TRUE(objects.insert(object).second); + } + + // Deallocate the rest of the objects + for (Foo* object : objects) { + allocator.Deallocate(object); + } +} diff --git a/third_party/dawn/src/tests/unittests/StackContainerTests.cpp b/third_party/dawn/src/tests/unittests/StackContainerTests.cpp new file mode 100644 index 00000000000..cb4cea69fc9 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/StackContainerTests.cpp @@ -0,0 +1,169 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is a modified copy of Chromium's /src/base/containers/stack_container_unittest.cc + +#include + +#include "common/RefCounted.h" +#include "common/StackContainer.h" + +#include +#include + +namespace { + + class Dummy : public RefCounted { + public: + explicit Dummy(int* alive) : mAlive(alive) { + ++*mAlive; + } + + private: + ~Dummy() { + --*mAlive; + } + + int* const mAlive; + }; + +} // namespace + +TEST(StackContainer, Vector) { + const int stack_size = 3; + StackVector vect; + const int* stack_buffer = &vect.stack_data().stack_buffer()[0]; + + // The initial |stack_size| elements should appear in the stack buffer. + EXPECT_EQ(static_cast(stack_size), vect.container().capacity()); + for (int i = 0; i < stack_size; i++) { + vect.container().push_back(i); + EXPECT_EQ(stack_buffer, &vect.container()[0]); + EXPECT_TRUE(vect.stack_data().used_stack_buffer_); + } + + // Adding more elements should push the array onto the heap. + for (int i = 0; i < stack_size; i++) { + vect.container().push_back(i + stack_size); + EXPECT_NE(stack_buffer, &vect.container()[0]); + EXPECT_FALSE(vect.stack_data().used_stack_buffer_); + } + + // The array should still be in order. + for (int i = 0; i < stack_size * 2; i++) + EXPECT_EQ(i, vect.container()[i]); + + // Resize to smaller. Our STL implementation won't reallocate in this case, + // otherwise it might use our stack buffer. We reserve right after the resize + // to guarantee it isn't using the stack buffer, even though it doesn't have + // much data. + vect.container().resize(stack_size); + vect.container().reserve(stack_size * 2); + EXPECT_FALSE(vect.stack_data().used_stack_buffer_); + + // Copying the small vector to another should use the same allocator and use + // the now-unused stack buffer. GENERALLY CALLERS SHOULD NOT DO THIS since + // they have to get the template types just right and it can cause errors. + std::vector> other(vect.container()); + EXPECT_EQ(stack_buffer, &other.front()); + EXPECT_TRUE(vect.stack_data().used_stack_buffer_); + for (int i = 0; i < stack_size; i++) + EXPECT_EQ(i, other[i]); +} + +TEST(StackContainer, VectorDoubleDelete) { + // Regression testing for double-delete. + typedef StackVector, 2> Vector; + Vector vect; + + int alive = 0; + Ref dummy = AcquireRef(new Dummy(&alive)); + EXPECT_EQ(alive, 1); + + vect->push_back(dummy); + EXPECT_EQ(alive, 1); + + Dummy* dummy_unref = dummy.Get(); + dummy = nullptr; + EXPECT_EQ(alive, 1); + + auto itr = std::find(vect->begin(), vect->end(), dummy_unref); + EXPECT_EQ(itr->Get(), dummy_unref); + vect->erase(itr); + EXPECT_EQ(alive, 0); + + // Shouldn't crash at exit. +} + +namespace { + + template + class AlignedData { + public: + AlignedData() { + memset(data_, 0, alignment); + } + ~AlignedData() = default; + alignas(alignment) char data_[alignment]; + }; + +} // anonymous namespace + +#define EXPECT_ALIGNED(ptr, align) EXPECT_EQ(0u, reinterpret_cast(ptr) & (align - 1)) + +TEST(StackContainer, BufferAlignment) { + StackVector text; + text->push_back(L'A'); + EXPECT_ALIGNED(&text[0], alignof(wchar_t)); + + StackVector doubles; + doubles->push_back(0.0); + EXPECT_ALIGNED(&doubles[0], alignof(double)); + + StackVector, 1> aligned16; + aligned16->push_back(AlignedData<16>()); + EXPECT_ALIGNED(&aligned16[0], 16); + +#if !defined(DAWN_COMPILER_GCC) || defined(__x86_64__) || defined(__i386__) + // It seems that non-X86 gcc doesn't respect greater than 16 byte alignment. + // See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=33721 for details. + // TODO(sbc):re-enable this if GCC starts respecting higher alignments. + StackVector, 1> aligned256; + aligned256->push_back(AlignedData<256>()); + EXPECT_ALIGNED(&aligned256[0], 256); +#endif +} + +template class StackVector; +template class StackVector, 2>; + +template +void CheckStackVectorElements(const StackVector& vec, std::initializer_list expected) { + auto expected_it = expected.begin(); + EXPECT_EQ(vec->size(), expected.size()); + for (T t : vec) { + EXPECT_NE(expected.end(), expected_it); + EXPECT_EQ(*expected_it, t); + ++expected_it; + } + EXPECT_EQ(expected.end(), expected_it); +} + +TEST(StackContainer, Iteration) { + StackVector vect; + vect->push_back(7); + vect->push_back(11); + + CheckStackVectorElements(vect, {7, 11}); + for (int& i : vect) { + ++i; + } + CheckStackVectorElements(vect, {8, 12}); + vect->push_back(13); + CheckStackVectorElements(vect, {8, 12, 13}); + vect->resize(5); + CheckStackVectorElements(vect, {8, 12, 13, 0, 0}); + vect->resize(1); + CheckStackVectorElements(vect, {8}); +} diff --git a/third_party/dawn/src/tests/unittests/SystemUtilsTests.cpp b/third_party/dawn/src/tests/unittests/SystemUtilsTests.cpp new file mode 100644 index 00000000000..3730f45f13a --- /dev/null +++ b/third_party/dawn/src/tests/unittests/SystemUtilsTests.cpp @@ -0,0 +1,86 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/SystemUtils.h" + +// Tests for GetEnvironmentVar +TEST(SystemUtilsTests, GetEnvironmentVar) { + // Test nonexistent environment variable + ASSERT_EQ(GetEnvironmentVar("NonexistentEnvironmentVar"), ""); +} + +// Tests for SetEnvironmentVar +TEST(SystemUtilsTests, SetEnvironmentVar) { + // Test new environment variable + ASSERT_TRUE(SetEnvironmentVar("EnvironmentVarForTest", "NewEnvironmentVarValue")); + ASSERT_EQ(GetEnvironmentVar("EnvironmentVarForTest"), "NewEnvironmentVarValue"); + // Test override environment variable + ASSERT_TRUE(SetEnvironmentVar("EnvironmentVarForTest", "OverrideEnvironmentVarValue")); + ASSERT_EQ(GetEnvironmentVar("EnvironmentVarForTest"), "OverrideEnvironmentVarValue"); +} + +// Tests for GetExecutableDirectory +TEST(SystemUtilsTests, GetExecutableDirectory) { + // Test returned value is non-empty string + ASSERT_NE(GetExecutableDirectory(), ""); + // Test last charecter in path + ASSERT_EQ(GetExecutableDirectory().back(), *GetPathSeparator()); +} + +// Tests for ScopedEnvironmentVar +TEST(SystemUtilsTests, ScopedEnvironmentVar) { + SetEnvironmentVar("ScopedEnvironmentVarForTest", "original"); + + // Test empty environment variable doesn't crash + { ScopedEnvironmentVar var; } + + // Test setting empty environment variable + { + ScopedEnvironmentVar var; + var.Set("ScopedEnvironmentVarForTest", "NewEnvironmentVarValue"); + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "NewEnvironmentVarValue"); + } + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "original"); + + // Test that the environment variable can be set, and it is unset at the end of the scope. + { + ScopedEnvironmentVar var("ScopedEnvironmentVarForTest", "NewEnvironmentVarValue"); + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "NewEnvironmentVarValue"); + } + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "original"); + + // Test nested scopes + { + ScopedEnvironmentVar outer("ScopedEnvironmentVarForTest", "outer"); + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "outer"); + { + ScopedEnvironmentVar inner("ScopedEnvironmentVarForTest", "inner"); + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "inner"); + } + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "outer"); + } + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "original"); + + // Test redundantly setting scoped variables + { + ScopedEnvironmentVar var1("ScopedEnvironmentVarForTest", "var1"); + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "var1"); + + ScopedEnvironmentVar var2("ScopedEnvironmentVarForTest", "var2"); + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "var2"); + } + ASSERT_EQ(GetEnvironmentVar("ScopedEnvironmentVarForTest"), "original"); +} diff --git a/third_party/dawn/src/tests/unittests/ToBackendTests.cpp b/third_party/dawn/src/tests/unittests/ToBackendTests.cpp index d16a4f70963..5234e40f186 100644 --- a/third_party/dawn/src/tests/unittests/ToBackendTests.cpp +++ b/third_party/dawn/src/tests/unittests/ToBackendTests.cpp @@ -14,28 +14,26 @@ #include -#include "dawn_native/RefCounted.h" +#include "common/RefCounted.h" #include "dawn_native/ToBackend.h" #include // Make our own Base - Backend object pair, reusing the CommandBuffer name namespace dawn_native { - class CommandBufferBase : public RefCounted { - }; -} + class CommandBufferBase : public RefCounted {}; +} // namespace dawn_native using namespace dawn_native; -class MyCommandBuffer : public CommandBufferBase { -}; +class MyCommandBuffer : public CommandBufferBase {}; struct MyBackendTraits { using CommandBufferType = MyCommandBuffer; }; // Instanciate ToBackend for our "backend" -template +template auto ToBackend(T&& common) -> decltype(ToBackendBase(common)) { return ToBackendBase(common); } @@ -71,7 +69,8 @@ TEST(ToBackend, Ref) { const Ref base(cmdBuf); const auto& backendCmdBuf = ToBackend(base); - static_assert(std::is_same&>::value, ""); + static_assert(std::is_same&>::value, + ""); ASSERT_EQ(cmdBuf, backendCmdBuf.Get()); cmdBuf->Release(); diff --git a/third_party/dawn/src/tests/unittests/TypedIntegerTests.cpp b/third_party/dawn/src/tests/unittests/TypedIntegerTests.cpp new file mode 100644 index 00000000000..50cfe1d9c72 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/TypedIntegerTests.cpp @@ -0,0 +1,234 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include "common/TypedInteger.h" +#include "common/UnderlyingType.h" + +class TypedIntegerTest : public testing::Test { + protected: + using Unsigned = TypedInteger; + using Signed = TypedInteger; +}; + +// Test that typed integers can be created and cast and the internal values are identical +TEST_F(TypedIntegerTest, ConstructionAndCast) { + Signed svalue(2); + EXPECT_EQ(static_cast(svalue), 2); + + Unsigned uvalue(7); + EXPECT_EQ(static_cast(uvalue), 7u); + + static_assert(static_cast(Signed(3)) == 3, ""); + static_assert(static_cast(Unsigned(28)) == 28, ""); +} + +// Test typed integer comparison operators +TEST_F(TypedIntegerTest, Comparison) { + Unsigned value(8); + + // Truthy usages of comparison operators + EXPECT_TRUE(value < Unsigned(9)); + EXPECT_TRUE(value <= Unsigned(9)); + EXPECT_TRUE(value <= Unsigned(8)); + EXPECT_TRUE(value == Unsigned(8)); + EXPECT_TRUE(value >= Unsigned(8)); + EXPECT_TRUE(value >= Unsigned(7)); + EXPECT_TRUE(value > Unsigned(7)); + EXPECT_TRUE(value != Unsigned(7)); + + // Falsy usages of comparison operators + EXPECT_FALSE(value >= Unsigned(9)); + EXPECT_FALSE(value > Unsigned(9)); + EXPECT_FALSE(value > Unsigned(8)); + EXPECT_FALSE(value != Unsigned(8)); + EXPECT_FALSE(value < Unsigned(8)); + EXPECT_FALSE(value < Unsigned(7)); + EXPECT_FALSE(value <= Unsigned(7)); + EXPECT_FALSE(value == Unsigned(7)); +} + +TEST_F(TypedIntegerTest, Arithmetic) { + // Postfix Increment + { + Signed value(0); + EXPECT_EQ(value++, Signed(0)); + EXPECT_EQ(value, Signed(1)); + } + + // Prefix Increment + { + Signed value(0); + EXPECT_EQ(++value, Signed(1)); + EXPECT_EQ(value, Signed(1)); + } + + // Postfix Decrement + { + Signed value(0); + EXPECT_EQ(value--, Signed(0)); + EXPECT_EQ(value, Signed(-1)); + } + + // Prefix Decrement + { + Signed value(0); + EXPECT_EQ(--value, Signed(-1)); + EXPECT_EQ(value, Signed(-1)); + } + + // Signed addition + { + Signed a(3); + Signed b(-4); + Signed c = a + b; + EXPECT_EQ(a, Signed(3)); + EXPECT_EQ(b, Signed(-4)); + EXPECT_EQ(c, Signed(-1)); + } + + // Signed subtraction + { + Signed a(3); + Signed b(-4); + Signed c = a - b; + EXPECT_EQ(a, Signed(3)); + EXPECT_EQ(b, Signed(-4)); + EXPECT_EQ(c, Signed(7)); + } + + // Unsigned addition + { + Unsigned a(9); + Unsigned b(3); + Unsigned c = a + b; + EXPECT_EQ(a, Unsigned(9)); + EXPECT_EQ(b, Unsigned(3)); + EXPECT_EQ(c, Unsigned(12)); + } + + // Unsigned subtraction + { + Unsigned a(9); + Unsigned b(2); + Unsigned c = a - b; + EXPECT_EQ(a, Unsigned(9)); + EXPECT_EQ(b, Unsigned(2)); + EXPECT_EQ(c, Unsigned(7)); + } + + // Negation + { + Signed a(5); + Signed b = -a; + EXPECT_EQ(a, Signed(5)); + EXPECT_EQ(b, Signed(-5)); + } +} + +TEST_F(TypedIntegerTest, NumericLimits) { + EXPECT_EQ(std::numeric_limits::max(), Unsigned(std::numeric_limits::max())); + EXPECT_EQ(std::numeric_limits::min(), Unsigned(std::numeric_limits::min())); + EXPECT_EQ(std::numeric_limits::max(), Signed(std::numeric_limits::max())); + EXPECT_EQ(std::numeric_limits::min(), Signed(std::numeric_limits::min())); +} + +TEST_F(TypedIntegerTest, UnderlyingType) { + static_assert(std::is_same, uint32_t>::value, ""); + static_assert(std::is_same, int32_t>::value, ""); +} + +// Tests for bounds assertions on arithmetic overflow and underflow. +#if defined(DAWN_ENABLE_ASSERTS) + +TEST_F(TypedIntegerTest, IncrementUnsignedOverflow) { + Unsigned value(std::numeric_limits::max() - 1); + + value++; // Doesn't overflow. + EXPECT_DEATH(value++, ""); // Overflows. +} + +TEST_F(TypedIntegerTest, IncrementSignedOverflow) { + Signed value(std::numeric_limits::max() - 1); + + value++; // Doesn't overflow. + EXPECT_DEATH(value++, ""); // Overflows. +} + +TEST_F(TypedIntegerTest, DecrementUnsignedUnderflow) { + Unsigned value(std::numeric_limits::min() + 1); + + value--; // Doesn't underflow. + EXPECT_DEATH(value--, ""); // Underflows. +} + +TEST_F(TypedIntegerTest, DecrementSignedUnderflow) { + Signed value(std::numeric_limits::min() + 1); + + value--; // Doesn't underflow. + EXPECT_DEATH(value--, ""); // Underflows. +} + +TEST_F(TypedIntegerTest, UnsignedAdditionOverflow) { + Unsigned value(std::numeric_limits::max() - 1); + + value + Unsigned(1); // Doesn't overflow. + EXPECT_DEATH(value + Unsigned(2), ""); // Overflows. +} + +TEST_F(TypedIntegerTest, UnsignedSubtractionUnderflow) { + Unsigned value(1); + + value - Unsigned(1); // Doesn't underflow. + EXPECT_DEATH(value - Unsigned(2), ""); // Underflows. +} + +TEST_F(TypedIntegerTest, SignedAdditionOverflow) { + Signed value(std::numeric_limits::max() - 1); + + value + Signed(1); // Doesn't overflow. + EXPECT_DEATH(value + Signed(2), ""); // Overflows. +} + +TEST_F(TypedIntegerTest, SignedAdditionUnderflow) { + Signed value(std::numeric_limits::min() + 1); + + value + Signed(-1); // Doesn't underflow. + EXPECT_DEATH(value + Signed(-2), ""); // Underflows. +} + +TEST_F(TypedIntegerTest, SignedSubtractionOverflow) { + Signed value(std::numeric_limits::max() - 1); + + value - Signed(-1); // Doesn't overflow. + EXPECT_DEATH(value - Signed(-2), ""); // Overflows. +} + +TEST_F(TypedIntegerTest, SignedSubtractionUnderflow) { + Signed value(std::numeric_limits::min() + 1); + + value - Signed(1); // Doesn't underflow. + EXPECT_DEATH(value - Signed(2), ""); // Underflows. +} + +TEST_F(TypedIntegerTest, NegationOverflow) { + Signed maxValue(std::numeric_limits::max()); + -maxValue; // Doesn't underflow. + + Signed minValue(std::numeric_limits::min()); + EXPECT_DEATH(-minValue, ""); // Overflows. +} + +#endif // defined(DAWN_ENABLE_ASSERTS) diff --git a/third_party/dawn/src/tests/unittests/d3d12/CopySplitTests.cpp b/third_party/dawn/src/tests/unittests/d3d12/CopySplitTests.cpp index 5ad16042f52..9a6754c10b2 100644 --- a/third_party/dawn/src/tests/unittests/d3d12/CopySplitTests.cpp +++ b/third_party/dawn/src/tests/unittests/d3d12/CopySplitTests.cpp @@ -14,11 +14,12 @@ #include -#include "dawn_native/d3d12/d3d12_platform.h" -#include "dawn_native/d3d12/TextureCopySplitter.h" #include "common/Assert.h" #include "common/Constants.h" #include "common/Math.h" +#include "dawn_native/Format.h" +#include "dawn_native/d3d12/TextureCopySplitter.h" +#include "dawn_native/d3d12/d3d12_platform.h" using namespace dawn_native::d3d12; @@ -31,17 +32,19 @@ namespace { uint32_t width; uint32_t height; uint32_t depth; - uint32_t texelSize; + uint32_t texelBlockSizeInBytes; + uint32_t blockWidth = 1; + uint32_t blockHeight = 1; }; struct BufferSpec { uint64_t offset; - uint32_t rowPitch; - uint32_t imageHeight; + uint32_t bytesPerRow; + uint32_t rowsPerImage; }; // Check that each copy region fits inside the buffer footprint - void ValidateFootprints(const TextureCopySplit& copySplit) { + void ValidateFootprints(const Texture2DCopySplit& copySplit) { for (uint32_t i = 0; i < copySplit.count; ++i) { const auto& copy = copySplit.copies[i]; ASSERT_LE(copy.bufferOffset.x + copy.copySize.width, copy.bufferSize.width); @@ -51,8 +54,9 @@ namespace { } // Check that the offset is aligned - void ValidateOffset(const TextureCopySplit& copySplit) { - ASSERT_TRUE(Align(copySplit.offset, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT) == copySplit.offset); + void ValidateOffset(const Texture2DCopySplit& copySplit) { + ASSERT_TRUE(Align(copySplit.offset, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT) == + copySplit.offset); } bool RangesOverlap(uint32_t minA, uint32_t maxA, uint32_t minB, uint32_t maxB) { @@ -60,21 +64,28 @@ namespace { } // Check that no pair of copy regions intersect each other - void ValidateDisjoint(const TextureCopySplit& copySplit) { + void ValidateDisjoint(const Texture2DCopySplit& copySplit) { for (uint32_t i = 0; i < copySplit.count; ++i) { const auto& a = copySplit.copies[i]; for (uint32_t j = i + 1; j < copySplit.count; ++j) { const auto& b = copySplit.copies[j]; - bool overlapX = RangesOverlap(a.textureOffset.x, a.textureOffset.x + a.copySize.width, b.textureOffset.x, b.textureOffset.x + b.copySize.width); - bool overlapY = RangesOverlap(a.textureOffset.y, a.textureOffset.y + a.copySize.height, b.textureOffset.y, b.textureOffset.y + b.copySize.height); - bool overlapZ = RangesOverlap(a.textureOffset.z, a.textureOffset.z + a.copySize.depth, b.textureOffset.z, b.textureOffset.z + b.copySize.depth); + bool overlapX = + RangesOverlap(a.textureOffset.x, a.textureOffset.x + a.copySize.width, + b.textureOffset.x, b.textureOffset.x + b.copySize.width); + bool overlapY = + RangesOverlap(a.textureOffset.y, a.textureOffset.y + a.copySize.height, + b.textureOffset.y, b.textureOffset.y + b.copySize.height); + bool overlapZ = + RangesOverlap(a.textureOffset.z, a.textureOffset.z + a.copySize.depth, + b.textureOffset.z, b.textureOffset.z + b.copySize.depth); ASSERT_TRUE(!overlapX || !overlapY || !overlapZ); } } } // Check that the union of the copy regions exactly covers the texture region - void ValidateTextureBounds(const TextureSpec& textureSpec, const TextureCopySplit& copySplit) { + void ValidateTextureBounds(const TextureSpec& textureSpec, + const Texture2DCopySplit& copySplit) { ASSERT_TRUE(copySplit.count > 0); uint32_t minX = copySplit.copies[0].textureOffset.x; @@ -102,8 +113,9 @@ namespace { ASSERT_EQ(maxZ, textureSpec.z + textureSpec.depth); } - // Validate that the number of pixels copied is exactly equal to the number of pixels in the texture region - void ValidatePixelCount(const TextureSpec& textureSpec, const TextureCopySplit& copySplit) { + // Validate that the number of pixels copied is exactly equal to the number of pixels in the + // texture region + void ValidatePixelCount(const TextureSpec& textureSpec, const Texture2DCopySplit& copySplit) { uint32_t count = 0; for (uint32_t i = 0; i < copySplit.count; ++i) { const auto& copy = copySplit.copies[i]; @@ -113,22 +125,34 @@ namespace { } // Check that every buffer offset is at the correct pixel location - void ValidateBufferOffset(const TextureSpec& textureSpec, const BufferSpec& bufferSpec, const TextureCopySplit& copySplit) { + void ValidateBufferOffset(const TextureSpec& textureSpec, + const BufferSpec& bufferSpec, + const Texture2DCopySplit& copySplit) { ASSERT_TRUE(copySplit.count > 0); + uint32_t texelsPerBlock = textureSpec.blockWidth * textureSpec.blockHeight; for (uint32_t i = 0; i < copySplit.count; ++i) { const auto& copy = copySplit.copies[i]; - uint32_t rowPitchInTexels = bufferSpec.rowPitch / textureSpec.texelSize; - uint32_t slicePitchInTexels = rowPitchInTexels * bufferSpec.imageHeight; - uint32_t absoluteTexelOffset = copySplit.offset / textureSpec.texelSize + copy.bufferOffset.x + copy.bufferOffset.y * rowPitchInTexels + copy.bufferOffset.z * slicePitchInTexels; - - ASSERT(absoluteTexelOffset >= bufferSpec.offset / textureSpec.texelSize); - uint32_t relativeTexelOffset = absoluteTexelOffset - bufferSpec.offset / textureSpec.texelSize; + uint32_t bytesPerRowInTexels = + bufferSpec.bytesPerRow / textureSpec.texelBlockSizeInBytes * texelsPerBlock; + uint32_t slicePitchInTexels = + bytesPerRowInTexels * (bufferSpec.rowsPerImage / textureSpec.blockHeight); + uint32_t absoluteTexelOffset = + copySplit.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock + + copy.bufferOffset.x / textureSpec.blockWidth * texelsPerBlock + + copy.bufferOffset.y / textureSpec.blockHeight * bytesPerRowInTexels + + copy.bufferOffset.z * slicePitchInTexels; + + ASSERT(absoluteTexelOffset >= + bufferSpec.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock); + uint32_t relativeTexelOffset = + absoluteTexelOffset - + bufferSpec.offset / textureSpec.texelBlockSizeInBytes * texelsPerBlock; uint32_t z = relativeTexelOffset / slicePitchInTexels; - uint32_t y = (relativeTexelOffset % slicePitchInTexels) / rowPitchInTexels; - uint32_t x = relativeTexelOffset % rowPitchInTexels; + uint32_t y = (relativeTexelOffset % slicePitchInTexels) / bytesPerRowInTexels; + uint32_t x = relativeTexelOffset % bytesPerRowInTexels; ASSERT_EQ(copy.textureOffset.x - textureSpec.x, x); ASSERT_EQ(copy.textureOffset.y - textureSpec.y, y); @@ -136,7 +160,9 @@ namespace { } } - void ValidateCopySplit(const TextureSpec& textureSpec, const BufferSpec& bufferSpec, const TextureCopySplit& copySplit) { + void ValidateCopySplit(const TextureSpec& textureSpec, + const BufferSpec& bufferSpec, + const Texture2DCopySplit& copySplit) { ValidateFootprints(copySplit); ValidateOffset(copySplit); ValidateDisjoint(copySplit); @@ -147,24 +173,29 @@ namespace { std::ostream& operator<<(std::ostream& os, const TextureSpec& textureSpec) { os << "TextureSpec(" - << "[(" << textureSpec.x << ", " << textureSpec.y << ", " << textureSpec.z << "), (" << textureSpec.width << ", " << textureSpec.height << ", " << textureSpec.depth << ")], " - << textureSpec.texelSize - << ")"; + << "[(" << textureSpec.x << ", " << textureSpec.y << ", " << textureSpec.z << "), (" + << textureSpec.width << ", " << textureSpec.height << ", " << textureSpec.depth << ")], " + << textureSpec.texelBlockSizeInBytes << ")"; return os; } std::ostream& operator<<(std::ostream& os, const BufferSpec& bufferSpec) { - os << "BufferSpec(" << bufferSpec.offset << ", " << bufferSpec.rowPitch << ", " - << bufferSpec.imageHeight << ")"; + os << "BufferSpec(" << bufferSpec.offset << ", " << bufferSpec.bytesPerRow << ", " + << bufferSpec.rowsPerImage << ")"; return os; } - std::ostream& operator<<(std::ostream& os, const TextureCopySplit& copySplit) { + std::ostream& operator<<(std::ostream& os, const Texture2DCopySplit& copySplit) { os << "CopySplit" << std::endl; for (uint32_t i = 0; i < copySplit.count; ++i) { const auto& copy = copySplit.copies[i]; - os << " " << i << ": Texture at (" << copy.textureOffset.x << ", " << copy.textureOffset.y << ", " << copy.textureOffset.z << "), size (" << copy.copySize.width << ", " << copy.copySize.height << ", " << copy.copySize.depth << ")" << std::endl; - os << " " << i << ": Buffer at (" << copy.bufferOffset.x << ", " << copy.bufferOffset.y << ", " << copy.bufferOffset.z << "), footprint (" << copy.bufferSize.width << ", " << copy.bufferSize.height << ", " << copy.bufferSize.depth << ")" << std::endl; + os << " " << i << ": Texture at (" << copy.textureOffset.x << ", " + << copy.textureOffset.y << ", " << copy.textureOffset.z << "), size (" + << copy.copySize.width << ", " << copy.copySize.height << ", " << copy.copySize.depth + << ")" << std::endl; + os << " " << i << ": Buffer at (" << copy.bufferOffset.x << ", " << copy.bufferOffset.y + << ", " << copy.bufferOffset.z << "), footprint (" << copy.bufferSize.width << ", " + << copy.bufferSize.height << ", " << copy.bufferSize.depth << ")" << std::endl; } return os; } @@ -186,66 +217,101 @@ namespace { {59, 13, 0, 257, 31, 1, 4}, {17, 73, 0, 17, 93, 1, 4}, {17, 73, 59, 17, 93, 99, 4}, + + {0, 0, 0, 4, 4, 1, 8, 4, 4}, + {64, 16, 0, 4, 4, 1, 8, 4, 4}, + {64, 16, 8, 4, 4, 1, 8, 4, 4}, + {0, 0, 0, 4, 4, 1, 16, 4, 4}, + {64, 16, 0, 4, 4, 1, 16, 4, 4}, + {64, 16, 8, 4, 4, 1, 16, 4, 4}, + + {0, 0, 0, 1024, 1024, 1, 8, 4, 4}, + {256, 512, 0, 1024, 1024, 1, 8, 4, 4}, + {64, 48, 0, 1024, 1024, 1, 8, 4, 4}, + {64, 48, 16, 1024, 1024, 1, 8, 4, 4}, + {0, 0, 0, 1024, 1024, 1, 16, 4, 4}, + {256, 512, 0, 1024, 1024, 1, 16, 4, 4}, + {64, 48, 0, 1024, 1024, 1, 4, 16, 4}, + {64, 48, 16, 1024, 1024, 1, 16, 4, 4}, }; - // Define base buffer sizes to work with: some offsets aligned, some unaligned. rowPitch is the minimum required + // Define base buffer sizes to work with: some offsets aligned, some unaligned. bytesPerRow is + // the minimum required std::array BaseBufferSpecs(const TextureSpec& textureSpec) { - uint32_t rowPitch = Align(textureSpec.texelSize * textureSpec.width, kTextureRowPitchAlignment); + uint32_t bytesPerRow = Align(textureSpec.texelBlockSizeInBytes * textureSpec.width, + kTextureBytesPerRowAlignment); auto alignNonPow2 = [](uint32_t value, uint32_t size) -> uint32_t { return value == 0 ? 0 : ((value - 1) / size + 1) * size; }; return { - BufferSpec{alignNonPow2(0, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(512, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(1024, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(1024, textureSpec.texelSize), rowPitch, textureSpec.height * 2}, - - BufferSpec{alignNonPow2(32, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(64, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(64, textureSpec.texelSize), rowPitch, textureSpec.height * 2}, - - BufferSpec{alignNonPow2(31, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(257, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(511, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(513, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(1023, textureSpec.texelSize), rowPitch, textureSpec.height}, - BufferSpec{alignNonPow2(1023, textureSpec.texelSize), rowPitch, textureSpec.height * 2}, + BufferSpec{alignNonPow2(0, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(512, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(1024, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(1024, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height * 2}, + + BufferSpec{alignNonPow2(32, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(64, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(64, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height * 2}, + + BufferSpec{alignNonPow2(31, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(257, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(511, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(513, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(1023, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height}, + BufferSpec{alignNonPow2(1023, textureSpec.texelBlockSizeInBytes), bytesPerRow, + textureSpec.height * 2}, }; } // Define a list of values to set properties in the spec structs - constexpr uint32_t kCheckValues[] = { - 1, 2, 3, 4, 5, 6, 7, 8, // small values - 16, 32, 64, 128, 256, 512, 1024, 2048, // powers of 2 - 15, 31, 63, 127, 257, 511, 1023, 2047, // misalignments - 17, 33, 65, 129, 257, 513, 1025, 2049 - }; + constexpr uint32_t kCheckValues[] = {1, 2, 3, 4, 5, 6, 7, 8, // small values + 16, 32, 64, 128, 256, 512, 1024, 2048, // powers of 2 + 15, 31, 63, 127, 257, 511, 1023, 2047, // misalignments + 17, 33, 65, 129, 257, 513, 1025, 2049}; -} +} // namespace class CopySplitTest : public testing::Test { - protected: - TextureCopySplit DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) { - TextureCopySplit copySplit = ComputeTextureCopySplit( - {textureSpec.x, textureSpec.y, textureSpec.z}, - {textureSpec.width, textureSpec.height, textureSpec.depth}, textureSpec.texelSize, - bufferSpec.offset, bufferSpec.rowPitch, bufferSpec.imageHeight); - ValidateCopySplit(textureSpec, bufferSpec, copySplit); - return copySplit; - } + protected: + Texture2DCopySplit DoTest(const TextureSpec& textureSpec, const BufferSpec& bufferSpec) { + ASSERT(textureSpec.width % textureSpec.blockWidth == 0 && + textureSpec.height % textureSpec.blockHeight == 0); + dawn_native::Format fakeFormat = {}; + fakeFormat.blockWidth = textureSpec.blockWidth; + fakeFormat.blockHeight = textureSpec.blockHeight; + fakeFormat.blockByteSize = textureSpec.texelBlockSizeInBytes; + Texture2DCopySplit copySplit = ComputeTextureCopySplit( + {textureSpec.x, textureSpec.y, textureSpec.z}, + {textureSpec.width, textureSpec.height, textureSpec.depth}, fakeFormat, + bufferSpec.offset, bufferSpec.bytesPerRow, bufferSpec.rowsPerImage); + ValidateCopySplit(textureSpec, bufferSpec, copySplit); + return copySplit; + } }; TEST_F(CopySplitTest, General) { for (TextureSpec textureSpec : kBaseTextureSpecs) { for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { - - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; - message << "Failed generating splits: " << textureSpec << ", " << bufferSpec << std::endl - << copySplit << std::endl; + message << "Failed generating splits: " << textureSpec << ", " << bufferSpec + << std::endl + << copySplit << std::endl; FAIL() << message.str(); } } @@ -255,14 +321,17 @@ TEST_F(CopySplitTest, General) { TEST_F(CopySplitTest, TextureWidth) { for (TextureSpec textureSpec : kBaseTextureSpecs) { for (uint32_t val : kCheckValues) { + if (val % textureSpec.blockWidth != 0) { + continue; + } textureSpec.width = val; for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { - - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; - message << "Failed generating splits: " << textureSpec << ", " << bufferSpec << std::endl - << copySplit << std::endl; + message << "Failed generating splits: " << textureSpec << ", " << bufferSpec + << std::endl + << copySplit << std::endl; FAIL() << message.str(); } } @@ -273,14 +342,17 @@ TEST_F(CopySplitTest, TextureWidth) { TEST_F(CopySplitTest, TextureHeight) { for (TextureSpec textureSpec : kBaseTextureSpecs) { for (uint32_t val : kCheckValues) { + if (val % textureSpec.blockHeight != 0) { + continue; + } textureSpec.height = val; for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { - - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; - message << "Failed generating splits: " << textureSpec << ", " << bufferSpec << std::endl - << copySplit << std::endl; + message << "Failed generating splits: " << textureSpec << ", " << bufferSpec + << std::endl + << copySplit << std::endl; FAIL() << message.str(); } } @@ -293,12 +365,12 @@ TEST_F(CopySplitTest, TextureX) { for (uint32_t val : kCheckValues) { textureSpec.x = val; for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { - - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; - message << "Failed generating splits: " << textureSpec << ", " << bufferSpec << std::endl - << copySplit << std::endl; + message << "Failed generating splits: " << textureSpec << ", " << bufferSpec + << std::endl + << copySplit << std::endl; FAIL() << message.str(); } } @@ -311,12 +383,12 @@ TEST_F(CopySplitTest, TextureY) { for (uint32_t val : kCheckValues) { textureSpec.y = val; for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { - - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; - message << "Failed generating splits: " << textureSpec << ", " << bufferSpec << std::endl - << copySplit << std::endl; + message << "Failed generating splits: " << textureSpec << ", " << bufferSpec + << std::endl + << copySplit << std::endl; FAIL() << message.str(); } } @@ -327,14 +399,14 @@ TEST_F(CopySplitTest, TextureY) { TEST_F(CopySplitTest, TexelSize) { for (TextureSpec textureSpec : kBaseTextureSpecs) { for (uint32_t texelSize : {4, 8, 16, 32, 64}) { - textureSpec.texelSize = texelSize; + textureSpec.texelBlockSizeInBytes = texelSize; for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { - - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; - message << "Failed generating splits: " << textureSpec << ", " << bufferSpec << std::endl - << copySplit << std::endl; + message << "Failed generating splits: " << textureSpec << ", " << bufferSpec + << std::endl + << copySplit << std::endl; FAIL() << message.str(); } } @@ -346,13 +418,14 @@ TEST_F(CopySplitTest, BufferOffset) { for (TextureSpec textureSpec : kBaseTextureSpecs) { for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { for (uint32_t val : kCheckValues) { - bufferSpec.offset = textureSpec.texelSize * val; + bufferSpec.offset = textureSpec.texelBlockSizeInBytes * val; - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; - message << "Failed generating splits: " << textureSpec << ", " << bufferSpec << std::endl - << copySplit << std::endl; + message << "Failed generating splits: " << textureSpec << ", " << bufferSpec + << std::endl + << copySplit << std::endl; FAIL() << message.str(); } } @@ -363,15 +436,16 @@ TEST_F(CopySplitTest, BufferOffset) { TEST_F(CopySplitTest, RowPitch) { for (TextureSpec textureSpec : kBaseTextureSpecs) { for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { - uint32_t baseRowPitch = bufferSpec.rowPitch; + uint32_t baseRowPitch = bufferSpec.bytesPerRow; for (uint32_t i = 0; i < 5; ++i) { - bufferSpec.rowPitch = baseRowPitch + i * 256; + bufferSpec.bytesPerRow = baseRowPitch + i * 256; - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; - message << "Failed generating splits: " << textureSpec << ", " << bufferSpec << std::endl - << copySplit << std::endl; + message << "Failed generating splits: " << textureSpec << ", " << bufferSpec + << std::endl + << copySplit << std::endl; FAIL() << message.str(); } } @@ -382,11 +456,11 @@ TEST_F(CopySplitTest, RowPitch) { TEST_F(CopySplitTest, ImageHeight) { for (TextureSpec textureSpec : kBaseTextureSpecs) { for (BufferSpec bufferSpec : BaseBufferSpecs(textureSpec)) { - uint32_t baseImageHeight = bufferSpec.imageHeight; + uint32_t baseImageHeight = bufferSpec.rowsPerImage; for (uint32_t i = 0; i < 5; ++i) { - bufferSpec.imageHeight = baseImageHeight + i * 256; + bufferSpec.rowsPerImage = baseImageHeight + i * 256; - TextureCopySplit copySplit = DoTest(textureSpec, bufferSpec); + Texture2DCopySplit copySplit = DoTest(textureSpec, bufferSpec); if (HasFatalFailure()) { std::ostringstream message; message << "Failed generating splits: " << textureSpec << ", " << bufferSpec diff --git a/third_party/dawn/src/tests/unittests/validation/BindGroupValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/BindGroupValidationTests.cpp index 843d1768970..69604daf61d 100644 --- a/third_party/dawn/src/tests/unittests/validation/BindGroupValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/BindGroupValidationTests.cpp @@ -14,126 +14,122 @@ #include "tests/unittests/validation/ValidationTest.h" +#include "common/Assert.h" #include "common/Constants.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class BindGroupValidationTest : public ValidationTest { public: + wgpu::Texture CreateTexture(wgpu::TextureUsage usage, + wgpu::TextureFormat format, + uint32_t layerCount) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size = {16, 16, layerCount}; + descriptor.sampleCount = 1; + descriptor.mipLevelCount = 1; + descriptor.usage = usage; + descriptor.format = format; + + return device.CreateTexture(&descriptor); + } + void SetUp() override { // Create objects to use as resources inside test bind groups. { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 1024; - descriptor.usage = dawn::BufferUsageBit::Uniform; + descriptor.usage = wgpu::BufferUsage::Uniform; mUBO = device.CreateBuffer(&descriptor); } { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 1024; - descriptor.usage = dawn::BufferUsageBit::Storage; + descriptor.usage = wgpu::BufferUsage::Storage; mSSBO = device.CreateBuffer(&descriptor); } { - dawn::SamplerDescriptor descriptor = utils::GetDefaultSamplerDescriptor(); + wgpu::SamplerDescriptor descriptor = utils::GetDefaultSamplerDescriptor(); mSampler = device.CreateSampler(&descriptor); } { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size = {16, 16, 1}; - descriptor.arrayLayerCount = 1; - descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::Sampled; - mSampledTexture = device.CreateTexture(&descriptor); - mSampledTextureView = mSampledTexture.CreateDefaultView(); + mSampledTexture = + CreateTexture(wgpu::TextureUsage::Sampled, wgpu::TextureFormat::RGBA8Unorm, 1); + mSampledTextureView = mSampledTexture.CreateView(); } } protected: - dawn::Buffer mUBO; - dawn::Buffer mSSBO; - dawn::Sampler mSampler; - dawn::Texture mSampledTexture; - dawn::TextureView mSampledTextureView; + wgpu::Buffer mUBO; + wgpu::Buffer mSSBO; + wgpu::Sampler mSampler; + wgpu::Texture mSampledTexture; + wgpu::TextureView mSampledTextureView; }; // Test the validation of BindGroupDescriptor::nextInChain TEST_F(BindGroupValidationTest, NextInChainNullptr) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {}); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout(device, {}); - dawn::BindGroupDescriptor descriptor; + wgpu::BindGroupDescriptor descriptor; descriptor.layout = layout; - descriptor.bindingCount = 0; - descriptor.bindings = nullptr; + descriptor.entryCount = 0; + descriptor.entries = nullptr; // Control case: check that nextInChain = nullptr is valid descriptor.nextInChain = nullptr; device.CreateBindGroup(&descriptor); // Check that nextInChain != nullptr is an error. - descriptor.nextInChain = static_cast(&descriptor); + wgpu::ChainedStruct chainedDescriptor; + descriptor.nextInChain = &chainedDescriptor; ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor)); } -// Check constraints on bindingCount -TEST_F(BindGroupValidationTest, bindingCountMismatch) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler} - }); +// Check constraints on entryCount +TEST_F(BindGroupValidationTest, EntryCountMismatch) { + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}}); // Control case: check that a descriptor with one binding is ok utils::MakeBindGroup(device, layout, {{0, mSampler}}); - // Check that bindingCount != layout.bindingCount fails. + // Check that entryCount != layout.entryCount fails. ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {})); } -// Check constraints on BindGroupBinding::binding +// Check constraints on BindGroupEntry::binding TEST_F(BindGroupValidationTest, WrongBindings) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler} - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}}); // Control case: check that a descriptor with a binding matching the layout's is ok utils::MakeBindGroup(device, layout, {{0, mSampler}}); // Check that binding must be present in the layout ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{1, mSampler}})); - - // Check that binding >= kMaxBindingsPerGroup fails. - ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{kMaxBindingsPerGroup, mSampler}})); } // Check that the same binding cannot be set twice TEST_F(BindGroupValidationTest, BindingSetTwice) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler}, - {1, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler} - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}, + {1, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}}); // Control case: check that different bindings work - utils::MakeBindGroup(device, layout, { - {0, mSampler}, - {1, mSampler} - }); + utils::MakeBindGroup(device, layout, {{0, mSampler}, {1, mSampler}}); // Check that setting the same binding twice is invalid - ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, { - {0, mSampler}, - {0, mSampler} - })); + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mSampler}, {0, mSampler}})); } // Check that a sampler binding must contain exactly one sampler TEST_F(BindGroupValidationTest, SamplerBindingType) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::Sampler} - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}}); - dawn::BindGroupBinding binding; + wgpu::BindGroupEntry binding; binding.binding = 0; binding.sampler = nullptr; binding.textureView = nullptr; @@ -141,10 +137,10 @@ TEST_F(BindGroupValidationTest, SamplerBindingType) { binding.offset = 0; binding.size = 0; - dawn::BindGroupDescriptor descriptor; + wgpu::BindGroupDescriptor descriptor; descriptor.layout = layout; - descriptor.bindingCount = 1; - descriptor.bindings = &binding; + descriptor.entryCount = 1; + descriptor.entries = &binding; // Not setting anything fails ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor)); @@ -165,10 +161,10 @@ TEST_F(BindGroupValidationTest, SamplerBindingType) { // Setting the sampler to an error sampler is an error. { - dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); - samplerDesc.minFilter = static_cast(0xFFFFFFFF); + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + samplerDesc.minFilter = static_cast(0xFFFFFFFF); - dawn::Sampler errorSampler; + wgpu::Sampler errorSampler; ASSERT_DEVICE_ERROR(errorSampler = device.CreateSampler(&samplerDesc)); binding.sampler = errorSampler; @@ -179,11 +175,10 @@ TEST_F(BindGroupValidationTest, SamplerBindingType) { // Check that a texture binding must contain exactly a texture view TEST_F(BindGroupValidationTest, TextureBindingType) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture} - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}}); - dawn::BindGroupBinding binding; + wgpu::BindGroupEntry binding; binding.binding = 0; binding.sampler = nullptr; binding.textureView = nullptr; @@ -191,10 +186,10 @@ TEST_F(BindGroupValidationTest, TextureBindingType) { binding.offset = 0; binding.size = 0; - dawn::BindGroupDescriptor descriptor; + wgpu::BindGroupDescriptor descriptor; descriptor.layout = layout; - descriptor.bindingCount = 1; - descriptor.bindings = &binding; + descriptor.entryCount = 1; + descriptor.entries = &binding; // Not setting anything fails ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor)); @@ -215,15 +210,15 @@ TEST_F(BindGroupValidationTest, TextureBindingType) { // Setting the texture view to an error texture view is an error. { - dawn::TextureViewDescriptor viewDesc; - viewDesc.format = dawn::TextureFormat::R8G8B8A8Unorm; - viewDesc.dimension = dawn::TextureViewDimension::e2D; + wgpu::TextureViewDescriptor viewDesc; + viewDesc.format = wgpu::TextureFormat::RGBA8Unorm; + viewDesc.dimension = wgpu::TextureViewDimension::e2D; viewDesc.baseMipLevel = 0; viewDesc.mipLevelCount = 0; viewDesc.baseArrayLayer = 0; - viewDesc.arrayLayerCount = 0; + viewDesc.arrayLayerCount = 1000; - dawn::TextureView errorView; + wgpu::TextureView errorView; ASSERT_DEVICE_ERROR(errorView = mSampledTexture.CreateView(&viewDesc)); binding.textureView = errorView; @@ -234,22 +229,21 @@ TEST_F(BindGroupValidationTest, TextureBindingType) { // Check that a buffer binding must contain exactly a buffer TEST_F(BindGroupValidationTest, BufferBindingType) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer} - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); - dawn::BindGroupBinding binding; + wgpu::BindGroupEntry binding; binding.binding = 0; binding.sampler = nullptr; binding.textureView = nullptr; binding.buffer = nullptr; binding.offset = 0; - binding.size = 0; + binding.size = 1024; - dawn::BindGroupDescriptor descriptor; + wgpu::BindGroupDescriptor descriptor; descriptor.layout = layout; - descriptor.bindingCount = 1; - descriptor.bindings = &binding; + descriptor.entryCount = 1; + descriptor.entries = &binding; // Not setting anything fails ASSERT_DEVICE_ERROR(device.CreateBindGroup(&descriptor)); @@ -270,11 +264,11 @@ TEST_F(BindGroupValidationTest, BufferBindingType) { // Setting the buffer to an error buffer is an error. { - dawn::BufferDescriptor bufferDesc; + wgpu::BufferDescriptor bufferDesc; bufferDesc.size = 1024; - bufferDesc.usage = static_cast(0xFFFFFFFF); + bufferDesc.usage = static_cast(0xFFFFFFFF); - dawn::Buffer errorBuffer; + wgpu::Buffer errorBuffer; ASSERT_DEVICE_ERROR(errorBuffer = device.CreateBuffer(&bufferDesc)); binding.buffer = errorBuffer; @@ -285,32 +279,57 @@ TEST_F(BindGroupValidationTest, BufferBindingType) { // Check that a texture must have the correct usage TEST_F(BindGroupValidationTest, TextureUsage) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::SampledTexture} - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}}); // Control case: setting a sampleable texture view works. utils::MakeBindGroup(device, layout, {{0, mSampledTextureView}}); // Make an output attachment texture and try to set it for a SampledTexture binding - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size = {16, 16, 1}; - descriptor.arrayLayerCount = 1; - descriptor.sampleCount = 1; - descriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment; - dawn::Texture outputTexture = device.CreateTexture(&descriptor); - dawn::TextureView outputTextureView = outputTexture.CreateDefaultView(); + wgpu::Texture outputTexture = + CreateTexture(wgpu::TextureUsage::OutputAttachment, wgpu::TextureFormat::RGBA8Unorm, 1); + wgpu::TextureView outputTextureView = outputTexture.CreateView(); ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, outputTextureView}})); } +// Check that a texture must have the correct component type +TEST_F(BindGroupValidationTest, TextureComponentType) { + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D, wgpu::TextureComponentType::Float}}); + + // Control case: setting a Float typed texture view works. + utils::MakeBindGroup(device, layout, {{0, mSampledTextureView}}); + + // Make a Uint component typed texture and try to set it to a Float component binding. + wgpu::Texture uintTexture = + CreateTexture(wgpu::TextureUsage::Sampled, wgpu::TextureFormat::RGBA8Uint, 1); + wgpu::TextureView uintTextureView = uintTexture.CreateView(); + + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, uintTextureView}})); +} + +// Check that a texture must have the correct dimension +TEST_F(BindGroupValidationTest, TextureDimension) { + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D, wgpu::TextureComponentType::Float}}); + + // Control case: setting a 2D texture view works. + utils::MakeBindGroup(device, layout, {{0, mSampledTextureView}}); + + // Make a 2DArray texture and try to set it to a 2D binding. + wgpu::Texture arrayTexture = + CreateTexture(wgpu::TextureUsage::Sampled, wgpu::TextureFormat::RGBA8Uint, 2); + wgpu::TextureView arrayTextureView = arrayTexture.CreateView(); + + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, arrayTextureView}})); +} + // Check that a UBO must have the correct usage TEST_F(BindGroupValidationTest, BufferUsageUBO) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::UniformBuffer} - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}}); // Control case: using a buffer with the uniform usage works utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 256}}); @@ -321,9 +340,20 @@ TEST_F(BindGroupValidationTest, BufferUsageUBO) { // Check that a SSBO must have the correct usage TEST_F(BindGroupValidationTest, BufferUsageSSBO) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Fragment, dawn::BindingType::StorageBuffer} - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + + // Control case: using a buffer with the storage usage works + utils::MakeBindGroup(device, layout, {{0, mSSBO, 0, 256}}); + + // Using a buffer without the storage usage fails + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 256}})); +} + +// Check that a readonly SSBO must have the correct usage +TEST_F(BindGroupValidationTest, BufferUsageReadonlySSBO) { + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer}}); // Control case: using a buffer with the storage usage works utils::MakeBindGroup(device, layout, {{0, mSSBO, 0, 256}}); @@ -334,9 +364,10 @@ TEST_F(BindGroupValidationTest, BufferUsageSSBO) { // Tests constraints on the buffer offset for bind groups. TEST_F(BindGroupValidationTest, BufferOffsetAlignment) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + }); // Check that offset 0 is valid utils::MakeBindGroup(device, layout, {{0, mUBO, 0, 512}}); @@ -352,355 +383,1664 @@ TEST_F(BindGroupValidationTest, BufferOffsetAlignment) { // Tests constraints to be sure the buffer binding fits in the buffer TEST_F(BindGroupValidationTest, BufferBindingOOB) { - dawn::BindGroupLayout layout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - }); + wgpu::BindGroupLayout layout = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + }); - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 1024; - descriptor.usage = dawn::BufferUsageBit::Uniform; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); + descriptor.usage = wgpu::BufferUsage::Uniform; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); // Success case, touching the start of the buffer works utils::MakeBindGroup(device, layout, {{0, buffer, 0, 256}}); // Success case, touching the end of the buffer works - utils::MakeBindGroup(device, layout, {{0, buffer, 3*256, 256}}); - utils::MakeBindGroup(device, layout, {{0, buffer, 1024, 0}}); + utils::MakeBindGroup(device, layout, {{0, buffer, 3 * 256, 256}}); + + // Error case, zero size is invalid. + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 1024, 0}})); // Success case, touching the full buffer works utils::MakeBindGroup(device, layout, {{0, buffer, 0, 1024}}); + utils::MakeBindGroup(device, layout, {{0, buffer, 0, wgpu::kWholeSize}}); + + // Success case, whole size causes the rest of the buffer to be used but not beyond. + utils::MakeBindGroup(device, layout, {{0, buffer, 256, wgpu::kWholeSize}}); // Error case, offset is OOB - ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 256*5, 0}})); + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 256 * 5, 0}})); // Error case, size is OOB - ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 0, 256*5}})); + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 0, 256 * 5}})); // Error case, offset+size is OOB - ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 1024, 1}})); + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 1024, 256}})); // Error case, offset+size overflows to be 0 - ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, layout, {{0, buffer, 256, uint32_t(0) - uint32_t(256)}})); + ASSERT_DEVICE_ERROR( + utils::MakeBindGroup(device, layout, {{0, buffer, 256, uint32_t(0) - uint32_t(256)}})); +} + +// Tests constraints to be sure the uniform buffer binding isn't too large +TEST_F(BindGroupValidationTest, MaxUniformBufferBindingSize) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 2 * kMaxUniformBufferBindingSize; + descriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + wgpu::BindGroupLayout uniformLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}); + + // Success case, this is exactly the limit + utils::MakeBindGroup(device, uniformLayout, {{0, buffer, 0, kMaxUniformBufferBindingSize}}); + + wgpu::BindGroupLayout doubleUniformLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + {1, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}); + + // Success case, individual bindings don't exceed the limit + utils::MakeBindGroup(device, doubleUniformLayout, + {{0, buffer, 0, kMaxUniformBufferBindingSize}, + {1, buffer, kMaxUniformBufferBindingSize, kMaxUniformBufferBindingSize}}); + + // Error case, this is above the limit + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, uniformLayout, + {{0, buffer, 0, kMaxUniformBufferBindingSize + 1}})); + + // Making sure the constraint doesn't apply to storage buffers + wgpu::BindGroupLayout readonlyStorageLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroupLayout storageLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + + // Success case, storage buffer can still be created. + utils::MakeBindGroup(device, readonlyStorageLayout, + {{0, buffer, 0, 2 * kMaxUniformBufferBindingSize}}); + utils::MakeBindGroup(device, storageLayout, {{0, buffer, 0, 2 * kMaxUniformBufferBindingSize}}); } // Test what happens when the layout is an error. TEST_F(BindGroupValidationTest, ErrorLayout) { - dawn::BindGroupLayout goodLayout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - }); + wgpu::BindGroupLayout goodLayout = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + }); - dawn::BindGroupLayout errorLayout; - ASSERT_DEVICE_ERROR(errorLayout = utils::MakeBindGroupLayout(device, { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - })); + wgpu::BindGroupLayout errorLayout; + ASSERT_DEVICE_ERROR( + errorLayout = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + })); // Control case, creating with the good layout works utils::MakeBindGroup(device, goodLayout, {{0, mUBO, 0, 256}}); - // Control case, creating with the good layout works + // Creating with an error layout fails ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, errorLayout, {{0, mUBO, 0, 256}})); } class BindGroupLayoutValidationTest : public ValidationTest { + public: + wgpu::BindGroupLayout MakeBindGroupLayout(wgpu::BindGroupLayoutEntry* binding, uint32_t count) { + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = count; + descriptor.entries = binding; + return device.CreateBindGroupLayout(&descriptor); + } + + void TestCreateBindGroupLayout(wgpu::BindGroupLayoutEntry* binding, + uint32_t count, + bool expected) { + wgpu::BindGroupLayoutDescriptor descriptor; + + descriptor.entryCount = count; + descriptor.entries = binding; + + if (!expected) { + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor)); + } else { + device.CreateBindGroupLayout(&descriptor); + } + } + + void TestCreatePipelineLayout(wgpu::BindGroupLayout* bgl, uint32_t count, bool expected) { + wgpu::PipelineLayoutDescriptor descriptor; + + descriptor.bindGroupLayoutCount = count; + descriptor.bindGroupLayouts = bgl; + + if (!expected) { + ASSERT_DEVICE_ERROR(device.CreatePipelineLayout(&descriptor)); + } else { + device.CreatePipelineLayout(&descriptor); + } + } }; -// Tests setting OOB checks for kMaxBindingsPerGroup in bind group layouts. -TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutBindingOOB) { - // Checks that kMaxBindingsPerGroup - 1 is valid. - utils::MakeBindGroupLayout(device, { - {kMaxBindingsPerGroup - 1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer} - }); +// Tests setting storage buffer and readonly storage buffer bindings in vertex and fragment shader. +TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutStorageBindingsInVertexShader) { + // Checks that storage buffer binding is not supported in vertex shader. + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::StorageBuffer}})); + + utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::ReadonlyStorageBuffer}}); + + utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + + utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer}}); +} - // Checks that kMaxBindingsPerGroup is OOB - ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(device, { - {kMaxBindingsPerGroup, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer} - })); +// Tests setting that bind group layout bindings numbers may be very large. +TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutEntryNumberLarge) { + // Checks that uint32_t max is valid. + utils::MakeBindGroupLayout(device, + {{std::numeric_limits::max(), wgpu::ShaderStage::Vertex, + wgpu::BindingType::UniformBuffer}}); } // This test verifies that the BindGroupLayout bindings are correctly validated, even if the // binding ids are out-of-order. -TEST_F(BindGroupLayoutValidationTest, BindGroupBinding) { - auto layout = utils::MakeBindGroupLayout( +TEST_F(BindGroupLayoutValidationTest, BindGroupEntry) { + utils::MakeBindGroupLayout(device, + { + {1, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + }); +} + +// Check that dynamic = true is only allowed with buffer bindings. +TEST_F(BindGroupLayoutValidationTest, DynamicAndTypeCompatibility) { + utils::MakeBindGroupLayout( device, { - {1, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer, true}, }); + + utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer, true}, + }); + + utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, + }); + + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, true}, + })); + + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::Sampler, true}, + })); } +// This test verifies that visibility of bindings in BindGroupLayout can be none +TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutVisibilityNone) { + utils::MakeBindGroupLayout(device, + { + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + }); + + wgpu::BindGroupLayoutEntry binding = {0, wgpu::ShaderStage::None, + wgpu::BindingType::UniformBuffer}; + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = 1; + descriptor.entries = &binding; + device.CreateBindGroupLayout(&descriptor); +} -// This test verifies that the BindGroupLayout cache is successfully caching/deduplicating objects. -// -// NOTE: This test only works currently because unittests are run without the wire - so the returned -// BindGroupLayout pointers are actually visibly equivalent. With the wire, this would not be true. -TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutCache) { - auto layout1 = utils::MakeBindGroupLayout( +// This test verifies that binding with none visibility in bind group layout can be supported in +// bind group +TEST_F(BindGroupLayoutValidationTest, BindGroupLayoutVisibilityNoneExpectsBindGroupEntry) { + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( device, { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, + {0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}, + {1, wgpu::ShaderStage::None, wgpu::BindingType::UniformBuffer}, }); - auto layout2 = utils::MakeBindGroupLayout( + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::Uniform; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}}); + + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bgl, {{0, buffer}})); +} + +TEST_F(BindGroupLayoutValidationTest, PerStageLimits) { + struct TestInfo { + uint32_t maxCount; + wgpu::BindingType bindingType; + wgpu::BindingType otherBindingType; + }; + + constexpr TestInfo kTestInfos[] = { + {kMaxSampledTexturesPerShaderStage, wgpu::BindingType::SampledTexture, + wgpu::BindingType::UniformBuffer}, + {kMaxSamplersPerShaderStage, wgpu::BindingType::Sampler, wgpu::BindingType::UniformBuffer}, + {kMaxSamplersPerShaderStage, wgpu::BindingType::ComparisonSampler, + wgpu::BindingType::UniformBuffer}, + {kMaxStorageBuffersPerShaderStage, wgpu::BindingType::StorageBuffer, + wgpu::BindingType::UniformBuffer}, + {kMaxStorageTexturesPerShaderStage, wgpu::BindingType::ReadonlyStorageTexture, + wgpu::BindingType::UniformBuffer}, + {kMaxStorageTexturesPerShaderStage, wgpu::BindingType::WriteonlyStorageTexture, + wgpu::BindingType::UniformBuffer}, + {kMaxUniformBuffersPerShaderStage, wgpu::BindingType::UniformBuffer, + wgpu::BindingType::SampledTexture}, + }; + + for (TestInfo info : kTestInfos) { + wgpu::BindGroupLayout bgl[2]; + std::vector maxBindings; + + auto PopulateEntry = [](wgpu::BindGroupLayoutEntry entry) { + switch (entry.type) { + case wgpu::BindingType::ReadonlyStorageTexture: + case wgpu::BindingType::WriteonlyStorageTexture: + entry.storageTextureFormat = wgpu::TextureFormat::RGBA8Unorm; + break; + default: + break; + } + return entry; + }; + + for (uint32_t i = 0; i < info.maxCount; ++i) { + maxBindings.push_back(PopulateEntry({i, wgpu::ShaderStage::Compute, info.bindingType})); + } + + // Creating with the maxes works. + bgl[0] = MakeBindGroupLayout(maxBindings.data(), maxBindings.size()); + + // Adding an extra binding of a different type works. + { + std::vector bindings = maxBindings; + bindings.push_back( + PopulateEntry({info.maxCount, wgpu::ShaderStage::Compute, info.otherBindingType})); + MakeBindGroupLayout(bindings.data(), bindings.size()); + } + + // Adding an extra binding of the maxed type in a different stage works + { + std::vector bindings = maxBindings; + bindings.push_back( + PopulateEntry({info.maxCount, wgpu::ShaderStage::Fragment, info.bindingType})); + MakeBindGroupLayout(bindings.data(), bindings.size()); + } + + // Adding an extra binding of the maxed type and stage exceeds the per stage limit. + { + std::vector bindings = maxBindings; + bindings.push_back( + PopulateEntry({info.maxCount, wgpu::ShaderStage::Compute, info.bindingType})); + ASSERT_DEVICE_ERROR(MakeBindGroupLayout(bindings.data(), bindings.size())); + } + + // Creating a pipeline layout from the valid BGL works. + TestCreatePipelineLayout(bgl, 1, true); + + // Adding an extra binding of a different type in a different BGL works + bgl[1] = utils::MakeBindGroupLayout( + device, {PopulateEntry({0, wgpu::ShaderStage::Compute, info.otherBindingType})}); + TestCreatePipelineLayout(bgl, 2, true); + + // Adding an extra binding of the maxed type in a different stage works + bgl[1] = utils::MakeBindGroupLayout( + device, {PopulateEntry({0, wgpu::ShaderStage::Fragment, info.bindingType})}); + TestCreatePipelineLayout(bgl, 2, true); + + // Adding an extra binding of the maxed type in a different BGL exceeds the per stage limit. + bgl[1] = utils::MakeBindGroupLayout( + device, {PopulateEntry({0, wgpu::ShaderStage::Compute, info.bindingType})}); + TestCreatePipelineLayout(bgl, 2, false); + } +} + +// Check that dynamic buffer numbers exceed maximum value in one bind group layout. +TEST_F(BindGroupLayoutValidationTest, DynamicBufferNumberLimit) { + wgpu::BindGroupLayout bgl[2]; + std::vector maxUniformDB; + std::vector maxStorageDB; + std::vector maxReadonlyStorageDB; + + // In this test, we use all the same shader stage. Ensure that this does not exceed the + // per-stage limit. + static_assert(kMaxDynamicUniformBuffersPerPipelineLayout <= kMaxUniformBuffersPerShaderStage, + ""); + static_assert(kMaxDynamicStorageBuffersPerPipelineLayout <= kMaxStorageBuffersPerShaderStage, + ""); + + for (uint32_t i = 0; i < kMaxDynamicUniformBuffersPerPipelineLayout; ++i) { + maxUniformDB.push_back( + {i, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer, true}); + } + + for (uint32_t i = 0; i < kMaxDynamicStorageBuffersPerPipelineLayout; ++i) { + maxStorageDB.push_back( + {i, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer, true}); + } + + for (uint32_t i = 0; i < kMaxDynamicStorageBuffersPerPipelineLayout; ++i) { + maxReadonlyStorageDB.push_back( + {i, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}); + } + + // Test creating with the maxes works + { + bgl[0] = MakeBindGroupLayout(maxUniformDB.data(), maxUniformDB.size()); + TestCreatePipelineLayout(bgl, 1, true); + + bgl[0] = MakeBindGroupLayout(maxStorageDB.data(), maxStorageDB.size()); + TestCreatePipelineLayout(bgl, 1, true); + + bgl[0] = MakeBindGroupLayout(maxReadonlyStorageDB.data(), maxReadonlyStorageDB.size()); + TestCreatePipelineLayout(bgl, 1, true); + } + + // The following tests exceed the per-pipeline layout limits. We use the Fragment stage to + // ensure we don't hit the per-stage limit. + + // Check dynamic uniform buffers exceed maximum in pipeline layout. + { + bgl[0] = MakeBindGroupLayout(maxUniformDB.data(), maxUniformDB.size()); + bgl[1] = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, true}, + }); + + TestCreatePipelineLayout(bgl, 2, false); + } + + // Check dynamic storage buffers exceed maximum in pipeline layout + { + bgl[0] = MakeBindGroupLayout(maxStorageDB.data(), maxStorageDB.size()); + bgl[1] = utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer, true}, + }); + + TestCreatePipelineLayout(bgl, 2, false); + } + + // Check dynamic readonly storage buffers exceed maximum in pipeline layout + { + bgl[0] = MakeBindGroupLayout(maxReadonlyStorageDB.data(), maxReadonlyStorageDB.size()); + bgl[1] = utils::MakeBindGroupLayout( + device, + { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer, true}, + }); + + TestCreatePipelineLayout(bgl, 2, false); + } + + // Check dynamic storage buffers + dynamic readonly storage buffers exceed maximum storage + // buffers in pipeline layout + { + bgl[0] = MakeBindGroupLayout(maxStorageDB.data(), maxStorageDB.size()); + bgl[1] = utils::MakeBindGroupLayout( + device, + { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer, true}, + }); + + TestCreatePipelineLayout(bgl, 2, false); + } + + // Check dynamic uniform buffers exceed maximum in bind group layout. + { + maxUniformDB.push_back({kMaxDynamicUniformBuffersPerPipelineLayout, + wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer, + true}); + TestCreateBindGroupLayout(maxUniformDB.data(), maxUniformDB.size(), false); + } + + // Check dynamic storage buffers exceed maximum in bind group layout. + { + maxStorageDB.push_back({kMaxDynamicStorageBuffersPerPipelineLayout, + wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer, + true}); + TestCreateBindGroupLayout(maxStorageDB.data(), maxStorageDB.size(), false); + } + + // Check dynamic readonly storage buffers exceed maximum in bind group layout. + { + maxReadonlyStorageDB.push_back({kMaxDynamicStorageBuffersPerPipelineLayout, + wgpu::ShaderStage::Fragment, + wgpu::BindingType::ReadonlyStorageBuffer, true}); + TestCreateBindGroupLayout(maxReadonlyStorageDB.data(), maxReadonlyStorageDB.size(), false); + } +} + +// Test that multisampled textures must be 2D sampled textures +TEST_F(BindGroupLayoutValidationTest, MultisampledTextures) { + // Multisampled 2D texture works. + utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + true, wgpu::TextureViewDimension::e2D}, + }); + + // Multisampled 2D (defaulted) texture works. + utils::MakeBindGroupLayout( device, { - {0, dawn::ShaderStageBit::Vertex, dawn::BindingType::UniformBuffer}, + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + true, wgpu::TextureViewDimension::Undefined}, }); - // Caching should cause these to be the same. - ASSERT_EQ(layout1.Get(), layout2.Get()); + // Multisampled 2D storage texture is invalid. + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, true, wgpu::TextureViewDimension::e2D}, + })); + + // Multisampled 2D array texture is invalid. + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + true, wgpu::TextureViewDimension::e2DArray}, + })); + + // Multisampled cube texture is invalid. + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + true, wgpu::TextureViewDimension::Cube}, + })); + + // Multisampled cube array texture is invalid. + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + true, wgpu::TextureViewDimension::CubeArray}, + })); + + // Multisampled 3D texture is invalid. + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + true, wgpu::TextureViewDimension::e3D}, + })); + + // Multisampled 1D texture is invalid. + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + true, wgpu::TextureViewDimension::e1D}, + })); } -constexpr uint32_t kBufferElementsCount = kMinDynamicBufferOffsetAlignment / sizeof(uint32_t) + 2; -constexpr uint32_t kBufferSize = kBufferElementsCount * sizeof(uint32_t); +// Test that it is an error to pass multisampled=true for non-texture bindings +TEST_F(BindGroupLayoutValidationTest, MultisampledMustBeTexture) { + // Base: Multisampled 2D texture works. + utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + true, wgpu::TextureViewDimension::e2D}, + }); -class SetBindGroupValidationTest : public ValidationTest { - public: - void SetUp() override { - std::array uniformData = {0}; + // Multisampled uniform buffer binding is invalid + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, + { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer, false, 0, true}, + })); - uniformData[0] = 1.0; - uniformData[1] = 2.0; - uniformData[uniformData.size() - 2] = 5.0; - uniformData[uniformData.size() - 1] = 6.0; + // Multisampled storage buffer binding is invalid + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, + { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer, false, 0, true}, + })); - dawn::BufferDescriptor bufferDescriptor; - bufferDescriptor.size = kBufferSize; - bufferDescriptor.usage = dawn::BufferUsageBit::Storage; + // Multisampled sampler binding is invalid + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::Sampler, false, 0, true}, + })); +} - mUniformBuffer = utils::CreateBufferFromData(device, uniformData.data(), kBufferSize, - dawn::BufferUsageBit::Uniform); - mStorageBuffer = device.CreateBuffer(&bufferDescriptor); +constexpr uint64_t kBufferSize = 3 * kMinDynamicBufferOffsetAlignment + 8; +constexpr uint32_t kBindingSize = 9; +class SetBindGroupValidationTest : public ValidationTest { + public: + void SetUp() override { mBindGroupLayout = utils::MakeBindGroupLayout( - device, {{0, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, - dawn::BindingType::DynamicUniformBuffer}, - {1, dawn::ShaderStageBit::Compute | dawn::ShaderStageBit::Fragment, - dawn::BindingType::DynamicStorageBuffer}}); + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer, true}, + {1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer, false}, + {2, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer, true}, + {3, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::ReadonlyStorageBuffer, true}}); + } - mBindGroup = utils::MakeBindGroup( - device, mBindGroupLayout, - {{0, mUniformBuffer, 0, kBufferSize}, {1, mStorageBuffer, 0, kBufferSize}}); + wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = bufferSize; + bufferDescriptor.usage = usage; + + return device.CreateBuffer(&bufferDescriptor); } - // Create objects to use as resources inside test bind groups. - dawn::BindGroup mBindGroup; - dawn::BindGroupLayout mBindGroupLayout; - dawn::Buffer mUniformBuffer; - dawn::Buffer mStorageBuffer; + wgpu::BindGroupLayout mBindGroupLayout; - dawn::RenderPipeline CreateRenderPipeline() { - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::RenderPipeline CreateRenderPipeline() { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 - layout(std140, set = 0, binding = 0) uniform uBuffer { - vec2 value1; + layout(std140, set = 0, binding = 0) uniform uBufferDynamic { + vec2 value0; + }; + layout(std140, set = 0, binding = 1) uniform uBuffer { + vec2 value1; }; - layout(std140, set = 0, binding = 1) buffer SBuffer { - vec2 value2; + layout(std140, set = 0, binding = 2) buffer SBufferDynamic { + vec2 value2; } sBuffer; - layout(location = 0) out uvec4 fragColor; + layout(std140, set = 0, binding = 3) readonly buffer RBufferDynamic { + vec2 value3; + } rBuffer; + layout(location = 0) out vec4 fragColor; void main() { })"); utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); - pipelineDescriptor.cVertexStage.module = vsModule; + pipelineDescriptor.vertexStage.module = vsModule; pipelineDescriptor.cFragmentStage.module = fsModule; - dawn::PipelineLayout pipelineLayout = + wgpu::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); pipelineDescriptor.layout = pipelineLayout; return device.CreateRenderPipeline(&pipelineDescriptor); } - dawn::ComputePipeline CreateComputePipeline() { - dawn::ShaderModule csModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"( + wgpu::ComputePipeline CreateComputePipeline() { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( #version 450 const uint kTileSize = 4; const uint kInstances = 11; layout(local_size_x = kTileSize, local_size_y = kTileSize, local_size_z = 1) in; - layout(std140, set = 0, binding = 0) uniform UniformBuffer { + layout(std140, set = 0, binding = 0) uniform UniformBufferDynamic { + float value0; + }; + layout(std140, set = 0, binding = 1) uniform UniformBuffer { float value1; }; - layout(std140, set = 0, binding = 1) buffer SBuffer { + layout(std140, set = 0, binding = 2) buffer SBufferDynamic { float value2; } dst; + layout(std140, set = 0, binding = 3) readonly buffer RBufferDynamic { + readonly float value3; + } rdst; + void main() { + })"); - void main() { - })"); - - dawn::ComputePipelineDescriptor csDesc; - dawn::PipelineLayout pipelineLayout = + wgpu::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, &mBindGroupLayout); - csDesc.layout = pipelineLayout; - dawn::PipelineStageDescriptor computeStage; - computeStage.module = csModule; - computeStage.entryPoint = "main"; - csDesc.computeStage = &computeStage; + wgpu::ComputePipelineDescriptor csDesc; + csDesc.layout = pipelineLayout; + csDesc.computeStage.module = csModule; + csDesc.computeStage.entryPoint = "main"; return device.CreateComputePipeline(&csDesc); } -}; - -// This is the test case that should work. -TEST_F(SetBindGroupValidationTest, Basic) { - std::array offsets = {256, 0}; - // RenderPipeline SetBindGroup - { - dawn::RenderPipeline renderPipeline = CreateRenderPipeline(); + void TestRenderPassBindGroup(wgpu::BindGroup bindGroup, + uint32_t* offsets, + uint32_t count, + bool expectation) { + wgpu::RenderPipeline renderPipeline = CreateRenderPipeline(); DummyRenderPass renderPass(device); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); renderPassEncoder.SetPipeline(renderPipeline); - renderPassEncoder.SetBindGroup(0, mBindGroup, 2, offsets.data()); - renderPassEncoder.Draw(3, 1, 0, 0); + if (bindGroup != nullptr) { + renderPassEncoder.SetBindGroup(0, bindGroup, count, offsets); + } + renderPassEncoder.Draw(3); renderPassEncoder.EndPass(); - commandEncoder.Finish(); + if (!expectation) { + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } else { + commandEncoder.Finish(); + } } - { - dawn::ComputePipeline computePipeline = CreateComputePipeline(); + void TestComputePassBindGroup(wgpu::BindGroup bindGroup, + uint32_t* offsets, + uint32_t count, + bool expectation) { + wgpu::ComputePipeline computePipeline = CreateComputePipeline(); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); computePassEncoder.SetPipeline(computePipeline); - computePassEncoder.SetBindGroup(0, mBindGroup, 2, offsets.data()); - computePassEncoder.Dispatch(1, 1, 1); + if (bindGroup != nullptr) { + computePassEncoder.SetBindGroup(0, bindGroup, count, offsets); + } + computePassEncoder.Dispatch(1); computePassEncoder.EndPass(); - commandEncoder.Finish(); + if (!expectation) { + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } else { + commandEncoder.Finish(); + } } +}; + +// This is the test case that should work. +TEST_F(SetBindGroupValidationTest, Basic) { + // Set up the bind group. + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, uniformBuffer, 0, kBindingSize}, + {1, uniformBuffer, 0, kBindingSize}, + {2, storageBuffer, 0, kBindingSize}, + {3, readonlyStorageBuffer, 0, kBindingSize}}); + + std::array offsets = {512, 256, 0}; + + TestRenderPassBindGroup(bindGroup, offsets.data(), 3, true); + + TestComputePassBindGroup(bindGroup, offsets.data(), 3, true); } -// Test cases that test dynamic offsets count mismatch with bind group layout. -TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) { - std::array mismatchOffsets = {0}; +// Draw/dispatch with a bind group missing is invalid +TEST_F(SetBindGroupValidationTest, MissingBindGroup) { + TestRenderPassBindGroup(nullptr, nullptr, 0, false); + TestComputePassBindGroup(nullptr, nullptr, 0, false); +} + +// Setting bind group after a draw / dispatch should re-verify the layout is compatible +TEST_F(SetBindGroupValidationTest, VerifyGroupIfChangedAfterAction) { + // Set up the bind group + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, uniformBuffer, 0, kBindingSize}, + {1, uniformBuffer, 0, kBindingSize}, + {2, storageBuffer, 0, kBindingSize}, + {3, readonlyStorageBuffer, 0, kBindingSize}}); + + std::array offsets = {512, 256, 0}; + + // Set up bind group that is incompatible + wgpu::BindGroupLayout invalidLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup invalidGroup = + utils::MakeBindGroup(device, invalidLayout, {{0, storageBuffer, 0, kBindingSize}}); - // RenderPipeline SetBindGroup { - dawn::RenderPipeline pipeline = CreateRenderPipeline(); + wgpu::ComputePipeline computePipeline = CreateComputePipeline(); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetPipeline(computePipeline); + computePassEncoder.SetBindGroup(0, bindGroup, 3, offsets.data()); + computePassEncoder.Dispatch(1); + computePassEncoder.SetBindGroup(0, invalidGroup, 0, nullptr); + computePassEncoder.Dispatch(1); + computePassEncoder.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + { + wgpu::RenderPipeline renderPipeline = CreateRenderPipeline(); DummyRenderPass renderPass(device); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); - renderPassEncoder.SetPipeline(pipeline); - renderPassEncoder.SetBindGroup(0, mBindGroup, 1, mismatchOffsets.data()); - renderPassEncoder.Draw(3, 1, 0, 0); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + renderPassEncoder.SetPipeline(renderPipeline); + renderPassEncoder.SetBindGroup(0, bindGroup, 3, offsets.data()); + renderPassEncoder.Draw(3); + renderPassEncoder.SetBindGroup(0, invalidGroup, 0, nullptr); + renderPassEncoder.Draw(3); renderPassEncoder.EndPass(); ASSERT_DEVICE_ERROR(commandEncoder.Finish()); } +} - { - dawn::ComputePipeline computePipeline = CreateComputePipeline(); +// Test cases that test dynamic offsets count mismatch with bind group layout. +TEST_F(SetBindGroupValidationTest, DynamicOffsetsMismatch) { + // Set up bind group. + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, uniformBuffer, 0, kBindingSize}, + {1, uniformBuffer, 0, kBindingSize}, + {2, storageBuffer, 0, kBindingSize}, + {3, readonlyStorageBuffer, 0, kBindingSize}}); + + // Number of offsets mismatch. + std::array mismatchOffsets = {768, 512, 256, 0}; + + TestRenderPassBindGroup(bindGroup, mismatchOffsets.data(), 1, false); + TestRenderPassBindGroup(bindGroup, mismatchOffsets.data(), 2, false); + TestRenderPassBindGroup(bindGroup, mismatchOffsets.data(), 4, false); + + TestComputePassBindGroup(bindGroup, mismatchOffsets.data(), 1, false); + TestComputePassBindGroup(bindGroup, mismatchOffsets.data(), 2, false); + TestComputePassBindGroup(bindGroup, mismatchOffsets.data(), 4, false); +} - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); - computePassEncoder.SetPipeline(computePipeline); - computePassEncoder.SetBindGroup(0, mBindGroup, 1, mismatchOffsets.data()); - computePassEncoder.Dispatch(1, 1, 1); +// Test cases that test dynamic offsets not aligned +TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) { + // Set up bind group. + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, uniformBuffer, 0, kBindingSize}, + {1, uniformBuffer, 0, kBindingSize}, + {2, storageBuffer, 0, kBindingSize}, + {3, readonlyStorageBuffer, 0, kBindingSize}}); + + // Dynamic offsets are not aligned. + std::array notAlignedOffsets = {512, 128, 0}; + + TestRenderPassBindGroup(bindGroup, notAlignedOffsets.data(), 3, false); + + TestComputePassBindGroup(bindGroup, notAlignedOffsets.data(), 3, false); +} + +// Test cases that test dynamic uniform buffer out of bound situation. +TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicUniformBuffer) { + // Set up bind group. + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, uniformBuffer, 0, kBindingSize}, + {1, uniformBuffer, 0, kBindingSize}, + {2, storageBuffer, 0, kBindingSize}, + {3, readonlyStorageBuffer, 0, kBindingSize}}); + + // Dynamic offset + offset is larger than buffer size. + std::array overFlowOffsets = {1024, 256, 0}; + + TestRenderPassBindGroup(bindGroup, overFlowOffsets.data(), 3, false); + + TestComputePassBindGroup(bindGroup, overFlowOffsets.data(), 3, false); +} + +// Test cases that test dynamic storage buffer out of bound situation. +TEST_F(SetBindGroupValidationTest, OffsetOutOfBoundDynamicStorageBuffer) { + // Set up bind group. + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, uniformBuffer, 0, kBindingSize}, + {1, uniformBuffer, 0, kBindingSize}, + {2, storageBuffer, 0, kBindingSize}, + {3, readonlyStorageBuffer, 0, kBindingSize}}); + + // Dynamic offset + offset is larger than buffer size. + std::array overFlowOffsets = {0, 256, 1024}; + + TestRenderPassBindGroup(bindGroup, overFlowOffsets.data(), 3, false); + + TestComputePassBindGroup(bindGroup, overFlowOffsets.data(), 3, false); +} + +// Test cases that test dynamic uniform buffer out of bound situation because of binding size. +TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicUniformBuffer) { + // Set up bind group, but binding size is larger than + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, uniformBuffer, 0, kBindingSize}, + {1, uniformBuffer, 0, kBindingSize}, + {2, storageBuffer, 0, kBindingSize}, + {3, readonlyStorageBuffer, 0, kBindingSize}}); + + // Dynamic offset + offset isn't larger than buffer size. + // But with binding size, it will trigger OOB error. + std::array offsets = {768, 256, 0}; + + TestRenderPassBindGroup(bindGroup, offsets.data(), 3, false); + + TestComputePassBindGroup(bindGroup, offsets.data(), 3, false); +} + +// Test cases that test dynamic storage buffer out of bound situation because of binding size. +TEST_F(SetBindGroupValidationTest, BindingSizeOutOfBoundDynamicStorageBuffer) { + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::Buffer readonlyStorageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, + {{0, uniformBuffer, 0, kBindingSize}, + {1, uniformBuffer, 0, kBindingSize}, + {2, storageBuffer, 0, kBindingSize}, + {3, readonlyStorageBuffer, 0, kBindingSize}}); + // Dynamic offset + offset isn't larger than buffer size. + // But with binding size, it will trigger OOB error. + std::array offsets = {0, 256, 768}; + + TestRenderPassBindGroup(bindGroup, offsets.data(), 3, false); + + TestComputePassBindGroup(bindGroup, offsets.data(), 3, false); +} + +// Regression test for crbug.com/dawn/408 where dynamic offsets were applied in the wrong order. +// Dynamic offsets should be applied in increasing order of binding number. +TEST_F(SetBindGroupValidationTest, DynamicOffsetOrder) { + // Note: The order of the binding numbers of the bind group and bind group layout are + // intentionally different and not in increasing order. + // This test uses both storage and uniform buffers to ensure buffer bindings are sorted first by + // binding number before type. + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, { + {3, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, + {0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer, true}, + {2, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer, true}, + }); + + // Create buffers which are 3x, 2x, and 1x the size of the minimum buffer offset, plus 4 bytes + // to spare (to avoid zero-sized bindings). We will offset the bindings so they reach the very + // end of the buffer. Any mismatch applying too-large of an offset to a smaller buffer will hit + // the out-of-bounds condition during validation. + wgpu::Buffer buffer3x = + CreateBuffer(3 * kMinDynamicBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage); + wgpu::Buffer buffer2x = + CreateBuffer(2 * kMinDynamicBufferOffsetAlignment + 4, wgpu::BufferUsage::Storage); + wgpu::Buffer buffer1x = + CreateBuffer(1 * kMinDynamicBufferOffsetAlignment + 4, wgpu::BufferUsage::Uniform); + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, + { + {0, buffer3x, 0, 4}, + {3, buffer2x, 0, 4}, + {2, buffer1x, 0, 4}, + }); + + std::array offsets; + { + // Base case works. + offsets = {/* binding 0 */ 0, + /* binding 2 */ 0, + /* binding 3 */ 0}; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data()); computePassEncoder.EndPass(); - ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + commandEncoder.Finish(); + } + { + // Offset the first binding to touch the end of the buffer. Should succeed. + // Will fail if the offset is applied to the first or second bindings since their buffers + // are too small. + offsets = {/* binding 0 */ 3 * kMinDynamicBufferOffsetAlignment, + /* binding 2 */ 0, + /* binding 3 */ 0}; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data()); + computePassEncoder.EndPass(); + commandEncoder.Finish(); + } + { + // Offset the second binding to touch the end of the buffer. Should succeed. + offsets = {/* binding 0 */ 0, + /* binding 2 */ 1 * kMinDynamicBufferOffsetAlignment, + /* binding 3 */ 0}; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data()); + computePassEncoder.EndPass(); + commandEncoder.Finish(); + } + { + // Offset the third binding to touch the end of the buffer. Should succeed. + // Will fail if the offset is applied to the second binding since its buffer + // is too small. + offsets = {/* binding 0 */ 0, + /* binding 2 */ 0, + /* binding 3 */ 2 * kMinDynamicBufferOffsetAlignment}; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data()); + computePassEncoder.EndPass(); + commandEncoder.Finish(); + } + { + // Offset each binding to touch the end of their buffer. Should succeed. + offsets = {/* binding 0 */ 3 * kMinDynamicBufferOffsetAlignment, + /* binding 2 */ 1 * kMinDynamicBufferOffsetAlignment, + /* binding 3 */ 2 * kMinDynamicBufferOffsetAlignment}; + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup, offsets.size(), offsets.data()); + computePassEncoder.EndPass(); + commandEncoder.Finish(); } } -// Test cases that test dynamic offsets not aligned -TEST_F(SetBindGroupValidationTest, DynamicOffsetsNotAligned) { - std::array notAlignedOffsets = {1, 2}; +// Test that an error is produced (and no ASSERTs fired) when using an error bindgroup in +// SetBindGroup +TEST_F(SetBindGroupValidationTest, ErrorBindGroup) { + // Bindgroup creation fails because not all bindings are specified. + wgpu::BindGroup bindGroup; + ASSERT_DEVICE_ERROR(bindGroup = utils::MakeBindGroup(device, mBindGroupLayout, {})); + + TestRenderPassBindGroup(bindGroup, nullptr, 0, false); + + TestComputePassBindGroup(bindGroup, nullptr, 0, false); +} + +class SetBindGroupPersistenceValidationTest : public ValidationTest { + protected: + void SetUp() override { + mVsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + })"); + } + + wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = bufferSize; + bufferDescriptor.usage = usage; + + return device.CreateBuffer(&bufferDescriptor); + } + + // Generates bind group layouts and a pipeline from a 2D list of binding types. + std::tuple, wgpu::RenderPipeline> SetUpLayoutsAndPipeline( + std::vector> layouts) { + std::vector bindGroupLayouts(layouts.size()); + + // Iterate through the desired bind group layouts. + for (uint32_t l = 0; l < layouts.size(); ++l) { + const auto& layout = layouts[l]; + std::vector bindings(layout.size()); + + // Iterate through binding types and populate a list of BindGroupLayoutEntrys. + for (uint32_t b = 0; b < layout.size(); ++b) { + bindings[b] = {b, wgpu::ShaderStage::Fragment, layout[b], false}; + } + + // Create the bind group layout. + wgpu::BindGroupLayoutDescriptor bglDescriptor; + bglDescriptor.entryCount = static_cast(bindings.size()); + bglDescriptor.entries = bindings.data(); + bindGroupLayouts[l] = device.CreateBindGroupLayout(&bglDescriptor); + } + + // Create a pipeline layout from the list of bind group layouts. + wgpu::PipelineLayoutDescriptor pipelineLayoutDescriptor; + pipelineLayoutDescriptor.bindGroupLayoutCount = + static_cast(bindGroupLayouts.size()); + pipelineLayoutDescriptor.bindGroupLayouts = bindGroupLayouts.data(); + + wgpu::PipelineLayout pipelineLayout = + device.CreatePipelineLayout(&pipelineLayoutDescriptor); + + std::stringstream ss; + ss << "#version 450\n"; + + // Build a shader which has bindings that match the pipeline layout. + for (uint32_t l = 0; l < layouts.size(); ++l) { + const auto& layout = layouts[l]; + + for (uint32_t b = 0; b < layout.size(); ++b) { + wgpu::BindingType binding = layout[b]; + ss << "layout(std140, set = " << l << ", binding = " << b << ") "; + switch (binding) { + case wgpu::BindingType::StorageBuffer: + ss << "buffer SBuffer"; + break; + case wgpu::BindingType::UniformBuffer: + ss << "uniform UBuffer"; + break; + default: + UNREACHABLE(); + } + ss << l << "_" << b << " { vec2 set" << l << "_binding" << b << "; };\n"; + } + } + + ss << "layout(location = 0) out vec4 fragColor;\n"; + ss << "void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); }\n"; + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, ss.str().c_str()); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = mVsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + pipelineDescriptor.layout = pipelineLayout; + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); + + return std::make_tuple(bindGroupLayouts, pipeline); + } + + private: + wgpu::ShaderModule mVsModule; +}; + +// Test it is valid to set bind groups before setting the pipeline. +TEST_F(SetBindGroupPersistenceValidationTest, BindGroupBeforePipeline) { + std::vector bindGroupLayouts; + wgpu::RenderPipeline pipeline; + std::tie(bindGroupLayouts, pipeline) = SetUpLayoutsAndPipeline({{ + {{ + wgpu::BindingType::UniformBuffer, + wgpu::BindingType::UniformBuffer, + }}, + {{ + wgpu::BindingType::StorageBuffer, + wgpu::BindingType::UniformBuffer, + }}, + }}); + + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + + wgpu::BindGroup bindGroup0 = utils::MakeBindGroup( + device, bindGroupLayouts[0], + {{0, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize}}); + + wgpu::BindGroup bindGroup1 = utils::MakeBindGroup( + device, bindGroupLayouts[1], + {{0, storageBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize}}); + + DummyRenderPass renderPass(device); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + + renderPassEncoder.SetBindGroup(0, bindGroup0); + renderPassEncoder.SetBindGroup(1, bindGroup1); + renderPassEncoder.SetPipeline(pipeline); + renderPassEncoder.Draw(3); + + renderPassEncoder.EndPass(); + commandEncoder.Finish(); +} + +// Dawn does not have a concept of bind group inheritance though the backing APIs may. +// Test that it is valid to draw with bind groups that are not "inherited". They persist +// after a pipeline change. +TEST_F(SetBindGroupPersistenceValidationTest, NotVulkanInheritance) { + std::vector bindGroupLayoutsA; + wgpu::RenderPipeline pipelineA; + std::tie(bindGroupLayoutsA, pipelineA) = SetUpLayoutsAndPipeline({{ + {{ + wgpu::BindingType::UniformBuffer, + wgpu::BindingType::StorageBuffer, + }}, + {{ + wgpu::BindingType::UniformBuffer, + wgpu::BindingType::UniformBuffer, + }}, + }}); + + std::vector bindGroupLayoutsB; + wgpu::RenderPipeline pipelineB; + std::tie(bindGroupLayoutsB, pipelineB) = SetUpLayoutsAndPipeline({{ + {{ + wgpu::BindingType::StorageBuffer, + wgpu::BindingType::UniformBuffer, + }}, + {{ + wgpu::BindingType::UniformBuffer, + wgpu::BindingType::UniformBuffer, + }}, + }}); + + wgpu::Buffer uniformBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + wgpu::Buffer storageBuffer = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + + wgpu::BindGroup bindGroupA0 = utils::MakeBindGroup( + device, bindGroupLayoutsA[0], + {{0, uniformBuffer, 0, kBindingSize}, {1, storageBuffer, 0, kBindingSize}}); + + wgpu::BindGroup bindGroupA1 = utils::MakeBindGroup( + device, bindGroupLayoutsA[1], + {{0, uniformBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize}}); + + wgpu::BindGroup bindGroupB0 = utils::MakeBindGroup( + device, bindGroupLayoutsB[0], + {{0, storageBuffer, 0, kBindingSize}, {1, uniformBuffer, 0, kBindingSize}}); + + DummyRenderPass renderPass(device); + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + + renderPassEncoder.SetPipeline(pipelineA); + renderPassEncoder.SetBindGroup(0, bindGroupA0); + renderPassEncoder.SetBindGroup(1, bindGroupA1); + renderPassEncoder.Draw(3); + + renderPassEncoder.SetPipeline(pipelineB); + renderPassEncoder.SetBindGroup(0, bindGroupB0); + // This draw is valid. + // Bind group 1 persists even though it is not "inherited". + renderPassEncoder.Draw(3); + + renderPassEncoder.EndPass(); + commandEncoder.Finish(); +} + +class BindGroupLayoutCompatibilityTest : public ValidationTest { + public: + wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = bufferSize; + bufferDescriptor.usage = usage; + + return device.CreateBuffer(&bufferDescriptor); + } + + wgpu::RenderPipeline CreateFSRenderPipeline( + const char* fsShader, + std::vector bindGroupLayout) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fsShader); + + wgpu::PipelineLayoutDescriptor descriptor; + descriptor.bindGroupLayoutCount = bindGroupLayout.size(); + descriptor.bindGroupLayouts = bindGroupLayout.data(); + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&descriptor); + pipelineDescriptor.layout = pipelineLayout; + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + wgpu::RenderPipeline CreateRenderPipeline(std::vector bindGroupLayout) { + return CreateFSRenderPipeline(R"( + #version 450 + layout(std140, set = 0, binding = 0) buffer SBuffer { + vec2 value2; + } sBuffer; + layout(std140, set = 1, binding = 0) readonly buffer RBuffer { + vec2 value3; + } rBuffer; + layout(location = 0) out vec4 fragColor; + void main() { + })", + std::move(bindGroupLayout)); + } + + wgpu::ComputePipeline CreateComputePipeline( + const char* shader, + std::vector bindGroupLayout) { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, shader); + + wgpu::PipelineLayoutDescriptor descriptor; + descriptor.bindGroupLayoutCount = bindGroupLayout.size(); + descriptor.bindGroupLayouts = bindGroupLayout.data(); + wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&descriptor); + + wgpu::ComputePipelineDescriptor csDesc; + csDesc.layout = pipelineLayout; + csDesc.computeStage.module = csModule; + csDesc.computeStage.entryPoint = "main"; + + return device.CreateComputePipeline(&csDesc); + } + + wgpu::ComputePipeline CreateComputePipeline( + std::vector bindGroupLayout) { + return CreateComputePipeline(R"( + #version 450 + const uint kTileSize = 4; + const uint kInstances = 11; + + layout(local_size_x = kTileSize, local_size_y = kTileSize, local_size_z = 1) in; + layout(std140, set = 0, binding = 0) buffer SBuffer { + float value2; + } dst; + layout(std140, set = 1, binding = 0) readonly buffer RBuffer { + readonly float value3; + } rdst; + void main() { + })", + std::move(bindGroupLayout)); + } +}; + +// Test that it is valid to pass a writable storage buffer in the pipeline layout when the shader +// uses the binding as a readonly storage buffer. +TEST_F(BindGroupLayoutCompatibilityTest, RWStorageInBGLWithROStorageInShader) { + // Set up the bind group layout. + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}}); + + CreateRenderPipeline({bgl0, bgl1}); + + CreateComputePipeline({bgl0, bgl1}); +} + +// Test that it is invalid to pass a readonly storage buffer in the pipeline layout when the shader +// uses the binding as a writable storage buffer. +TEST_F(BindGroupLayoutCompatibilityTest, ROStorageInBGLWithRWStorageInShader) { + // Set up the bind group layout. + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::ReadonlyStorageBuffer}}); + + ASSERT_DEVICE_ERROR(CreateRenderPipeline({bgl0, bgl1})); - // RenderPipeline SetBindGroup + ASSERT_DEVICE_ERROR(CreateComputePipeline({bgl0, bgl1})); +} + +TEST_F(BindGroupLayoutCompatibilityTest, TextureViewDimension) { + constexpr char kTexture2DShader[] = R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2D texture; + void main() { + })"; + + // Render: Test that 2D texture with 2D view dimension works + CreateFSRenderPipeline( + kTexture2DShader, + {utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D}})}); + + // Render: Test that 2D texture with 2D array view dimension is invalid + ASSERT_DEVICE_ERROR(CreateFSRenderPipeline( + kTexture2DShader, + {utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2DArray}})})); + + // Compute: Test that 2D texture with 2D view dimension works + CreateComputePipeline( + kTexture2DShader, + {utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D}})}); + + // Compute: Test that 2D texture with 2D array view dimension is invalid + ASSERT_DEVICE_ERROR(CreateComputePipeline( + kTexture2DShader, + {utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2DArray}})})); + + constexpr char kTexture2DArrayShader[] = R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2DArray texture; + void main() { + })"; + + // Render: Test that 2D texture array with 2D array view dimension works + CreateFSRenderPipeline( + kTexture2DArrayShader, + {utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2DArray}})}); + + // Render: Test that 2D texture array with 2D view dimension is invalid + ASSERT_DEVICE_ERROR(CreateFSRenderPipeline( + kTexture2DArrayShader, + {utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D}})})); + + // Compute: Test that 2D texture array with 2D array view dimension works + CreateComputePipeline( + kTexture2DArrayShader, + {utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2DArray}})}); + + // Compute: Test that 2D texture array with 2D view dimension is invalid + ASSERT_DEVICE_ERROR(CreateComputePipeline( + kTexture2DArrayShader, + {utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, + false, wgpu::TextureViewDimension::e2D}})})); +} + +class BindingsValidationTest : public BindGroupLayoutCompatibilityTest { + public: + void TestRenderPassBindings(const wgpu::BindGroup* bg, + uint32_t count, + wgpu::RenderPipeline pipeline, + bool expectation) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder rp = encoder.BeginRenderPass(&dummyRenderPass); + for (uint32_t i = 0; i < count; ++i) { + rp.SetBindGroup(i, bg[i]); + } + rp.SetPipeline(pipeline); + rp.Draw(3); + rp.EndPass(); + if (!expectation) { + ASSERT_DEVICE_ERROR(encoder.Finish()); + } else { + encoder.Finish(); + } + } + + void TestComputePassBindings(const wgpu::BindGroup* bg, + uint32_t count, + wgpu::ComputePipeline pipeline, + bool expectation) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder cp = encoder.BeginComputePass(); + for (uint32_t i = 0; i < count; ++i) { + cp.SetBindGroup(i, bg[i]); + } + cp.SetPipeline(pipeline); + cp.Dispatch(1); + cp.EndPass(); + if (!expectation) { + ASSERT_DEVICE_ERROR(encoder.Finish()); + } else { + encoder.Finish(); + } + } + + static constexpr uint32_t kBindingNum = 3; +}; + +// Test that it is valid to set a pipeline layout with bindings unused by the pipeline. +TEST_F(BindingsValidationTest, PipelineLayoutWithMoreBindingsThanPipeline) { + // Set up bind group layouts. + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}, + {1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroupLayout bgl2 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}}); + + // pipelineLayout has unused binding set (bgl2) and unused entry in a binding set (bgl0). + CreateRenderPipeline({bgl0, bgl1, bgl2}); + + CreateComputePipeline({bgl0, bgl1, bgl2}); +} + +// Test that it is invalid to set a pipeline layout that doesn't have all necessary bindings +// required by the pipeline. +TEST_F(BindingsValidationTest, PipelineLayoutWithLessBindingsThanPipeline) { + // Set up bind group layout. + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}}); + + // missing a binding set (bgl1) in pipeline layout { - dawn::RenderPipeline pipeline = CreateRenderPipeline(); - DummyRenderPass renderPass(device); + ASSERT_DEVICE_ERROR(CreateRenderPipeline({bgl0})); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); - renderPassEncoder.SetPipeline(pipeline); - renderPassEncoder.SetBindGroup(0, mBindGroup, 2, notAlignedOffsets.data()); - renderPassEncoder.Draw(3, 1, 0, 0); - renderPassEncoder.EndPass(); - ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + ASSERT_DEVICE_ERROR(CreateComputePipeline({bgl0})); } - // ComputePipeline SetBindGroup + // bgl1 is not missing, but it is empty { - dawn::ComputePipeline pipeline = CreateComputePipeline(); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout(device, {}); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); - computePassEncoder.SetPipeline(pipeline); - computePassEncoder.SetBindGroup(0, mBindGroup, 2, notAlignedOffsets.data()); - computePassEncoder.Dispatch(1, 1, 1); - computePassEncoder.EndPass(); - ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + ASSERT_DEVICE_ERROR(CreateRenderPipeline({bgl0, bgl1})); + + ASSERT_DEVICE_ERROR(CreateComputePipeline({bgl0, bgl1})); + } + + // bgl1 is neither missing nor empty, but it doesn't contain the necessary binding + { + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer}}); + + ASSERT_DEVICE_ERROR(CreateRenderPipeline({bgl0, bgl1})); + + ASSERT_DEVICE_ERROR(CreateComputePipeline({bgl0, bgl1})); } } -// Test cases that test dynamic uniform buffer out of bound situation. -TEST_F(SetBindGroupValidationTest, OutOfBoundDynamicUniformBuffer) { - std::array overFlowOffsets = {512, 0}; +// Test that it is valid to set bind groups whose layout is not set in the pipeline layout. +// But it's invalid to set extra entry for a given bind group's layout if that layout is set in +// the pipeline layout. +TEST_F(BindingsValidationTest, BindGroupsWithMoreBindingsThanPipelineLayout) { + // Set up bind group layouts, buffers, bind groups, pipeline layouts and pipelines. + std::array bgl; + std::array bg; + std::array buffer; + for (uint32_t i = 0; i < kBindingNum + 1; ++i) { + bgl[i] = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}}); + buffer[i] = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + bg[i] = utils::MakeBindGroup(device, bgl[i], {{0, buffer[i]}}); + } - // RenderPipeline SetBindGroup + // Set 3 bindings (and 3 pipeline layouts) in pipeline. + wgpu::RenderPipeline renderPipeline = CreateRenderPipeline({bgl[0], bgl[1], bgl[2]}); + wgpu::ComputePipeline computePipeline = CreateComputePipeline({bgl[0], bgl[1], bgl[2]}); + + // Comprared to pipeline layout, there is an extra bind group (bg[3]) + TestRenderPassBindings(bg.data(), kBindingNum + 1, renderPipeline, true); + + TestComputePassBindings(bg.data(), kBindingNum + 1, computePipeline, true); + + // If a bind group has entry (like bgl1_1 below) unused by the pipeline layout, it is invalid. + // Bind groups associated layout should exactly match bind group layout if that layout is + // set in pipeline layout. + bgl[1] = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::ReadonlyStorageBuffer}, + {1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer}}); + buffer[1] = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform); + bg[1] = utils::MakeBindGroup(device, bgl[1], {{0, buffer[1]}, {1, buffer[1]}}); + + TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false); + + TestComputePassBindings(bg.data(), kBindingNum, computePipeline, false); +} + +// Test that it is invalid to set bind groups that don't have all necessary bindings required +// by the pipeline layout. Note that both pipeline layout and bind group have enough bindings for +// pipeline in the following test. +TEST_F(BindingsValidationTest, BindGroupsWithLessBindingsThanPipelineLayout) { + // Set up bind group layouts, buffers, bind groups, pipeline layouts and pipelines. + std::array bgl; + std::array bg; + std::array buffer; + for (uint32_t i = 0; i < kBindingNum; ++i) { + bgl[i] = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::StorageBuffer}}); + buffer[i] = CreateBuffer(kBufferSize, wgpu::BufferUsage::Storage); + bg[i] = utils::MakeBindGroup(device, bgl[i], {{0, buffer[i]}}); + } + + wgpu::RenderPipeline renderPipeline = CreateRenderPipeline({bgl[0], bgl[1], bgl[2]}); + wgpu::ComputePipeline computePipeline = CreateComputePipeline({bgl[0], bgl[1], bgl[2]}); + + // Compared to pipeline layout, a binding set (bgl2) related bind group is missing + TestRenderPassBindings(bg.data(), kBindingNum - 1, renderPipeline, false); + + TestComputePassBindings(bg.data(), kBindingNum - 1, computePipeline, false); + + // bgl[2] related bind group is not missing, but its bind group is empty + bgl[2] = utils::MakeBindGroupLayout(device, {}); + bg[2] = utils::MakeBindGroup(device, bgl[2], {}); + + TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false); + + TestComputePassBindings(bg.data(), kBindingNum, computePipeline, false); + + // bgl[2] related bind group is neither missing nor empty, but it doesn't contain the necessary + // binding + bgl[2] = utils::MakeBindGroupLayout( + device, {{1, wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment, + wgpu::BindingType::UniformBuffer}}); + buffer[2] = CreateBuffer(kBufferSize, wgpu::BufferUsage::Uniform); + bg[2] = utils::MakeBindGroup(device, bgl[2], {{1, buffer[2]}}); + + TestRenderPassBindings(bg.data(), kBindingNum, renderPipeline, false); + + TestComputePassBindings(bg.data(), kBindingNum, computePipeline, false); +} + +class ComparisonSamplerBindingTest : public ValidationTest { + protected: + wgpu::RenderPipeline CreateFragmentPipeline(wgpu::BindGroupLayout* bindGroupLayout, + const char* fragmentSource) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentSource); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + wgpu::PipelineLayout pipelineLayout = + utils::MakeBasicPipelineLayout(device, bindGroupLayout); + pipelineDescriptor.layout = pipelineLayout; + return device.CreateRenderPipeline(&pipelineDescriptor); + } +}; + +// TODO(crbug.com/dawn/367): Disabled until we can perform shader analysis +// of which samplers are comparison samplers. +TEST_F(ComparisonSamplerBindingTest, DISABLED_ShaderAndBGLMatches) { + // Test that sampler binding works with normal sampler in the shader. { - dawn::RenderPipeline pipeline = CreateRenderPipeline(); - DummyRenderPass renderPass(device); + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}}); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); - renderPassEncoder.SetPipeline(pipeline); - renderPassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data()); - renderPassEncoder.Draw(3, 1, 0, 0); - renderPassEncoder.EndPass(); - ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + CreateFragmentPipeline(&bindGroupLayout, R"( + #version 450 + layout(set = 0, binding = 0) uniform sampler samp; + + void main() { + })"); } - // ComputePipeline SetBindGroup + // Test that comparison sampler binding works with shadow sampler in the shader. { - dawn::ComputePipeline pipeline = CreateComputePipeline(); + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ComparisonSampler}}); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); - computePassEncoder.SetPipeline(pipeline); - computePassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data()); - computePassEncoder.Dispatch(1, 1, 1); - computePassEncoder.EndPass(); - ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + CreateFragmentPipeline(&bindGroupLayout, R"( + #version 450 + layout(set = 0, binding = 0) uniform samplerShadow samp; + + void main() { + })"); + } + + // Test that sampler binding does not work with comparison sampler in the shader. + { + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}}); + + ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"( + #version 450 + layout(set = 0, binding = 0) uniform samplerShadow samp; + + void main() { + })")); + } + + // Test that comparison sampler binding does not work with normal sampler in the shader. + { + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ComparisonSampler}}); + + ASSERT_DEVICE_ERROR(CreateFragmentPipeline(&bindGroupLayout, R"( + #version 450 + layout(set = 0, binding = 0) uniform sampler samp; + + void main() { + })")); } } -// Test cases that test dynamic storage buffer out of bound situation. -TEST_F(SetBindGroupValidationTest, OutOfBoundDynamicStorageBuffer) { - std::array overFlowOffsets = {0, 512}; +TEST_F(ComparisonSamplerBindingTest, SamplerAndBindGroupMatches) { + // Test that sampler binding works with normal sampler. + { + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}}); + + wgpu::SamplerDescriptor desc = {}; + utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}); + } - // RenderPipeline SetBindGroup + // Test that comparison sampler binding works with sampler w/ compare function. { - dawn::RenderPipeline pipeline = CreateRenderPipeline(); - DummyRenderPass renderPass(device); + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ComparisonSampler}}); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); - renderPassEncoder.SetPipeline(pipeline); - renderPassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data()); - renderPassEncoder.Draw(3, 1, 0, 0); - renderPassEncoder.EndPass(); - ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + wgpu::SamplerDescriptor desc = {}; + desc.compare = wgpu::CompareFunction::Never; + utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}}); } - // ComputePipeline SetBindGroup + // Test that sampler binding does not work with sampler w/ compare function. { - dawn::ComputePipeline pipeline = CreateComputePipeline(); + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::Sampler}}); - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); - computePassEncoder.SetPipeline(pipeline); - computePassEncoder.SetBindGroup(0, mBindGroup, 2, overFlowOffsets.data()); - computePassEncoder.Dispatch(1, 1, 1); - computePassEncoder.EndPass(); - ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + wgpu::SamplerDescriptor desc; + desc.compare = wgpu::CompareFunction::Never; + ASSERT_DEVICE_ERROR( + utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}})); + } + + // Test that comparison sampler binding does not work with normal sampler. + { + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ComparisonSampler}}); + + wgpu::SamplerDescriptor desc = {}; + ASSERT_DEVICE_ERROR( + utils::MakeBindGroup(device, bindGroupLayout, {{0, device.CreateSampler(&desc)}})); } } diff --git a/third_party/dawn/src/tests/unittests/validation/BufferValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/BufferValidationTests.cpp index 53694d78b40..4af888a0065 100644 --- a/third_party/dawn/src/tests/unittests/validation/BufferValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/BufferValidationTests.cpp @@ -21,16 +21,17 @@ using namespace testing; class MockBufferMapReadCallback { - public: - MOCK_METHOD4(Call, - void(DawnBufferMapAsyncStatus status, - const uint32_t* ptr, - uint64_t dataLength, - void* userdata)); + public: + MOCK_METHOD(void, + Call, + (WGPUBufferMapAsyncStatus status, + const uint32_t* ptr, + uint64_t dataLength, + void* userdata)); }; static std::unique_ptr mockBufferMapReadCallback; -static void ToMockBufferMapReadCallback(DawnBufferMapAsyncStatus status, +static void ToMockBufferMapReadCallback(WGPUBufferMapAsyncStatus status, const void* ptr, uint64_t dataLength, void* userdata) { @@ -40,16 +41,15 @@ static void ToMockBufferMapReadCallback(DawnBufferMapAsyncStatus status, } class MockBufferMapWriteCallback { - public: - MOCK_METHOD4(Call, - void(DawnBufferMapAsyncStatus status, - uint32_t* ptr, - uint64_t dataLength, - void* userdata)); + public: + MOCK_METHOD( + void, + Call, + (WGPUBufferMapAsyncStatus status, uint32_t* ptr, uint64_t dataLength, void* userdata)); }; static std::unique_ptr mockBufferMapWriteCallback; -static void ToMockBufferMapWriteCallback(DawnBufferMapAsyncStatus status, +static void ToMockBufferMapWriteCallback(WGPUBufferMapAsyncStatus status, void* ptr, uint64_t dataLength, void* userdata) { @@ -58,66 +58,87 @@ static void ToMockBufferMapWriteCallback(DawnBufferMapAsyncStatus status, userdata); } +class MockBufferMapAsyncCallback { + public: + MOCK_METHOD(void, Call, (WGPUBufferMapAsyncStatus status, void* userdata)); +}; + +static std::unique_ptr mockBufferMapAsyncCallback; +static void ToMockBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, void* userdata) { + mockBufferMapAsyncCallback->Call(status, userdata); +} + class BufferValidationTest : public ValidationTest { - protected: - dawn::Buffer CreateMapReadBuffer(uint64_t size) { - dawn::BufferDescriptor descriptor; - descriptor.size = size; - descriptor.usage = dawn::BufferUsageBit::MapRead; - - return device.CreateBuffer(&descriptor); - } - dawn::Buffer CreateMapWriteBuffer(uint64_t size) { - dawn::BufferDescriptor descriptor; - descriptor.size = size; - descriptor.usage = dawn::BufferUsageBit::MapWrite; - - return device.CreateBuffer(&descriptor); - } - dawn::Buffer CreateSetSubDataBuffer(uint64_t size) { - dawn::BufferDescriptor descriptor; - descriptor.size = size; - descriptor.usage = dawn::BufferUsageBit::TransferDst; - - return device.CreateBuffer(&descriptor); - } - - dawn::CreateBufferMappedResult CreateBufferMapped(uint64_t size, - dawn::BufferUsageBit usage) { - dawn::BufferDescriptor descriptor; - descriptor.size = size; - descriptor.usage = usage; - - return device.CreateBufferMapped(&descriptor); - } - - dawn::Queue queue; - - private: - void SetUp() override { - ValidationTest::SetUp(); - - mockBufferMapReadCallback = std::make_unique(); - mockBufferMapWriteCallback = std::make_unique(); - queue = device.CreateQueue(); - } - - void TearDown() override { - // Delete mocks so that expectations are checked - mockBufferMapReadCallback = nullptr; - mockBufferMapWriteCallback = nullptr; - - ValidationTest::TearDown(); - } + protected: + wgpu::Buffer CreateMapReadBuffer(uint64_t size) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = wgpu::BufferUsage::MapRead; + + return device.CreateBuffer(&descriptor); + } + + wgpu::Buffer CreateMapWriteBuffer(uint64_t size) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = wgpu::BufferUsage::MapWrite; + + return device.CreateBuffer(&descriptor); + } + + wgpu::CreateBufferMappedResult CreateBufferMapped(uint64_t size, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage; + + return device.CreateBufferMapped(&descriptor); + } + + wgpu::Buffer BufferMappedAtCreation(uint64_t size, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage; + descriptor.mappedAtCreation = true; + + return device.CreateBuffer(&descriptor); + } + + void AssertMapAsyncError(wgpu::Buffer buffer, wgpu::MapMode mode, size_t offset, size_t size) { + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); + + ASSERT_DEVICE_ERROR( + buffer.MapAsync(mode, offset, size, ToMockBufferMapAsyncCallback, nullptr)); + } + + wgpu::Queue queue; + + private: + void SetUp() override { + ValidationTest::SetUp(); + + mockBufferMapReadCallback = std::make_unique(); + mockBufferMapWriteCallback = std::make_unique(); + mockBufferMapAsyncCallback = std::make_unique(); + queue = device.GetDefaultQueue(); + } + + void TearDown() override { + // Delete mocks so that expectations are checked + mockBufferMapReadCallback = nullptr; + mockBufferMapWriteCallback = nullptr; + mockBufferMapAsyncCallback = nullptr; + + ValidationTest::TearDown(); + } }; // Test case where creation should succeed TEST_F(BufferValidationTest, CreationSuccess) { // Success { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::Uniform; + descriptor.usage = wgpu::BufferUsage::Uniform; device.CreateBuffer(&descriptor); } @@ -125,74 +146,379 @@ TEST_F(BufferValidationTest, CreationSuccess) { // Test restriction on usages allowed with MapRead and MapWrite TEST_F(BufferValidationTest, CreationMapUsageRestrictions) { - // MapRead with TransferDst is ok + // MapRead with CopyDst is ok { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferDst; + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst; device.CreateBuffer(&descriptor); } // MapRead with something else is an error { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::Uniform; + descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::Uniform; ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); } - // MapWrite with TransferSrc is ok + // MapWrite with CopySrc is ok { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; device.CreateBuffer(&descriptor); } // MapWrite with something else is an error { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::Uniform; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Uniform; ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); } } // Test the success case for mapping buffer for reading -TEST_F(BufferValidationTest, MapReadSuccess) { - dawn::Buffer buf = CreateMapReadBuffer(4); +TEST_F(BufferValidationTest, MapAsync_ReadSuccess) { + wgpu::Buffer buf = CreateMapReadBuffer(4); + + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); + WaitForAllOperations(device); + + buf.Unmap(); +} + +// Test the success case for mapping buffer for writing +TEST_F(BufferValidationTest, MapAsync_WriteSuccess) { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); + WaitForAllOperations(device); + + buf.Unmap(); +} + +// Test map async with a buffer that's an error +TEST_F(BufferValidationTest, MapAsync_ErrorBuffer) { + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite; + wgpu::Buffer buffer; + ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&desc)); + + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4); + AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4); +} + +// Test map async with an invalid offset and size alignment. +TEST_F(BufferValidationTest, MapAsync_OffsetSizeAlignment) { + // Control case, both aligned to 4 is ok. + { + wgpu::Buffer buffer = CreateMapReadBuffer(8); + buffer.MapAsync(wgpu::MapMode::Read, 4, 4, nullptr, nullptr); + } + { + wgpu::Buffer buffer = CreateMapWriteBuffer(8); + buffer.MapAsync(wgpu::MapMode::Write, 4, 4, nullptr, nullptr); + } + + // Error case, offset aligned to 2 is an error. + { + wgpu::Buffer buffer = CreateMapReadBuffer(8); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 2, 4); + } + { + wgpu::Buffer buffer = CreateMapWriteBuffer(8); + AssertMapAsyncError(buffer, wgpu::MapMode::Write, 2, 4); + } + + // Error case, size aligned to 2 is an error. + { + wgpu::Buffer buffer = CreateMapReadBuffer(8); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 6); + } + { + wgpu::Buffer buffer = CreateMapWriteBuffer(8); + AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 6); + } +} + +// Test map async with an invalid offset and size OOB checks +TEST_F(BufferValidationTest, MapAsync_OffsetSizeOOB) { + // Valid case: full buffer is ok. + { + wgpu::Buffer buffer = CreateMapReadBuffer(8); + buffer.MapAsync(wgpu::MapMode::Read, 0, 8, nullptr, nullptr); + } + + // Valid case: range in the middle of the buffer is ok. + { + wgpu::Buffer buffer = CreateMapReadBuffer(12); + buffer.MapAsync(wgpu::MapMode::Read, 4, 4, nullptr, nullptr); + } + + // Valid case: empty range at the end of the buffer is ok. + { + wgpu::Buffer buffer = CreateMapReadBuffer(8); + buffer.MapAsync(wgpu::MapMode::Read, 8, 0, nullptr, nullptr); + } + + // Error case, offset is larger than the buffer size (even if size is 0). + { + wgpu::Buffer buffer = CreateMapReadBuffer(8); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 12, 0); + } + + // Error case, offset + size is larger than the buffer + { + wgpu::Buffer buffer = CreateMapReadBuffer(12); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 8, 8); + } + + // Error case, offset + size is larger than the buffer, overflow case. + { + wgpu::Buffer buffer = CreateMapReadBuffer(12); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 4, + std::numeric_limits::max() & ~size_t(3)); + } +} + +// Test map async with a buffer that has the wrong usage +TEST_F(BufferValidationTest, MapAsync_WrongUsage) { + { + wgpu::BufferDescriptor desc; + desc.usage = wgpu::BufferUsage::Vertex; + desc.size = 4; + wgpu::Buffer buffer = device.CreateBuffer(&desc); + + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4); + AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4); + } + { + wgpu::Buffer buffer = CreateMapReadBuffer(4); + AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4); + } + { + wgpu::Buffer buffer = CreateMapWriteBuffer(4); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4); + } +} + +// Test map async with a wrong mode +TEST_F(BufferValidationTest, MapAsync_WrongMode) { + { + wgpu::Buffer buffer = CreateMapReadBuffer(4); + AssertMapAsyncError(buffer, wgpu::MapMode::None, 0, 4); + } + { + wgpu::Buffer buffer = CreateMapReadBuffer(4); + AssertMapAsyncError(buffer, wgpu::MapMode::Read | wgpu::MapMode::Write, 0, 4); + } +} + +// Test map async with a buffer that's already mapped +TEST_F(BufferValidationTest, MapAsync_AlreadyMapped) { + { + wgpu::Buffer buffer = CreateMapReadBuffer(4); + buffer.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4); + } + { + wgpu::Buffer buffer = BufferMappedAtCreation(4, wgpu::BufferUsage::MapRead); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4); + } + { + wgpu::Buffer buffer = CreateMapWriteBuffer(4); + buffer.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr); + AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4); + } + { + wgpu::Buffer buffer = BufferMappedAtCreation(4, wgpu::BufferUsage::MapWrite); + AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4); + } +} + +// Test map async with a buffer that's destroyed +TEST_F(BufferValidationTest, MapAsync_Destroy) { + { + wgpu::Buffer buffer = CreateMapReadBuffer(4); + buffer.Destroy(); + AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4); + } + { + wgpu::Buffer buffer = CreateMapWriteBuffer(4); + buffer.Destroy(); + AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4); + } +} + +// Test map async but unmapping before the result is ready. +TEST_F(BufferValidationTest, MapAsync_UnmapBeforeResult) { + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Unknown, _)) + .Times(1); + buf.Unmap(); + + // The callback shouldn't be called again. + WaitForAllOperations(device); + } + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Unknown, _)) + .Times(1); + buf.Unmap(); + + // The callback shouldn't be called again. + WaitForAllOperations(device); + } +} + +// When a MapAsync is cancelled with Unmap it might still be in flight, test doing a new request +// works as expected and we don't get the cancelled request's data. +TEST_F(BufferValidationTest, MapAsync_UnmapBeforeResultAndMapAgain) { + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, this + 0); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Unknown, this + 0)) + .Times(1); + buf.Unmap(); + + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, this + 1); + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this + 1)) + .Times(1); + WaitForAllOperations(device); + } + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, this + 0); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Unknown, this + 0)) + .Times(1); + buf.Unmap(); + + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, this + 1); + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this + 1)) + .Times(1); + WaitForAllOperations(device); + } +} + +// Test map async but destroying before the result is ready. +TEST_F(BufferValidationTest, MapAsync_DestroyBeforeResult) { + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Unknown, _)) + .Times(1); + buf.Destroy(); + + // The callback shouldn't be called again. + WaitForAllOperations(device); + } + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Unknown, _)) + .Times(1); + buf.Destroy(); + + // The callback shouldn't be called again. + WaitForAllOperations(device); + } +} + +// Test that the MapCallback isn't fired twice when unmap() is called inside the callback +TEST_F(BufferValidationTest, MapAsync_UnmapCalledInCallback) { + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .WillOnce(InvokeWithoutArgs([&]() { buf.Unmap(); })); + + WaitForAllOperations(device); + } + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .WillOnce(InvokeWithoutArgs([&]() { buf.Unmap(); })); + + WaitForAllOperations(device); + } +} + +// Test that the MapCallback isn't fired twice when destroy() is called inside the callback +TEST_F(BufferValidationTest, MapAsync_DestroyCalledInCallback) { + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .WillOnce(InvokeWithoutArgs([&]() { buf.Destroy(); })); + + WaitForAllOperations(device); + } + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .WillOnce(InvokeWithoutArgs([&]() { buf.Destroy(); })); + + WaitForAllOperations(device); + } +} + +// Test the success case for mapping buffer for reading +TEST_F(BufferValidationTest, MapReadAsyncSuccess) { + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, _)) + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) .Times(1); - queue.Submit(0, nullptr); + WaitForAllOperations(device); buf.Unmap(); } // Test the success case for mapping buffer for writing -TEST_F(BufferValidationTest, MapWriteSuccess) { - dawn::Buffer buf = CreateMapWriteBuffer(4); +TEST_F(BufferValidationTest, MapWriteAsyncSuccess) { + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, _)) + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) .Times(1); - queue.Submit(0, nullptr); + WaitForAllOperations(device); buf.Unmap(); } // Test the success case for CreateBufferMapped TEST_F(BufferValidationTest, CreateBufferMappedSuccess) { - dawn::CreateBufferMappedResult result = CreateBufferMapped(4, dawn::BufferUsageBit::MapWrite); + wgpu::CreateBufferMappedResult result = CreateBufferMapped(4, wgpu::BufferUsage::MapWrite); ASSERT_NE(result.data, nullptr); ASSERT_EQ(result.dataLength, 4u); result.buffer.Unmap(); @@ -200,23 +526,36 @@ TEST_F(BufferValidationTest, CreateBufferMappedSuccess) { // Test the success case for non-mappable CreateBufferMapped TEST_F(BufferValidationTest, NonMappableCreateBufferMappedSuccess) { - dawn::CreateBufferMappedResult result = - CreateBufferMapped(4, dawn::BufferUsageBit::TransferSrc); + wgpu::CreateBufferMappedResult result = CreateBufferMapped(4, wgpu::BufferUsage::CopySrc); ASSERT_NE(result.data, nullptr); ASSERT_EQ(result.dataLength, 4u); result.buffer.Unmap(); } +// Test the success case for mappedAtCreation +TEST_F(BufferValidationTest, MappedAtCreationSuccess) { + BufferMappedAtCreation(4, wgpu::BufferUsage::MapWrite); +} + +// Test the success case for mappedAtCreation for a non-mappable usage +TEST_F(BufferValidationTest, NonMappableMappedAtCreationSuccess) { + BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc); +} + +// Test there is an error when mappedAtCreation is set but the size isn't aligned to 4. +TEST_F(BufferValidationTest, MappedAtCreationSizeAlignment) { + ASSERT_DEVICE_ERROR(BufferMappedAtCreation(2, wgpu::BufferUsage::MapWrite)); +} + // Test map reading a buffer with wrong current usage TEST_F(BufferValidationTest, MapReadWrongUsage) { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::TransferDst; + descriptor.usage = wgpu::BufferUsage::CopyDst; - dawn::Buffer buf = device.CreateBuffer(&descriptor); + wgpu::Buffer buf = device.CreateBuffer(&descriptor); - EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0u, _)) + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0u, _)) .Times(1); ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr)); @@ -224,14 +563,13 @@ TEST_F(BufferValidationTest, MapReadWrongUsage) { // Test map writing a buffer with wrong current usage TEST_F(BufferValidationTest, MapWriteWrongUsage) { - dawn::BufferDescriptor descriptor; + wgpu::BufferDescriptor descriptor; descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::TransferSrc; + descriptor.usage = wgpu::BufferUsage::CopySrc; - dawn::Buffer buf = device.CreateBuffer(&descriptor); + wgpu::Buffer buf = device.CreateBuffer(&descriptor); - EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0u, _)) + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0u, _)) .Times(1); ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr)); @@ -239,69 +577,65 @@ TEST_F(BufferValidationTest, MapWriteWrongUsage) { // Test map reading a buffer that is already mapped TEST_F(BufferValidationTest, MapReadAlreadyMapped) { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, this + 0); EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, this + 0)) + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, this + 0)) .Times(1); EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0u, this + 1)) + Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0u, this + 1)) .Times(1); ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, this + 1)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } // Test map writing a buffer that is already mapped TEST_F(BufferValidationTest, MapWriteAlreadyMapped) { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 0); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, this + 0)) + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, this + 0)) .Times(1); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0u, this + 1)) + Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0u, this + 1)) .Times(1); ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 1)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } // TODO(cwallez@chromium.org) Test a MapWrite and already MapRead and vice-versa // Test unmapping before having the result gives UNKNOWN - for reading TEST_F(BufferValidationTest, MapReadUnmapBeforeResult) { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); - EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u, _)) + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, _)) .Times(1); buf.Unmap(); - // Submitting the queue makes the null backend process map request, but the callback shouldn't - // be called again - queue.Submit(0, nullptr); + // The callback shouldn't be called again. + WaitForAllOperations(device); } // Test unmapping before having the result gives UNKNOWN - for writing TEST_F(BufferValidationTest, MapWriteUnmapBeforeResult) { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); - EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u, _)) + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, _)) .Times(1); buf.Unmap(); - // Submitting the queue makes the null backend process map request, but the callback shouldn't - // be called again - queue.Submit(0, nullptr); + // The callback shouldn't be called again. + WaitForAllOperations(device); } // Test destroying the buffer before having the result gives UNKNOWN - for reading @@ -309,18 +643,17 @@ TEST_F(BufferValidationTest, MapWriteUnmapBeforeResult) { // when its external ref count reaches 0. TEST_F(BufferValidationTest, DISABLED_MapReadDestroyBeforeResult) { { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u, _)) + Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, _)) .Times(1); } - // Submitting the queue makes the null backend process map request, but the callback shouldn't - // be called again - queue.Submit(0, nullptr); + // The callback shouldn't be called again. + WaitForAllOperations(device); } // Test destroying the buffer before having the result gives UNKNOWN - for writing @@ -328,187 +661,122 @@ TEST_F(BufferValidationTest, DISABLED_MapReadDestroyBeforeResult) { // when its external ref count reaches 0. TEST_F(BufferValidationTest, DISABLED_MapWriteDestroyBeforeResult) { { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u, _)) + Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, _)) .Times(1); } - // Submitting the queue makes the null backend process map request, but the callback shouldn't - // be called again - queue.Submit(0, nullptr); + // The callback shouldn't be called again. + WaitForAllOperations(device); } // When a MapRead is cancelled with Unmap it might still be in flight, test doing a new request // works as expected and we don't get the cancelled request's data. TEST_F(BufferValidationTest, MapReadUnmapBeforeResultThenMapAgain) { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, this + 0); EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u, this + 0)) + Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, this + 0)) .Times(1); buf.Unmap(); buf.MapReadAsync(ToMockBufferMapReadCallback, this + 1); EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, this + 1)) + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, this + 1)) .Times(1); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } // TODO(cwallez@chromium.org) Test a MapWrite and already MapRead and vice-versa // When a MapWrite is cancelled with Unmap it might still be in flight, test doing a new request // works as expected and we don't get the cancelled request's data. TEST_F(BufferValidationTest, MapWriteUnmapBeforeResultThenMapAgain) { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 0); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u, this + 0)) + Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, this + 0)) .Times(1); buf.Unmap(); buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 1); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, this + 1)) + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, this + 1)) .Times(1); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } // Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback TEST_F(BufferValidationTest, UnmapInsideMapReadCallback) { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, _)) + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) .WillOnce(InvokeWithoutArgs([&]() { buf.Unmap(); })); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } // Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback TEST_F(BufferValidationTest, UnmapInsideMapWriteCallback) { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, _)) + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) .WillOnce(InvokeWithoutArgs([&]() { buf.Unmap(); })); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } -// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the callback +// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the +// callback TEST_F(BufferValidationTest, DestroyInsideMapReadCallback) { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, _)) - .WillOnce(InvokeWithoutArgs([&]() { buf = dawn::Buffer(); })); + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .WillOnce(InvokeWithoutArgs([&]() { buf = wgpu::Buffer(); })); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } -// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the callback +// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the +// callback TEST_F(BufferValidationTest, DestroyInsideMapWriteCallback) { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), 4u, _)) - .WillOnce(InvokeWithoutArgs([&]() { buf = dawn::Buffer(); })); - - queue.Submit(0, nullptr); -} - -// Test the success case for Buffer::SetSubData -TEST_F(BufferValidationTest, SetSubDataSuccess) { - dawn::Buffer buf = CreateSetSubDataBuffer(4); - - uint32_t foo = 0x01020304; - buf.SetSubData(0, sizeof(foo), &foo); -} - -// Test error case for SetSubData out of bounds -TEST_F(BufferValidationTest, SetSubDataOutOfBounds) { - dawn::Buffer buf = CreateSetSubDataBuffer(1); + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .WillOnce(InvokeWithoutArgs([&]() { buf = wgpu::Buffer(); })); - uint8_t foo[2] = {0, 0}; - ASSERT_DEVICE_ERROR(buf.SetSubData(0, 2, foo)); -} - -// Test error case for SetSubData out of bounds with an overflow -TEST_F(BufferValidationTest, SetSubDataOutOfBoundsOverflow) { - dawn::Buffer buf = CreateSetSubDataBuffer(1000); - - uint8_t foo[2] = {0, 0}; - - // An offset that when added to "2" would overflow to be zero and pass validation without - // overflow checks. - uint64_t offset = uint64_t(int64_t(0) - int64_t(2)); - - ASSERT_DEVICE_ERROR(buf.SetSubData(offset, 2, foo)); -} - -// Test error case for SetSubData with the wrong usage -TEST_F(BufferValidationTest, SetSubDataWrongUsage) { - dawn::BufferDescriptor descriptor; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::Vertex; - - dawn::Buffer buf = device.CreateBuffer(&descriptor); - - uint8_t foo = 0; - ASSERT_DEVICE_ERROR(buf.SetSubData(0, sizeof(foo), &foo)); -} - -// Test SetSubData with unaligned size -TEST_F(BufferValidationTest, SetSubDataWithUnalignedSize) { - dawn::BufferDescriptor descriptor; - descriptor.size = 4; - descriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - - dawn::Buffer buf = device.CreateBuffer(&descriptor); - - uint8_t value = 123; - ASSERT_DEVICE_ERROR(buf.SetSubData(0, sizeof(value), &value)); -} - -// Test SetSubData with unaligned offset -TEST_F(BufferValidationTest, SetSubDataWithUnalignedOffset) { - dawn::BufferDescriptor descriptor; - descriptor.size = 4000; - descriptor.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - - dawn::Buffer buf = device.CreateBuffer(&descriptor); - - uint64_t kOffset = 2999; - uint32_t value = 0x01020304; - ASSERT_DEVICE_ERROR(buf.SetSubData(kOffset, sizeof(value), &value)); + WaitForAllOperations(device); } // Test that it is valid to destroy an unmapped buffer TEST_F(BufferValidationTest, DestroyUnmappedBuffer) { { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.Destroy(); } { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.Destroy(); } } @@ -516,12 +784,12 @@ TEST_F(BufferValidationTest, DestroyUnmappedBuffer) { // Test that it is valid to destroy a mapped buffer TEST_F(BufferValidationTest, DestroyMappedBuffer) { { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); buf.Destroy(); } { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); buf.Destroy(); } @@ -530,30 +798,30 @@ TEST_F(BufferValidationTest, DestroyMappedBuffer) { // Test that destroying a buffer implicitly unmaps it TEST_F(BufferValidationTest, DestroyMappedBufferCausesImplicitUnmap) { { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, this + 0); // Buffer is destroyed. Callback should be called with UNKNOWN status EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0, this + 0)) + Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, this + 0)) .Times(1); buf.Destroy(); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 1); // Buffer is destroyed. Callback should be called with UNKNOWN status EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0, this + 1)) + Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, this + 1)) .Times(1); buf.Destroy(); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } } // Test that it is valid to Destroy a destroyed buffer TEST_F(BufferValidationTest, DestroyDestroyedBuffer) { - dawn::Buffer buf = CreateSetSubDataBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.Destroy(); buf.Destroy(); } @@ -561,12 +829,12 @@ TEST_F(BufferValidationTest, DestroyDestroyedBuffer) { // Test that it is invalid to Unmap a destroyed buffer TEST_F(BufferValidationTest, UnmapDestroyedBuffer) { { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.Destroy(); ASSERT_DEVICE_ERROR(buf.Unmap()); } { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.Destroy(); ASSERT_DEVICE_ERROR(buf.Unmap()); } @@ -575,177 +843,217 @@ TEST_F(BufferValidationTest, UnmapDestroyedBuffer) { // Test that it is invalid to map a destroyed buffer TEST_F(BufferValidationTest, MapDestroyedBuffer) { { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.Destroy(); ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr)); } { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.Destroy(); ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr)); } } -// Test that it is invalid to call SetSubData on a destroyed buffer -TEST_F(BufferValidationTest, SetSubDataDestroyedBuffer) { - dawn::Buffer buf = CreateSetSubDataBuffer(4); - buf.Destroy(); - uint8_t foo = 0; - ASSERT_DEVICE_ERROR(buf.SetSubData(0, sizeof(foo), &foo)); -} - // Test that is is invalid to Map a mapped buffer TEST_F(BufferValidationTest, MapMappedBuffer) { { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } } // Test that is is invalid to Map a CreateBufferMapped buffer TEST_F(BufferValidationTest, MapCreateBufferMappedBuffer) { { - dawn::Buffer buf = CreateBufferMapped(4, dawn::BufferUsageBit::MapRead).buffer; + wgpu::Buffer buf = CreateBufferMapped(4, wgpu::BufferUsage::MapRead).buffer; ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } { - dawn::Buffer buf = CreateBufferMapped(4, dawn::BufferUsageBit::MapWrite).buffer; + wgpu::Buffer buf = CreateBufferMapped(4, wgpu::BufferUsage::MapWrite).buffer; ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } } -// Test that it is invalid to call SetSubData on a mapped buffer -TEST_F(BufferValidationTest, SetSubDataMappedBuffer) { +// Test that is is invalid to Map a buffer mapped at creation. +TEST_F(BufferValidationTest, MapBufferMappedAtCreation) { { - dawn::Buffer buf = CreateMapReadBuffer(4); - buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); - uint8_t foo = 0; - ASSERT_DEVICE_ERROR(buf.SetSubData(0, sizeof(foo), &foo)); - queue.Submit(0, nullptr); + wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::MapRead); + ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr)); + WaitForAllOperations(device); } { - dawn::Buffer buf = CreateMapWriteBuffer(4); - buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); - uint8_t foo = 0; - ASSERT_DEVICE_ERROR(buf.SetSubData(0, sizeof(foo), &foo)); - queue.Submit(0, nullptr); + wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::MapWrite); + ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr)); + WaitForAllOperations(device); } } // Test that it is valid to submit a buffer in a queue with a map usage if it is unmapped TEST_F(BufferValidationTest, SubmitBufferWithMapUsage) { - dawn::BufferDescriptor descriptorA; + wgpu::BufferDescriptor descriptorA; descriptorA.size = 4; - descriptorA.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::MapWrite; + descriptorA.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; - dawn::BufferDescriptor descriptorB; + wgpu::BufferDescriptor descriptorB; descriptorB.size = 4; - descriptorB.usage = dawn::BufferUsageBit::TransferDst | dawn::BufferUsageBit::MapRead; + descriptorB.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead; - dawn::Buffer bufA = device.CreateBuffer(&descriptorA); - dawn::Buffer bufB = device.CreateBuffer(&descriptorB); + wgpu::Buffer bufA = device.CreateBuffer(&descriptorA); + wgpu::Buffer bufB = device.CreateBuffer(&descriptorB); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); queue.Submit(1, &commands); } // Test that it is invalid to submit a mapped buffer in a queue TEST_F(BufferValidationTest, SubmitMappedBuffer) { - dawn::BufferDescriptor descriptorA; + wgpu::BufferDescriptor descriptorA; descriptorA.size = 4; - descriptorA.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::MapWrite; + descriptorA.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; - dawn::BufferDescriptor descriptorB; + wgpu::BufferDescriptor descriptorB; descriptorB.size = 4; - descriptorB.usage = dawn::BufferUsageBit::TransferDst | dawn::BufferUsageBit::MapRead; + descriptorB.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead; { - dawn::Buffer bufA = device.CreateBuffer(&descriptorA); - dawn::Buffer bufB = device.CreateBuffer(&descriptorB); + wgpu::Buffer bufA = device.CreateBuffer(&descriptorA); + wgpu::Buffer bufB = device.CreateBuffer(&descriptorB); + + bufA.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); + wgpu::CommandBuffer commands = encoder.Finish(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + WaitForAllOperations(device); + } + { + wgpu::Buffer bufA = device.CreateBuffer(&descriptorA); + wgpu::Buffer bufB = device.CreateBuffer(&descriptorB); + + bufB.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); + wgpu::CommandBuffer commands = encoder.Finish(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + WaitForAllOperations(device); + } + { + wgpu::Buffer bufA = device.CreateBuffer(&descriptorA); + wgpu::Buffer bufB = device.CreateBuffer(&descriptorB); bufA.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } { - dawn::Buffer bufA = device.CreateBuffer(&descriptorA); - dawn::Buffer bufB = device.CreateBuffer(&descriptorB); + wgpu::Buffer bufA = device.CreateBuffer(&descriptorA); + wgpu::Buffer bufB = device.CreateBuffer(&descriptorB); bufB.MapReadAsync(ToMockBufferMapReadCallback, nullptr); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } { - dawn::Buffer bufA = device.CreateBufferMapped(&descriptorA).buffer; - dawn::Buffer bufB = device.CreateBuffer(&descriptorB); + wgpu::Buffer bufA = device.CreateBufferMapped(&descriptorA).buffer; + wgpu::Buffer bufB = device.CreateBuffer(&descriptorB); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); } { - dawn::Buffer bufA = device.CreateBuffer(&descriptorA); - dawn::Buffer bufB = device.CreateBufferMapped(&descriptorB).buffer; + wgpu::Buffer bufA = device.CreateBuffer(&descriptorA); + wgpu::Buffer bufB = device.CreateBufferMapped(&descriptorB).buffer; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); - queue.Submit(0, nullptr); + WaitForAllOperations(device); + } + { + wgpu::BufferDescriptor mappedBufferDesc = descriptorA; + mappedBufferDesc.mappedAtCreation = true; + wgpu::Buffer bufA = device.CreateBuffer(&mappedBufferDesc); + wgpu::Buffer bufB = device.CreateBuffer(&descriptorB); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); + wgpu::CommandBuffer commands = encoder.Finish(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + WaitForAllOperations(device); + } + { + wgpu::BufferDescriptor mappedBufferDesc = descriptorB; + mappedBufferDesc.mappedAtCreation = true; + wgpu::Buffer bufA = device.CreateBuffer(&descriptorA); + wgpu::Buffer bufB = device.CreateBuffer(&mappedBufferDesc); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); + wgpu::CommandBuffer commands = encoder.Finish(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + WaitForAllOperations(device); } } // Test that it is invalid to submit a destroyed buffer in a queue TEST_F(BufferValidationTest, SubmitDestroyedBuffer) { - dawn::BufferDescriptor descriptorA; + wgpu::BufferDescriptor descriptorA; descriptorA.size = 4; - descriptorA.usage = dawn::BufferUsageBit::TransferSrc; + descriptorA.usage = wgpu::BufferUsage::CopySrc; - dawn::BufferDescriptor descriptorB; + wgpu::BufferDescriptor descriptorB; descriptorB.size = 4; - descriptorB.usage = dawn::BufferUsageBit::TransferDst; + descriptorB.usage = wgpu::BufferUsage::CopyDst; - dawn::Buffer bufA = device.CreateBuffer(&descriptorA); - dawn::Buffer bufB = device.CreateBuffer(&descriptorB); + wgpu::Buffer bufA = device.CreateBuffer(&descriptorA); + wgpu::Buffer bufB = device.CreateBuffer(&descriptorB); bufA.Destroy(); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4); - dawn::CommandBuffer commands = encoder.Finish(); + wgpu::CommandBuffer commands = encoder.Finish(); ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); } // Test that a map usage is required to call Unmap TEST_F(BufferValidationTest, UnmapWithoutMapUsage) { - dawn::Buffer buf = CreateSetSubDataBuffer(4); + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopyDst; + wgpu::Buffer buf = device.CreateBuffer(&descriptor); + ASSERT_DEVICE_ERROR(buf.Unmap()); } // Test that it is valid to call Unmap on a buffer that is not mapped TEST_F(BufferValidationTest, UnmapUnmappedBuffer) { { - dawn::Buffer buf = CreateMapReadBuffer(4); + wgpu::Buffer buf = CreateMapReadBuffer(4); // Buffer starts unmapped. Unmap should succeed. buf.Unmap(); buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); @@ -754,7 +1062,7 @@ TEST_F(BufferValidationTest, UnmapUnmappedBuffer) { buf.Unmap(); } { - dawn::Buffer buf = CreateMapWriteBuffer(4); + wgpu::Buffer buf = CreateMapWriteBuffer(4); // Buffer starts unmapped. Unmap should succeed. buf.Unmap(); buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); @@ -762,4 +1070,409 @@ TEST_F(BufferValidationTest, UnmapUnmappedBuffer) { buf.Unmap(); buf.Unmap(); } + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + // Buffer starts unmapped. Unmap should succeed. + buf.Unmap(); + buf.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr); + buf.Unmap(); + // Unmapping twice should succeed + buf.Unmap(); + } + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + // Buffer starts unmapped. Unmap should succeed. + buf.Unmap(); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr); + // Unmapping twice should succeed + buf.Unmap(); + buf.Unmap(); + } +} + +// Test that it is invalid to call GetMappedRange on an unmapped buffer. +TEST_F(BufferValidationTest, GetMappedRange_OnUnmappedBuffer) { + // Unmapped at creation case. + { + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::CopySrc; + wgpu::Buffer buf = device.CreateBuffer(&desc); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Unmapped after CreateBufferMapped case. + { + wgpu::Buffer buf = CreateBufferMapped(4, wgpu::BufferUsage::CopySrc).buffer; + buf.Unmap(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Unmapped after mappedAtCreation case. + { + wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc); + buf.Unmap(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Unmapped after MapReadAsync case. + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + + buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .Times(1); + WaitForAllOperations(device); + buf.Unmap(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Unmapped after MapWriteAsync case. + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .Times(1); + WaitForAllOperations(device); + buf.Unmap(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + // Unmapped after MapAsync read case. + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .Times(1); + WaitForAllOperations(device); + buf.Unmap(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Unmapped after MapAsync write case. + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .Times(1); + WaitForAllOperations(device); + buf.Unmap(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } +} + +// Test that it is invalid to call GetMappedRange on a destroyed buffer. +TEST_F(BufferValidationTest, GetMappedRange_OnDestroyedBuffer) { + // Destroyed after creation case. + { + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::CopySrc; + wgpu::Buffer buf = device.CreateBuffer(&desc); + buf.Destroy(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Destroyed after CreateBufferMapped case. + { + wgpu::Buffer buf = CreateBufferMapped(4, wgpu::BufferUsage::CopySrc).buffer; + buf.Destroy(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Destroyed after mappedAtCreation case. + { + wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc); + buf.Destroy(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Destroyed after MapReadAsync case. + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + + buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .Times(1); + WaitForAllOperations(device); + buf.Destroy(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Destroyed after MapWriteAsync case. + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .Times(1); + WaitForAllOperations(device); + buf.Destroy(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + // Destroyed after MapAsync read case. + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .Times(1); + WaitForAllOperations(device); + buf.Destroy(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } + + // Destroyed after MapAsync write case. + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .Times(1); + WaitForAllOperations(device); + buf.Destroy(); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + ASSERT_EQ(nullptr, buf.GetConstMappedRange()); + } +} + +// Test that it is invalid to call GetMappedRange on a buffer after MapReadAsync +TEST_F(BufferValidationTest, GetMappedRange_OnMappedForReading) { + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + + buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .Times(1); + WaitForAllOperations(device); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + } + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + + buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr); + EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) + .Times(1); + WaitForAllOperations(device); + + ASSERT_EQ(nullptr, buf.GetMappedRange()); + } +} + +// Test valid cases to call GetMappedRange on a buffer. +TEST_F(BufferValidationTest, GetMappedRange_ValidBufferStateCases) { + // GetMappedRange after CreateBufferMapped case. + { + wgpu::CreateBufferMappedResult result = CreateBufferMapped(4, wgpu::BufferUsage::CopySrc); + ASSERT_NE(result.buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(result.buffer.GetConstMappedRange(), result.buffer.GetMappedRange()); + ASSERT_EQ(result.buffer.GetConstMappedRange(), result.data); + } + + // GetMappedRange after mappedAtCreation case. + { + wgpu::Buffer buffer = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc); + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange()); + } + + // GetMappedRange after MapReadAsync case. + { + wgpu::Buffer buf = CreateMapReadBuffer(4); + + buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr); + + const void* mappedPointer = nullptr; + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .WillOnce(SaveArg<1>(&mappedPointer)); + + WaitForAllOperations(device); + + ASSERT_NE(buf.GetConstMappedRange(), nullptr); + ASSERT_EQ(buf.GetConstMappedRange(), mappedPointer); + } + + // GetMappedRange after MapWriteAsync case. + { + wgpu::Buffer buf = CreateMapWriteBuffer(4); + buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr); + + const void* mappedPointer = nullptr; + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _)) + .WillOnce(SaveArg<1>(&mappedPointer)); + + WaitForAllOperations(device); + + ASSERT_NE(buf.GetConstMappedRange(), nullptr); + ASSERT_EQ(buf.GetConstMappedRange(), buf.GetMappedRange()); + ASSERT_EQ(buf.GetConstMappedRange(), mappedPointer); + } +} + +// Test valid cases to call GetMappedRange on an error buffer. +TEST_F(BufferValidationTest, GetMappedRange_OnErrorBuffer) { + wgpu::BufferDescriptor desc; + desc.size = 4; + desc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead; + + uint64_t kStupidLarge = uint64_t(1) << uint64_t(63); + + // GetMappedRange after CreateBufferMapped a zero-sized buffer returns a non-nullptr. + // This is to check we don't do a malloc(0). + { + wgpu::CreateBufferMappedResult result; + ASSERT_DEVICE_ERROR(result = CreateBufferMapped( + 0, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead)); + + ASSERT_NE(result.buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(result.buffer.GetConstMappedRange(), result.buffer.GetMappedRange()); + ASSERT_EQ(result.buffer.GetConstMappedRange(), result.data); + } + + // GetMappedRange after CreateBufferMapped non-OOM returns a non-nullptr. + { + wgpu::CreateBufferMappedResult result; + ASSERT_DEVICE_ERROR(result = CreateBufferMapped( + 4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead)); + + ASSERT_NE(result.buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(result.buffer.GetConstMappedRange(), result.buffer.GetMappedRange()); + ASSERT_EQ(result.buffer.GetConstMappedRange(), result.data); + } + + // GetMappedRange after CreateBufferMapped OOM case returns nullptr. + { + wgpu::CreateBufferMappedResult result; + ASSERT_DEVICE_ERROR(result = + CreateBufferMapped(kStupidLarge, wgpu::BufferUsage::Storage | + wgpu::BufferUsage::MapRead)); + + ASSERT_EQ(result.buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(result.buffer.GetConstMappedRange(), result.buffer.GetMappedRange()); + ASSERT_EQ(result.buffer.GetConstMappedRange(), result.data); + } + + // GetMappedRange after mappedAtCreation a zero-sized buffer returns a non-nullptr. + // This is to check we don't do a malloc(0). + { + wgpu::Buffer buffer; + ASSERT_DEVICE_ERROR(buffer = BufferMappedAtCreation( + 0, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead)); + + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange()); + } + + // GetMappedRange after mappedAtCreation non-OOM returns a non-nullptr. + { + wgpu::Buffer buffer; + ASSERT_DEVICE_ERROR(buffer = BufferMappedAtCreation( + 4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead)); + + ASSERT_NE(buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange()); + } + + // GetMappedRange after mappedAtCreation OOM case returns nullptr. + { + wgpu::Buffer buffer; + ASSERT_DEVICE_ERROR( + buffer = BufferMappedAtCreation( + kStupidLarge, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead)); + + ASSERT_EQ(buffer.GetConstMappedRange(), nullptr); + ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange()); + } +} + +// Test validation of the GetMappedRange parameters +TEST_F(BufferValidationTest, GetMappedRange_OffsetSizeOOB) { + // Valid case: full range is ok + { + wgpu::Buffer buffer = CreateMapWriteBuffer(8); + buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr); + EXPECT_NE(buffer.GetMappedRange(0, 8), nullptr); + } + + // Valid case: full range is ok with defaulted MapAsync size + { + wgpu::Buffer buffer = CreateMapWriteBuffer(8); + buffer.MapAsync(wgpu::MapMode::Write, 0, 0, nullptr, nullptr); + EXPECT_NE(buffer.GetMappedRange(0, 8), nullptr); + } + + // Valid case: empty range at the end is ok + { + wgpu::Buffer buffer = CreateMapWriteBuffer(8); + buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr); + EXPECT_NE(buffer.GetMappedRange(8, 0), nullptr); + } + + // Valid case: range in the middle is ok. + { + wgpu::Buffer buffer = CreateMapWriteBuffer(12); + buffer.MapAsync(wgpu::MapMode::Write, 0, 12, nullptr, nullptr); + EXPECT_NE(buffer.GetMappedRange(4, 4), nullptr); + } + + // Error case: offset is larger than the mapped range (even with size = 0) + { + wgpu::Buffer buffer = CreateMapWriteBuffer(8); + buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr); + EXPECT_EQ(buffer.GetMappedRange(9, 0), nullptr); + } + + // Error case: offset + size is larger than the mapped range + { + wgpu::Buffer buffer = CreateMapWriteBuffer(12); + buffer.MapAsync(wgpu::MapMode::Write, 0, 12, nullptr, nullptr); + EXPECT_EQ(buffer.GetMappedRange(8, 5), nullptr); + } + + // Error case: offset + size is larger than the mapped range, overflow case + { + wgpu::Buffer buffer = CreateMapWriteBuffer(12); + buffer.MapAsync(wgpu::MapMode::Write, 0, 12, nullptr, nullptr); + EXPECT_EQ(buffer.GetMappedRange(8, std::numeric_limits::max()), nullptr); + } + + // Error case: offset is before the start of the range + { + wgpu::Buffer buffer = CreateMapWriteBuffer(12); + buffer.MapAsync(wgpu::MapMode::Write, 4, 4, nullptr, nullptr); + EXPECT_EQ(buffer.GetMappedRange(3, 4), nullptr); + } } diff --git a/third_party/dawn/src/tests/unittests/validation/CommandBufferValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/CommandBufferValidationTests.cpp index c679cf7d9ec..e31879bcb69 100644 --- a/third_party/dawn/src/tests/unittests/validation/CommandBufferValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/CommandBufferValidationTests.cpp @@ -14,10 +14,9 @@ #include "tests/unittests/validation/ValidationTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" -class CommandBufferValidationTest : public ValidationTest { -}; +class CommandBufferValidationTest : public ValidationTest {}; // Test for an empty command buffer TEST_F(CommandBufferValidationTest, Empty) { @@ -30,28 +29,26 @@ TEST_F(CommandBufferValidationTest, EndedMidRenderPass) { // Control case, command buffer ended after the pass is ended. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); pass.EndPass(); encoder.Finish(); } // Error case, command buffer ended mid-pass. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); ASSERT_DEVICE_ERROR(encoder.Finish()); } // Error case, command buffer ended mid-pass. Trying to use encoders after Finish // should fail too. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); ASSERT_DEVICE_ERROR(encoder.Finish()); - // TODO(cwallez@chromium.org) this should probably be a device error, but currently it - // produces a encoder error. - pass.EndPass(); + ASSERT_DEVICE_ERROR(pass.EndPass()); } } @@ -59,28 +56,26 @@ TEST_F(CommandBufferValidationTest, EndedMidRenderPass) { TEST_F(CommandBufferValidationTest, EndedMidComputePass) { // Control case, command buffer ended after the pass is ended. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.EndPass(); encoder.Finish(); } // Error case, command buffer ended mid-pass. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); ASSERT_DEVICE_ERROR(encoder.Finish()); } // Error case, command buffer ended mid-pass. Trying to use encoders after Finish // should fail too. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); ASSERT_DEVICE_ERROR(encoder.Finish()); - // TODO(cwallez@chromium.org) this should probably be a device error, but currently it - // produces a encoder error. - pass.EndPass(); + ASSERT_DEVICE_ERROR(pass.EndPass()); } } @@ -90,19 +85,17 @@ TEST_F(CommandBufferValidationTest, RenderPassEndedTwice) { // Control case, pass is ended once { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); pass.EndPass(); encoder.Finish(); } // Error case, pass ended twice { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); pass.EndPass(); - // TODO(cwallez@chromium.org) this should probably be a device error, but currently it - // produces a encoder error. pass.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); } @@ -112,19 +105,17 @@ TEST_F(CommandBufferValidationTest, RenderPassEndedTwice) { TEST_F(CommandBufferValidationTest, ComputePassEndedTwice) { // Control case, pass is ended once. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.EndPass(); encoder.Finish(); } // Error case, pass ended twice { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.EndPass(); - // TODO(cwallez@chromium.org) this should probably be a device error, but currently it - // produces a encoder error. pass.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); } @@ -136,9 +127,9 @@ TEST_F(CommandBufferValidationTest, BeginComputePassBeforeEndPreviousPass) { // Beginning a compute pass before ending a render pass causes an error. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPass = encoder.BeginRenderPass(&dummyRenderPass); - dawn::ComputePassEncoder computePass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&dummyRenderPass); + wgpu::ComputePassEncoder computePass = encoder.BeginComputePass(); computePass.EndPass(); renderPass.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); @@ -146,9 +137,9 @@ TEST_F(CommandBufferValidationTest, BeginComputePassBeforeEndPreviousPass) { // Beginning a compute pass before ending a compute pass causes an error. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder computePass1 = encoder.BeginComputePass(); - dawn::ComputePassEncoder computePass2 = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePass1 = encoder.BeginComputePass(); + wgpu::ComputePassEncoder computePass2 = encoder.BeginComputePass(); computePass2.EndPass(); computePass1.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); @@ -158,12 +149,12 @@ TEST_F(CommandBufferValidationTest, BeginComputePassBeforeEndPreviousPass) { // Test that encoding command after a successful finish produces an error TEST_F(CommandBufferValidationTest, CallsAfterASuccessfulFinish) { // A buffer that can be used in CopyBufferToBuffer - dawn::BufferDescriptor copyBufferDesc; + wgpu::BufferDescriptor copyBufferDesc; copyBufferDesc.size = 16; - copyBufferDesc.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer copyBuffer = device.CreateBuffer(©BufferDesc); + copyBufferDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer copyBuffer = device.CreateBuffer(©BufferDesc); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.Finish(); ASSERT_DEVICE_ERROR(encoder.CopyBufferToBuffer(copyBuffer, 0, copyBuffer, 0, 0)); @@ -172,94 +163,20 @@ TEST_F(CommandBufferValidationTest, CallsAfterASuccessfulFinish) { // Test that encoding command after a failed finish produces an error TEST_F(CommandBufferValidationTest, CallsAfterAFailedFinish) { // A buffer that can be used in CopyBufferToBuffer - dawn::BufferDescriptor copyBufferDesc; + wgpu::BufferDescriptor copyBufferDesc; copyBufferDesc.size = 16; - copyBufferDesc.usage = dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst; - dawn::Buffer copyBuffer = device.CreateBuffer(©BufferDesc); + copyBufferDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer copyBuffer = device.CreateBuffer(©BufferDesc); // A buffer that can't be used in CopyBufferToBuffer - dawn::BufferDescriptor bufferDesc; + wgpu::BufferDescriptor bufferDesc; bufferDesc.size = 16; - bufferDesc.usage = dawn::BufferUsageBit::Uniform; - dawn::Buffer buffer = device.CreateBuffer(&bufferDesc); + bufferDesc.usage = wgpu::BufferUsage::Uniform; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(buffer, 0, buffer, 0, 0); ASSERT_DEVICE_ERROR(encoder.Finish()); ASSERT_DEVICE_ERROR(encoder.CopyBufferToBuffer(copyBuffer, 0, copyBuffer, 0, 0)); } - -// Test that using a single buffer in multiple read usages in the same pass is allowed. -TEST_F(CommandBufferValidationTest, BufferWithMultipleReadUsage) { - // Create a buffer used as both vertex and index buffer. - dawn::BufferDescriptor bufferDescriptor; - bufferDescriptor.usage = dawn::BufferUsageBit::Vertex | dawn::BufferUsageBit::Index; - bufferDescriptor.size = 4; - dawn::Buffer buffer = device.CreateBuffer(&bufferDescriptor); - - // Use the buffer as both index and vertex in the same pass - uint64_t zero = 0; - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - DummyRenderPass dummyRenderPass(device); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); - pass.SetIndexBuffer(buffer, 0); - pass.SetVertexBuffers(0, 1, &buffer, &zero); - pass.EndPass(); - encoder.Finish(); -} - -// Test that using the same buffer as both readable and writable in the same pass is disallowed -TEST_F(CommandBufferValidationTest, BufferWithReadAndWriteUsage) { - // Create a buffer that will be used as an index buffer and as a storage buffer - dawn::BufferDescriptor bufferDescriptor; - bufferDescriptor.usage = dawn::BufferUsageBit::Storage | dawn::BufferUsageBit::Index; - bufferDescriptor.size = 4; - dawn::Buffer buffer = device.CreateBuffer(&bufferDescriptor); - - // Create the bind group to use the buffer as storage - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(device, {{ - 0, dawn::ShaderStageBit::Vertex, dawn::BindingType::StorageBuffer - }}); - dawn::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}}); - - // Use the buffer as both index and storage in the same pass - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - DummyRenderPass dummyRenderPass(device); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); - pass.SetIndexBuffer(buffer, 0); - pass.SetBindGroup(0, bg, 0, nullptr); - pass.EndPass(); - ASSERT_DEVICE_ERROR(encoder.Finish()); -} - -// Test that using the same texture as both readable and writable in the same pass is disallowed -TEST_F(CommandBufferValidationTest, TextureWithReadAndWriteUsage) { - // Create a texture that will be used both as a sampled texture and a render target - dawn::TextureDescriptor textureDescriptor; - textureDescriptor.usage = dawn::TextureUsageBit::Sampled | dawn::TextureUsageBit::OutputAttachment; - textureDescriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; - textureDescriptor.dimension = dawn::TextureDimension::e2D; - textureDescriptor.size = {1, 1, 1}; - textureDescriptor.arrayLayerCount = 1; - textureDescriptor.sampleCount = 1; - textureDescriptor.mipLevelCount = 1; - dawn::Texture texture = device.CreateTexture(&textureDescriptor); - dawn::TextureView view = texture.CreateDefaultView(); - - // Create the bind group to use the texture as sampled - dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(device, {{ - 0, dawn::ShaderStageBit::Vertex, dawn::BindingType::SampledTexture - }}); - dawn::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}}); - - // Create the render pass that will use the texture as an output attachment - utils::ComboRenderPassDescriptor renderPass({view}); - - // Use the texture as both sampeld and output attachment in the same pass - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); - pass.SetBindGroup(0, bg, 0, nullptr); - pass.EndPass(); - ASSERT_DEVICE_ERROR(encoder.Finish()); -} diff --git a/third_party/dawn/src/tests/unittests/validation/ComputeIndirectValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/ComputeIndirectValidationTests.cpp index 294b5b1f4ab..6bd5f8ec3b5 100644 --- a/third_party/dawn/src/tests/unittests/validation/ComputeIndirectValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/ComputeIndirectValidationTests.cpp @@ -15,35 +15,31 @@ #include #include #include "tests/unittests/validation/ValidationTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class ComputeIndirectValidationTest : public ValidationTest { protected: void SetUp() override { ValidationTest::SetUp(); - dawn::ShaderModule computeModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"( + wgpu::ShaderModule computeModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( #version 450 layout(local_size_x = 1) in; void main() { })"); // Set up compute pipeline - dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, nullptr); + wgpu::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, nullptr); - dawn::ComputePipelineDescriptor csDesc; + wgpu::ComputePipelineDescriptor csDesc; csDesc.layout = pl; - - dawn::PipelineStageDescriptor computeStage; - computeStage.module = computeModule; - computeStage.entryPoint = "main"; - csDesc.computeStage = &computeStage; - + csDesc.computeStage.module = computeModule; + csDesc.computeStage.entryPoint = "main"; pipeline = device.CreateComputePipeline(&csDesc); } - void ValidateExpectation(dawn::CommandEncoder encoder, utils::Expectation expectation) { + void ValidateExpectation(wgpu::CommandEncoder encoder, utils::Expectation expectation) { if (expectation == utils::Expectation::Success) { encoder.Finish(); } else { @@ -54,11 +50,11 @@ class ComputeIndirectValidationTest : public ValidationTest { void TestIndirectOffset(utils::Expectation expectation, std::initializer_list bufferList, uint64_t indirectOffset) { - dawn::Buffer indirectBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Indirect, bufferList); + wgpu::Buffer indirectBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Indirect, bufferList); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.SetPipeline(pipeline); pass.DispatchIndirect(indirectBuffer, indirectOffset); pass.EndPass(); @@ -66,7 +62,7 @@ class ComputeIndirectValidationTest : public ValidationTest { ValidateExpectation(encoder, expectation); } - dawn::ComputePipeline pipeline; + wgpu::ComputePipeline pipeline; }; // Verify out of bounds indirect dispatch calls are caught early diff --git a/third_party/dawn/src/tests/unittests/validation/ComputeValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/ComputeValidationTests.cpp index fded92564f1..8135997c999 100644 --- a/third_party/dawn/src/tests/unittests/validation/ComputeValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/ComputeValidationTests.cpp @@ -14,7 +14,7 @@ #include "tests/unittests/validation/ValidationTest.h" -class ComputeValidationTest : public ValidationTest { -}; +class ComputeValidationTest : public ValidationTest {}; -//TODO(cwallez@chromium.org): Add a regression test for Disptach validation trying to acces the input state. +// TODO(cwallez@chromium.org): Add a regression test for Disptach validation trying to acces the +// input state. diff --git a/third_party/dawn/src/tests/unittests/validation/CopyCommandsValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/CopyCommandsValidationTests.cpp index afad75c5136..e5877af1f4b 100644 --- a/third_party/dawn/src/tests/unittests/validation/CopyCommandsValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/CopyCommandsValidationTests.cpp @@ -16,143 +16,178 @@ #include "common/Constants.h" #include "common/Math.h" #include "tests/unittests/validation/ValidationTest.h" -#include "utils/DawnHelpers.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" class CopyCommandTest : public ValidationTest { - protected: - dawn::Buffer CreateBuffer(uint64_t size, dawn::BufferUsageBit usage) { - dawn::BufferDescriptor descriptor; - descriptor.size = size; - descriptor.usage = usage; + protected: + wgpu::Buffer CreateBuffer(uint64_t size, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage; - return device.CreateBuffer(&descriptor); - } + return device.CreateBuffer(&descriptor); + } - dawn::Texture Create2DTexture(uint32_t width, uint32_t height, uint32_t mipLevelCount, - uint32_t arrayLayerCount, dawn::TextureFormat format, - dawn::TextureUsageBit usage, uint32_t sampleCount = 1) { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = width; - descriptor.size.height = height; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = arrayLayerCount; - descriptor.sampleCount = sampleCount; - descriptor.format = format; - descriptor.mipLevelCount = mipLevelCount; - descriptor.usage = usage; - dawn::Texture tex = device.CreateTexture(&descriptor); - return tex; - } + wgpu::Texture Create2DTexture(uint32_t width, + uint32_t height, + uint32_t mipLevelCount, + uint32_t arrayLayerCount, + wgpu::TextureFormat format, + wgpu::TextureUsage usage, + uint32_t sampleCount = 1) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = width; + descriptor.size.height = height; + descriptor.size.depth = arrayLayerCount; + descriptor.sampleCount = sampleCount; + descriptor.format = format; + descriptor.mipLevelCount = mipLevelCount; + descriptor.usage = usage; + wgpu::Texture tex = device.CreateTexture(&descriptor); + return tex; + } - // TODO(jiawei.shao@intel.com): support more pixel formats - uint32_t TextureFormatPixelSize(dawn::TextureFormat format) { - switch (format) { - case dawn::TextureFormat::R8G8Unorm: - return 2; - case dawn::TextureFormat::R8G8B8A8Unorm: - return 4; - default: - UNREACHABLE(); - return 0; - } - } + uint32_t BufferSizeForTextureCopy( + uint32_t width, + uint32_t height, + uint32_t depth, + wgpu::TextureFormat format = wgpu::TextureFormat::RGBA8Unorm) { + uint32_t bytesPerPixel = utils::GetTexelBlockSizeInBytes(format); + uint32_t bytesPerRow = Align(width * bytesPerPixel, kTextureBytesPerRowAlignment); + return (bytesPerRow * (height - 1) + width * bytesPerPixel) * depth; + } - uint32_t BufferSizeForTextureCopy( - uint32_t width, - uint32_t height, - uint32_t depth, - dawn::TextureFormat format = dawn::TextureFormat::R8G8B8A8Unorm) { - uint32_t bytesPerPixel = TextureFormatPixelSize(format); - uint32_t rowPitch = Align(width * bytesPerPixel, kTextureRowPitchAlignment); - return (rowPitch * (height - 1) + width * bytesPerPixel) * depth; + void ValidateExpectation(wgpu::CommandEncoder encoder, utils::Expectation expectation) { + if (expectation == utils::Expectation::Success) { + encoder.Finish(); + } else { + ASSERT_DEVICE_ERROR(encoder.Finish()); } + } - void ValidateExpectation(dawn::CommandEncoder encoder, utils::Expectation expectation) { - if (expectation == utils::Expectation::Success) { - encoder.Finish(); - } else { - ASSERT_DEVICE_ERROR(encoder.Finish()); - } - } + void TestB2TCopy(utils::Expectation expectation, + wgpu::Buffer srcBuffer, + uint64_t srcOffset, + uint32_t srcBytesPerRow, + uint32_t srcRowsPerImage, + wgpu::Texture destTexture, + uint32_t destLevel, + wgpu::Origin3D destOrigin, + wgpu::Extent3D extent3D) { + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(srcBuffer, srcOffset, srcBytesPerRow, srcRowsPerImage); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(destTexture, destLevel, destOrigin); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D); + + ValidateExpectation(encoder, expectation); + } - void TestB2TCopy(utils::Expectation expectation, - dawn::Buffer srcBuffer, - uint64_t srcOffset, - uint32_t srcRowPitch, - uint32_t srcImageHeight, - dawn::Texture destTexture, - uint32_t destLevel, - uint32_t destSlice, - dawn::Origin3D destOrigin, - dawn::Extent3D extent3D) { - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(srcBuffer, srcOffset, srcRowPitch, srcImageHeight); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(destTexture, destLevel, destSlice, destOrigin); - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyBufferToTexture(&bufferCopyView, &textureCopyView, &extent3D); - - ValidateExpectation(encoder, expectation); - } + void TestT2BCopy(utils::Expectation expectation, + wgpu::Texture srcTexture, + uint32_t srcLevel, + wgpu::Origin3D srcOrigin, + wgpu::Buffer destBuffer, + uint64_t destOffset, + uint32_t destBytesPerRow, + uint32_t destRowsPerImage, + wgpu::Extent3D extent3D) { + wgpu::BufferCopyView bufferCopyView = + utils::CreateBufferCopyView(destBuffer, destOffset, destBytesPerRow, destRowsPerImage); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(srcTexture, srcLevel, srcOrigin); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D); + + ValidateExpectation(encoder, expectation); + } - void TestT2BCopy(utils::Expectation expectation, - dawn::Texture srcTexture, - uint32_t srcLevel, - uint32_t srcSlice, - dawn::Origin3D srcOrigin, - dawn::Buffer destBuffer, - uint64_t destOffset, - uint32_t destRowPitch, - uint32_t destImageHeight, - dawn::Extent3D extent3D) { - dawn::BufferCopyView bufferCopyView = - utils::CreateBufferCopyView(destBuffer, destOffset, destRowPitch, destImageHeight); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(srcTexture, srcLevel, srcSlice, srcOrigin); - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToBuffer(&textureCopyView, &bufferCopyView, &extent3D); - - ValidateExpectation(encoder, expectation); - } + void TestT2TCopy(utils::Expectation expectation, + wgpu::Texture srcTexture, + uint32_t srcLevel, + wgpu::Origin3D srcOrigin, + wgpu::Texture dstTexture, + uint32_t dstLevel, + wgpu::Origin3D dstOrigin, + wgpu::Extent3D extent3D) { + wgpu::TextureCopyView srcTextureCopyView = + utils::CreateTextureCopyView(srcTexture, srcLevel, srcOrigin); + wgpu::TextureCopyView dstTextureCopyView = + utils::CreateTextureCopyView(dstTexture, dstLevel, dstOrigin); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, &extent3D); + + ValidateExpectation(encoder, expectation); + } - void TestT2TCopy(utils::Expectation expectation, - dawn::Texture srcTexture, - uint32_t srcLevel, - uint32_t srcSlice, - dawn::Origin3D srcOrigin, - dawn::Texture dstTexture, - uint32_t dstLevel, - uint32_t dstSlice, - dawn::Origin3D dstOrigin, - dawn::Extent3D extent3D) { - dawn::TextureCopyView srcTextureCopyView = - utils::CreateTextureCopyView(srcTexture, srcLevel, srcSlice, srcOrigin); - dawn::TextureCopyView dstTextureCopyView = - utils::CreateTextureCopyView(dstTexture, dstLevel, dstSlice, dstOrigin); - - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyTextureToTexture(&srcTextureCopyView, &dstTextureCopyView, &extent3D); - - ValidateExpectation(encoder, expectation); - } -}; + void TestBothTBCopies(utils::Expectation expectation, + wgpu::Buffer buffer, + uint64_t bufferOffset, + uint32_t bufferBytesPerRow, + uint32_t rowsPerImage, + wgpu::Texture texture, + uint32_t level, + wgpu::Origin3D origin, + wgpu::Extent3D extent3D) { + TestB2TCopy(expectation, buffer, bufferOffset, bufferBytesPerRow, rowsPerImage, texture, + level, origin, extent3D); + TestT2BCopy(expectation, texture, level, origin, buffer, bufferOffset, bufferBytesPerRow, + rowsPerImage, extent3D); + } -class CopyCommandTest_B2B : public CopyCommandTest { + void TestBothT2TCopies(utils::Expectation expectation, + wgpu::Texture texture1, + uint32_t level1, + wgpu::Origin3D origin1, + wgpu::Texture texture2, + uint32_t level2, + wgpu::Origin3D origin2, + wgpu::Extent3D extent3D) { + TestT2TCopy(expectation, texture1, level1, origin1, texture2, level2, origin2, extent3D); + TestT2TCopy(expectation, texture2, level2, origin2, texture1, level1, origin1, extent3D); + } + + void TestBothTBCopiesExactBufferSize(uint32_t bufferBytesPerRow, + uint32_t rowsPerImage, + wgpu::Texture texture, + wgpu::TextureFormat textureFormat, + wgpu::Origin3D origin, + wgpu::Extent3D extent3D) { + // Check the minimal valid bufferSize. + uint64_t bufferSize = + utils::RequiredBytesInCopy(bufferBytesPerRow, rowsPerImage, extent3D, textureFormat); + wgpu::Buffer source = + CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + TestBothTBCopies(utils::Expectation::Success, source, 0, bufferBytesPerRow, rowsPerImage, + texture, 0, origin, extent3D); + + // Check bufferSize was indeed minimal. + uint64_t invalidSize = bufferSize - 1; + wgpu::Buffer invalidSource = + CreateBuffer(invalidSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + TestBothTBCopies(utils::Expectation::Failure, invalidSource, 0, bufferBytesPerRow, + rowsPerImage, texture, 0, origin, extent3D); + } }; +class CopyCommandTest_B2B : public CopyCommandTest {}; + // TODO(cwallez@chromium.org): Test that copies are forbidden inside renderpasses // Test a successfull B2B copy TEST_F(CopyCommandTest_B2B, Success) { - dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst); // Copy different copies, including some that touch the OOB condition { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 0, destination, 0, 16); encoder.CopyBufferToBuffer(source, 8, destination, 0, 8); encoder.CopyBufferToBuffer(source, 0, destination, 8, 8); @@ -161,7 +196,7 @@ TEST_F(CopyCommandTest_B2B, Success) { // Empty copies are valid { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 0, destination, 0, 0); encoder.CopyBufferToBuffer(source, 0, destination, 16, 0); encoder.CopyBufferToBuffer(source, 16, destination, 0, 0); @@ -171,19 +206,19 @@ TEST_F(CopyCommandTest_B2B, Success) { // Test B2B copies with OOB TEST_F(CopyCommandTest_B2B, OutOfBounds) { - dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst); // OOB on the source { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 8, destination, 0, 12); ASSERT_DEVICE_ERROR(encoder.Finish()); } // OOB on the destination { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 0, destination, 8, 12); ASSERT_DEVICE_ERROR(encoder.Finish()); } @@ -191,20 +226,20 @@ TEST_F(CopyCommandTest_B2B, OutOfBounds) { // Test B2B copies with incorrect buffer usage TEST_F(CopyCommandTest_B2B, BadUsage) { - dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); - dawn::Buffer vertex = CreateBuffer(16, dawn::BufferUsageBit::Vertex); + wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst); + wgpu::Buffer vertex = CreateBuffer(16, wgpu::BufferUsage::Vertex); // Source with incorrect usage { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(vertex, 0, destination, 0, 16); ASSERT_DEVICE_ERROR(encoder.Finish()); } // Destination with incorrect usage { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 0, vertex, 0, 16); ASSERT_DEVICE_ERROR(encoder.Finish()); } @@ -212,29 +247,29 @@ TEST_F(CopyCommandTest_B2B, BadUsage) { // Test B2B copies with unaligned data size TEST_F(CopyCommandTest_B2B, UnalignedSize) { - dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 8, destination, 0, sizeof(uint8_t)); ASSERT_DEVICE_ERROR(encoder.Finish()); } // Test B2B copies with unaligned offset TEST_F(CopyCommandTest_B2B, UnalignedOffset) { - dawn::Buffer source = CreateBuffer(16, dawn::BufferUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(16, dawn::BufferUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(16, wgpu::BufferUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(16, wgpu::BufferUsage::CopyDst); // Unaligned source offset { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 9, destination, 0, 4); ASSERT_DEVICE_ERROR(encoder.Finish()); } // Unaligned destination offset { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(source, 8, destination, 1, 4); ASSERT_DEVICE_ERROR(encoder.Finish()); } @@ -242,111 +277,167 @@ TEST_F(CopyCommandTest_B2B, UnalignedOffset) { // Test B2B copies with buffers in error state cause errors. TEST_F(CopyCommandTest_B2B, BuffersInErrorState) { - dawn::BufferDescriptor errorBufferDescriptor; + wgpu::BufferDescriptor errorBufferDescriptor; errorBufferDescriptor.size = 4; - errorBufferDescriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferSrc; - ASSERT_DEVICE_ERROR(dawn::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); + errorBufferDescriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc; + ASSERT_DEVICE_ERROR(wgpu::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); constexpr uint64_t bufferSize = 4; - dawn::Buffer validBuffer = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); + wgpu::Buffer validBuffer = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(errorBuffer, 0, validBuffer, 0, 4); ASSERT_DEVICE_ERROR(encoder.Finish()); } { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToBuffer(validBuffer, 0, errorBuffer, 0, 4); ASSERT_DEVICE_ERROR(encoder.Finish()); } } -class CopyCommandTest_B2T : public CopyCommandTest { -}; +// Test it is not allowed to do B2B copies within same buffer. +TEST_F(CopyCommandTest_B2B, CopyWithinSameBuffer) { + constexpr uint32_t kBufferSize = 16u; + wgpu::Buffer buffer = + CreateBuffer(kBufferSize, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + + // srcOffset < dstOffset, and srcOffset + copySize > dstOffset (overlapping) + { + constexpr uint32_t kSrcOffset = 0u; + constexpr uint32_t kDstOffset = 4u; + constexpr uint32_t kCopySize = 8u; + ASSERT(kDstOffset > kSrcOffset && kDstOffset < kSrcOffset + kCopySize); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(buffer, kSrcOffset, buffer, kDstOffset, kCopySize); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // srcOffset < dstOffset, and srcOffset + copySize == dstOffset (not overlapping) + { + constexpr uint32_t kSrcOffset = 0u; + constexpr uint32_t kDstOffset = 8u; + constexpr uint32_t kCopySize = kDstOffset - kSrcOffset; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(buffer, kSrcOffset, buffer, kDstOffset, kCopySize); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // srcOffset > dstOffset, and srcOffset < dstOffset + copySize (overlapping) + { + constexpr uint32_t kSrcOffset = 4u; + constexpr uint32_t kDstOffset = 0u; + constexpr uint32_t kCopySize = 8u; + ASSERT(kSrcOffset > kDstOffset && kSrcOffset < kDstOffset + kCopySize); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(buffer, kSrcOffset, buffer, kDstOffset, kCopySize); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // srcOffset > dstOffset, and srcOffset + copySize == dstOffset (not overlapping) + { + constexpr uint32_t kSrcOffset = 8u; + constexpr uint32_t kDstOffset = 0u; + constexpr uint32_t kCopySize = kSrcOffset - kDstOffset; + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(buffer, kSrcOffset, buffer, kDstOffset, kCopySize); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +class CopyCommandTest_B2T : public CopyCommandTest {}; // Test a successfull B2T copy TEST_F(CopyCommandTest_B2T, Success) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // Different copies, including some that touch the OOB condition { // Copy 4x4 block in corner of first mip. - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1}); // Copy 4x4 block in opposite corner of first mip. - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {12, 12, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, {12, 12, 0}, {4, 4, 1}); // Copy 4x4 block in the 4x4 mip. - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 2, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 2, {0, 0, 0}, {4, 4, 1}); // Copy with a buffer offset - TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 0, destination, 0, 0, + TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1}); } - // Copies with a 256-byte aligned row pitch but unaligned texture region + // Copies with a 256-byte aligned bytes per row but unaligned texture region { // Unaligned region - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, {0, 0, 0}, {3, 4, 1}); // Unaligned region with texture offset - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {5, 7, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, {5, 7, 0}, {2, 3, 1}); // Unaligned region, with buffer offset - TestB2TCopy(utils::Expectation::Success, source, 31 * 4, 256, 0, destination, 0, 0, - {0, 0, 0}, {3, 3, 1}); + TestB2TCopy(utils::Expectation::Success, source, 31 * 4, 256, 0, destination, 0, {0, 0, 0}, + {3, 3, 1}); } // Empty copies are valid { // An empty copy - TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 1}); // An empty copy with depth = 0 - TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 0}); // An empty copy touching the end of the buffer - TestB2TCopy(utils::Expectation::Success, source, bufferSize, 0, 0, destination, 0, 0, + TestB2TCopy(utils::Expectation::Success, source, bufferSize, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 1}); // An empty copy touching the side of the texture - TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, 0, {16, 16, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 0, 0, destination, 0, {16, 16, 0}, {0, 0, 1}); + // An empty copy with depth = 1 and bytesPerRow > 0 + TestB2TCopy(utils::Expectation::Success, source, 0, kTextureBytesPerRowAlignment, 0, + destination, 0, {0, 0, 0}, {0, 0, 1}); + // An empty copy with height > 0, depth = 0, bytesPerRow > 0 and rowsPerImage > 0 + TestB2TCopy(utils::Expectation::Success, source, 0, kTextureBytesPerRowAlignment, 16, + destination, 0, {0, 0, 0}, {0, 1, 0}); } } // Test OOB conditions on the buffer TEST_F(CopyCommandTest_B2T, OutOfBoundsOnBuffer) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // OOB on the buffer because we copy too many pixels - TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 5, 1}); // OOB on the buffer because of the offset - TestB2TCopy(utils::Expectation::Failure, source, 4, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Failure, source, 4, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1}); - // OOB on the buffer because (row pitch * (height - 1) + width * bytesPerPixel) * depth + // OOB on the buffer because (bytes per row * (height - 1) + width * bytesPerPixel) * depth // overflows - TestB2TCopy(utils::Expectation::Failure, source, 0, 512, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Failure, source, 0, 512, 0, destination, 0, {0, 0, 0}, {4, 3, 1}); - // Not OOB on the buffer although row pitch * height overflows - // but (row pitch * (height - 1) + width * bytesPerPixel) * depth does not overflow + // Not OOB on the buffer although bytes per row * height overflows + // but (bytes per row * (height - 1) + width * bytesPerPixel) * depth does not overflow { uint32_t sourceBufferSize = BufferSizeForTextureCopy(7, 3, 1); - ASSERT_TRUE(256 * 3 > sourceBufferSize) << "row pitch * height should overflow buffer"; - dawn::Buffer sourceBuffer = CreateBuffer(sourceBufferSize, dawn::BufferUsageBit::TransferSrc); + ASSERT_TRUE(256 * 3 > sourceBufferSize) << "bytes per row * height should overflow buffer"; + wgpu::Buffer sourceBuffer = CreateBuffer(sourceBufferSize, wgpu::BufferUsage::CopySrc); - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, {0, 0, 0}, {7, 3, 1}); } } @@ -354,124 +445,116 @@ TEST_F(CopyCommandTest_B2T, OutOfBoundsOnBuffer) { // Test OOB conditions on the texture TEST_F(CopyCommandTest_B2T, OutOfBoundsOnTexture) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // OOB on the texture because x + width overflows - TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {13, 12, 0}, + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {13, 12, 0}, {4, 4, 1}); // OOB on the texture because y + width overflows - TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {12, 13, 0}, + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {12, 13, 0}, {4, 4, 1}); // OOB on the texture because we overflow a non-zero mip - TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 2, 0, {1, 0, 0}, + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 2, {1, 0, 0}, {4, 4, 1}); // OOB on the texture even on an empty copy when we copy to a non-existent mip. - TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 5, 0, {0, 0, 0}, - {0, 0, 1}); + TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 5, {0, 0, 0}, {0, 0, 1}); // OOB on the texture because slice overflows - TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 2, {0, 0, 0}, - {0, 0, 1}); + TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, {0, 0, 2}, {0, 0, 1}); } -// Test that we force Z=0 and Depth=1 on copies to 2D textures -TEST_F(CopyCommandTest_B2T, ZDepthConstraintFor2DTextures) { - dawn::Buffer source = CreateBuffer(16 * 4, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); - - // Z=1 on an empty copy still errors - TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 1}, - {0, 0, 1}); +// Test that we force Depth=1 on copies to 2D textures +TEST_F(CopyCommandTest_B2T, DepthConstraintFor2DTextures) { + wgpu::Buffer source = CreateBuffer(16 * 4, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // Depth > 1 on an empty copy still errors - TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 0}, - {0, 0, 2}); + TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 2}); } // Test B2T copies with incorrect buffer usage TEST_F(CopyCommandTest_B2T, IncorrectUsage) { - dawn::Buffer source = CreateBuffer(16 * 4, dawn::BufferUsageBit::TransferSrc); - dawn::Buffer vertex = CreateBuffer(16 * 4, dawn::BufferUsageBit::Vertex); - dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); - dawn::Texture sampled = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::Sampled); + wgpu::Buffer source = CreateBuffer(16 * 4, wgpu::BufferUsage::CopySrc); + wgpu::Buffer vertex = CreateBuffer(16 * 4, wgpu::BufferUsage::Vertex); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); + wgpu::Texture sampled = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::Sampled); // Incorrect source usage - TestB2TCopy(utils::Expectation::Failure, vertex, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Failure, vertex, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1}); // Incorrect destination usage - TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, sampled, 0, 0, {0, 0, 0}, - {4, 4, 1}); + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, sampled, 0, {0, 0, 0}, {4, 4, 1}); } -TEST_F(CopyCommandTest_B2T, IncorrectRowPitch) { +TEST_F(CopyCommandTest_B2T, IncorrectBytesPerRow) { uint64_t bufferSize = BufferSizeForTextureCopy(128, 16, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(128, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = Create2DTexture(128, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); - // Default row pitch is not 256-byte aligned - TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, 0, {0, 0, 0}, - {3, 4, 1}); + // bytes per row is 0 + TestB2TCopy(utils::Expectation::Failure, source, 0, 0, 0, destination, 0, {0, 0, 0}, + {64, 4, 1}); - // Row pitch is not 256-byte aligned - TestB2TCopy(utils::Expectation::Failure, source, 0, 128, 0, destination, 0, 0, {0, 0, 0}, + // bytes per row is not 256-byte aligned + TestB2TCopy(utils::Expectation::Failure, source, 0, 128, 0, destination, 0, {0, 0, 0}, {4, 4, 1}); - // Row pitch is less than width * bytesPerPixel - TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + // bytes per row is less than width * bytesPerPixel + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, {65, 1, 1}); } TEST_F(CopyCommandTest_B2T, ImageHeightConstraint) { uint64_t bufferSize = BufferSizeForTextureCopy(5, 5, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // Image height is zero (Valid) - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1}); // Image height is equal to copy height (Valid) - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1}); // Image height is larger than copy height (Valid) - TestB2TCopy(utils::Expectation::Success, source, 0, 256, 4, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 5, destination, 0, {0, 0, 0}, {4, 4, 1}); // Image height is less than copy height (Invalid) - TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 3, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 3, destination, 0, {0, 0, 0}, {4, 4, 1}); } // Test B2T copies with incorrect buffer offset usage TEST_F(CopyCommandTest_B2T, IncorrectBufferOffset) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // Correct usage - TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 0, destination, 0, 0, + TestB2TCopy(utils::Expectation::Success, source, bufferSize - 4, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1}); // Incorrect usages { - TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 5, 256, 0, destination, 0, 0, + TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 5, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1}); - TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 6, 256, 0, destination, 0, 0, + TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 6, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1}); - TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 7, 256, 0, destination, 0, 0, + TestB2TCopy(utils::Expectation::Failure, source, bufferSize - 7, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1}); } } @@ -479,50 +562,49 @@ TEST_F(CopyCommandTest_B2T, IncorrectBufferOffset) { // Test multisampled textures cannot be used in B2T copies. TEST_F(CopyCommandTest_B2T, CopyToMultisampledTexture) { uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(2, 2, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst, 4); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = Create2DTexture(2, 2, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst, 4); - TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, 0, {0, 0, 0}, + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, 0, {0, 0, 0}, {2, 2, 1}); } // Test B2T copies with buffer or texture in error state causes errors. TEST_F(CopyCommandTest_B2T, BufferOrTextureInErrorState) { - dawn::BufferDescriptor errorBufferDescriptor; + wgpu::BufferDescriptor errorBufferDescriptor; errorBufferDescriptor.size = 4; - errorBufferDescriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferSrc; - ASSERT_DEVICE_ERROR(dawn::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); + errorBufferDescriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc; + ASSERT_DEVICE_ERROR(wgpu::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); - dawn::TextureDescriptor errorTextureDescriptor; - errorTextureDescriptor.arrayLayerCount = 0; - ASSERT_DEVICE_ERROR(dawn::Texture errorTexture = device.CreateTexture(&errorTextureDescriptor)); + wgpu::TextureDescriptor errorTextureDescriptor; + errorTextureDescriptor.size.depth = 0; + ASSERT_DEVICE_ERROR(wgpu::Texture errorTexture = device.CreateTexture(&errorTextureDescriptor)); - dawn::BufferCopyView errorBufferCopyView = utils::CreateBufferCopyView(errorBuffer, 0, 0, 0); - dawn::TextureCopyView errorTextureCopyView = - utils::CreateTextureCopyView(errorTexture, 0, 0, {1, 1, 1}); + wgpu::BufferCopyView errorBufferCopyView = utils::CreateBufferCopyView(errorBuffer, 0, 0, 0); + wgpu::TextureCopyView errorTextureCopyView = + utils::CreateTextureCopyView(errorTexture, 0, {0, 0, 0}); - dawn::Extent3D extent3D = {1, 1, 1}; + wgpu::Extent3D extent3D = {1, 1, 1}; { - dawn::Texture destination = - Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(destination, 0, 0, {1, 1, 1}); + wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(destination, 0, {0, 0, 0}); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&errorBufferCopyView, &textureCopyView, &extent3D); ASSERT_DEVICE_ERROR(encoder.Finish()); } { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); - dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(source, 0, 0, 0); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(source, 0, 0, 0); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyBufferToTexture(&bufferCopyView, &errorTextureCopyView, &extent3D); ASSERT_DEVICE_ERROR(encoder.Finish()); } @@ -530,106 +612,127 @@ TEST_F(CopyCommandTest_B2T, BufferOrTextureInErrorState) { // Regression tests for a bug in the computation of texture copy buffer size in Dawn. TEST_F(CopyCommandTest_B2T, TextureCopyBufferSizeLastRowComputation) { - constexpr uint32_t kRowPitch = 256; + constexpr uint32_t kBytesPerRow = 256; constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; - constexpr std::array kFormats = {dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureFormat::R8G8Unorm}; + constexpr std::array kFormats = {wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureFormat::RG8Unorm}; { - // kRowPitch * (kHeight - 1) + kWidth is not large enough to be the valid buffer size in + // kBytesPerRow * (kHeight - 1) + kWidth is not large enough to be the valid buffer size in // this test because the buffer sizes in B2T copies are not in texels but in bytes. - constexpr uint32_t kInvalidBufferSize = kRowPitch * (kHeight - 1) + kWidth; - - for (dawn::TextureFormat format : kFormats) { - dawn::Buffer source = - CreateBuffer(kInvalidBufferSize, dawn::BufferUsageBit::TransferSrc); - dawn::Texture destination = - Create2DTexture(kWidth, kHeight, 1, 1, format, dawn::TextureUsageBit::TransferDst); - TestB2TCopy(utils::Expectation::Failure, source, 0, kRowPitch, 0, destination, 0, 0, + constexpr uint32_t kInvalidBufferSize = kBytesPerRow * (kHeight - 1) + kWidth; + + for (wgpu::TextureFormat format : kFormats) { + wgpu::Buffer source = CreateBuffer(kInvalidBufferSize, wgpu::BufferUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(kWidth, kHeight, 1, 1, format, wgpu::TextureUsage::CopyDst); + TestB2TCopy(utils::Expectation::Failure, source, 0, kBytesPerRow, 0, destination, 0, {0, 0, 0}, {kWidth, kHeight, 1}); } } { - for (dawn::TextureFormat format : kFormats) { + for (wgpu::TextureFormat format : kFormats) { uint32_t validBufferSize = BufferSizeForTextureCopy(kWidth, kHeight, 1, format); - dawn::Texture destination = - Create2DTexture(kWidth, kHeight, 1, 1, format, dawn::TextureUsageBit::TransferDst); + wgpu::Texture destination = + Create2DTexture(kWidth, kHeight, 1, 1, format, wgpu::TextureUsage::CopyDst); // Verify the return value of BufferSizeForTextureCopy() is exactly the minimum valid // buffer size in this test. { uint32_t invalidBuffferSize = validBufferSize - 1; - dawn::Buffer source = - CreateBuffer(invalidBuffferSize, dawn::BufferUsageBit::TransferSrc); - TestB2TCopy(utils::Expectation::Failure, source, 0, kRowPitch, 0, destination, 0, 0, + wgpu::Buffer source = CreateBuffer(invalidBuffferSize, wgpu::BufferUsage::CopySrc); + TestB2TCopy(utils::Expectation::Failure, source, 0, kBytesPerRow, 0, destination, 0, {0, 0, 0}, {kWidth, kHeight, 1}); } { - dawn::Buffer source = - CreateBuffer(validBufferSize, dawn::BufferUsageBit::TransferSrc); - TestB2TCopy(utils::Expectation::Success, source, 0, kRowPitch, 0, destination, 0, 0, + wgpu::Buffer source = CreateBuffer(validBufferSize, wgpu::BufferUsage::CopySrc); + TestB2TCopy(utils::Expectation::Success, source, 0, kBytesPerRow, 0, destination, 0, {0, 0, 0}, {kWidth, kHeight, 1}); } } } } -class CopyCommandTest_T2B : public CopyCommandTest { -}; +// Test copy from buffer to mip map of non square texture +TEST_F(CopyCommandTest_B2T, CopyToMipmapOfNonSquareTexture) { + uint64_t bufferSize = BufferSizeForTextureCopy(4, 2, 1); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); + uint32_t maxMipmapLevel = 3; + wgpu::Texture destination = Create2DTexture( + 4, 2, maxMipmapLevel, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); + + // Copy to top level mip map + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, maxMipmapLevel - 1, + {0, 0, 0}, {1, 1, 1}); + // Copy to high level mip map + TestB2TCopy(utils::Expectation::Success, source, 0, 256, 0, destination, maxMipmapLevel - 2, + {0, 0, 0}, {2, 1, 1}); + // Mip level out of range + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, maxMipmapLevel, + {0, 0, 0}, {1, 1, 1}); + // Copy origin out of range + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, maxMipmapLevel - 2, + {1, 0, 0}, {2, 1, 1}); + // Copy size out of range + TestB2TCopy(utils::Expectation::Failure, source, 0, 256, 0, destination, maxMipmapLevel - 2, + {0, 0, 0}, {2, 2, 1}); +} + +class CopyCommandTest_T2B : public CopyCommandTest {}; // Test a successfull T2B copy TEST_F(CopyCommandTest_T2B, Success) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); // Different copies, including some that touch the OOB condition { // Copy from 4x4 block in corner of first mip. - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // Copy from 4x4 block in opposite corner of first mip. - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {12, 12, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Success, source, 0, {12, 12, 0}, destination, 0, 256, 0, {4, 4, 1}); // Copy from 4x4 block in the 4x4 mip. - TestT2BCopy(utils::Expectation::Success, source, 2, 0, {0, 0, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Success, source, 2, {0, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // Copy with a buffer offset - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, - bufferSize - 4, 256, 0, {1, 1, 1}); + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, bufferSize - 4, + 256, 0, {1, 1, 1}); } - // Copies with a 256-byte aligned row pitch but unaligned texture region + // Copies with a 256-byte aligned bytes per row but unaligned texture region { // Unaligned region - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 0, {3, 4, 1}); // Unaligned region with texture offset - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {5, 7, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Success, source, 0, {5, 7, 0}, destination, 0, 256, 0, {2, 3, 1}); // Unaligned region, with buffer offset - TestT2BCopy(utils::Expectation::Success, source, 2, 0, {0, 0, 0}, destination, 31 * 4, 256, - 0, {3, 3, 1}); + TestT2BCopy(utils::Expectation::Success, source, 2, {0, 0, 0}, destination, 31 * 4, 256, 0, + {3, 3, 1}); } // Empty copies are valid { // An empty copy - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, 0, + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 0, 0, {0, 0, 1}); // An empty copy with depth = 0 - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, 0, + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 0, 0, {0, 0, 0}); // An empty copy touching the end of the buffer - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, bufferSize, - 0, 0, {0, 0, 1}); + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, bufferSize, 0, + 0, {0, 0, 1}); // An empty copy touching the side of the texture - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {16, 16, 0}, destination, 0, 0, 0, + TestT2BCopy(utils::Expectation::Success, source, 0, {16, 16, 0}, destination, 0, 0, 0, {0, 0, 1}); } } @@ -637,201 +740,196 @@ TEST_F(CopyCommandTest_T2B, Success) { // Test OOB conditions on the texture TEST_F(CopyCommandTest_T2B, OutOfBoundsOnTexture) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); // OOB on the texture because x + width overflows - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {13, 12, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Failure, source, 0, {13, 12, 0}, destination, 0, 256, 0, {4, 4, 1}); // OOB on the texture because y + width overflows - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {12, 13, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Failure, source, 0, {12, 13, 0}, destination, 0, 256, 0, {4, 4, 1}); // OOB on the texture because we overflow a non-zero mip - TestT2BCopy(utils::Expectation::Failure, source, 2, 0, {1, 0, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Failure, source, 2, {1, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // OOB on the texture even on an empty copy when we copy from a non-existent mip. - TestT2BCopy(utils::Expectation::Failure, source, 5, 0, {0, 0, 0}, destination, 0, 0, 0, - {0, 0, 1}); + TestT2BCopy(utils::Expectation::Failure, source, 5, {0, 0, 0}, destination, 0, 0, 0, {0, 0, 1}); } // Test OOB conditions on the buffer TEST_F(CopyCommandTest_T2B, OutOfBoundsOnBuffer) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); // OOB on the buffer because we copy too many pixels - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 5, 1}); // OOB on the buffer because of the offset - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 4, 256, 0, + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 4, 256, 0, {4, 4, 1}); - // OOB on the buffer because (row pitch * (height - 1) + width * bytesPerPixel) * depth + // OOB on the buffer because (bytes per row * (height - 1) + width * bytesPerPixel) * depth // overflows - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 512, 0, + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 512, 0, {4, 3, 1}); - // Not OOB on the buffer although row pitch * height overflows - // but (row pitch * (height - 1) + width * bytesPerPixel) * depth does not overflow + // Not OOB on the buffer although bytes per row * height overflows + // but (bytes per row * (height - 1) + width * bytesPerPixel) * depth does not overflow { uint32_t destinationBufferSize = BufferSizeForTextureCopy(7, 3, 1); - ASSERT_TRUE(256 * 3 > destinationBufferSize) << "row pitch * height should overflow buffer"; - dawn::Buffer destinationBuffer = CreateBuffer(destinationBufferSize, dawn::BufferUsageBit::TransferDst); - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destinationBuffer, 0, 256, - 0, {7, 3, 1}); + ASSERT_TRUE(256 * 3 > destinationBufferSize) + << "bytes per row * height should overflow buffer"; + wgpu::Buffer destinationBuffer = + CreateBuffer(destinationBufferSize, wgpu::BufferUsage::CopyDst); + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destinationBuffer, 0, 256, 0, + {7, 3, 1}); } } -// Test that we force Z=0 and Depth=1 on copies from to 2D textures -TEST_F(CopyCommandTest_T2B, ZDepthConstraintFor2DTextures) { +// Test that we force Depth=1 on copies from to 2D textures +TEST_F(CopyCommandTest_T2B, DepthConstraintFor2DTextures) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); - - // Z=1 on an empty copy still errors - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 1}, destination, 0, 0, 0, - {0, 0, 1}); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); // Depth > 1 on an empty copy still errors - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, 0, - {0, 0, 2}); + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 0, 0, {0, 0, 2}); } // Test T2B copies with incorrect buffer usage TEST_F(CopyCommandTest_T2B, IncorrectUsage) { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Texture source = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Texture sampled = Create2DTexture(16, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::Sampled); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); - dawn::Buffer vertex = CreateBuffer(bufferSize, dawn::BufferUsageBit::Vertex); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Texture sampled = + Create2DTexture(16, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::Sampled); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); + wgpu::Buffer vertex = CreateBuffer(bufferSize, wgpu::BufferUsage::Vertex); // Incorrect source usage - TestT2BCopy(utils::Expectation::Failure, sampled, 0, 0, {0, 0, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Failure, sampled, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // Incorrect destination usage - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, vertex, 0, 256, 0, {4, 4, 1}); + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, vertex, 0, 256, 0, {4, 4, 1}); } -TEST_F(CopyCommandTest_T2B, IncorrectRowPitch) { +TEST_F(CopyCommandTest_T2B, IncorrectBytesPerRow) { uint64_t bufferSize = BufferSizeForTextureCopy(128, 16, 1); - dawn::Texture source = Create2DTexture(128, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); + wgpu::Texture source = Create2DTexture(128, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); - // Default row pitch is not 256-byte aligned - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, - {3, 4, 1}); + // bytes per row is 0 + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, + {64, 4, 1}); - // Row pitch is not 256-byte aligned - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 257, 0, + // bytes per row is not 256-byte aligned + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 257, 0, {4, 4, 1}); - // Row pitch is less than width * bytesPerPixel - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, + // bytes per row is less than width * bytesPerPixel + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, {65, 1, 1}); } TEST_F(CopyCommandTest_T2B, ImageHeightConstraint) { uint64_t bufferSize = BufferSizeForTextureCopy(5, 5, 1); - dawn::Texture source = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); + wgpu::Texture source = + Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); // Image height is zero (Valid) - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 0, {4, 4, 1}); // Image height is equal to copy height (Valid) - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 4, + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 4, {4, 4, 1}); // Image height exceeds copy height (Valid) - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 256, 5, + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, 256, 5, {4, 4, 1}); // Image height is less than copy height (Invalid) - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 3, + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 3, {4, 4, 1}); } // Test T2B copies with incorrect buffer offset usage TEST_F(CopyCommandTest_T2B, IncorrectBufferOffset) { uint64_t bufferSize = BufferSizeForTextureCopy(128, 16, 1); - dawn::Texture source = Create2DTexture(128, 16, 5, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); + wgpu::Texture source = Create2DTexture(128, 16, 5, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopySrc); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); // Correct usage - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, bufferSize - 4, - 256, 0, {1, 1, 1}); + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, bufferSize - 4, 256, + 0, {1, 1, 1}); // Incorrect usages - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, bufferSize - 5, - 256, 0, {1, 1, 1}); - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, bufferSize - 6, - 256, 0, {1, 1, 1}); - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, bufferSize - 7, - 256, 0, {1, 1, 1}); + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, bufferSize - 5, 256, + 0, {1, 1, 1}); + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, bufferSize - 6, 256, + 0, {1, 1, 1}); + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, bufferSize - 7, 256, + 0, {1, 1, 1}); } // Test multisampled textures cannot be used in T2B copies. TEST_F(CopyCommandTest_T2B, CopyFromMultisampledTexture) { - dawn::Texture source = Create2DTexture(2, 2, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc, 4); + wgpu::Texture source = Create2DTexture(2, 2, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopySrc, 4); uint64_t bufferSize = BufferSizeForTextureCopy(16, 16, 1); - dawn::Buffer destination = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferDst); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 256, 0, + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, 256, 0, {2, 2, 1}); } // Test T2B copies with buffer or texture in error state cause errors. TEST_F(CopyCommandTest_T2B, BufferOrTextureInErrorState) { - dawn::BufferDescriptor errorBufferDescriptor; + wgpu::BufferDescriptor errorBufferDescriptor; errorBufferDescriptor.size = 4; - errorBufferDescriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferSrc; - ASSERT_DEVICE_ERROR(dawn::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); + errorBufferDescriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopySrc; + ASSERT_DEVICE_ERROR(wgpu::Buffer errorBuffer = device.CreateBuffer(&errorBufferDescriptor)); - dawn::TextureDescriptor errorTextureDescriptor; - errorTextureDescriptor.arrayLayerCount = 0; - ASSERT_DEVICE_ERROR(dawn::Texture errorTexture = device.CreateTexture(&errorTextureDescriptor)); + wgpu::TextureDescriptor errorTextureDescriptor; + errorTextureDescriptor.size.depth = 0; + ASSERT_DEVICE_ERROR(wgpu::Texture errorTexture = device.CreateTexture(&errorTextureDescriptor)); - dawn::BufferCopyView errorBufferCopyView = utils::CreateBufferCopyView(errorBuffer, 0, 0, 0); - dawn::TextureCopyView errorTextureCopyView = - utils::CreateTextureCopyView(errorTexture, 0, 0, {1, 1, 1}); + wgpu::BufferCopyView errorBufferCopyView = utils::CreateBufferCopyView(errorBuffer, 0, 0, 0); + wgpu::TextureCopyView errorTextureCopyView = + utils::CreateTextureCopyView(errorTexture, 0, {0, 0, 0}); - dawn::Extent3D extent3D = {1, 1, 1}; + wgpu::Extent3D extent3D = {1, 1, 1}; { uint64_t bufferSize = BufferSizeForTextureCopy(4, 4, 1); - dawn::Buffer source = CreateBuffer(bufferSize, dawn::BufferUsageBit::TransferSrc); + wgpu::Buffer source = CreateBuffer(bufferSize, wgpu::BufferUsage::CopySrc); - dawn::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(source, 0, 0, 0); + wgpu::BufferCopyView bufferCopyView = utils::CreateBufferCopyView(source, 0, 0, 0); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&errorTextureCopyView, &bufferCopyView, &extent3D); ASSERT_DEVICE_ERROR(encoder.Finish()); } { - dawn::Texture destination = - Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); - dawn::TextureCopyView textureCopyView = - utils::CreateTextureCopyView(destination, 0, 0, {1, 1, 1}); + wgpu::Texture destination = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(destination, 0, {0, 0, 0}); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); encoder.CopyTextureToBuffer(&textureCopyView, &errorBufferCopyView, &extent3D); ASSERT_DEVICE_ERROR(encoder.Finish()); } @@ -839,245 +937,726 @@ TEST_F(CopyCommandTest_T2B, BufferOrTextureInErrorState) { // Regression tests for a bug in the computation of texture copy buffer size in Dawn. TEST_F(CopyCommandTest_T2B, TextureCopyBufferSizeLastRowComputation) { - constexpr uint32_t kRowPitch = 256; + constexpr uint32_t kBytesPerRow = 256; constexpr uint32_t kWidth = 4; constexpr uint32_t kHeight = 4; - constexpr std::array kFormats = {dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureFormat::R8G8Unorm}; + constexpr std::array kFormats = {wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureFormat::RG8Unorm}; { - // kRowPitch * (kHeight - 1) + kWidth is not large enough to be the valid buffer size in + // kBytesPerRow * (kHeight - 1) + kWidth is not large enough to be the valid buffer size in // this test because the buffer sizes in T2B copies are not in texels but in bytes. - constexpr uint32_t kInvalidBufferSize = kRowPitch * (kHeight - 1) + kWidth; + constexpr uint32_t kInvalidBufferSize = kBytesPerRow * (kHeight - 1) + kWidth; - for (dawn::TextureFormat format : kFormats) { - dawn::Texture source = - Create2DTexture(kWidth, kHeight, 1, 1, format, dawn::TextureUsageBit::TransferDst); + for (wgpu::TextureFormat format : kFormats) { + wgpu::Texture source = + Create2DTexture(kWidth, kHeight, 1, 1, format, wgpu::TextureUsage::CopyDst); - dawn::Buffer destination = - CreateBuffer(kInvalidBufferSize, dawn::BufferUsageBit::TransferSrc); - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, - kRowPitch, 0, {kWidth, kHeight, 1}); + wgpu::Buffer destination = CreateBuffer(kInvalidBufferSize, wgpu::BufferUsage::CopySrc); + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, + kBytesPerRow, 0, {kWidth, kHeight, 1}); } } { - for (dawn::TextureFormat format : kFormats) { + for (wgpu::TextureFormat format : kFormats) { uint32_t validBufferSize = BufferSizeForTextureCopy(kWidth, kHeight, 1, format); - dawn::Texture source = - Create2DTexture(kWidth, kHeight, 1, 1, format, dawn::TextureUsageBit::TransferSrc); + wgpu::Texture source = + Create2DTexture(kWidth, kHeight, 1, 1, format, wgpu::TextureUsage::CopySrc); // Verify the return value of BufferSizeForTextureCopy() is exactly the minimum valid // buffer size in this test. { uint32_t invalidBufferSize = validBufferSize - 1; - dawn::Buffer destination = - CreateBuffer(invalidBufferSize, dawn::BufferUsageBit::TransferDst); - TestT2BCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, - kRowPitch, 0, {kWidth, kHeight, 1}); + wgpu::Buffer destination = + CreateBuffer(invalidBufferSize, wgpu::BufferUsage::CopyDst); + TestT2BCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, + kBytesPerRow, 0, {kWidth, kHeight, 1}); } { - dawn::Buffer destination = - CreateBuffer(validBufferSize, dawn::BufferUsageBit::TransferDst); - TestT2BCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, - kRowPitch, 0, {kWidth, kHeight, 1}); + wgpu::Buffer destination = + CreateBuffer(validBufferSize, wgpu::BufferUsage::CopyDst); + TestT2BCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, + kBytesPerRow, 0, {kWidth, kHeight, 1}); } } } } +// Test copy from mip map of non square texture to buffer +TEST_F(CopyCommandTest_T2B, CopyFromMipmapOfNonSquareTexture) { + uint32_t maxMipmapLevel = 3; + wgpu::Texture source = Create2DTexture(4, 2, maxMipmapLevel, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopySrc); + uint64_t bufferSize = BufferSizeForTextureCopy(4, 2, 1); + wgpu::Buffer destination = CreateBuffer(bufferSize, wgpu::BufferUsage::CopyDst); + + // Copy from top level mip map + TestT2BCopy(utils::Expectation::Success, source, maxMipmapLevel - 1, {0, 0, 0}, destination, 0, + 256, 0, {1, 1, 1}); + // Copy from high level mip map + TestT2BCopy(utils::Expectation::Success, source, maxMipmapLevel - 2, {0, 0, 0}, destination, 0, + 256, 0, {2, 1, 1}); + // Mip level out of range + TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel, {0, 0, 0}, destination, 0, 256, + 0, {2, 1, 1}); + // Copy origin out of range + TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, {2, 0, 0}, destination, 0, + 256, 0, {2, 1, 1}); + // Copy size out of range + TestT2BCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, {1, 0, 0}, destination, 0, + 256, 0, {2, 1, 1}); +} + class CopyCommandTest_T2T : public CopyCommandTest {}; TEST_F(CopyCommandTest_T2T, Success) { - dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // Different copies, including some that touch the OOB condition { // Copy a region along top left boundary - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {4, 4, 1}); + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, + {4, 4, 1}); // Copy entire texture - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {16, 16, 1}); + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, + {16, 16, 1}); // Copy a region along bottom right boundary - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {8, 8, 0}, destination, 0, 0, - {8, 8, 0}, {8, 8, 1}); + TestT2TCopy(utils::Expectation::Success, source, 0, {8, 8, 0}, destination, 0, {8, 8, 0}, + {8, 8, 1}); // Copy region into mip - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 2, 0, - {0, 0, 0}, {4, 4, 1}); + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 2, {0, 0, 0}, + {4, 4, 1}); // Copy mip into region - TestT2TCopy(utils::Expectation::Success, source, 2, 0, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {4, 4, 1}); + TestT2TCopy(utils::Expectation::Success, source, 2, {0, 0, 0}, destination, 0, {0, 0, 0}, + {4, 4, 1}); // Copy between slices - TestT2TCopy(utils::Expectation::Success, source, 0, 1, {0, 0, 0}, destination, 0, 1, - {0, 0, 0}, {16, 16, 1}); + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 1}, destination, 0, {0, 0, 1}, + {16, 16, 1}); + + // Copy multiple slices (srcTextureCopyView.arrayLayer + copySize.depth == + // srcTextureCopyView.texture.arrayLayerCount) + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 2}, destination, 0, {0, 0, 0}, + {16, 16, 2}); + + // Copy multiple slices (dstTextureCopyView.arrayLayer + copySize.depth == + // dstTextureCopyView.texture.arrayLayerCount) + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 2}, + {16, 16, 2}); } // Empty copies are valid { // An empty copy - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {0, 0, 1}); + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, + {0, 0, 1}); // An empty copy with depth = 0 - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {0, 0, 0}); + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, + {0, 0, 0}); // An empty copy touching the side of the source texture - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, - {16, 16, 0}, {0, 0, 1}); + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {16, 16, 0}, + {0, 0, 1}); // An empty copy touching the side of the destination texture - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, - {16, 16, 0}, {0, 0, 1}); + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {16, 16, 0}, + {0, 0, 1}); } } TEST_F(CopyCommandTest_T2T, IncorrectUsage) { - dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // Incorrect source usage causes failure - TestT2TCopy(utils::Expectation::Failure, destination, 0, 0, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {16, 16, 1}); + TestT2TCopy(utils::Expectation::Failure, destination, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, + {16, 16, 1}); // Incorrect destination usage causes failure - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, source, 0, 0, {0, 0, 0}, + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, source, 0, {0, 0, 0}, {16, 16, 1}); } TEST_F(CopyCommandTest_T2T, OutOfBounds) { - dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 4, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // OOB on source { // x + width overflows - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {1, 0, 0}, destination, 0, 0, - {0, 0, 0}, {16, 16, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 0, {1, 0, 0}, destination, 0, {0, 0, 0}, + {16, 16, 1}); // y + height overflows - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 1, 0}, destination, 0, 0, - {0, 0, 0}, {16, 16, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 1, 0}, destination, 0, {0, 0, 0}, + {16, 16, 1}); // non-zero mip overflows - TestT2TCopy(utils::Expectation::Failure, source, 1, 0, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {9, 9, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 1, {0, 0, 0}, destination, 0, {0, 0, 0}, + {9, 9, 1}); + + // arrayLayer + depth OOB + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 3}, destination, 0, {0, 0, 0}, + {16, 16, 2}); // empty copy on non-existent mip fails - TestT2TCopy(utils::Expectation::Failure, source, 6, 0, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {0, 0, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 6, {0, 0, 0}, destination, 0, {0, 0, 0}, + {0, 0, 1}); // empty copy from non-existent slice fails - TestT2TCopy(utils::Expectation::Failure, source, 0, 2, {0, 0, 0}, destination, 0, 0, - {0, 0, 0}, {0, 0, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 4}, destination, 0, {0, 0, 0}, + {0, 0, 1}); } // OOB on destination { // x + width overflows - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, - {1, 0, 0}, {16, 16, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {1, 0, 0}, + {16, 16, 1}); // y + height overflows - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, - {0, 1, 0}, {16, 16, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 1, 0}, + {16, 16, 1}); // non-zero mip overflows - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 1, 0, - {0, 0, 0}, {9, 9, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 1, {0, 0, 0}, + {9, 9, 1}); + + // arrayLayer + depth OOB + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 3}, + {16, 16, 2}); // empty copy on non-existent mip fails - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 6, 0, - {0, 0, 0}, {0, 0, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 6, {0, 0, 0}, + {0, 0, 1}); // empty copy on non-existent slice fails - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 2, - {0, 0, 0}, {0, 0, 1}); + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 4}, + {0, 0, 1}); } } -TEST_F(CopyCommandTest_T2T, 2DTextureDepthConstraints) { - dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); - - // Empty copy on source with z > 0 fails - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 1}, destination, 0, 0, {0, 0, 0}, - {0, 0, 1}); - - // Empty copy on destination with z > 0 fails - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 1}, - {0, 0, 1}); - - // Empty copy with depth > 1 fails - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0}, - {0, 0, 2}); -} - TEST_F(CopyCommandTest_T2T, 2DTextureDepthStencil) { - dawn::Texture source = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::D32FloatS8Uint, - dawn::TextureUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 1, 1, dawn::TextureFormat::D32FloatS8Uint, - dawn::TextureUsageBit::TransferDst); + wgpu::Texture source = Create2DTexture(16, 16, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8, + wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopyDst); // Success when entire depth stencil subresource is copied - TestT2TCopy(utils::Expectation::Success, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0}, + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, {16, 16, 1}); // Failure when depth stencil subresource is partially copied - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0}, + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, {15, 15, 1}); } +TEST_F(CopyCommandTest_T2T, 2DTextureArrayDepthStencil) { + { + wgpu::Texture source = Create2DTexture( + 16, 16, 1, 3, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopyDst); + + // Success when entire depth stencil subresource (layer) is the copy source + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 1}, destination, 0, {0, 0, 0}, + {16, 16, 1}); + } + + { + wgpu::Texture source = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = Create2DTexture( + 16, 16, 1, 3, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopyDst); + + // Success when entire depth stencil subresource (layer) is the copy destination + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 1}, + {16, 16, 1}); + } + + { + wgpu::Texture source = Create2DTexture( + 16, 16, 1, 3, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = Create2DTexture( + 16, 16, 1, 3, wgpu::TextureFormat::Depth24PlusStencil8, wgpu::TextureUsage::CopyDst); + + // Success when src and dst are an entire depth stencil subresource (layer) + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 2}, destination, 0, {0, 0, 1}, + {16, 16, 1}); + + // Success when src and dst are an array of entire depth stencil subresources + TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 1}, destination, 0, {0, 0, 0}, + {16, 16, 2}); + } +} + TEST_F(CopyCommandTest_T2T, FormatsMismatch) { - dawn::Texture source = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Uint, - dawn::TextureUsageBit::TransferSrc); - dawn::Texture destination = Create2DTexture(16, 16, 5, 2, dawn::TextureFormat::R8G8B8A8Unorm, - dawn::TextureUsageBit::TransferDst); + wgpu::Texture source = + Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Uint, wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = + Create2DTexture(16, 16, 5, 2, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); // Failure when formats don't match - TestT2TCopy(utils::Expectation::Failure, source, 0, 0, {0, 0, 0}, destination, 0, 0, {0, 0, 0}, + TestT2TCopy(utils::Expectation::Failure, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0}, {0, 0, 1}); } TEST_F(CopyCommandTest_T2T, MultisampledCopies) { - dawn::Texture sourceMultiSampled1x = Create2DTexture( - 16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc, 1); - dawn::Texture sourceMultiSampled4x = Create2DTexture( - 16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferSrc, 4); - dawn::Texture destinationMultiSampled4x = Create2DTexture( - 16, 16, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm, dawn::TextureUsageBit::TransferDst, 4); + wgpu::Texture sourceMultiSampled1x = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc, 1); + wgpu::Texture sourceMultiSampled4x = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc, 4); + wgpu::Texture destinationMultiSampled4x = Create2DTexture( + 16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst, 4); // Success when entire multisampled subresource is copied { - TestT2TCopy(utils::Expectation::Success, sourceMultiSampled4x, 0, 0, {0, 0, 0}, - destinationMultiSampled4x, 0, 0, {0, 0, 0}, {16, 16, 1}); + TestT2TCopy(utils::Expectation::Success, sourceMultiSampled4x, 0, {0, 0, 0}, + destinationMultiSampled4x, 0, {0, 0, 0}, {16, 16, 1}); } // Failures { // An empty copy with mismatched samples fails - TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled1x, 0, 0, {0, 0, 0}, - destinationMultiSampled4x, 0, 0, {0, 0, 0}, {0, 0, 1}); + TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled1x, 0, {0, 0, 0}, + destinationMultiSampled4x, 0, {0, 0, 0}, {0, 0, 1}); // A copy fails when samples are greater than 1, and entire subresource isn't copied - TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled4x, 0, 0, {0, 0, 0}, - destinationMultiSampled4x, 0, 0, {0, 0, 0}, {15, 15, 1}); + TestT2TCopy(utils::Expectation::Failure, sourceMultiSampled4x, 0, {0, 0, 0}, + destinationMultiSampled4x, 0, {0, 0, 0}, {15, 15, 1}); + } +} + +// Test copy to mip map of non square textures +TEST_F(CopyCommandTest_T2T, CopyToMipmapOfNonSquareTexture) { + uint32_t maxMipmapLevel = 3; + wgpu::Texture source = Create2DTexture(4, 2, maxMipmapLevel, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopySrc); + wgpu::Texture destination = Create2DTexture( + 4, 2, maxMipmapLevel, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopyDst); + // Copy to top level mip map + TestT2TCopy(utils::Expectation::Success, source, maxMipmapLevel - 1, {0, 0, 0}, destination, + maxMipmapLevel - 1, {0, 0, 0}, {1, 1, 1}); + // Copy to high level mip map + TestT2TCopy(utils::Expectation::Success, source, maxMipmapLevel - 2, {0, 0, 0}, destination, + maxMipmapLevel - 2, {0, 0, 0}, {2, 1, 1}); + // Mip level out of range + TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel, {0, 0, 0}, destination, + maxMipmapLevel, {0, 0, 0}, {2, 1, 1}); + // Copy origin out of range + TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, {2, 0, 0}, destination, + maxMipmapLevel - 2, {2, 0, 0}, {2, 1, 1}); + // Copy size out of range + TestT2TCopy(utils::Expectation::Failure, source, maxMipmapLevel - 2, {1, 0, 0}, destination, + maxMipmapLevel - 2, {0, 0, 0}, {2, 1, 1}); +} + +// Test copy within the same texture +TEST_F(CopyCommandTest_T2T, CopyWithinSameTexture) { + wgpu::Texture texture = + Create2DTexture(32, 32, 2, 4, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst); + + // The base array layer of the copy source being equal to that of the copy destination is not + // allowed. + { + constexpr uint32_t kBaseArrayLayer = 0; + + // copyExtent.z == 1 + { + constexpr uint32_t kCopyArrayLayerCount = 1; + TestT2TCopy(utils::Expectation::Failure, texture, 0, {0, 0, kBaseArrayLayer}, texture, + 0, {2, 2, kBaseArrayLayer}, {1, 1, kCopyArrayLayerCount}); + } + + // copyExtent.z > 1 + { + constexpr uint32_t kCopyArrayLayerCount = 2; + TestT2TCopy(utils::Expectation::Failure, texture, 0, {0, 0, kBaseArrayLayer}, texture, + 0, {2, 2, kBaseArrayLayer}, {1, 1, kCopyArrayLayerCount}); + } } -} \ No newline at end of file + + // The array slices of the source involved in the copy have no overlap with those of the + // destination is allowed. + { + constexpr uint32_t kCopyArrayLayerCount = 2; + + // srcBaseArrayLayer < dstBaseArrayLayer + { + constexpr uint32_t kSrcBaseArrayLayer = 0; + constexpr uint32_t kDstBaseArrayLayer = kSrcBaseArrayLayer + kCopyArrayLayerCount; + + TestT2TCopy(utils::Expectation::Success, texture, 0, {0, 0, kSrcBaseArrayLayer}, + texture, 0, {0, 0, kDstBaseArrayLayer}, {1, 1, kCopyArrayLayerCount}); + } + + // srcBaseArrayLayer > dstBaseArrayLayer + { + constexpr uint32_t kSrcBaseArrayLayer = 2; + constexpr uint32_t kDstBaseArrayLayer = kSrcBaseArrayLayer - kCopyArrayLayerCount; + TestT2TCopy(utils::Expectation::Success, texture, 0, {0, 0, kSrcBaseArrayLayer}, + texture, 0, {0, 0, kDstBaseArrayLayer}, {1, 1, kCopyArrayLayerCount}); + } + } + + // Copy between different mipmap levels is allowed. + { + constexpr uint32_t kSrcMipLevel = 0; + constexpr uint32_t kDstMipLevel = 1; + + // Copy one slice + { + constexpr uint32_t kCopyArrayLayerCount = 1; + TestT2TCopy(utils::Expectation::Success, texture, kSrcMipLevel, {0, 0, 0}, texture, + kDstMipLevel, {1, 1, 0}, {1, 1, kCopyArrayLayerCount}); + } + + // The base array layer of the copy source is equal to that of the copy destination. + { + constexpr uint32_t kCopyArrayLayerCount = 2; + constexpr uint32_t kBaseArrayLayer = 0; + + TestT2TCopy(utils::Expectation::Success, texture, kSrcMipLevel, {0, 0, kBaseArrayLayer}, + texture, kDstMipLevel, {1, 1, kBaseArrayLayer}, + {1, 1, kCopyArrayLayerCount}); + } + + // The array slices of the source involved in the copy have overlaps with those of the + // destination, and the copy areas have overlaps. + { + constexpr uint32_t kCopyArrayLayerCount = 2; + + constexpr uint32_t kSrcBaseArrayLayer = 0; + constexpr uint32_t kDstBaseArrayLayer = 1; + ASSERT(kSrcBaseArrayLayer + kCopyArrayLayerCount > kDstBaseArrayLayer); + + constexpr wgpu::Extent3D kCopyExtent = {1, 1, kCopyArrayLayerCount}; + + TestT2TCopy(utils::Expectation::Success, texture, kSrcMipLevel, + {0, 0, kSrcBaseArrayLayer}, texture, kDstMipLevel, + {0, 0, kDstBaseArrayLayer}, kCopyExtent); + } + } + + // The array slices of the source involved in the copy have overlaps with those of the + // destination is not allowed. + { + constexpr uint32_t kMipmapLevel = 0; + constexpr uint32_t kMinBaseArrayLayer = 0; + constexpr uint32_t kMaxBaseArrayLayer = 1; + constexpr uint32_t kCopyArrayLayerCount = 3; + ASSERT(kMinBaseArrayLayer + kCopyArrayLayerCount > kMaxBaseArrayLayer); + + constexpr wgpu::Extent3D kCopyExtent = {4, 4, kCopyArrayLayerCount}; + + const wgpu::Origin3D srcOrigin = {0, 0, kMinBaseArrayLayer}; + const wgpu::Origin3D dstOrigin = {4, 4, kMaxBaseArrayLayer}; + TestT2TCopy(utils::Expectation::Failure, texture, kMipmapLevel, srcOrigin, texture, + kMipmapLevel, dstOrigin, kCopyExtent); + } + + // Copy between different mipmap levels and array slices is allowed. + TestT2TCopy(utils::Expectation::Success, texture, 0, {0, 0, 1}, texture, 1, {1, 1, 0}, + {1, 1, 1}); +} + +class CopyCommandTest_CompressedTextureFormats : public CopyCommandTest { + public: + CopyCommandTest_CompressedTextureFormats() : CopyCommandTest() { + device = CreateDeviceFromAdapter(adapter, {"texture_compression_bc"}); + } + + protected: + wgpu::Texture Create2DTexture(wgpu::TextureFormat format, + uint32_t mipmapLevels = 1, + uint32_t width = kWidth, + uint32_t height = kHeight) { + constexpr wgpu::TextureUsage kUsage = + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::Sampled; + constexpr uint32_t kArrayLayers = 1; + return CopyCommandTest::Create2DTexture(width, height, mipmapLevels, kArrayLayers, format, + kUsage, 1); + } + + static constexpr uint32_t kWidth = 16; + static constexpr uint32_t kHeight = 16; +}; + +// Tests to verify that bufferOffset must be a multiple of the compressed texture blocks in bytes +// in buffer-to-texture or texture-to-buffer copies with compressed texture formats. +TEST_F(CopyCommandTest_CompressedTextureFormats, BufferOffset) { + wgpu::Buffer buffer = + CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat); + + // Valid usages of BufferOffset in B2T and T2B copies with compressed texture formats. + { + uint32_t validBufferOffset = utils::GetTexelBlockSizeInBytes(bcFormat); + TestBothTBCopies(utils::Expectation::Success, buffer, validBufferOffset, 256, 4, + texture, 0, {0, 0, 0}, {4, 4, 1}); + } + + // Failures on invalid bufferOffset. + { + uint32_t kInvalidBufferOffset = utils::GetTexelBlockSizeInBytes(bcFormat) / 2; + TestBothTBCopies(utils::Expectation::Failure, buffer, kInvalidBufferOffset, 256, 4, + texture, 0, {0, 0, 0}, {4, 4, 1}); + } + } +} + +// Tests to verify that bytesPerRow must not be less than (width / blockWidth) * blockSizeInBytes. +// Note that in Dawn we require bytesPerRow be a multiple of 256, which ensures bytesPerRow will +// always be the multiple of compressed texture block width in bytes. +TEST_F(CopyCommandTest_CompressedTextureFormats, BytesPerRow) { + wgpu::Buffer buffer = + CreateBuffer(1024, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + + { + constexpr uint32_t kTestWidth = 160; + constexpr uint32_t kTestHeight = 160; + + // Failures on the BytesPerRow that is not large enough. + { + constexpr uint32_t kSmallBytesPerRow = 256; + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight); + TestBothTBCopies(utils::Expectation::Failure, buffer, 0, kSmallBytesPerRow, 4, + texture, 0, {0, 0, 0}, {kTestWidth, 4, 1}); + } + } + + // Test it is not valid to use a BytesPerRow that is not a multiple of 256. + { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight); + uint32_t inValidBytesPerRow = + kTestWidth / 4 * utils::GetTexelBlockSizeInBytes(bcFormat); + ASSERT_NE(0u, inValidBytesPerRow % 256); + TestBothTBCopies(utils::Expectation::Failure, buffer, 0, inValidBytesPerRow, 4, + texture, 0, {0, 0, 0}, {kTestWidth, 4, 1}); + } + } + + // Test the smallest valid BytesPerRow should work. + { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight); + uint32_t smallestValidBytesPerRow = + Align(kTestWidth / 4 * utils::GetTexelBlockSizeInBytes(bcFormat), 256); + TestBothTBCopies(utils::Expectation::Success, buffer, 0, smallestValidBytesPerRow, + 4, texture, 0, {0, 0, 0}, {kTestWidth, 4, 1}); + } + } + } +} + +// Tests to verify that rowsPerImage must be a multiple of the compressed texture block height in +// buffer-to-texture or texture-to-buffer copies with compressed texture formats. +TEST_F(CopyCommandTest_CompressedTextureFormats, ImageHeight) { + wgpu::Buffer buffer = + CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat); + + // Valid usages of rowsPerImage in B2T and T2B copies with compressed texture formats. + { + constexpr uint32_t kValidImageHeight = 8; + TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, kValidImageHeight, + texture, 0, {0, 0, 0}, {4, 4, 1}); + } + + // Failures on invalid rowsPerImage. + { + constexpr uint32_t kInvalidImageHeight = 3; + TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, kInvalidImageHeight, + texture, 0, {0, 0, 0}, {4, 4, 1}); + } + } +} + +// Tests to verify that ImageOffset.x must be a multiple of the compressed texture block width and +// ImageOffset.y must be a multiple of the compressed texture block height in buffer-to-texture, +// texture-to-buffer or texture-to-texture copies with compressed texture formats. +TEST_F(CopyCommandTest_CompressedTextureFormats, ImageOffset) { + wgpu::Buffer buffer = + CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat); + wgpu::Texture texture2 = Create2DTexture(bcFormat); + + constexpr wgpu::Origin3D kSmallestValidOrigin3D = {4, 4, 0}; + + // Valid usages of ImageOffset in B2T, T2B and T2T copies with compressed texture formats. + { + TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 4, texture, 0, + kSmallestValidOrigin3D, {4, 4, 1}); + TestBothT2TCopies(utils::Expectation::Success, texture, 0, {0, 0, 0}, texture2, 0, + kSmallestValidOrigin3D, {4, 4, 1}); + } + + // Failures on invalid ImageOffset.x. + { + constexpr wgpu::Origin3D kInvalidOrigin3D = {kSmallestValidOrigin3D.x - 1, + kSmallestValidOrigin3D.y, 0}; + TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, + kInvalidOrigin3D, {4, 4, 1}); + TestBothT2TCopies(utils::Expectation::Failure, texture, 0, kInvalidOrigin3D, texture2, + 0, {0, 0, 0}, {4, 4, 1}); + } + + // Failures on invalid ImageOffset.y. + { + constexpr wgpu::Origin3D kInvalidOrigin3D = {kSmallestValidOrigin3D.x, + kSmallestValidOrigin3D.y - 1, 0}; + TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, + kInvalidOrigin3D, {4, 4, 1}); + TestBothT2TCopies(utils::Expectation::Failure, texture, 0, kInvalidOrigin3D, texture2, + 0, {0, 0, 0}, {4, 4, 1}); + } + } +} + +// Tests to verify that ImageExtent.x must be a multiple of the compressed texture block width and +// ImageExtent.y must be a multiple of the compressed texture block height in buffer-to-texture, +// texture-to-buffer or texture-to-texture copies with compressed texture formats. +TEST_F(CopyCommandTest_CompressedTextureFormats, ImageExtent) { + wgpu::Buffer buffer = + CreateBuffer(512, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + + constexpr uint32_t kMipmapLevels = 3; + constexpr uint32_t kTestWidth = 60; + constexpr uint32_t kTestHeight = 60; + + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight); + wgpu::Texture texture2 = Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight); + + constexpr wgpu::Extent3D kSmallestValidExtent3D = {4, 4, 1}; + + // Valid usages of ImageExtent in B2T, T2B and T2T copies with compressed texture formats. + { + TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 8, texture, 0, {0, 0, 0}, + kSmallestValidExtent3D); + TestBothT2TCopies(utils::Expectation::Success, texture, 0, {0, 0, 0}, texture2, 0, + {0, 0, 0}, kSmallestValidExtent3D); + } + + // Valid usages of ImageExtent in B2T, T2B and T2T copies with compressed texture formats + // and non-zero mipmap levels. + { + constexpr uint32_t kTestMipmapLevel = 2; + constexpr wgpu::Origin3D kTestOrigin = { + (kTestWidth >> kTestMipmapLevel) - kSmallestValidExtent3D.width + 1, + (kTestHeight >> kTestMipmapLevel) - kSmallestValidExtent3D.height + 1, 0}; + + TestBothTBCopies(utils::Expectation::Success, buffer, 0, 256, 4, texture, + kTestMipmapLevel, kTestOrigin, kSmallestValidExtent3D); + TestBothT2TCopies(utils::Expectation::Success, texture, kTestMipmapLevel, kTestOrigin, + texture2, 0, {0, 0, 0}, kSmallestValidExtent3D); + } + + // Failures on invalid ImageExtent.x. + { + constexpr wgpu::Extent3D kInValidExtent3D = {kSmallestValidExtent3D.width - 1, + kSmallestValidExtent3D.height, 1}; + TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, {0, 0, 0}, + kInValidExtent3D); + TestBothT2TCopies(utils::Expectation::Failure, texture, 0, {0, 0, 0}, texture2, 0, + {0, 0, 0}, kInValidExtent3D); + } + + // Failures on invalid ImageExtent.y. + { + constexpr wgpu::Extent3D kInValidExtent3D = {kSmallestValidExtent3D.width, + kSmallestValidExtent3D.height - 1, 1}; + TestBothTBCopies(utils::Expectation::Failure, buffer, 0, 256, 4, texture, 0, {0, 0, 0}, + kInValidExtent3D); + TestBothT2TCopies(utils::Expectation::Failure, texture, 0, {0, 0, 0}, texture2, 0, + {0, 0, 0}, kInValidExtent3D); + } + } +} + +// Test copies between buffer and multiple array layers of an uncompressed texture +TEST_F(CopyCommandTest, CopyToMultipleArrayLayers) { + wgpu::Texture destination = + CopyCommandTest::Create2DTexture(4, 2, 1, 5, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); + + // Copy to all array layers + TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 0}, + {4, 2, 5}); + + // Copy to the highest array layer + TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 4}, + {4, 2, 1}); + + // Copy to array layers in the middle + TestBothTBCopiesExactBufferSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 1}, + {4, 2, 3}); + + // Copy with a non-packed rowsPerImage + TestBothTBCopiesExactBufferSize(256, 3, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 0}, + {4, 2, 5}); + + // Copy with bytesPerRow = 512 + TestBothTBCopiesExactBufferSize(512, 2, destination, wgpu::TextureFormat::RGBA8Unorm, {0, 0, 1}, + {4, 2, 3}); +} + +// Test copies between buffer and multiple array layers of a compressed texture +TEST_F(CopyCommandTest_CompressedTextureFormats, CopyToMultipleArrayLayers) { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = CopyCommandTest::Create2DTexture( + 12, 16, 1, 20, bcFormat, wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); + + // Copy to all array layers + TestBothTBCopiesExactBufferSize(256, 16, texture, bcFormat, {0, 0, 0}, {12, 16, 20}); + + // Copy to the highest array layer + TestBothTBCopiesExactBufferSize(256, 16, texture, bcFormat, {0, 0, 19}, {12, 16, 1}); + + // Copy to array layers in the middle + TestBothTBCopiesExactBufferSize(256, 16, texture, bcFormat, {0, 0, 1}, {12, 16, 18}); + + // Copy touching the texture corners with a non-packed rowsPerImage + TestBothTBCopiesExactBufferSize(256, 24, texture, bcFormat, {4, 4, 4}, {8, 12, 16}); + + // rowsPerImage needs to be a multiple of blockHeight + { + wgpu::Buffer source = + CreateBuffer(8192, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + TestBothTBCopies(utils::Expectation::Failure, source, 0, 256, 6, texture, 0, {0, 0, 0}, + {4, 4, 1}); + } + + // rowsPerImage must be a multiple of blockHeight even with an empty copy + { + wgpu::Buffer source = + CreateBuffer(0, wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst); + TestBothTBCopies(utils::Expectation::Failure, source, 0, 256, 2, texture, 0, {0, 0, 0}, + {0, 0, 0}); + } + } +} diff --git a/third_party/dawn/src/tests/unittests/validation/DebugMarkerValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/DebugMarkerValidationTests.cpp index 9993cee6ffa..49520cb9d75 100644 --- a/third_party/dawn/src/tests/unittests/validation/DebugMarkerValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/DebugMarkerValidationTests.cpp @@ -14,7 +14,7 @@ #include "tests/unittests/validation/ValidationTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class DebugMarkerValidationTest : public ValidationTest {}; @@ -22,9 +22,9 @@ class DebugMarkerValidationTest : public ValidationTest {}; TEST_F(DebugMarkerValidationTest, RenderSuccess) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.PushDebugGroup("Event Start"); pass.PushDebugGroup("Event Start"); pass.InsertDebugMarker("Marker"); @@ -40,9 +40,9 @@ TEST_F(DebugMarkerValidationTest, RenderSuccess) { TEST_F(DebugMarkerValidationTest, RenderUnbalancedPush) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.PushDebugGroup("Event Start"); pass.PushDebugGroup("Event Start"); pass.InsertDebugMarker("Marker"); @@ -57,9 +57,9 @@ TEST_F(DebugMarkerValidationTest, RenderUnbalancedPush) { TEST_F(DebugMarkerValidationTest, RenderUnbalancedPop) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.PushDebugGroup("Event Start"); pass.InsertDebugMarker("Marker"); pass.PopDebugGroup(); @@ -72,9 +72,9 @@ TEST_F(DebugMarkerValidationTest, RenderUnbalancedPop) { // Correct usage of debug markers should succeed in compute pass. TEST_F(DebugMarkerValidationTest, ComputeSuccess) { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.PushDebugGroup("Event Start"); pass.PushDebugGroup("Event Start"); pass.InsertDebugMarker("Marker"); @@ -88,9 +88,9 @@ TEST_F(DebugMarkerValidationTest, ComputeSuccess) { // A PushDebugGroup call without a following PopDebugGroup produces an error in compute pass. TEST_F(DebugMarkerValidationTest, ComputeUnbalancedPush) { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.PushDebugGroup("Event Start"); pass.PushDebugGroup("Event Start"); pass.InsertDebugMarker("Marker"); @@ -103,9 +103,9 @@ TEST_F(DebugMarkerValidationTest, ComputeUnbalancedPush) { // A PopDebugGroup call without a preceding PushDebugGroup produces an error in compute pass. TEST_F(DebugMarkerValidationTest, ComputeUnbalancedPop) { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::ComputePassEncoder pass = encoder.BeginComputePass(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); pass.PushDebugGroup("Event Start"); pass.InsertDebugMarker("Marker"); pass.PopDebugGroup(); @@ -114,4 +114,95 @@ TEST_F(DebugMarkerValidationTest, ComputeUnbalancedPop) { } ASSERT_DEVICE_ERROR(encoder.Finish()); -} \ No newline at end of file +} + +// Correct usage of debug markers should succeed in command encoder. +TEST_F(DebugMarkerValidationTest, CommandEncoderSuccess) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.PushDebugGroup("Event Start"); + encoder.PushDebugGroup("Event Start"); + encoder.InsertDebugMarker("Marker"); + encoder.PopDebugGroup(); + encoder.PopDebugGroup(); + encoder.Finish(); +} + +// A PushDebugGroup call without a following PopDebugGroup produces an error in command encoder. +TEST_F(DebugMarkerValidationTest, CommandEncoderUnbalancedPush) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.PushDebugGroup("Event Start"); + encoder.PushDebugGroup("Event Start"); + encoder.InsertDebugMarker("Marker"); + encoder.PopDebugGroup(); + ASSERT_DEVICE_ERROR(encoder.Finish()); +} + +// A PopDebugGroup call without a preceding PushDebugGroup produces an error in command encoder. +TEST_F(DebugMarkerValidationTest, CommandEncoderUnbalancedPop) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.PushDebugGroup("Event Start"); + encoder.InsertDebugMarker("Marker"); + encoder.PopDebugGroup(); + encoder.PopDebugGroup(); + ASSERT_DEVICE_ERROR(encoder.Finish()); +} + +// It is possible to nested pushes in a compute pass in a command encoder. +TEST_F(DebugMarkerValidationTest, NestedComputeInCommandEncoder) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.PushDebugGroup("Event Start"); + { + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.PushDebugGroup("Event Start"); + pass.InsertDebugMarker("Marker"); + pass.PopDebugGroup(); + pass.EndPass(); + } + encoder.PopDebugGroup(); + encoder.Finish(); +} + +// Command encoder and compute pass pushes must be balanced independently. +TEST_F(DebugMarkerValidationTest, NestedComputeInCommandEncoderIndependent) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.PushDebugGroup("Event Start"); + { + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.InsertDebugMarker("Marker"); + pass.PopDebugGroup(); + pass.EndPass(); + } + ASSERT_DEVICE_ERROR(encoder.Finish()); +} + +// It is possible to nested pushes in a render pass in a command encoder. +TEST_F(DebugMarkerValidationTest, NestedRenderInCommandEncoder) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.PushDebugGroup("Event Start"); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.PushDebugGroup("Event Start"); + pass.InsertDebugMarker("Marker"); + pass.PopDebugGroup(); + pass.EndPass(); + } + encoder.PopDebugGroup(); + encoder.Finish(); +} + +// Command encoder and render pass pushes must be balanced independently. +TEST_F(DebugMarkerValidationTest, NestedRenderInCommandEncoderIndependent) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.PushDebugGroup("Event Start"); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.InsertDebugMarker("Marker"); + pass.PopDebugGroup(); + pass.EndPass(); + } + ASSERT_DEVICE_ERROR(encoder.Finish()); +} diff --git a/third_party/dawn/src/tests/unittests/validation/DrawIndirectValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/DrawIndirectValidationTests.cpp index de3a3bbe597..4b30651416e 100644 --- a/third_party/dawn/src/tests/unittests/validation/DrawIndirectValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/DrawIndirectValidationTests.cpp @@ -16,22 +16,22 @@ #include #include "tests/unittests/validation/ValidationTest.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class DrawIndirectValidationTest : public ValidationTest { protected: void SetUp() override { ValidationTest::SetUp(); - dawn::ShaderModule vsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { gl_Position = vec4(0.0); })"); - dawn::ShaderModule fsModule = - utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { @@ -39,17 +39,17 @@ class DrawIndirectValidationTest : public ValidationTest { })"); // Set up render pipeline - dawn::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, nullptr); + wgpu::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, nullptr); utils::ComboRenderPipelineDescriptor descriptor(device); descriptor.layout = pipelineLayout; - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; pipeline = device.CreateRenderPipeline(&descriptor); } - void ValidateExpectation(dawn::CommandEncoder encoder, utils::Expectation expectation) { + void ValidateExpectation(wgpu::CommandEncoder encoder, utils::Expectation expectation) { if (expectation == utils::Expectation::Success) { encoder.Finish(); } else { @@ -73,18 +73,18 @@ class DrawIndirectValidationTest : public ValidationTest { std::initializer_list bufferList, uint64_t indirectOffset, bool indexed) { - dawn::Buffer indirectBuffer = utils::CreateBufferFromData( - device, dawn::BufferUsageBit::Indirect, bufferList); + wgpu::Buffer indirectBuffer = + utils::CreateBufferFromData(device, wgpu::BufferUsage::Indirect, bufferList); DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline); if (indexed) { uint32_t zeros[100] = {}; - dawn::Buffer indexBuffer = utils::CreateBufferFromData(device, zeros, sizeof(zeros), - dawn::BufferUsageBit::Index); - pass.SetIndexBuffer(indexBuffer, 0); + wgpu::Buffer indexBuffer = + utils::CreateBufferFromData(device, zeros, sizeof(zeros), wgpu::BufferUsage::Index); + pass.SetIndexBuffer(indexBuffer); pass.DrawIndexedIndirect(indirectBuffer, indirectOffset); } else { pass.DrawIndirect(indirectBuffer, indirectOffset); @@ -94,7 +94,7 @@ class DrawIndirectValidationTest : public ValidationTest { ValidateExpectation(encoder, expectation); } - dawn::RenderPipeline pipeline; + wgpu::RenderPipeline pipeline; }; // Verify out of bounds indirect draw calls are caught early diff --git a/third_party/dawn/src/tests/unittests/validation/DynamicStateCommandValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/DynamicStateCommandValidationTests.cpp index d76a84ce43d..cc166400a7a 100644 --- a/third_party/dawn/src/tests/unittests/validation/DynamicStateCommandValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/DynamicStateCommandValidationTests.cpp @@ -14,16 +14,224 @@ #include "tests/unittests/validation/ValidationTest.h" -class SetScissorRectTest : public ValidationTest { -}; +#include + +class SetViewportTest : public ValidationTest {}; + +// Test to check basic use of SetViewport +TEST_F(SetViewportTest, Success) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.0, 1.0); + pass.EndPass(); + } + encoder.Finish(); +} + +// Test to check that NaN in viewport parameters is not allowed +TEST_F(SetViewportTest, ViewportParameterNaN) { + DummyRenderPass renderPass(device); + + // x or y is NaN. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(NAN, 0.0, 1.0, 1.0, 0.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // width or height is NaN. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, NAN, 1.0, 0.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // minDepth or maxDepth is NaN. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 1.0, NAN, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Test to check that an empty viewport is not allowed +TEST_F(SetViewportTest, EmptyViewport) { + DummyRenderPass renderPass(device); + + // Width of viewport is zero. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 0.0, 1.0, 0.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Height of viewport is zero. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 0.0, 0.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Both width and height of viewport are zero. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 0.0, 0.0, 0.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Test to check that viewport larger than the framebuffer is allowed +TEST_F(SetViewportTest, ViewportLargerThanFramebuffer) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, renderPass.width + 1, renderPass.height + 1, 0.0, 1.0); + pass.EndPass(); + } + encoder.Finish(); +} + +// Test to check that negative x in viewport is allowed +TEST_F(SetViewportTest, NegativeX) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(-1.0, 0.0, 1.0, 1.0, 0.0, 1.0); + pass.EndPass(); + } + encoder.Finish(); +} + +// Test to check that negative y in viewport is allowed +TEST_F(SetViewportTest, NegativeY) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, -1.0, 1.0, 1.0, 0.0, 1.0); + pass.EndPass(); + } + encoder.Finish(); +} + +// Test to check that negative width in viewport is not allowed +TEST_F(SetViewportTest, NegativeWidth) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, -1.0, 1.0, 0.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Test to check that negative height in viewport is not allowed +TEST_F(SetViewportTest, NegativeHeight) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 0.0, -1.0, 0.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Test to check that minDepth out of range [0, 1] is not allowed +TEST_F(SetViewportTest, MinDepthOutOfRange) { + DummyRenderPass renderPass(device); + + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 1.0, -1.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 1.0, 2.0, 1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Test to check that maxDepth out of range [0, 1] is not allowed +TEST_F(SetViewportTest, MaxDepthOutOfRange) { + DummyRenderPass renderPass(device); + + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.0, -1.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.0, 2.0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Test to check that minDepth equal or greater than maxDepth is allowed +TEST_F(SetViewportTest, MinDepthEqualOrGreaterThanMaxDepth) { + DummyRenderPass renderPass(device); + + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.5, 0.5); + pass.EndPass(); + encoder.Finish(); + } + + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.8, 0.5); + pass.EndPass(); + encoder.Finish(); + } +} + +class SetScissorRectTest : public ValidationTest {}; // Test to check basic use of SetScissor TEST_F(SetScissorRectTest, Success) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetScissorRect(0, 0, 1, 1); pass.EndPass(); } @@ -36,8 +244,8 @@ TEST_F(SetScissorRectTest, EmptyScissor) { // Width of scissor rect is zero. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetScissorRect(0, 0, 0, 1); pass.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); @@ -45,8 +253,8 @@ TEST_F(SetScissorRectTest, EmptyScissor) { // Height of scissor rect is zero. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetScissorRect(0, 0, 1, 0); pass.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); @@ -54,8 +262,8 @@ TEST_F(SetScissorRectTest, EmptyScissor) { // Both width and height of scissor rect are zero. { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetScissorRect(0, 0, 0, 0); pass.EndPass(); ASSERT_DEVICE_ERROR(encoder.Finish()); @@ -66,26 +274,25 @@ TEST_F(SetScissorRectTest, EmptyScissor) { TEST_F(SetScissorRectTest, ScissorLargerThanFramebuffer) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetScissorRect(0, 0, renderPass.width + 1, renderPass.height + 1); pass.EndPass(); } encoder.Finish(); } -class SetBlendColorTest : public ValidationTest { -}; +class SetBlendColorTest : public ValidationTest {}; // Test to check basic use of SetBlendColor TEST_F(SetBlendColorTest, Success) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); - constexpr dawn::Color kTransparentBlack{0.0f, 0.0f, 0.0f, 0.0f}; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + constexpr wgpu::Color kTransparentBlack{0.0f, 0.0f, 0.0f, 0.0f}; pass.SetBlendColor(&kTransparentBlack); pass.EndPass(); } @@ -96,26 +303,25 @@ TEST_F(SetBlendColorTest, Success) { TEST_F(SetBlendColorTest, AnyValueAllowed) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); - constexpr dawn::Color kAnyColorValue{-1.0f, 42.0f, -0.0f, 0.0f}; + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + constexpr wgpu::Color kAnyColorValue{-1.0f, 42.0f, -0.0f, 0.0f}; pass.SetBlendColor(&kAnyColorValue); pass.EndPass(); } encoder.Finish(); } -class SetStencilReferenceTest : public ValidationTest { -}; +class SetStencilReferenceTest : public ValidationTest {}; // Test to check basic use of SetStencilReferenceTest TEST_F(SetStencilReferenceTest, Success) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetStencilReference(0); pass.EndPass(); } @@ -126,9 +332,9 @@ TEST_F(SetStencilReferenceTest, Success) { TEST_F(SetStencilReferenceTest, AllBitsAllowed) { DummyRenderPass renderPass(device); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetStencilReference(0xFFFFFFFF); pass.EndPass(); } diff --git a/third_party/dawn/src/tests/unittests/validation/ErrorScopeValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/ErrorScopeValidationTests.cpp new file mode 100644 index 00000000000..5bcbac1f05c --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/ErrorScopeValidationTests.cpp @@ -0,0 +1,206 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include + +using namespace testing; + +class MockDevicePopErrorScopeCallback { + public: + MOCK_METHOD(void, Call, (WGPUErrorType type, const char* message, void* userdata)); +}; + +static std::unique_ptr mockDevicePopErrorScopeCallback; +static void ToMockDevicePopErrorScopeCallback(WGPUErrorType type, + const char* message, + void* userdata) { + mockDevicePopErrorScopeCallback->Call(type, message, userdata); +} + +class ErrorScopeValidationTest : public ValidationTest { + private: + void SetUp() override { + ValidationTest::SetUp(); + mockDevicePopErrorScopeCallback = std::make_unique(); + } + + void TearDown() override { + // Delete mocks so that expectations are checked + mockDevicePopErrorScopeCallback = nullptr; + ValidationTest::TearDown(); + } +}; + +// Test the simple success case. +TEST_F(ErrorScopeValidationTest, Success) { + device.PushErrorScope(wgpu::ErrorFilter::Validation); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); +} + +// Test the simple case where the error scope catches an error. +TEST_F(ErrorScopeValidationTest, CatchesError) { + device.PushErrorScope(wgpu::ErrorFilter::Validation); + + wgpu::BufferDescriptor desc = {}; + desc.usage = static_cast(WGPUBufferUsage_Force32); + device.CreateBuffer(&desc); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_Validation, _, this)).Times(1); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); +} + +// Test that errors bubble to the parent scope if not handled by the current scope. +TEST_F(ErrorScopeValidationTest, ErrorBubbles) { + device.PushErrorScope(wgpu::ErrorFilter::Validation); + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + + wgpu::BufferDescriptor desc = {}; + desc.usage = static_cast(WGPUBufferUsage_Force32); + device.CreateBuffer(&desc); + + // OutOfMemory does not match Validation error. + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); + + // Parent validation error scope captures the error. + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_Validation, _, this + 1)) + .Times(1); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1); +} + +// Test that if an error scope matches an error, it does not bubble to the parent scope. +TEST_F(ErrorScopeValidationTest, HandledErrorsStopBubbling) { + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + device.PushErrorScope(wgpu::ErrorFilter::Validation); + + wgpu::BufferDescriptor desc = {}; + desc.usage = static_cast(WGPUBufferUsage_Force32); + device.CreateBuffer(&desc); + + // Inner scope catches the error. + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_Validation, _, this)).Times(1); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); + + // Parent scope does not see the error. + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this + 1)) + .Times(1); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1); +} + +// Test that if no error scope handles an error, it goes to the device UncapturedError callback +TEST_F(ErrorScopeValidationTest, UnhandledErrorsMatchUncapturedErrorCallback) { + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + + wgpu::BufferDescriptor desc = {}; + desc.usage = static_cast(WGPUBufferUsage_Force32); + ASSERT_DEVICE_ERROR(device.CreateBuffer(&desc)); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); +} + +// Check that push/popping error scopes must be balanced. +TEST_F(ErrorScopeValidationTest, PushPopBalanced) { + // No error scopes to pop. + { EXPECT_FALSE(device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this)); } + + // Too many pops + { + device.PushErrorScope(wgpu::ErrorFilter::Validation); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this + 1)) + .Times(1); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1); + + EXPECT_FALSE(device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 2)); + } +} + +// Test that error scopes do not call their callbacks until after an enclosed Queue::Submit +// completes +TEST_F(ErrorScopeValidationTest, CallbackAfterQueueSubmit) { + wgpu::Queue queue = device.GetDefaultQueue(); + + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + queue.Submit(0, nullptr); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1); + + // Side effects of Queue::Submit only are seen after Tick() + device.Tick(); +} + +// Test that parent error scopes do not call their callbacks until after an enclosed Queue::Submit +// completes +TEST_F(ErrorScopeValidationTest, CallbackAfterQueueSubmitNested) { + wgpu::Queue queue = device.GetDefaultQueue(); + + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + queue.Submit(0, nullptr); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1); + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this + 1)) + .Times(1); + + // Side effects of Queue::Submit only are seen after Tick() + device.Tick(); +} + +// Test a callback that returns asynchronously followed by a synchronous one +TEST_F(ErrorScopeValidationTest, AsynchronousThenSynchronous) { + wgpu::Queue queue = device.GetDefaultQueue(); + + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + queue.Submit(0, nullptr); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this + 1)) + .Times(1); + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this + 1); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1); + + // Side effects of Queue::Submit only are seen after Tick() + device.Tick(); +} + +// Test that if the device is destroyed before the callback occurs, it is called with NoError +// because all previous operations are waited upon before the destruction returns. +TEST_F(ErrorScopeValidationTest, DeviceDestroyedBeforeCallback) { + wgpu::Queue queue = device.GetDefaultQueue(); + + device.PushErrorScope(wgpu::ErrorFilter::OutOfMemory); + queue.Submit(0, nullptr); + device.PopErrorScope(ToMockDevicePopErrorScopeCallback, this); + + EXPECT_CALL(*mockDevicePopErrorScopeCallback, Call(WGPUErrorType_NoError, _, this)).Times(1); + device = nullptr; +} + +// Regression test that on device shutdown, we don't get a recursion in O(pushed error scope) that +// would lead to a stack overflow +TEST_F(ErrorScopeValidationTest, ShutdownStackOverflow) { + for (size_t i = 0; i < 1'000'000; i++) { + device.PushErrorScope(wgpu::ErrorFilter::Validation); + } +} diff --git a/third_party/dawn/src/tests/unittests/validation/FenceValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/FenceValidationTests.cpp index 8cb1787b3ae..127ea02fe8f 100644 --- a/third_party/dawn/src/tests/unittests/validation/FenceValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/FenceValidationTests.cpp @@ -20,23 +20,23 @@ using namespace testing; class MockFenceOnCompletionCallback { public: - MOCK_METHOD2(Call, void(DawnFenceCompletionStatus status, void* userdata)); + MOCK_METHOD(void, Call, (WGPUFenceCompletionStatus status, void* userdata)); }; struct FenceOnCompletionExpectation { - dawn::Fence fence; + wgpu::Fence fence; uint64_t value; - DawnFenceCompletionStatus status; + WGPUFenceCompletionStatus status; }; static std::unique_ptr mockFenceOnCompletionCallback; -static void ToMockFenceOnCompletionCallback(DawnFenceCompletionStatus status, void* userdata) { +static void ToMockFenceOnCompletionCallback(WGPUFenceCompletionStatus status, void* userdata) { mockFenceOnCompletionCallback->Call(status, userdata); } class FenceValidationTest : public ValidationTest { protected: - void TestOnCompletion(dawn::Fence fence, uint64_t value, DawnFenceCompletionStatus status) { + void TestOnCompletion(wgpu::Fence fence, uint64_t value, WGPUFenceCompletionStatus status) { FenceOnCompletionExpectation* expectation = new FenceOnCompletionExpectation; expectation->fence = fence; expectation->value = value; @@ -50,14 +50,14 @@ class FenceValidationTest : public ValidationTest { device.Tick(); } - dawn::Queue queue; + wgpu::Queue queue; private: void SetUp() override { ValidationTest::SetUp(); mockFenceOnCompletionCallback = std::make_unique(); - queue = device.CreateQueue(); + queue = device.GetDefaultQueue(); } void TearDown() override { @@ -72,18 +72,24 @@ class FenceValidationTest : public ValidationTest { TEST_F(FenceValidationTest, CreationSuccess) { // Success { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 0; queue.CreateFence(&descriptor); } } +// Creation succeeds if no descriptor is provided +TEST_F(FenceValidationTest, DefaultDescriptor) { + wgpu::Fence fence = queue.CreateFence(); + EXPECT_EQ(fence.GetCompletedValue(), 0u); +} + TEST_F(FenceValidationTest, GetCompletedValue) { // Starts at initial value { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); EXPECT_EQ(fence.GetCompletedValue(), 1u); } } @@ -91,35 +97,33 @@ TEST_F(FenceValidationTest, GetCompletedValue) { // Test that OnCompletion handlers are called immediately for // already completed fence values TEST_F(FenceValidationTest, OnCompletionImmediate) { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 0)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0)) .Times(1); fence.OnCompletion(0u, ToMockFenceOnCompletionCallback, this + 0); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 1)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 1)) .Times(1); fence.OnCompletion(1u, ToMockFenceOnCompletionCallback, this + 1); } // Test setting OnCompletion handlers for values > signaled value TEST_F(FenceValidationTest, OnCompletionLargerThanSignaled) { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); // Cannot signal for values > signaled value - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, nullptr)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Error, nullptr)) .Times(1); ASSERT_DEVICE_ERROR(fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, nullptr)); // Can set handler after signaling queue.Signal(fence, 2); - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, nullptr)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr)) .Times(1); fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, nullptr); @@ -127,14 +131,14 @@ TEST_F(FenceValidationTest, OnCompletionLargerThanSignaled) { } TEST_F(FenceValidationTest, GetCompletedValueInsideCallback) { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); queue.Signal(fence, 3); fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, nullptr); - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, nullptr)) - .WillOnce(Invoke([&](DawnFenceCompletionStatus status, void* userdata) { + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr)) + .WillOnce(Invoke([&](WGPUFenceCompletionStatus status, void* userdata) { EXPECT_EQ(fence.GetCompletedValue(), 3u); })); @@ -142,13 +146,13 @@ TEST_F(FenceValidationTest, GetCompletedValueInsideCallback) { } TEST_F(FenceValidationTest, GetCompletedValueAfterCallback) { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); queue.Signal(fence, 2); fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, nullptr); - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, nullptr)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr)) .Times(1); Flush(); @@ -156,9 +160,9 @@ TEST_F(FenceValidationTest, GetCompletedValueAfterCallback) { } TEST_F(FenceValidationTest, SignalError) { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); // value < fence signaled value ASSERT_DEVICE_ERROR(queue.Signal(fence, 0)); @@ -168,9 +172,9 @@ TEST_F(FenceValidationTest, SignalError) { } TEST_F(FenceValidationTest, SignalSuccess) { - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); // Success queue.Signal(fence, 2); @@ -184,23 +188,25 @@ TEST_F(FenceValidationTest, SignalSuccess) { } // Test it is invalid to signal a fence on a different queue than it was created on -TEST_F(FenceValidationTest, SignalWrongQueue) { - dawn::Queue queue2 = device.CreateQueue(); +// DISABLED until we have support for multiple queues +TEST_F(FenceValidationTest, DISABLED_SignalWrongQueue) { + wgpu::Queue queue2 = device.GetDefaultQueue(); - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); ASSERT_DEVICE_ERROR(queue2.Signal(fence, 2)); } // Test that signaling a fence on a wrong queue does not update fence signaled value -TEST_F(FenceValidationTest, SignalWrongQueueDoesNotUpdateValue) { - dawn::Queue queue2 = device.CreateQueue(); +// DISABLED until we have support for multiple queues +TEST_F(FenceValidationTest, DISABLED_SignalWrongQueueDoesNotUpdateValue) { + wgpu::Queue queue2 = device.GetDefaultQueue(); - dawn::FenceDescriptor descriptor; + wgpu::FenceDescriptor descriptor; descriptor.initialValue = 1; - dawn::Fence fence = queue.CreateFence(&descriptor); + wgpu::Fence fence = queue.CreateFence(&descriptor); ASSERT_DEVICE_ERROR(queue2.Signal(fence, 2)); diff --git a/third_party/dawn/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp new file mode 100644 index 00000000000..f4a8ff0eb3f --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/GetBindGroupLayoutValidationTests.cpp @@ -0,0 +1,677 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class GetBindGroupLayoutTests : public ValidationTest { + protected: + static constexpr wgpu::ShaderStage kVisibilityAll = + wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Vertex; + + wgpu::RenderPipeline RenderPipelineFromFragmentShader(const char* shader) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + })"); + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, shader); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + + return device.CreateRenderPipeline(&descriptor); + } +}; + +// Test that GetBindGroupLayout returns the same object for the same index +// and for matching layouts. +TEST_F(GetBindGroupLayoutTests, SameObject) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform UniformBuffer1 { + vec4 pos1; + }; + + layout(set = 1, binding = 0) uniform UniformBuffer2 { + vec4 pos2; + }; + + void main() { + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 2, binding = 0) uniform UniformBuffer3 { + vec4 pos3; + }; + + layout(set = 3, binding = 0) buffer StorageBuffer { + mat4 pos4; + }; + + void main() { + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor); + + EXPECT_EQ(pipeline.GetBindGroupLayout(0).Get(), pipeline.GetBindGroupLayout(0).Get()); + + EXPECT_EQ(pipeline.GetBindGroupLayout(1).Get(), pipeline.GetBindGroupLayout(1).Get()); + + EXPECT_EQ(pipeline.GetBindGroupLayout(0).Get(), pipeline.GetBindGroupLayout(1).Get()); + + EXPECT_EQ(pipeline.GetBindGroupLayout(0).Get(), pipeline.GetBindGroupLayout(2).Get()); + + EXPECT_NE(pipeline.GetBindGroupLayout(0).Get(), pipeline.GetBindGroupLayout(3).Get()); +} + +// Test that getBindGroupLayout defaults are correct +// - shader stage visibility is All +// - dynamic offsets is false +TEST_F(GetBindGroupLayoutTests, DefaultShaderStageAndDynamicOffsets) { + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform UniformBuffer { + vec4 pos; + }; + + void main() { + })"); + + wgpu::BindGroupLayoutEntry binding = {}; + binding.binding = 0; + binding.type = wgpu::BindingType::UniformBuffer; + binding.multisampled = false; + binding.minBufferBindingSize = 4 * sizeof(float); + + wgpu::BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 1; + desc.entries = &binding; + + // Check that visibility and dynamic offsets match + binding.hasDynamicOffset = false; + binding.visibility = kVisibilityAll; + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + + // Check that any change in visibility doesn't match. + binding.visibility = wgpu::ShaderStage::Vertex; + EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + + binding.visibility = wgpu::ShaderStage::Fragment; + EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + + binding.visibility = wgpu::ShaderStage::Compute; + EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + + // Check that any change in hasDynamicOffsets doesn't match. + binding.hasDynamicOffset = true; + binding.visibility = kVisibilityAll; + EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); +} + +// Test GetBindGroupLayout works with a compute pipeline +TEST_F(GetBindGroupLayoutTests, ComputePipeline) { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(set = 0, binding = 0) uniform UniformBuffer { + vec4 pos; + }; + void main() { + })"); + + wgpu::ComputePipelineDescriptor descriptor; + descriptor.layout = nullptr; + descriptor.computeStage.module = csModule; + descriptor.computeStage.entryPoint = "main"; + + wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&descriptor); + + wgpu::BindGroupLayoutEntry binding = {}; + binding.binding = 0; + binding.type = wgpu::BindingType::UniformBuffer; + binding.visibility = kVisibilityAll; + binding.hasDynamicOffset = false; + binding.minBufferBindingSize = 4 * sizeof(float); + + wgpu::BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 1; + desc.entries = &binding; + + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); +} + +// Test that the binding type matches the shader. +TEST_F(GetBindGroupLayoutTests, BindingType) { + wgpu::BindGroupLayoutEntry binding = {}; + binding.binding = 0; + binding.hasDynamicOffset = false; + binding.multisampled = false; + binding.minBufferBindingSize = 4 * sizeof(float); + + wgpu::BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 1; + desc.entries = &binding; + + { + // Storage buffer binding is not supported in vertex shader. + binding.visibility = wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment; + binding.type = wgpu::BindingType::StorageBuffer; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) buffer Storage { + vec4 pos; + }; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + binding.visibility = kVisibilityAll; + { + binding.type = wgpu::BindingType::UniformBuffer; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform Buffer { + vec4 pos; + }; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.type = wgpu::BindingType::ReadonlyStorageBuffer; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) readonly buffer Storage { + vec4 pos; + }; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + binding.minBufferBindingSize = 0; + { + binding.type = wgpu::BindingType::SampledTexture; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2D tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.type = wgpu::BindingType::Sampler; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform sampler samp; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } +} + +// Test that multisampling matches the shader. +TEST_F(GetBindGroupLayoutTests, Multisampled) { + wgpu::BindGroupLayoutEntry binding = {}; + binding.binding = 0; + binding.type = wgpu::BindingType::SampledTexture; + binding.visibility = kVisibilityAll; + binding.hasDynamicOffset = false; + + wgpu::BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 1; + desc.entries = &binding; + + { + binding.multisampled = false; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2D tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.multisampled = true; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2DMS tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } +} + +// Test that texture view dimension matches the shader. +TEST_F(GetBindGroupLayoutTests, ViewDimension) { + wgpu::BindGroupLayoutEntry binding = {}; + binding.binding = 0; + binding.type = wgpu::BindingType::SampledTexture; + binding.visibility = kVisibilityAll; + binding.hasDynamicOffset = false; + binding.multisampled = false; + + wgpu::BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 1; + desc.entries = &binding; + + { + binding.viewDimension = wgpu::TextureViewDimension::e1D; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform texture1D tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.viewDimension = wgpu::TextureViewDimension::e2D; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2D tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.viewDimension = wgpu::TextureViewDimension::e2DArray; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2DArray tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.viewDimension = wgpu::TextureViewDimension::e3D; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform texture3D tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.viewDimension = wgpu::TextureViewDimension::Cube; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform textureCube tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.viewDimension = wgpu::TextureViewDimension::CubeArray; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform textureCubeArray tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } +} + +// Test that texture component type matches the shader. +TEST_F(GetBindGroupLayoutTests, TextureComponentType) { + wgpu::BindGroupLayoutEntry binding = {}; + binding.binding = 0; + binding.type = wgpu::BindingType::SampledTexture; + binding.visibility = kVisibilityAll; + binding.hasDynamicOffset = false; + binding.multisampled = false; + + wgpu::BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 1; + desc.entries = &binding; + + { + binding.textureComponentType = wgpu::TextureComponentType::Float; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2D tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.textureComponentType = wgpu::TextureComponentType::Sint; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform itexture2D tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.textureComponentType = wgpu::TextureComponentType::Uint; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform utexture2D tex; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } +} + +// Test that binding= indices match. +TEST_F(GetBindGroupLayoutTests, BindingIndices) { + wgpu::BindGroupLayoutEntry binding = {}; + binding.type = wgpu::BindingType::UniformBuffer; + binding.visibility = kVisibilityAll; + binding.hasDynamicOffset = false; + binding.multisampled = false; + binding.minBufferBindingSize = 4 * sizeof(float); + + wgpu::BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 1; + desc.entries = &binding; + + { + binding.binding = 0; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform Buffer { + vec4 pos; + }; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.binding = 1; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 1) uniform Buffer { + vec4 pos; + }; + + void main() {})"); + EXPECT_EQ(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } + + { + binding.binding = 2; + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 1) uniform Buffer { + vec4 pos; + }; + + void main() {})"); + EXPECT_NE(device.CreateBindGroupLayout(&desc).Get(), pipeline.GetBindGroupLayout(0).Get()); + } +} + +// Test it is valid to have duplicate bindings in the shaders. +TEST_F(GetBindGroupLayoutTests, DuplicateBinding) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform UniformBuffer1 { + vec4 pos1; + }; + + layout(set = 1, binding = 0) uniform UniformBuffer2 { + vec4 pos2; + }; + + void main() {})"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 1, binding = 0) uniform UniformBuffer3 { + vec4 pos3; + }; + + void main() {})"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + + device.CreateRenderPipeline(&descriptor); +} + +// Test it is invalid to have conflicting binding types in the shaders. +TEST_F(GetBindGroupLayoutTests, ConflictingBindingType) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform UniformBuffer { + vec4 pos; + }; + + void main() {})"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0) buffer StorageBuffer { + vec4 pos; + }; + + void main() {})"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); +} + +// Test it is invalid to have conflicting binding texture multisampling in the shaders. +// TODO: Support multisampling +TEST_F(GetBindGroupLayoutTests, DISABLED_ConflictingBindingTextureMultisampling) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2D tex; + + void main() {})"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2DMS tex; + + void main() {})"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); +} + +// Test it is invalid to have conflicting binding texture dimension in the shaders. +TEST_F(GetBindGroupLayoutTests, ConflictingBindingViewDimension) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2D tex; + + void main() {})"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0) uniform texture3D tex; + + void main() {})"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); +} + +// Test it is invalid to have conflicting binding texture component type in the shaders. +TEST_F(GetBindGroupLayoutTests, ConflictingBindingTextureComponentType) { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2D tex; + + void main() {})"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0) uniform utexture2D tex; + + void main() {})"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); +} + +// Test it is an error to query an out of range bind group layout. +TEST_F(GetBindGroupLayoutTests, OutOfRangeIndex) { + ASSERT_DEVICE_ERROR(RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform Buffer1 { + vec4 pos1; + }; + void main() {})") + .GetBindGroupLayout(kMaxBindGroups)); + + ASSERT_DEVICE_ERROR(RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform Buffer1 { + vec4 pos1; + }; + void main() {})") + .GetBindGroupLayout(kMaxBindGroups + 1)); +} + +// Test that unused indices return the empty bind group layout. +TEST_F(GetBindGroupLayoutTests, UnusedIndex) { + wgpu::RenderPipeline pipeline = RenderPipelineFromFragmentShader(R"( + #version 450 + layout(set = 0, binding = 0) uniform Buffer1 { + vec4 pos1; + }; + + layout(set = 2, binding = 0) uniform Buffer2 { + vec4 pos2; + }; + + void main() {})"); + + wgpu::BindGroupLayoutDescriptor desc = {}; + desc.entryCount = 0; + desc.entries = nullptr; + + wgpu::BindGroupLayout emptyBindGroupLayout = device.CreateBindGroupLayout(&desc); + + EXPECT_NE(pipeline.GetBindGroupLayout(0).Get(), emptyBindGroupLayout.Get()); // Used + EXPECT_EQ(pipeline.GetBindGroupLayout(1).Get(), emptyBindGroupLayout.Get()); // Not Used. + EXPECT_NE(pipeline.GetBindGroupLayout(2).Get(), emptyBindGroupLayout.Get()); // Used. + EXPECT_EQ(pipeline.GetBindGroupLayout(3).Get(), emptyBindGroupLayout.Get()); // Not used +} + +// Test that after explicitly creating a pipeline with a pipeline layout, calling +// GetBindGroupLayout reflects the same bind group layouts. +TEST_F(GetBindGroupLayoutTests, Reflection) { + wgpu::BindGroupLayoutEntry binding = {}; + binding.binding = 0; + binding.type = wgpu::BindingType::UniformBuffer; + binding.visibility = wgpu::ShaderStage::Vertex; + + wgpu::BindGroupLayoutDescriptor bglDesc = {}; + bglDesc.entryCount = 1; + bglDesc.entries = &binding; + + wgpu::BindGroupLayout bindGroupLayout = device.CreateBindGroupLayout(&bglDesc); + + wgpu::PipelineLayoutDescriptor pipelineLayoutDesc = {}; + pipelineLayoutDesc.bindGroupLayoutCount = 1; + pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout; + + wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc); + + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform Buffer1 { + vec4 pos1; + }; + + void main() { + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + void main() { + })"); + + utils::ComboRenderPipelineDescriptor pipelineDesc(device); + pipelineDesc.layout = pipelineLayout; + pipelineDesc.vertexStage.module = vsModule; + pipelineDesc.cFragmentStage.module = fsModule; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDesc); + + EXPECT_EQ(pipeline.GetBindGroupLayout(0).Get(), bindGroupLayout.Get()); + + { + wgpu::BindGroupLayoutDescriptor emptyDesc = {}; + emptyDesc.entryCount = 0; + emptyDesc.entries = nullptr; + + wgpu::BindGroupLayout emptyBindGroupLayout = device.CreateBindGroupLayout(&emptyDesc); + + // Check that the rest of the bind group layouts reflect the empty one. + EXPECT_EQ(pipeline.GetBindGroupLayout(1).Get(), emptyBindGroupLayout.Get()); + EXPECT_EQ(pipeline.GetBindGroupLayout(2).Get(), emptyBindGroupLayout.Get()); + EXPECT_EQ(pipeline.GetBindGroupLayout(3).Get(), emptyBindGroupLayout.Get()); + } +} diff --git a/third_party/dawn/src/tests/unittests/validation/IndexBufferValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/IndexBufferValidationTests.cpp new file mode 100644 index 00000000000..32ea15375d4 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/IndexBufferValidationTests.cpp @@ -0,0 +1,94 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include "utils/ComboRenderBundleEncoderDescriptor.h" + +class IndexBufferValidationTest : public ValidationTest {}; + +// Test that for OOB validation of index buffer offset and size. +TEST_F(IndexBufferValidationTest, IndexBufferOffsetOOBValidation) { + wgpu::BufferDescriptor bufferDesc; + bufferDesc.usage = wgpu::BufferUsage::Index; + bufferDesc.size = 256; + wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc); + + DummyRenderPass renderPass(device); + // Control case, using the full buffer, with or without an explicit size is valid. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + // Explicit size + pass.SetIndexBuffer(buffer, 0, 256); + // Implicit size + pass.SetIndexBuffer(buffer, 0, 0); + pass.SetIndexBuffer(buffer, 256 - 4, 0); + pass.SetIndexBuffer(buffer, 4, 0); + // Implicit size of zero + pass.SetIndexBuffer(buffer, 256, 0); + pass.EndPass(); + encoder.Finish(); + } + + // Bad case, offset + size is larger than the buffer + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetIndexBuffer(buffer, 4, 256); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Bad case, size is 0 but the offset is larger than the buffer + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetIndexBuffer(buffer, 256 + 4, 0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 1; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + + // Control case, using the full buffer, with or without an explicit size is valid. + { + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc); + // Explicit size + encoder.SetIndexBuffer(buffer, 0, 256); + // Implicit size + encoder.SetIndexBuffer(buffer, 0, 0); + encoder.SetIndexBuffer(buffer, 256 - 4, 0); + encoder.SetIndexBuffer(buffer, 4, 0); + // Implicit size of zero + encoder.SetIndexBuffer(buffer, 256, 0); + encoder.Finish(); + } + + // Bad case, offset + size is larger than the buffer + { + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc); + encoder.SetIndexBuffer(buffer, 4, 256); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Bad case, size is 0 but the offset is larger than the buffer + { + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc); + encoder.SetIndexBuffer(buffer, 256 + 4, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} diff --git a/third_party/dawn/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp new file mode 100644 index 00000000000..b3ba5710a30 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/MinimumBufferSizeValidationTests.cpp @@ -0,0 +1,587 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include "common/Assert.h" +#include "common/Constants.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +namespace { + // Helper for describing bindings throughout the tests + struct BindingDescriptor { + uint32_t set; + uint32_t binding; + std::string text; + uint64_t size; + wgpu::BindingType type = wgpu::BindingType::StorageBuffer; + wgpu::ShaderStage visibility = wgpu::ShaderStage::Compute | wgpu::ShaderStage::Fragment; + }; + + // Runs |func| with a modified version of |originalSizes| as an argument, adding |offset| to + // each element one at a time This is useful to verify some behavior happens if any element is + // offset from original + template + void WithEachSizeOffsetBy(int64_t offset, const std::vector& originalSizes, F func) { + std::vector modifiedSizes = originalSizes; + for (size_t i = 0; i < originalSizes.size(); ++i) { + if (offset < 0) { + ASSERT(originalSizes[i] >= static_cast(-offset)); + } + // Run the function with an element offset, and restore element afterwards + modifiedSizes[i] += offset; + func(modifiedSizes); + modifiedSizes[i] -= offset; + } + } + + // Runs |func| with |correctSizes|, and an expectation of success and failure + template + void CheckSizeBounds(const std::vector& correctSizes, F func) { + // To validate size: + // Check invalid with bind group with one less + // Check valid with bind group with correct size + + // Make sure (every size - 1) produces an error + WithEachSizeOffsetBy(-1, correctSizes, + [&](const std::vector& sizes) { func(sizes, false); }); + + // Make sure correct sizes work + func(correctSizes, true); + + // Make sure (every size + 1) works + WithEachSizeOffsetBy(1, correctSizes, + [&](const std::vector& sizes) { func(sizes, true); }); + } + + // Convert binding type to a glsl string + std::string BindingTypeToStr(wgpu::BindingType type) { + switch (type) { + case wgpu::BindingType::UniformBuffer: + return "uniform"; + case wgpu::BindingType::StorageBuffer: + return "buffer"; + case wgpu::BindingType::ReadonlyStorageBuffer: + return "readonly buffer"; + default: + UNREACHABLE(); + return ""; + } + } + + // Creates a bind group with given bindings for shader text + std::string GenerateBindingString(const std::string& layout, + const std::vector& bindings) { + std::ostringstream ostream; + size_t ctr = 0; + for (const BindingDescriptor& b : bindings) { + ostream << "layout(" << layout << ", set = " << b.set << ", binding = " << b.binding + << ") " << BindingTypeToStr(b.type) << " b" << ctr++ << "{\n" + << b.text << ";\n};\n"; + } + return ostream.str(); + } + + // Used for adding custom types available throughout the tests + static const std::string kStructs = "struct ThreeFloats{float f1; float f2; float f3;};\n"; + + // Creates a compute shader with given bindings + std::string CreateComputeShaderWithBindings(const std::string& layoutType, + const std::vector& bindings) { + return R"( + #version 450 + layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + )" + + kStructs + GenerateBindingString(layoutType, bindings) + "void main() {}"; + } + + // Creates a vertex shader with given bindings + std::string CreateVertexShaderWithBindings(const std::string& layoutType, + const std::vector& bindings) { + return "#version 450\n" + kStructs + GenerateBindingString(layoutType, bindings) + + "void main() {}"; + } + + // Creates a fragment shader with given bindings + std::string CreateFragmentShaderWithBindings(const std::string& layoutType, + const std::vector& bindings) { + return R"( + #version 450 + layout(location = 0) out vec4 fragColor; + )" + + kStructs + GenerateBindingString(layoutType, bindings) + "void main() {}"; + } + + // Concatenates vectors containing BindingDescriptor + std::vector CombineBindings( + std::initializer_list> bindings) { + std::vector result; + for (const std::vector& b : bindings) { + result.insert(result.end(), b.begin(), b.end()); + } + return result; + } +} // namespace + +class MinBufferSizeTestsBase : public ValidationTest { + public: + void SetUp() override { + ValidationTest::SetUp(); + } + + wgpu::Buffer CreateBuffer(uint64_t bufferSize, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor bufferDescriptor; + bufferDescriptor.size = bufferSize; + bufferDescriptor.usage = usage; + + return device.CreateBuffer(&bufferDescriptor); + } + + // Creates compute pipeline given a layout and shader + wgpu::ComputePipeline CreateComputePipeline(const std::vector& layouts, + const std::string& shader) { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, shader.c_str()); + + wgpu::ComputePipelineDescriptor csDesc; + csDesc.layout = nullptr; + if (!layouts.empty()) { + wgpu::PipelineLayoutDescriptor descriptor; + descriptor.bindGroupLayoutCount = layouts.size(); + descriptor.bindGroupLayouts = layouts.data(); + csDesc.layout = device.CreatePipelineLayout(&descriptor); + } + csDesc.computeStage.module = csModule; + csDesc.computeStage.entryPoint = "main"; + + return device.CreateComputePipeline(&csDesc); + } + + // Creates compute pipeline with default layout + wgpu::ComputePipeline CreateComputePipelineWithDefaultLayout(const std::string& shader) { + return CreateComputePipeline({}, shader); + } + + // Creates render pipeline give na layout and shaders + wgpu::RenderPipeline CreateRenderPipeline(const std::vector& layouts, + const std::string& vertexShader, + const std::string& fragShader) { + wgpu::ShaderModule vsModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Vertex, vertexShader.c_str()); + + wgpu::ShaderModule fsModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Fragment, fragShader.c_str()); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + pipelineDescriptor.layout = nullptr; + if (!layouts.empty()) { + wgpu::PipelineLayoutDescriptor descriptor; + descriptor.bindGroupLayoutCount = layouts.size(); + descriptor.bindGroupLayouts = layouts.data(); + pipelineDescriptor.layout = device.CreatePipelineLayout(&descriptor); + } + + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + // Creates render pipeline with default layout + wgpu::RenderPipeline CreateRenderPipelineWithDefaultLayout(const std::string& vertexShader, + const std::string& fragShader) { + return CreateRenderPipeline({}, vertexShader, fragShader); + } + + // Creates bind group layout with given minimum sizes for each binding + wgpu::BindGroupLayout CreateBindGroupLayout(const std::vector& bindings, + const std::vector& minimumSizes) { + ASSERT(bindings.size() == minimumSizes.size()); + std::vector entries; + + for (size_t i = 0; i < bindings.size(); ++i) { + const BindingDescriptor& b = bindings[i]; + wgpu::BindGroupLayoutEntry e = {}; + e.binding = b.binding; + e.type = b.type; + e.visibility = b.visibility; + e.minBufferBindingSize = minimumSizes[i]; + entries.push_back(e); + } + + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = static_cast(entries.size()); + descriptor.entries = entries.data(); + return device.CreateBindGroupLayout(&descriptor); + } + + // Extract the first bind group from a compute shader + wgpu::BindGroupLayout GetBGLFromComputeShader(const std::string& shader, uint32_t index) { + wgpu::ComputePipeline pipeline = CreateComputePipelineWithDefaultLayout(shader); + return pipeline.GetBindGroupLayout(index); + } + + // Extract the first bind group from a render pass + wgpu::BindGroupLayout GetBGLFromRenderShaders(const std::string& vertexShader, + const std::string& fragShader, + uint32_t index) { + wgpu::RenderPipeline pipeline = + CreateRenderPipelineWithDefaultLayout(vertexShader, fragShader); + return pipeline.GetBindGroupLayout(index); + } + + // Create a bind group with given binding sizes for each entry (backed by the same buffer) + wgpu::BindGroup CreateBindGroup(wgpu::BindGroupLayout layout, + const std::vector& bindings, + const std::vector& bindingSizes) { + ASSERT(bindings.size() == bindingSizes.size()); + wgpu::Buffer buffer = + CreateBuffer(1024, wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage); + + std::vector entries; + entries.reserve(bindingSizes.size()); + + for (uint32_t i = 0; i < bindingSizes.size(); ++i) { + wgpu::BindGroupEntry entry = {}; + entry.binding = bindings[i].binding; + entry.buffer = buffer; + ASSERT(bindingSizes[i] < 1024); + entry.size = bindingSizes[i]; + entries.push_back(entry); + } + + wgpu::BindGroupDescriptor descriptor; + descriptor.layout = layout; + descriptor.entryCount = entries.size(); + descriptor.entries = entries.data(); + + return device.CreateBindGroup(&descriptor); + } + + // Runs a single dispatch with given pipeline and bind group (to test lazy validation during + // dispatch) + void TestDispatch(const wgpu::ComputePipeline& computePipeline, + const std::vector& bindGroups, + bool expectation) { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = commandEncoder.BeginComputePass(); + computePassEncoder.SetPipeline(computePipeline); + for (size_t i = 0; i < bindGroups.size(); ++i) { + computePassEncoder.SetBindGroup(i, bindGroups[i]); + } + computePassEncoder.Dispatch(1); + computePassEncoder.EndPass(); + if (!expectation) { + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } else { + commandEncoder.Finish(); + } + } + + // Runs a single draw with given pipeline and bind group (to test lazy validation during draw) + void TestDraw(const wgpu::RenderPipeline& renderPipeline, + const std::vector& bindGroups, + bool expectation) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(&renderPass); + renderPassEncoder.SetPipeline(renderPipeline); + for (size_t i = 0; i < bindGroups.size(); ++i) { + renderPassEncoder.SetBindGroup(i, bindGroups[i]); + } + renderPassEncoder.Draw(3); + renderPassEncoder.EndPass(); + if (!expectation) { + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } else { + commandEncoder.Finish(); + } + } +}; + +// The check between BGL and pipeline at pipeline creation time +class MinBufferSizePipelineCreationTests : public MinBufferSizeTestsBase {}; + +// Pipeline can be created if minimum buffer size in layout is specified as 0 +TEST_F(MinBufferSizePipelineCreationTests, ZeroMinBufferSize) { + std::vector bindings = {{0, 0, "float a; float b", 8}, {0, 1, "float c", 4}}; + + std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); + std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); + std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + + wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, {0, 0}); + CreateRenderPipeline({layout}, vertexShader, fragShader); + CreateComputePipeline({layout}, computeShader); +} + +// Fail if layout given has non-zero minimum sizes smaller than shader requirements +TEST_F(MinBufferSizePipelineCreationTests, LayoutSizesTooSmall) { + std::vector bindings = {{0, 0, "float a; float b", 8}, {0, 1, "float c", 4}}; + + std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); + std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); + std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + + CheckSizeBounds({8, 4}, [&](const std::vector& sizes, bool expectation) { + wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, sizes); + if (expectation) { + CreateRenderPipeline({layout}, vertexShader, fragShader); + CreateComputePipeline({layout}, computeShader); + } else { + ASSERT_DEVICE_ERROR(CreateRenderPipeline({layout}, vertexShader, fragShader)); + ASSERT_DEVICE_ERROR(CreateComputePipeline({layout}, computeShader)); + } + }); +} + +// Fail if layout given has non-zero minimum sizes smaller than shader requirements +TEST_F(MinBufferSizePipelineCreationTests, LayoutSizesTooSmallMultipleGroups) { + std::vector bg0Bindings = {{0, 0, "float a; float b", 8}, + {0, 1, "float c", 4}}; + std::vector bg1Bindings = {{1, 0, "float d; float e; float f", 12}, + {1, 1, "mat2 g", 32}}; + std::vector bindings = CombineBindings({bg0Bindings, bg1Bindings}); + + std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); + std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); + std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + + CheckSizeBounds({8, 4, 12, 32}, [&](const std::vector& sizes, bool expectation) { + wgpu::BindGroupLayout layout0 = CreateBindGroupLayout(bg0Bindings, {sizes[0], sizes[1]}); + wgpu::BindGroupLayout layout1 = CreateBindGroupLayout(bg1Bindings, {sizes[2], sizes[3]}); + if (expectation) { + CreateRenderPipeline({layout0, layout1}, vertexShader, fragShader); + CreateComputePipeline({layout0, layout1}, computeShader); + } else { + ASSERT_DEVICE_ERROR(CreateRenderPipeline({layout0, layout1}, vertexShader, fragShader)); + ASSERT_DEVICE_ERROR(CreateComputePipeline({layout0, layout1}, computeShader)); + } + }); +} + +// The check between the BGL and the bindings at bindgroup creation time +class MinBufferSizeBindGroupCreationTests : public MinBufferSizeTestsBase {}; + +// Fail if a binding is smaller than minimum buffer size +TEST_F(MinBufferSizeBindGroupCreationTests, BindingTooSmall) { + std::vector bindings = {{0, 0, "float a; float b", 8}, {0, 1, "float c", 4}}; + wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, {8, 4}); + + CheckSizeBounds({8, 4}, [&](const std::vector& sizes, bool expectation) { + if (expectation) { + CreateBindGroup(layout, bindings, sizes); + } else { + ASSERT_DEVICE_ERROR(CreateBindGroup(layout, bindings, sizes)); + } + }); +} + +// Check two layouts with different minimum size are unequal +TEST_F(MinBufferSizeBindGroupCreationTests, LayoutEquality) { + auto MakeLayout = [&](uint64_t size) { + return utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer, false, size, + false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, wgpu::TextureFormat::Undefined}}); + }; + + EXPECT_EQ(MakeLayout(0).Get(), MakeLayout(0).Get()); + EXPECT_NE(MakeLayout(0).Get(), MakeLayout(4).Get()); +} + +// The check between the bindgroup binding sizes and the required pipeline sizes at draw time +class MinBufferSizeDrawTimeValidationTests : public MinBufferSizeTestsBase {}; + +// Fail if binding sizes are too small at draw time +TEST_F(MinBufferSizeDrawTimeValidationTests, ZeroMinSizeAndTooSmallBinding) { + std::vector bindings = {{0, 0, "float a; float b", 8}, {0, 1, "float c", 4}}; + + std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); + std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); + std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + + wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, {0, 0}); + + wgpu::ComputePipeline computePipeline = CreateComputePipeline({layout}, computeShader); + wgpu::RenderPipeline renderPipeline = CreateRenderPipeline({layout}, vertexShader, fragShader); + + CheckSizeBounds({8, 4}, [&](const std::vector& sizes, bool expectation) { + wgpu::BindGroup bindGroup = CreateBindGroup(layout, bindings, sizes); + TestDispatch(computePipeline, {bindGroup}, expectation); + TestDraw(renderPipeline, {bindGroup}, expectation); + }); +} + +// Draw time validation works for non-contiguous bindings +TEST_F(MinBufferSizeDrawTimeValidationTests, UnorderedBindings) { + std::vector bindings = {{0, 2, "float a; float b", 8}, + {0, 0, "float c", 4}, + {0, 4, "float d; float e; float f", 12}}; + + std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); + std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); + std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + + wgpu::BindGroupLayout layout = CreateBindGroupLayout(bindings, {0, 0, 0}); + + wgpu::ComputePipeline computePipeline = CreateComputePipeline({layout}, computeShader); + wgpu::RenderPipeline renderPipeline = CreateRenderPipeline({layout}, vertexShader, fragShader); + + CheckSizeBounds({8, 4, 12}, [&](const std::vector& sizes, bool expectation) { + wgpu::BindGroup bindGroup = CreateBindGroup(layout, bindings, sizes); + TestDispatch(computePipeline, {bindGroup}, expectation); + TestDraw(renderPipeline, {bindGroup}, expectation); + }); +} + +// Draw time validation works for multiple bind groups +TEST_F(MinBufferSizeDrawTimeValidationTests, MultipleGroups) { + std::vector bg0Bindings = {{0, 0, "float a; float b", 8}, + {0, 1, "float c", 4}}; + std::vector bg1Bindings = {{1, 0, "float d; float e; float f", 12}, + {1, 1, "mat2 g", 32}}; + std::vector bindings = CombineBindings({bg0Bindings, bg1Bindings}); + + std::string computeShader = CreateComputeShaderWithBindings("std140", bindings); + std::string vertexShader = CreateVertexShaderWithBindings("std140", {}); + std::string fragShader = CreateFragmentShaderWithBindings("std140", bindings); + + wgpu::BindGroupLayout layout0 = CreateBindGroupLayout(bg0Bindings, {0, 0}); + wgpu::BindGroupLayout layout1 = CreateBindGroupLayout(bg1Bindings, {0, 0}); + + wgpu::ComputePipeline computePipeline = + CreateComputePipeline({layout0, layout1}, computeShader); + wgpu::RenderPipeline renderPipeline = + CreateRenderPipeline({layout0, layout1}, vertexShader, fragShader); + + CheckSizeBounds({8, 4, 12, 32}, [&](const std::vector& sizes, bool expectation) { + wgpu::BindGroup bindGroup0 = CreateBindGroup(layout0, bg0Bindings, {sizes[0], sizes[1]}); + wgpu::BindGroup bindGroup1 = CreateBindGroup(layout0, bg0Bindings, {sizes[2], sizes[3]}); + TestDispatch(computePipeline, {bindGroup0, bindGroup1}, expectation); + TestDraw(renderPipeline, {bindGroup0, bindGroup1}, expectation); + }); +} + +// The correctness of minimum buffer size for the defaulted layout for a pipeline +class MinBufferSizeDefaultLayoutTests : public MinBufferSizeTestsBase { + public: + // Checks BGL |layout| has minimum buffer sizes equal to sizes in |bindings| + void CheckLayoutBindingSizeValidation(const wgpu::BindGroupLayout& layout, + const std::vector& bindings) { + std::vector correctSizes; + correctSizes.reserve(bindings.size()); + for (const BindingDescriptor& b : bindings) { + correctSizes.push_back(b.size); + } + + CheckSizeBounds(correctSizes, [&](const std::vector& sizes, bool expectation) { + if (expectation) { + CreateBindGroup(layout, bindings, sizes); + } else { + ASSERT_DEVICE_ERROR(CreateBindGroup(layout, bindings, sizes)); + } + }); + } + + // Constructs shaders with given layout type and bindings, checking defaulted sizes match sizes + // in |bindings| + void CheckShaderBindingSizeReflection( + const std::string& layoutType, + std::initializer_list> bindings) { + std::vector combinedBindings = CombineBindings(bindings); + std::string computeShader = CreateComputeShaderWithBindings(layoutType, combinedBindings); + std::string vertexShader = CreateVertexShaderWithBindings(layoutType, {}); + std::string fragShader = CreateFragmentShaderWithBindings(layoutType, combinedBindings); + + size_t i = 0; + for (const std::vector& b : bindings) { + wgpu::BindGroupLayout computeLayout = GetBGLFromComputeShader(computeShader, i); + wgpu::BindGroupLayout renderLayout = + GetBGLFromRenderShaders(vertexShader, fragShader, i); + + CheckLayoutBindingSizeValidation(computeLayout, b); + CheckLayoutBindingSizeValidation(renderLayout, b); + ++i; + } + } +}; + +// Various bindings in std140 have correct minimum size reflection +TEST_F(MinBufferSizeDefaultLayoutTests, std140Inferred) { + CheckShaderBindingSizeReflection( + "std140", {{{0, 0, "float a", 4}, {0, 1, "float b[]", 16}, {0, 2, "mat2 c", 32}}}); + CheckShaderBindingSizeReflection("std140", {{{0, 3, "int d; float e[]", 32}, + {0, 4, "ThreeFloats f", 12}, + {0, 5, "ThreeFloats g[]", 16}}}); +} + +// Various bindings in std430 have correct minimum size reflection +TEST_F(MinBufferSizeDefaultLayoutTests, std430Inferred) { + CheckShaderBindingSizeReflection( + "std430", {{{0, 0, "float a", 4}, {0, 1, "float b[]", 4}, {0, 2, "mat2 c", 16}}}); + CheckShaderBindingSizeReflection("std430", {{{0, 3, "int d; float e[]", 8}, + {0, 4, "ThreeFloats f", 12}, + {0, 5, "ThreeFloats g[]", 12}}}); +} + +// Sizes are inferred for all binding types with std140 layout +TEST_F(MinBufferSizeDefaultLayoutTests, std140BindingTypes) { + CheckShaderBindingSizeReflection( + "std140", {{{0, 0, "int d; float e[]", 32, wgpu::BindingType::UniformBuffer}, + {0, 1, "ThreeFloats f", 12, wgpu::BindingType::StorageBuffer}, + {0, 2, "ThreeFloats g[]", 16, wgpu::BindingType::ReadonlyStorageBuffer}}}); +} + +// Sizes are inferred for all binding types with std430 layout +TEST_F(MinBufferSizeDefaultLayoutTests, std430BindingTypes) { + CheckShaderBindingSizeReflection( + "std430", {{{0, 0, "float a", 4, wgpu::BindingType::StorageBuffer}, + {0, 1, "ThreeFloats b[]", 12, wgpu::BindingType::ReadonlyStorageBuffer}}}); +} + +// Various bindings have correct size across multiple groups +TEST_F(MinBufferSizeDefaultLayoutTests, std140MultipleBindGroups) { + CheckShaderBindingSizeReflection("std140", + {{{0, 0, "float a", 4}, {0, 1, "float b[]", 16}}, + {{1, 2, "mat2 c", 32}, {1, 3, "int d; float e[]", 32}}}); + CheckShaderBindingSizeReflection( + "std140", {{{0, 4, "ThreeFloats f", 12}, {0, 1, "float b[]", 16}}, + {{1, 5, "ThreeFloats g[]", 16}, {1, 3, "int d; float e[]", 32}}}); +} + +// Various bindings have correct size across multiple groups +TEST_F(MinBufferSizeDefaultLayoutTests, std430MultipleBindGroups) { + CheckShaderBindingSizeReflection("std430", + {{{0, 0, "float a", 4}, {0, 1, "float b[]", 4}}, + {{1, 2, "mat2 c", 16}, {1, 3, "int d; float e[]", 8}}}); + CheckShaderBindingSizeReflection( + "std430", {{{0, 4, "ThreeFloats f", 12}, {0, 1, "float b[]", 4}}, + {{1, 5, "ThreeFloats g[]", 12}, {1, 3, "int d; float e[]", 8}}}); +} + +// Minimum size should be the max requirement of both vertex and fragment stages +TEST_F(MinBufferSizeDefaultLayoutTests, RenderPassConsidersBothStages) { + std::string vertexShader = CreateVertexShaderWithBindings( + "std140", {{0, 0, "float a", 4, wgpu::BindingType::UniformBuffer}, + {0, 1, "float b[]", 16, wgpu::BindingType::UniformBuffer}}); + std::string fragShader = CreateFragmentShaderWithBindings( + "std140", {{0, 0, "float a; float b", 8, wgpu::BindingType::UniformBuffer}, + {0, 1, "float c; float d", 8, wgpu::BindingType::UniformBuffer}}); + + wgpu::BindGroupLayout renderLayout = GetBGLFromRenderShaders(vertexShader, fragShader, 0); + + CheckLayoutBindingSizeValidation(renderLayout, {{0, 0, "", 8}, {0, 1, "", 16}}); +} diff --git a/third_party/dawn/src/tests/unittests/validation/QuerySetValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/QuerySetValidationTests.cpp new file mode 100644 index 00000000000..5bf7babdd39 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/QuerySetValidationTests.cpp @@ -0,0 +1,462 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include "utils/WGPUHelpers.h" + +class QuerySetValidationTest : public ValidationTest { + protected: + void SetUp() override { + ValidationTest::SetUp(); + + // Initialize the device with required extensions + deviceWithPipelineStatistics = + CreateDeviceFromAdapter(adapter, {"pipeline_statistics_query"}); + deviceWithTimestamp = CreateDeviceFromAdapter(adapter, {"timestamp_query"}); + } + + wgpu::QuerySet CreateQuerySet( + wgpu::Device cDevice, + wgpu::QueryType queryType, + uint32_t queryCount, + std::vector pipelineStatistics = {}) { + wgpu::QuerySetDescriptor descriptor; + descriptor.type = queryType; + descriptor.count = queryCount; + + if (pipelineStatistics.size() > 0) { + descriptor.pipelineStatistics = pipelineStatistics.data(); + descriptor.pipelineStatisticsCount = pipelineStatistics.size(); + } + + return cDevice.CreateQuerySet(&descriptor); + } + + wgpu::Device deviceWithPipelineStatistics; + wgpu::Device deviceWithTimestamp; +}; + +// Test creating query set with/without extensions +TEST_F(QuerySetValidationTest, Creation) { + // Create query set for Occlusion query + { + // Success on default device without any extension enabled + // Occlusion query does not require any extension. + CreateQuerySet(device, wgpu::QueryType::Occlusion, 1); + + // Success on the device with extension enabled. + CreateQuerySet(deviceWithPipelineStatistics, wgpu::QueryType::Occlusion, 1); + CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Occlusion, 1); + } + + // Create query set for PipelineStatistics query + { + // Fail on default device without any extension enabled + ASSERT_DEVICE_ERROR(CreateQuerySet(device, wgpu::QueryType::PipelineStatistics, 1, + {wgpu::PipelineStatisticName::VertexShaderInvocations})); + + // Success on the device if the extension is enabled. + CreateQuerySet(deviceWithPipelineStatistics, wgpu::QueryType::PipelineStatistics, 1, + {wgpu::PipelineStatisticName::VertexShaderInvocations}); + } + + // Create query set for Timestamp query + { + // Fail on default device without any extension enabled + ASSERT_DEVICE_ERROR(CreateQuerySet(device, wgpu::QueryType::Timestamp, 1)); + + // Success on the device if the extension is enabled. + CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Timestamp, 1); + } +} + +// Test creating query set with invalid type +TEST_F(QuerySetValidationTest, InvalidQueryType) { + ASSERT_DEVICE_ERROR(CreateQuerySet(device, static_cast(0xFFFFFFFF), 1)); +} + +// Test creating query set with unnecessary pipeline statistics +TEST_F(QuerySetValidationTest, UnnecessaryPipelineStatistics) { + // Fail to create with pipeline statistics for Occlusion query + { + ASSERT_DEVICE_ERROR(CreateQuerySet(device, wgpu::QueryType::Occlusion, 1, + {wgpu::PipelineStatisticName::VertexShaderInvocations})); + } + + // Fail to create with pipeline statistics for Timestamp query + { + ASSERT_DEVICE_ERROR(CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Timestamp, 1, + {wgpu::PipelineStatisticName::VertexShaderInvocations})); + } +} + +// Test creating query set with invalid pipeline statistics +TEST_F(QuerySetValidationTest, InvalidPipelineStatistics) { + // Success to create with all pipeline statistics names which are not in the same order as + // defined in webgpu header file + { + CreateQuerySet(deviceWithPipelineStatistics, wgpu::QueryType::PipelineStatistics, 1, + {wgpu::PipelineStatisticName::ClipperInvocations, + wgpu::PipelineStatisticName::ClipperPrimitivesOut, + wgpu::PipelineStatisticName::ComputeShaderInvocations, + wgpu::PipelineStatisticName::FragmentShaderInvocations, + wgpu::PipelineStatisticName::VertexShaderInvocations}); + } + + // Fail to create with empty pipeline statistics + { + ASSERT_DEVICE_ERROR(CreateQuerySet(deviceWithPipelineStatistics, + wgpu::QueryType::PipelineStatistics, 1, {})); + } + + // Fail to create with invalid pipeline statistics + { + ASSERT_DEVICE_ERROR(CreateQuerySet(deviceWithPipelineStatistics, + wgpu::QueryType::PipelineStatistics, 1, + {static_cast(0xFFFFFFFF)})); + } + + // Fail to create with duplicate pipeline statistics + { + ASSERT_DEVICE_ERROR(CreateQuerySet(deviceWithPipelineStatistics, + wgpu::QueryType::PipelineStatistics, 1, + {wgpu::PipelineStatisticName::VertexShaderInvocations, + wgpu::PipelineStatisticName::VertexShaderInvocations})); + } +} + +// Test destroying a destroyed query set +TEST_F(QuerySetValidationTest, DestroyDestroyedQuerySet) { + wgpu::QuerySetDescriptor descriptor; + descriptor.type = wgpu::QueryType::Occlusion; + descriptor.count = 1; + wgpu::QuerySet querySet = device.CreateQuerySet(&descriptor); + querySet.Destroy(); + querySet.Destroy(); +} + +class TimestampQueryValidationTest : public QuerySetValidationTest {}; + +// Test write timestamp on command encoder +TEST_F(TimestampQueryValidationTest, WriteTimestampOnCommandEncoder) { + wgpu::QuerySet timestampQuerySet = + CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Timestamp, 2); + wgpu::QuerySet occlusionQuerySet = + CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Occlusion, 2); + + // Success on command encoder + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + encoder.WriteTimestamp(timestampQuerySet, 0); + encoder.Finish(); + } + + // Not allow to write timestamp from another device + { + // Write timestamp from default device + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.WriteTimestamp(timestampQuerySet, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Not allow to write timestamp to the query set with other query type + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + encoder.WriteTimestamp(occlusionQuerySet, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to write timestamp to the index which exceeds the number of queries in query set + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + encoder.WriteTimestamp(timestampQuerySet, 2); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to submit timestamp query with a destroyed query set + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + encoder.WriteTimestamp(timestampQuerySet, 0); + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = deviceWithTimestamp.GetDefaultQueue(); + timestampQuerySet.Destroy(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + } +} + +// Test write timestamp on compute pass encoder +TEST_F(TimestampQueryValidationTest, WriteTimestampOnComputePassEncoder) { + wgpu::QuerySet timestampQuerySet = + CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Timestamp, 2); + wgpu::QuerySet occlusionQuerySet = + CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Occlusion, 2); + + // Success on compute pass encoder + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.WriteTimestamp(timestampQuerySet, 0); + pass.EndPass(); + encoder.Finish(); + } + + // Not allow to write timestamp from another device + { + // Write timestamp from default device + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.WriteTimestamp(timestampQuerySet, 0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Not allow to write timestamp to the query set with other query type + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.WriteTimestamp(occlusionQuerySet, 0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to write timestamp to the index which exceeds the number of queries in query set + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.WriteTimestamp(timestampQuerySet, 2); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to submit timestamp query with a destroyed query set + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.WriteTimestamp(timestampQuerySet, 0); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = deviceWithTimestamp.GetDefaultQueue(); + timestampQuerySet.Destroy(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + } +} + +// Test write timestamp on render pass encoder +TEST_F(TimestampQueryValidationTest, WriteTimestampOnRenderPassEncoder) { + DummyRenderPass renderPass(deviceWithTimestamp); + + wgpu::QuerySet timestampQuerySet = + CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Timestamp, 2); + wgpu::QuerySet occlusionQuerySet = + CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Occlusion, 2); + + // Success on render pass encoder + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.WriteTimestamp(timestampQuerySet, 0); + pass.EndPass(); + encoder.Finish(); + } + + // Not allow to write timestamp from another device + { + // Write timestamp from default device + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.WriteTimestamp(timestampQuerySet, 0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Not allow to write timestamp to the query set with other query type + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.WriteTimestamp(occlusionQuerySet, 0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to write timestamp to the index which exceeds the number of queries in query set + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.WriteTimestamp(timestampQuerySet, 2); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to submit timestamp query with a destroyed query set + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.WriteTimestamp(timestampQuerySet, 0); + pass.EndPass(); + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = deviceWithTimestamp.GetDefaultQueue(); + timestampQuerySet.Destroy(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + } +} + +class ResolveQuerySetValidationTest : public QuerySetValidationTest { + protected: + wgpu::Buffer CreateBuffer(wgpu::Device cDevice, uint64_t size, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage; + + return cDevice.CreateBuffer(&descriptor); + } +}; + +// Test resolve query set with invalid query set, first query and query count +TEST_F(ResolveQuerySetValidationTest, ResolveInvalidQuerySetAndIndexCount) { + constexpr uint32_t kQueryCount = 4; + + wgpu::QuerySet querySet = CreateQuerySet(device, wgpu::QueryType::Occlusion, kQueryCount); + wgpu::Buffer destination = + CreateBuffer(device, kQueryCount * sizeof(uint64_t), wgpu::BufferUsage::QueryResolve); + + // Success + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0); + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = device.GetDefaultQueue(); + queue.Submit(1, &commands); + } + + // Fail to resolve query set from another device + { + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to resolve query set if first query out of range + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, kQueryCount, 0, destination, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to resolve query set if the sum of first query and query count is larger than queries + // number in the query set + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 1, kQueryCount, destination, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to resolve a destroyed query set + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0); + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = device.GetDefaultQueue(); + querySet.Destroy(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + } +} + +// Test resolve query set with invalid query set, first query and query count +TEST_F(ResolveQuerySetValidationTest, ResolveToInvalidBufferAndOffset) { + constexpr uint32_t kQueryCount = 4; + constexpr uint64_t kBufferSize = kQueryCount * sizeof(uint64_t); + + wgpu::QuerySet querySet = CreateQuerySet(device, wgpu::QueryType::Occlusion, kQueryCount); + wgpu::Buffer destination = CreateBuffer(device, kBufferSize, wgpu::BufferUsage::QueryResolve); + + // Success + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 1, kQueryCount - 1, destination, 8); + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = device.GetDefaultQueue(); + queue.Submit(1, &commands); + } + + // Fail to resolve query set to a buffer created from another device + { + wgpu::Buffer bufferOnTimestamp = + CreateBuffer(deviceWithTimestamp, kBufferSize, wgpu::BufferUsage::QueryResolve); + wgpu::CommandEncoder encoder = deviceWithTimestamp.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, bufferOnTimestamp, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to resolve query set to a buffer if offset is not a multiple of 8 bytes + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 4); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to resolve query set to a buffer if the data size overflow the buffer + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 8); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to resolve query set to a buffer if the offset is past the end of the buffer + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, 1, destination, kBufferSize); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to resolve query set to a buffer does not have the usage of QueryResolve + { + wgpu::Buffer dstBuffer = CreateBuffer(device, kBufferSize, wgpu::BufferUsage::CopyDst); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, dstBuffer, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Fail to resolve query set to a destroyed buffer. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0); + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = device.GetDefaultQueue(); + destination.Destroy(); + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + } +} + +// Check that in 32bit mode the computation of queryCount * sizeof(uint64_t) doesn't overflow (which +// would skip validation). +TEST_F(ResolveQuerySetValidationTest, BufferOverflowOn32Bits) { + // If compiling for 32-bits mode, the data size calculated by queryCount * sizeof(uint64_t) + // is 8, which is less than the buffer size. + constexpr uint32_t kQueryCount = std::numeric_limits::max() / sizeof(uint64_t) + 2; + + wgpu::QuerySet querySet = CreateQuerySet(device, wgpu::QueryType::Occlusion, kQueryCount); + wgpu::Buffer destination = CreateBuffer(device, 1024, wgpu::BufferUsage::QueryResolve); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.ResolveQuerySet(querySet, 0, kQueryCount, destination, 0); + + ASSERT_DEVICE_ERROR(encoder.Finish()); +} diff --git a/third_party/dawn/src/tests/unittests/validation/QueueSubmitValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/QueueSubmitValidationTests.cpp index ccaa107ff4b..8779ce34130 100644 --- a/third_party/dawn/src/tests/unittests/validation/QueueSubmitValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/QueueSubmitValidationTests.cpp @@ -14,56 +14,184 @@ #include "tests/unittests/validation/ValidationTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" namespace { -class QueueSubmitValidationTest : public ValidationTest { -}; - -static void StoreTrueMapWriteCallback(DawnBufferMapAsyncStatus status, - void*, - uint64_t, - void* userdata) { - *static_cast(userdata) = true; -} - -// Test submitting with a mapped buffer is disallowed -TEST_F(QueueSubmitValidationTest, SubmitWithMappedBuffer) { - // Create a map-write buffer. - dawn::BufferDescriptor descriptor; - descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; - descriptor.size = 4; - dawn::Buffer buffer = device.CreateBuffer(&descriptor); - - // Create a fake copy destination buffer - descriptor.usage = dawn::BufferUsageBit::TransferDst; - dawn::Buffer targetBuffer = device.CreateBuffer(&descriptor); - - // Create a command buffer that reads from the mappable buffer. - dawn::CommandBuffer commands; - { - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - encoder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, 4); - commands = encoder.Finish(); + class QueueSubmitValidationTest : public ValidationTest {}; + + // Test submitting with a mapped buffer is disallowed + TEST_F(QueueSubmitValidationTest, SubmitWithMappedBuffer) { + // Create a map-write buffer. + wgpu::BufferDescriptor descriptor; + descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc; + descriptor.size = 4; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + // Create a fake copy destination buffer + descriptor.usage = wgpu::BufferUsage::CopyDst; + wgpu::Buffer targetBuffer = device.CreateBuffer(&descriptor); + + // Create a command buffer that reads from the mappable buffer. + wgpu::CommandBuffer commands; + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, 4); + commands = encoder.Finish(); + } + + wgpu::Queue queue = device.GetDefaultQueue(); + + // Submitting when the buffer has never been mapped should succeed + queue.Submit(1, &commands); + + // Map the buffer, submitting when the buffer is mapped should fail + buffer.MapWriteAsync(nullptr, nullptr); + + // Try submitting before the callback is fired. + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + + WaitForAllOperations(device); + + // Try submitting after the callback is fired. + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + + // Unmap the buffer, queue submit should succeed + buffer.Unmap(); + queue.Submit(1, &commands); + } + + class QueueWriteBufferValidationTest : public ValidationTest { + private: + void SetUp() override { + ValidationTest::SetUp(); + queue = device.GetDefaultQueue(); + } + + protected: + wgpu::Buffer CreateBuffer(uint64_t size) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = wgpu::BufferUsage::CopyDst; + return device.CreateBuffer(&descriptor); + } + + wgpu::Queue queue; + }; + + // Test the success case for WriteBuffer + TEST_F(QueueWriteBufferValidationTest, Success) { + wgpu::Buffer buf = CreateBuffer(4); + + uint32_t foo = 0x01020304; + queue.WriteBuffer(buf, 0, &foo, sizeof(foo)); + } + + // Test error case for WriteBuffer out of bounds + TEST_F(QueueWriteBufferValidationTest, OutOfBounds) { + wgpu::Buffer buf = CreateBuffer(4); + + uint32_t foo[2] = {0, 0}; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, 0, foo, 8)); + } + + // Test error case for WriteBuffer out of bounds with an overflow + TEST_F(QueueWriteBufferValidationTest, OutOfBoundsOverflow) { + wgpu::Buffer buf = CreateBuffer(1024); + + uint32_t foo[2] = {0, 0}; + + // An offset that when added to "4" would overflow to be zero and pass validation without + // overflow checks. + uint64_t offset = uint64_t(int64_t(0) - int64_t(4)); + + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, offset, foo, 4)); + } + + // Test error case for WriteBuffer with the wrong usage + TEST_F(QueueWriteBufferValidationTest, WrongUsage) { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::Vertex; + wgpu::Buffer buf = device.CreateBuffer(&descriptor); + + uint32_t foo = 0; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, 0, &foo, sizeof(foo))); + } + + // Test WriteBuffer with unaligned size + TEST_F(QueueWriteBufferValidationTest, UnalignedSize) { + wgpu::Buffer buf = CreateBuffer(4); + + uint16_t value = 123; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, 0, &value, sizeof(value))); } - dawn::Queue queue = device.CreateQueue(); + // Test WriteBuffer with unaligned offset + TEST_F(QueueWriteBufferValidationTest, UnalignedOffset) { + wgpu::Buffer buf = CreateBuffer(8); - // Submitting when the buffer has never been mapped should succeed - queue.Submit(1, &commands); + uint32_t value = 0x01020304; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, 2, &value, sizeof(value))); + } + + // Test WriteBuffer with destroyed buffer + TEST_F(QueueWriteBufferValidationTest, DestroyedBuffer) { + wgpu::Buffer buf = CreateBuffer(4); + buf.Destroy(); + + uint32_t value = 0; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, 0, &value, sizeof(value))); + } + + // Test WriteBuffer with mapped buffer + TEST_F(QueueWriteBufferValidationTest, MappedBuffer) { + // CreateBufferMapped + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopyDst; + wgpu::CreateBufferMappedResult result = device.CreateBufferMapped(&descriptor); - // Map the buffer, submitting when the buffer is mapped should fail - bool mapWriteFinished = false; - buffer.MapWriteAsync(StoreTrueMapWriteCallback, &mapWriteFinished); - queue.Submit(0, nullptr); - ASSERT_TRUE(mapWriteFinished); + uint32_t value = 0; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(result.buffer, 0, &value, sizeof(value))); + } - ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + // mappedAtCreation + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopyDst; + descriptor.mappedAtCreation = true; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); - // Unmap the buffer, queue submit should succeed - buffer.Unmap(); - queue.Submit(1, &commands); -} + uint32_t value = 0; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buffer, 0, &value, sizeof(value))); + } + + // MapReadAsync + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead; + wgpu::Buffer buf = device.CreateBuffer(&descriptor); + + buf.MapReadAsync(nullptr, nullptr); + uint32_t value = 0; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, 0, &value, sizeof(value))); + } + + // MapWriteAsync + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; + wgpu::Buffer buf = device.CreateBuffer(&descriptor); + + buf.MapWriteAsync(nullptr, nullptr); + uint32_t value = 0; + ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, 0, &value, sizeof(value))); + } + } } // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp new file mode 100644 index 00000000000..c238acf3e77 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/QueueWriteTextureValidationTests.cpp @@ -0,0 +1,639 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include "common/Math.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" + +namespace { + + class QueueWriteTextureValidationTest : public ValidationTest { + private: + void SetUp() override { + ValidationTest::SetUp(); + queue = device.GetDefaultQueue(); + } + + protected: + wgpu::Texture Create2DTexture(wgpu::Extent3D size, + uint32_t mipLevelCount, + wgpu::TextureFormat format, + wgpu::TextureUsage usage, + uint32_t sampleCount = 1) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = size.width; + descriptor.size.height = size.height; + descriptor.size.depth = size.depth; + descriptor.sampleCount = sampleCount; + descriptor.format = format; + descriptor.mipLevelCount = mipLevelCount; + descriptor.usage = usage; + wgpu::Texture tex = device.CreateTexture(&descriptor); + return tex; + } + + void TestWriteTexture(size_t dataSize, + uint32_t dataOffset, + uint32_t dataBytesPerRow, + uint32_t dataRowsPerImage, + wgpu::Texture texture, + uint32_t texLevel, + wgpu::Origin3D texOrigin, + wgpu::Extent3D size) { + std::vector data(dataSize); + + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = dataOffset; + textureDataLayout.bytesPerRow = dataBytesPerRow; + textureDataLayout.rowsPerImage = dataRowsPerImage; + + wgpu::TextureCopyView textureCopyView = + utils::CreateTextureCopyView(texture, texLevel, texOrigin); + + queue.WriteTexture(&textureCopyView, data.data(), dataSize, &textureDataLayout, &size); + } + + void TestWriteTextureExactDataSize(uint32_t bytesPerRow, + uint32_t rowsPerImage, + wgpu::Texture texture, + wgpu::TextureFormat textureFormat, + wgpu::Origin3D origin, + wgpu::Extent3D extent3D) { + // Check the minimal valid dataSize. + uint64_t dataSize = + utils::RequiredBytesInCopy(bytesPerRow, rowsPerImage, extent3D, textureFormat); + TestWriteTexture(dataSize, 0, bytesPerRow, rowsPerImage, texture, 0, origin, extent3D); + + // Check dataSize was indeed minimal. + uint64_t invalidSize = dataSize - 1; + ASSERT_DEVICE_ERROR(TestWriteTexture(invalidSize, 0, bytesPerRow, rowsPerImage, texture, + 0, origin, extent3D)); + } + + wgpu::Queue queue; + }; + + // Test the success case for WriteTexture + TEST_F(QueueWriteTextureValidationTest, Success) { + const uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture destination = Create2DTexture({16, 16, 4}, 5, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + + // Different copies, including some that touch the OOB condition + { + // Copy 4x4 block in corner of first mip. + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1}); + // Copy 4x4 block in opposite corner of first mip. + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {12, 12, 0}, {4, 4, 1}); + // Copy 4x4 block in the 4x4 mip. + TestWriteTexture(dataSize, 0, 256, 0, destination, 2, {0, 0, 0}, {4, 4, 1}); + // Copy with a data offset + TestWriteTexture(dataSize, dataSize - 4, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1}); + } + + // Copies with a 256-byte aligned bytes per row but unaligned texture region + { + // Unaligned region + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {3, 4, 1}); + // Unaligned region with texture offset + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {5, 7, 0}, {2, 3, 1}); + // Unaligned region, with data offset + TestWriteTexture(dataSize, 31 * 4, 256, 0, destination, 0, {0, 0, 0}, {3, 3, 1}); + } + + // Empty copies are valid + { + // An empty copy + TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 1}); + // An empty copy with depth = 0 + TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 0}); + // An empty copy touching the end of the data + TestWriteTexture(dataSize, dataSize, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 1}); + // An empty copy touching the side of the texture + TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {16, 16, 0}, {0, 0, 1}); + // An empty copy with depth = 1 and bytesPerRow > 0 + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {0, 0, 1}); + // An empty copy with height > 0, depth = 0, bytesPerRow > 0 and rowsPerImage > 0 + TestWriteTexture(dataSize, 0, 256, 16, destination, 0, {0, 0, 0}, {0, 1, 0}); + } + } + + // Test OOB conditions on the data + TEST_F(QueueWriteTextureValidationTest, OutOfBoundsOnData) { + const uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + + // OOB on the data because we copy too many pixels + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 5, 1})); + + // OOB on the data because of the offset + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 4, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1})); + + // OOB on the data because utils::RequiredBytesInCopy overflows + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 512, 0, destination, 0, {0, 0, 0}, {4, 3, 1})); + + // Not OOB on the data although bytes per row * height overflows + // but utils::RequiredBytesInCopy * depth does not overflow + { + uint32_t sourceDataSize = + utils::RequiredBytesInCopy(256, 0, {7, 3, 1}, wgpu::TextureFormat::RGBA8Unorm); + ASSERT_TRUE(256 * 3 > sourceDataSize) << "bytes per row * height should overflow data"; + + TestWriteTexture(sourceDataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {7, 3, 1}); + } + } + + // Test OOB conditions on the texture + TEST_F(QueueWriteTextureValidationTest, OutOfBoundsOnTexture) { + const uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture destination = Create2DTexture({16, 16, 2}, 5, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + + // OOB on the texture because x + width overflows + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {13, 12, 0}, {4, 4, 1})); + + // OOB on the texture because y + width overflows + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {12, 13, 0}, {4, 4, 1})); + + // OOB on the texture because we overflow a non-zero mip + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 256, 0, destination, 2, {1, 0, 0}, {4, 4, 1})); + + // OOB on the texture even on an empty copy when we copy to a non-existent mip. + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 0, 0, destination, 5, {0, 0, 0}, {0, 0, 1})); + + // OOB on the texture because slice overflows + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 2}, {0, 0, 1})); + } + + // Test that we force Depth=1 on writes to 2D textures + TEST_F(QueueWriteTextureValidationTest, DepthConstraintFor2DTextures) { + const uint64_t dataSize = + utils::RequiredBytesInCopy(0, 0, {0, 0, 2}, wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + + // Depth > 1 on an empty copy still errors + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 0, 0, destination, 0, {0, 0, 0}, {0, 0, 2})); + } + + // Test WriteTexture with incorrect texture usage + TEST_F(QueueWriteTextureValidationTest, IncorrectUsage) { + const uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture sampled = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::Sampled); + + // Incorrect destination usage + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 256, 0, sampled, 0, {0, 0, 0}, {4, 4, 1})); + } + + // Test incorrect values of bytesPerRow and that values not divisible by 256 are allowed. + TEST_F(QueueWriteTextureValidationTest, BytesPerRowLimitations) { + wgpu::Texture destination = Create2DTexture({3, 7, 1}, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + + // bytesPerRow = 0 is invalid + ASSERT_DEVICE_ERROR(TestWriteTexture(128, 0, 0, 0, destination, 0, {0, 0, 0}, {3, 7, 1})); + + // bytesPerRow = 11 is invalid since a row takes 12 bytes. + ASSERT_DEVICE_ERROR(TestWriteTexture(128, 0, 11, 0, destination, 0, {0, 0, 0}, {3, 7, 1})); + + // bytesPerRow = 12 is valid since a row takes 12 bytes. + TestWriteTexture(128, 0, 12, 0, destination, 0, {0, 0, 0}, {3, 7, 1}); + + // bytesPerRow = 13 is valid since a row takes 12 bytes. + TestWriteTexture(128, 0, 13, 0, destination, 0, {0, 0, 0}, {3, 7, 1}); + } + + // Test that if rowsPerImage is greater than 0, it must be at least copy height. + TEST_F(QueueWriteTextureValidationTest, ImageHeightConstraint) { + uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture destination = Create2DTexture({16, 16, 1}, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + + // Image height is zero (Valid) + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {4, 4, 1}); + + // Image height is equal to copy height (Valid) + TestWriteTexture(dataSize, 0, 256, 4, destination, 0, {0, 0, 0}, {4, 4, 1}); + + // Image height is larger than copy height (Valid) + TestWriteTexture(dataSize, 0, 256, 5, destination, 0, {0, 0, 0}, {4, 4, 1}); + + // Image height is less than copy height (Invalid) + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 256, 3, destination, 0, {0, 0, 0}, {4, 4, 1})); + } + + // Test WriteTexture with incorrect data offset usage + TEST_F(QueueWriteTextureValidationTest, IncorrectDataOffset) { + uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 4, 1}, wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture destination = Create2DTexture({16, 16, 1}, 5, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + + // Correct usage + TestWriteTexture(dataSize, dataSize - 4, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1}); + + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, dataSize - 6, 256, 0, destination, 0, {0, 0, 0}, {1, 1, 1})); + } + + // Test multisampled textures can be used in WriteTexture. + TEST_F(QueueWriteTextureValidationTest, WriteToMultisampledTexture) { + uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {2, 2, 1}, wgpu::TextureFormat::RGBA8Unorm); + wgpu::Texture destination = Create2DTexture({2, 2, 1}, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst, 4); + + ASSERT_DEVICE_ERROR( + TestWriteTexture(dataSize, 0, 256, 0, destination, 0, {0, 0, 0}, {2, 2, 1})); + } + + // Test WriteTexture with texture in error state causes errors. + TEST_F(QueueWriteTextureValidationTest, TextureInErrorState) { + wgpu::TextureDescriptor errorTextureDescriptor; + errorTextureDescriptor.size.depth = 0; + ASSERT_DEVICE_ERROR(wgpu::Texture errorTexture = + device.CreateTexture(&errorTextureDescriptor)); + wgpu::TextureCopyView errorTextureCopyView = + utils::CreateTextureCopyView(errorTexture, 0, {0, 0, 0}); + + wgpu::Extent3D extent3D = {1, 1, 1}; + + { + std::vector data(4); + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = 0; + textureDataLayout.bytesPerRow = 0; + textureDataLayout.rowsPerImage = 0; + + ASSERT_DEVICE_ERROR(queue.WriteTexture(&errorTextureCopyView, data.data(), 4, + &textureDataLayout, &extent3D)); + } + } + + // Regression tests for a bug in the computation of texture data size in Dawn. + TEST_F(QueueWriteTextureValidationTest, TextureWriteDataSizeLastRowComputation) { + constexpr uint32_t kBytesPerRow = 256; + constexpr uint32_t kWidth = 4; + constexpr uint32_t kHeight = 4; + + constexpr std::array kFormats = {wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureFormat::RG8Unorm}; + + { + // kBytesPerRow * (kHeight - 1) + kWidth is not large enough to be the valid data size + // in this test because the data sizes in WriteTexture are not in texels but in bytes. + constexpr uint32_t kInvalidDataSize = kBytesPerRow * (kHeight - 1) + kWidth; + + for (wgpu::TextureFormat format : kFormats) { + wgpu::Texture destination = + Create2DTexture({kWidth, kHeight, 1}, 1, format, wgpu::TextureUsage::CopyDst); + ASSERT_DEVICE_ERROR(TestWriteTexture(kInvalidDataSize, 0, kBytesPerRow, 0, + destination, 0, {0, 0, 0}, + {kWidth, kHeight, 1})); + } + } + + { + for (wgpu::TextureFormat format : kFormats) { + uint32_t validDataSize = + utils::RequiredBytesInCopy(kBytesPerRow, 0, {kWidth, kHeight, 1}, format); + wgpu::Texture destination = + Create2DTexture({kWidth, kHeight, 1}, 1, format, wgpu::TextureUsage::CopyDst); + + // Verify the return value of RequiredBytesInCopy() is exactly the minimum valid + // data size in this test. + { + uint32_t invalidDataSize = validDataSize - 1; + ASSERT_DEVICE_ERROR(TestWriteTexture(invalidDataSize, 0, kBytesPerRow, 0, + destination, 0, {0, 0, 0}, + {kWidth, kHeight, 1})); + } + + { + TestWriteTexture(validDataSize, 0, kBytesPerRow, 0, destination, 0, {0, 0, 0}, + {kWidth, kHeight, 1}); + } + } + } + } + + // Test write from data to mip map of non square texture + TEST_F(QueueWriteTextureValidationTest, WriteToMipmapOfNonSquareTexture) { + uint64_t dataSize = + utils::RequiredBytesInCopy(256, 0, {4, 2, 1}, wgpu::TextureFormat::RGBA8Unorm); + uint32_t maxMipmapLevel = 3; + wgpu::Texture destination = + Create2DTexture({4, 2, 1}, maxMipmapLevel, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst); + + // Copy to top level mip map + TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel - 1, {0, 0, 0}, + {1, 1, 1}); + // Copy to high level mip map + TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel - 2, {0, 0, 0}, + {2, 1, 1}); + // Mip level out of range + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel, + {0, 0, 0}, {1, 1, 1})); + // Copy origin out of range + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel - 2, + {1, 0, 0}, {2, 1, 1})); + // Copy size out of range + ASSERT_DEVICE_ERROR(TestWriteTexture(dataSize, 0, 256, 0, destination, maxMipmapLevel - 2, + {0, 0, 0}, {2, 2, 1})); + } + + // Test writes to multiple array layers of an uncompressed texture + TEST_F(QueueWriteTextureValidationTest, WriteToMultipleArrayLayers) { + wgpu::Texture destination = QueueWriteTextureValidationTest::Create2DTexture( + {4, 2, 5}, 1, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); + + // Write to all array layers + TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 0}, {4, 2, 5}); + + // Write to the highest array layer + TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 4}, {4, 2, 1}); + + // Write to array layers in the middle + TestWriteTextureExactDataSize(256, 2, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 1}, {4, 2, 3}); + + // Copy with a non-packed rowsPerImage + TestWriteTextureExactDataSize(256, 3, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 0}, {4, 2, 5}); + + // Copy with bytesPerRow = 500 + TestWriteTextureExactDataSize(500, 2, destination, wgpu::TextureFormat::RGBA8Unorm, + {0, 0, 1}, {4, 2, 3}); + } + + class WriteTextureTest_CompressedTextureFormats : public QueueWriteTextureValidationTest { + public: + WriteTextureTest_CompressedTextureFormats() : QueueWriteTextureValidationTest() { + device = CreateDeviceFromAdapter(adapter, {"texture_compression_bc"}); + } + + protected: + wgpu::Texture Create2DTexture(wgpu::TextureFormat format, + uint32_t mipmapLevels = 1, + uint32_t width = kWidth, + uint32_t height = kHeight) { + constexpr wgpu::TextureUsage kUsage = wgpu::TextureUsage::CopyDst; + constexpr uint32_t kArrayLayers = 1; + return QueueWriteTextureValidationTest::Create2DTexture( + {width, height, kArrayLayers}, mipmapLevels, format, kUsage, 1); + } + + void TestWriteTexture(size_t dataSize, + uint32_t dataOffset, + uint32_t dataBytesPerRow, + uint32_t dataRowsPerImage, + wgpu::Texture texture, + uint32_t textLevel, + wgpu::Origin3D textOrigin, + wgpu::Extent3D size) { + QueueWriteTextureValidationTest::TestWriteTexture(dataSize, dataOffset, dataBytesPerRow, + dataRowsPerImage, texture, textLevel, + textOrigin, size); + } + + static constexpr uint32_t kWidth = 16; + static constexpr uint32_t kHeight = 16; + }; + + // Tests to verify that data offset must be a multiple of the compressed texture blocks in bytes + TEST_F(WriteTextureTest_CompressedTextureFormats, DataOffset) { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat); + + // Valid usages of data offset. + { + uint32_t validDataOffset = utils::GetTexelBlockSizeInBytes(bcFormat); + QueueWriteTextureValidationTest::TestWriteTexture(512, validDataOffset, 256, 4, + texture, 0, {0, 0, 0}, {4, 4, 1}); + } + + // Failures on invalid data offset. + { + uint32_t kInvalidDataOffset = utils::GetTexelBlockSizeInBytes(bcFormat) / 2; + ASSERT_DEVICE_ERROR(TestWriteTexture(512, kInvalidDataOffset, 256, 4, texture, 0, + {0, 0, 0}, {4, 4, 1})); + } + } + } + + // Tests to verify that bytesPerRow must not be less than (width / blockWidth) * + // blockSizeInBytes and that it doesn't have to be a multiple of the compressed + // texture block width. + TEST_F(WriteTextureTest_CompressedTextureFormats, BytesPerRow) { + constexpr uint32_t kTestWidth = 160; + constexpr uint32_t kTestHeight = 160; + + // Failures on the BytesPerRow that is not large enough. + { + constexpr uint32_t kSmallBytesPerRow = 256; + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight); + ASSERT_DEVICE_ERROR(TestWriteTexture(1024, 0, kSmallBytesPerRow, 4, texture, 0, + {0, 0, 0}, {kTestWidth, 4, 1})); + } + } + + // Test it is valid to use a BytesPerRow that is not a multiple of 256. + { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat, 1, kTestWidth, kTestHeight); + uint32_t ValidBytesPerRow = + kTestWidth / 4 * utils::GetTexelBlockSizeInBytes(bcFormat); + ASSERT_NE(0u, ValidBytesPerRow % 256); + TestWriteTexture(1024, 0, ValidBytesPerRow, 4, texture, 0, {0, 0, 0}, + {kTestWidth, 4, 1}); + } + } + + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat); + + // Valid usage of bytesPerRow in WriteTexture with compressed texture formats. + { + constexpr uint32_t kValidBytesPerRow = 20; + TestWriteTexture(512, 0, kValidBytesPerRow, 0, texture, 0, {0, 0, 0}, {4, 4, 1}); + } + + // Valid bytesPerRow. + // Note that image width is not a multiple of blockWidth. + { + constexpr uint32_t kValidBytesPerRow = 17; + TestWriteTexture(512, 0, kValidBytesPerRow, 0, texture, 0, {0, 0, 0}, {4, 4, 1}); + } + } + } + + // Tests to verify that rowsPerImage must be a multiple of the compressed texture block height + TEST_F(WriteTextureTest_CompressedTextureFormats, RowsPerImage) { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat); + + // Valid usages of rowsPerImage in WriteTexture with compressed texture formats. + { + constexpr uint32_t kValidRowsPerImage = 8; + TestWriteTexture(512, 0, 256, kValidRowsPerImage, texture, 0, {0, 0, 0}, {4, 4, 1}); + } + + // 4 is the exact limit for rowsPerImage here. + { + constexpr uint32_t kValidRowsPerImage = 4; + TestWriteTexture(512, 0, 256, kValidRowsPerImage, texture, 0, {0, 0, 0}, {4, 4, 1}); + } + + // Failure on invalid rowsPerImage. + { + constexpr uint32_t kInvalidRowsPerImage = 2; + ASSERT_DEVICE_ERROR(TestWriteTexture(512, 0, 256, kInvalidRowsPerImage, texture, 0, + {0, 0, 0}, {4, 4, 1})); + } + } + } + + // Tests to verify that ImageOffset.x must be a multiple of the compressed texture block width + // and ImageOffset.y must be a multiple of the compressed texture block height + TEST_F(WriteTextureTest_CompressedTextureFormats, ImageOffset) { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = Create2DTexture(bcFormat); + wgpu::Texture texture2 = Create2DTexture(bcFormat); + + constexpr wgpu::Origin3D kSmallestValidOrigin3D = {4, 4, 0}; + + // Valid usages of ImageOffset in WriteTexture with compressed texture formats. + { TestWriteTexture(512, 0, 256, 4, texture, 0, kSmallestValidOrigin3D, {4, 4, 1}); } + + // Failures on invalid ImageOffset.x. + { + constexpr wgpu::Origin3D kInvalidOrigin3D = {kSmallestValidOrigin3D.x - 1, + kSmallestValidOrigin3D.y, 0}; + ASSERT_DEVICE_ERROR( + TestWriteTexture(512, 0, 256, 4, texture, 0, kInvalidOrigin3D, {4, 4, 1})); + } + + // Failures on invalid ImageOffset.y. + { + constexpr wgpu::Origin3D kInvalidOrigin3D = {kSmallestValidOrigin3D.x, + kSmallestValidOrigin3D.y - 1, 0}; + ASSERT_DEVICE_ERROR( + TestWriteTexture(512, 0, 256, 4, texture, 0, kInvalidOrigin3D, {4, 4, 1})); + } + } + } + + // Tests to verify that ImageExtent.x must be a multiple of the compressed texture block width + // and ImageExtent.y must be a multiple of the compressed texture block height + TEST_F(WriteTextureTest_CompressedTextureFormats, ImageExtent) { + constexpr uint32_t kMipmapLevels = 3; + constexpr uint32_t kTestWidth = 60; + constexpr uint32_t kTestHeight = 60; + + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = + Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight); + wgpu::Texture texture2 = + Create2DTexture(bcFormat, kMipmapLevels, kTestWidth, kTestHeight); + + constexpr wgpu::Extent3D kSmallestValidExtent3D = {4, 4, 1}; + + // Valid usages of ImageExtent in WriteTexture with compressed texture formats. + { TestWriteTexture(512, 0, 256, 8, texture, 0, {0, 0, 0}, kSmallestValidExtent3D); } + + // Valid usages of ImageExtent in WriteTexture with compressed texture formats + // and non-zero mipmap levels. + { + constexpr uint32_t kTestMipmapLevel = 2; + constexpr wgpu::Origin3D kTestOrigin = { + (kTestWidth >> kTestMipmapLevel) - kSmallestValidExtent3D.width + 1, + (kTestHeight >> kTestMipmapLevel) - kSmallestValidExtent3D.height + 1, 0}; + + TestWriteTexture(512, 0, 256, 4, texture, kTestMipmapLevel, kTestOrigin, + kSmallestValidExtent3D); + } + + // Failures on invalid ImageExtent.x. + { + constexpr wgpu::Extent3D kInValidExtent3D = {kSmallestValidExtent3D.width - 1, + kSmallestValidExtent3D.height, 1}; + ASSERT_DEVICE_ERROR( + TestWriteTexture(512, 0, 256, 4, texture, 0, {0, 0, 0}, kInValidExtent3D)); + } + + // Failures on invalid ImageExtent.y. + { + constexpr wgpu::Extent3D kInValidExtent3D = {kSmallestValidExtent3D.width, + kSmallestValidExtent3D.height - 1, 1}; + ASSERT_DEVICE_ERROR( + TestWriteTexture(512, 0, 256, 4, texture, 0, {0, 0, 0}, kInValidExtent3D)); + } + } + } + + // Test writes to multiple array layers of a compressed texture + TEST_F(WriteTextureTest_CompressedTextureFormats, WriteToMultipleArrayLayers) { + for (wgpu::TextureFormat bcFormat : utils::kBCFormats) { + wgpu::Texture texture = QueueWriteTextureValidationTest::Create2DTexture( + {12, 16, 20}, 1, bcFormat, + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc); + + // Write to all array layers + TestWriteTextureExactDataSize(256, 16, texture, bcFormat, {0, 0, 0}, {12, 16, 20}); + + // Write to the highest array layer + TestWriteTextureExactDataSize(256, 16, texture, bcFormat, {0, 0, 19}, {12, 16, 1}); + + // Write to array layers in the middle + TestWriteTextureExactDataSize(256, 16, texture, bcFormat, {0, 0, 1}, {12, 16, 18}); + + // Write touching the texture corners with a non-packed rowsPerImage + TestWriteTextureExactDataSize(256, 24, texture, bcFormat, {4, 4, 4}, {8, 12, 16}); + + // rowsPerImage needs to be a multiple of blockHeight + ASSERT_DEVICE_ERROR( + TestWriteTexture(8192, 0, 256, 6, texture, 0, {0, 0, 0}, {4, 4, 1})); + + // rowsPerImage must be a multiple of blockHeight even with an empty write + ASSERT_DEVICE_ERROR(TestWriteTexture(0, 0, 256, 2, texture, 0, {0, 0, 0}, {0, 0, 0})); + } + } + +} // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/validation/RenderBundleValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/RenderBundleValidationTests.cpp new file mode 100644 index 00000000000..4e372e9abc5 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/RenderBundleValidationTests.cpp @@ -0,0 +1,1040 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include "common/Constants.h" + +#include "utils/ComboRenderBundleEncoderDescriptor.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +namespace { + + class RenderBundleValidationTest : public ValidationTest { + protected: + void SetUp() override { + ValidationTest::SetUp(); + + vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(location = 0) in vec2 pos; + layout (set = 0, binding = 0) uniform vertexUniformBuffer { + mat2 transform; + }; + void main() { + })"); + + fsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (set = 1, binding = 0) uniform fragmentUniformBuffer { + vec4 color; + }; + layout (set = 1, binding = 1) buffer storageBuffer { + float dummy[]; + }; + void main() { + })"); + + wgpu::BindGroupLayout bgls[] = { + utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::UniformBuffer}}), + utils::MakeBindGroupLayout( + device, { + {0, wgpu::ShaderStage::Fragment, wgpu::BindingType::UniformBuffer}, + {1, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}, + })}; + + wgpu::PipelineLayoutDescriptor pipelineLayoutDesc = {}; + pipelineLayoutDesc.bindGroupLayoutCount = 2; + pipelineLayoutDesc.bindGroupLayouts = bgls; + + pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc); + + utils::ComboRenderPipelineDescriptor descriptor(device); + InitializeRenderPipelineDescriptor(&descriptor); + pipeline = device.CreateRenderPipeline(&descriptor); + + float data[8]; + wgpu::Buffer buffer = utils::CreateBufferFromData(device, data, 8 * sizeof(float), + wgpu::BufferUsage::Uniform); + + constexpr static float kVertices[] = {-1.f, 1.f, 1.f, -1.f, -1.f, 1.f}; + + vertexBuffer = utils::CreateBufferFromData(device, kVertices, sizeof(kVertices), + wgpu::BufferUsage::Vertex); + + // Dummy storage buffer. + wgpu::Buffer storageBuffer = utils::CreateBufferFromData( + device, kVertices, sizeof(kVertices), wgpu::BufferUsage::Storage); + + // Vertex buffer with storage usage for testing read+write error usage. + vertexStorageBuffer = + utils::CreateBufferFromData(device, kVertices, sizeof(kVertices), + wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Storage); + + bg0 = utils::MakeBindGroup(device, bgls[0], {{0, buffer, 0, 8 * sizeof(float)}}); + bg1 = utils::MakeBindGroup( + device, bgls[1], + {{0, buffer, 0, 4 * sizeof(float)}, {1, storageBuffer, 0, sizeof(kVertices)}}); + + bg1Vertex = utils::MakeBindGroup(device, bgls[1], + {{0, buffer, 0, 8 * sizeof(float)}, + {1, vertexStorageBuffer, 0, sizeof(kVertices)}}); + } + + void InitializeRenderPipelineDescriptor(utils::ComboRenderPipelineDescriptor* descriptor) { + descriptor->layout = pipelineLayout; + descriptor->vertexStage.module = vsModule; + descriptor->cFragmentStage.module = fsModule; + descriptor->cVertexState.vertexBufferCount = 1; + descriptor->cVertexState.cVertexBuffers[0].arrayStride = 2 * sizeof(float); + descriptor->cVertexState.cVertexBuffers[0].attributeCount = 1; + descriptor->cVertexState.cAttributes[0].format = wgpu::VertexFormat::Float2; + descriptor->cVertexState.cAttributes[0].shaderLocation = 0; + } + + wgpu::ShaderModule vsModule; + wgpu::ShaderModule fsModule; + wgpu::PipelineLayout pipelineLayout; + wgpu::RenderPipeline pipeline; + wgpu::Buffer vertexBuffer; + wgpu::Buffer vertexStorageBuffer; + wgpu::BindGroup bg0; + wgpu::BindGroup bg1; + wgpu::BindGroup bg1Vertex; + }; + +} // anonymous namespace + +// Test creating and encoding an empty render bundle. +TEST_F(RenderBundleValidationTest, Empty) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + commandEncoder.Finish(); +} + +// Test executing zero render bundles. +TEST_F(RenderBundleValidationTest, ZeroBundles) { + DummyRenderPass renderPass(device); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(0, nullptr); + pass.EndPass(); + commandEncoder.Finish(); +} + +// Test successfully creating and encoding a render bundle into a command buffer. +TEST_F(RenderBundleValidationTest, SimpleSuccess) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.Draw(3); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + commandEncoder.Finish(); +} + +// Test that render bundle debug groups must be well nested. +TEST_F(RenderBundleValidationTest, DebugGroups) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + // Test a single debug group works. + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.PushDebugGroup("group"); + renderBundleEncoder.PopDebugGroup(); + renderBundleEncoder.Finish(); + } + + // Test nested debug groups work. + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.PushDebugGroup("group"); + renderBundleEncoder.PushDebugGroup("group2"); + renderBundleEncoder.PopDebugGroup(); + renderBundleEncoder.PopDebugGroup(); + renderBundleEncoder.Finish(); + } + + // Test popping when no group is pushed is invalid. + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.PopDebugGroup(); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } + + // Test popping too many times is invalid. + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.PushDebugGroup("group"); + renderBundleEncoder.PopDebugGroup(); + renderBundleEncoder.PopDebugGroup(); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } + + // Test that a single debug group must be popped. + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.PushDebugGroup("group"); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } + + // Test that all debug groups must be popped. + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.PushDebugGroup("group"); + renderBundleEncoder.PushDebugGroup("group2"); + renderBundleEncoder.PopDebugGroup(); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } +} + +// Test render bundles do not inherit command buffer state +TEST_F(RenderBundleValidationTest, StateInheritance) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + // Render bundle does not inherit pipeline so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + pass.SetPipeline(pipeline); + + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.Draw(3); + ASSERT_DEVICE_ERROR(wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish()); + + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle does not inherit bind groups so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.Draw(3); + ASSERT_DEVICE_ERROR(wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish()); + + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle does not inherit pipeline and bind groups so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.Draw(3); + ASSERT_DEVICE_ERROR(wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish()); + + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle does not inherit buffers so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + + pass.SetVertexBuffer(0, vertexBuffer); + + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1); + renderBundleEncoder.Draw(3); + ASSERT_DEVICE_ERROR(wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish()); + + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } +} + +// Test render bundles do not persist command buffer state +TEST_F(RenderBundleValidationTest, StatePersistence) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + // Render bundle does not persist pipeline so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetPipeline(pipeline); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + pass.ExecuteBundles(1, &renderBundle); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); + pass.EndPass(); + + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle does not persist bind groups so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + pass.ExecuteBundles(1, &renderBundle); + pass.SetPipeline(pipeline); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); + pass.EndPass(); + + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle does not persist pipeline and bind groups so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + pass.ExecuteBundles(1, &renderBundle); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); + pass.EndPass(); + + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle does not persist buffers so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + pass.ExecuteBundles(1, &renderBundle); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.Draw(3); + pass.EndPass(); + + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } +} + +// Test executing render bundles clears command buffer state +TEST_F(RenderBundleValidationTest, ClearsState) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + // Render bundle clears pipeline so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + pass.SetPipeline(pipeline); + pass.ExecuteBundles(1, &renderBundle); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); + pass.EndPass(); + + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle clears bind groups so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.ExecuteBundles(1, &renderBundle); + pass.SetPipeline(pipeline); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); + pass.EndPass(); + + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle clears pipeline and bind groups so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.ExecuteBundles(1, &renderBundle); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); + pass.EndPass(); + + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Render bundle clears buffers so the draw is invalid. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + pass.SetVertexBuffer(0, vertexBuffer); + pass.ExecuteBundles(1, &renderBundle); + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.Draw(3); + pass.EndPass(); + + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Test executing 0 bundles does not clear command buffer state. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.SetVertexBuffer(0, vertexBuffer); + pass.ExecuteBundles(0, nullptr); + pass.Draw(3); + + pass.EndPass(); + commandEncoder.Finish(); + } +} + +// Test creating and encoding multiple render bundles. +TEST_F(RenderBundleValidationTest, MultipleBundles) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + wgpu::RenderBundle renderBundles[2] = {}; + + wgpu::RenderBundleEncoder renderBundleEncoder0 = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder0.SetPipeline(pipeline); + renderBundleEncoder0.SetBindGroup(0, bg0); + renderBundleEncoder0.SetBindGroup(1, bg1); + renderBundleEncoder0.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder0.Draw(3); + renderBundles[0] = renderBundleEncoder0.Finish(); + + wgpu::RenderBundleEncoder renderBundleEncoder1 = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder1.SetPipeline(pipeline); + renderBundleEncoder1.SetBindGroup(0, bg0); + renderBundleEncoder1.SetBindGroup(1, bg1); + renderBundleEncoder1.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder1.Draw(3); + renderBundles[1] = renderBundleEncoder1.Finish(); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(2, renderBundles); + pass.EndPass(); + commandEncoder.Finish(); +} + +// Test that is is valid to execute a render bundle more than once. +TEST_F(RenderBundleValidationTest, ExecuteMultipleTimes) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.Draw(3); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.ExecuteBundles(1, &renderBundle); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + commandEncoder.Finish(); +} + +// Test that it is an error to call Finish() on a render bundle encoder twice. +TEST_F(RenderBundleValidationTest, FinishTwice) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Uint; + + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.Finish(); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); +} + +// Test that it is invalid to create a render bundle with no texture formats +TEST_F(RenderBundleValidationTest, RequiresAtLeastOneTextureFormat) { + // Test failure case. + { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); + } + + // Test success with one color format. + { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Uint; + device.CreateRenderBundleEncoder(&desc); + } + + // Test success with a depth stencil format. + { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.depthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; + device.CreateRenderBundleEncoder(&desc); + } +} + +// Test that render bundle color formats cannot be set to undefined. +TEST_F(RenderBundleValidationTest, ColorFormatUndefined) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = wgpu::TextureFormat::Undefined; + ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); +} + +// Test that the render bundle depth stencil format cannot be set to undefined. +TEST_F(RenderBundleValidationTest, DepthStencilFormatUndefined) { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.depthStencilFormat = wgpu::TextureFormat::Undefined; + ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); +} + +// Test that resource usages are validated inside render bundles. +TEST_F(RenderBundleValidationTest, UsageTracking) { + DummyRenderPass renderPass(device); + + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = renderPass.attachmentFormat; + + wgpu::RenderBundle renderBundle0; + wgpu::RenderBundle renderBundle1; + + // First base case is successful. |bg1Vertex| does not reference |vertexBuffer|. + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1Vertex); + renderBundleEncoder.SetVertexBuffer(0, vertexBuffer); + renderBundleEncoder.Draw(3); + renderBundle0 = renderBundleEncoder.Finish(); + } + + // Second base case is successful. |bg1| does not reference |vertexStorageBuffer| + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1); + renderBundleEncoder.SetVertexBuffer(0, vertexStorageBuffer); + renderBundleEncoder.Draw(3); + renderBundle1 = renderBundleEncoder.Finish(); + } + + // Test that a render bundle which sets a buffer as both vertex and storage is invalid. + // |bg1Vertex| references |vertexStorageBuffer| + { + wgpu::RenderBundleEncoder renderBundleEncoder = device.CreateRenderBundleEncoder(&desc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.SetBindGroup(0, bg0); + renderBundleEncoder.SetBindGroup(1, bg1Vertex); + renderBundleEncoder.SetVertexBuffer(0, vertexStorageBuffer); + renderBundleEncoder.Draw(3); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } + + // When both render bundles are in the same pass, |vertexStorageBuffer| is used + // as both read and write usage. This is invalid. + // renderBundle0 uses |vertexStorageBuffer| as a storage buffer. + // renderBundle1 uses |vertexStorageBuffer| as a vertex buffer. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle0); + pass.ExecuteBundles(1, &renderBundle1); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // |vertexStorageBuffer| is used as both read and write usage. This is invalid. + // The render pass uses |vertexStorageBuffer| as a storage buffer. + // renderBundle1 uses |vertexStorageBuffer| as a vertex buffer. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1Vertex); + pass.SetVertexBuffer(0, vertexBuffer); + pass.Draw(3); + + pass.ExecuteBundles(1, &renderBundle1); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // |vertexStorageBuffer| is used as both read and write usage. This is invalid. + // renderBundle0 uses |vertexStorageBuffer| as a storage buffer. + // The render pass uses |vertexStorageBuffer| as a vertex buffer. + { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + + pass.ExecuteBundles(1, &renderBundle0); + + pass.SetPipeline(pipeline); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.SetVertexBuffer(0, vertexStorageBuffer); + pass.Draw(3); + + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } +} + +// Test that encoding SetPipline with an incompatible color format produces an error. +TEST_F(RenderBundleValidationTest, PipelineColorFormatMismatch) { + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 3; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + renderBundleDesc.cColorFormats[1] = wgpu::TextureFormat::RG16Float; + renderBundleDesc.cColorFormats[2] = wgpu::TextureFormat::R16Sint; + + auto SetupRenderPipelineDescForTest = [this](utils::ComboRenderPipelineDescriptor* desc) { + InitializeRenderPipelineDescriptor(desc); + desc->colorStateCount = 3; + desc->cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; + desc->cColorStates[1].format = wgpu::TextureFormat::RG16Float; + desc->cColorStates[2].format = wgpu::TextureFormat::R16Sint; + }; + + // Test the success case. + { + utils::ComboRenderPipelineDescriptor desc(device); + SetupRenderPipelineDescForTest(&desc); + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.Finish(); + } + + // Test the failure case for mismatched format types. + { + utils::ComboRenderPipelineDescriptor desc(device); + SetupRenderPipelineDescForTest(&desc); + desc.cColorStates[1].format = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + renderBundleEncoder.SetPipeline(pipeline); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } + + // Test the failure case for missing format + { + utils::ComboRenderPipelineDescriptor desc(device); + SetupRenderPipelineDescForTest(&desc); + desc.colorStateCount = 2; + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + renderBundleEncoder.SetPipeline(pipeline); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } +} + +// Test that encoding SetPipline with an incompatible depth stencil format produces an error. +TEST_F(RenderBundleValidationTest, PipelineDepthStencilFormatMismatch) { + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 1; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + renderBundleDesc.depthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; + + auto SetupRenderPipelineDescForTest = [this](utils::ComboRenderPipelineDescriptor* desc) { + InitializeRenderPipelineDescriptor(desc); + desc->colorStateCount = 1; + desc->cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; + desc->depthStencilState = &desc->cDepthStencilState; + desc->cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; + }; + + // Test the success case. + { + utils::ComboRenderPipelineDescriptor desc(device); + SetupRenderPipelineDescForTest(&desc); + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.Finish(); + } + + // Test the failure case for mismatched format. + { + utils::ComboRenderPipelineDescriptor desc(device); + SetupRenderPipelineDescForTest(&desc); + desc.cDepthStencilState.format = wgpu::TextureFormat::Depth24Plus; + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + renderBundleEncoder.SetPipeline(pipeline); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } + + // Test the failure case for missing format. + { + utils::ComboRenderPipelineDescriptor desc(device); + SetupRenderPipelineDescForTest(&desc); + desc.depthStencilState = nullptr; + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&desc); + renderBundleEncoder.SetPipeline(pipeline); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } +} + +// Test that encoding SetPipline with an incompatible sample count produces an error. +TEST_F(RenderBundleValidationTest, PipelineSampleCountMismatch) { + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 1; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + renderBundleDesc.sampleCount = 4; + + utils::ComboRenderPipelineDescriptor renderPipelineDesc(device); + InitializeRenderPipelineDescriptor(&renderPipelineDesc); + renderPipelineDesc.colorStateCount = 1; + renderPipelineDesc.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; + renderPipelineDesc.sampleCount = 4; + + // Test the success case. + { + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc); + renderBundleEncoder.SetPipeline(pipeline); + renderBundleEncoder.Finish(); + } + + // Test the failure case. + { + renderPipelineDesc.sampleCount = 1; + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&renderPipelineDesc); + renderBundleEncoder.SetPipeline(pipeline); + ASSERT_DEVICE_ERROR(renderBundleEncoder.Finish()); + } +} + +// Test that encoding ExecuteBundles with an incompatible color format produces an error. +TEST_F(RenderBundleValidationTest, RenderPassColorFormatMismatch) { + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 3; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + renderBundleDesc.cColorFormats[1] = wgpu::TextureFormat::RG16Float; + renderBundleDesc.cColorFormats[2] = wgpu::TextureFormat::R16Sint; + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + wgpu::TextureDescriptor textureDesc = {}; + textureDesc.usage = wgpu::TextureUsage::OutputAttachment; + textureDesc.size = wgpu::Extent3D({400, 400, 1}); + + textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture tex0 = device.CreateTexture(&textureDesc); + + textureDesc.format = wgpu::TextureFormat::RG16Float; + wgpu::Texture tex1 = device.CreateTexture(&textureDesc); + + textureDesc.format = wgpu::TextureFormat::R16Sint; + wgpu::Texture tex2 = device.CreateTexture(&textureDesc); + + // Test the success case + { + utils::ComboRenderPassDescriptor renderPass({ + tex0.CreateView(), + tex1.CreateView(), + tex2.CreateView(), + }); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + commandEncoder.Finish(); + } + + // Test the failure case for mismatched format + { + utils::ComboRenderPassDescriptor renderPass({ + tex0.CreateView(), + tex1.CreateView(), + tex0.CreateView(), + }); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Test the failure case for missing format + { + utils::ComboRenderPassDescriptor renderPass({ + tex0.CreateView(), + tex1.CreateView(), + }); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } +} + +// Test that encoding ExecuteBundles with an incompatible depth stencil format produces an +// error. +TEST_F(RenderBundleValidationTest, RenderPassDepthStencilFormatMismatch) { + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 1; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + renderBundleDesc.depthStencilFormat = wgpu::TextureFormat::Depth24Plus; + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + wgpu::TextureDescriptor textureDesc = {}; + textureDesc.usage = wgpu::TextureUsage::OutputAttachment; + textureDesc.size = wgpu::Extent3D({400, 400, 1}); + + textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture tex0 = device.CreateTexture(&textureDesc); + + textureDesc.format = wgpu::TextureFormat::Depth24Plus; + wgpu::Texture tex1 = device.CreateTexture(&textureDesc); + + textureDesc.format = wgpu::TextureFormat::Depth32Float; + wgpu::Texture tex2 = device.CreateTexture(&textureDesc); + + // Test the success case + { + utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}, tex1.CreateView()); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + commandEncoder.Finish(); + } + + // Test the failure case for mismatched format + { + utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}, tex2.CreateView()); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + // Test the failure case for missing format + { + utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } +} + +// Test that encoding ExecuteBundles with an incompatible sample count produces an error. +TEST_F(RenderBundleValidationTest, RenderPassSampleCountMismatch) { + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 1; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::RenderBundleEncoder renderBundleEncoder = + device.CreateRenderBundleEncoder(&renderBundleDesc); + wgpu::RenderBundle renderBundle = renderBundleEncoder.Finish(); + + wgpu::TextureDescriptor textureDesc = {}; + textureDesc.usage = wgpu::TextureUsage::OutputAttachment; + textureDesc.size = wgpu::Extent3D({400, 400, 1}); + + textureDesc.format = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture tex0 = device.CreateTexture(&textureDesc); + + textureDesc.sampleCount = 4; + wgpu::Texture tex1 = device.CreateTexture(&textureDesc); + + // Test the success case + { + utils::ComboRenderPassDescriptor renderPass({tex0.CreateView()}); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + commandEncoder.Finish(); + } + + // Test the failure case + { + utils::ComboRenderPassDescriptor renderPass({tex1.CreateView()}); + + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = commandEncoder.BeginRenderPass(&renderPass); + pass.ExecuteBundles(1, &renderBundle); + pass.EndPass(); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } +} + +// Test that color attachment texture formats must be color renderable and +// depth stencil texture formats must be depth/stencil. +TEST_F(RenderBundleValidationTest, TextureFormats) { + // Test that color formats are validated as color. + { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = wgpu::TextureFormat::Depth24PlusStencil8; + ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); + } + + // Test that color formats are validated as renderable. + { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.colorFormatsCount = 1; + desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Snorm; + ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); + } + + // Test that depth/stencil formats are validated as depth/stencil. + { + utils::ComboRenderBundleEncoderDescriptor desc = {}; + desc.depthStencilFormat = wgpu::TextureFormat::RGBA8Unorm; + ASSERT_DEVICE_ERROR(device.CreateRenderBundleEncoder(&desc)); + } + + // Don't test non-renerable depth/stencil formats because we don't have any. +} diff --git a/third_party/dawn/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp index 12f68f73c16..b1658eaffbc 100644 --- a/third_party/dawn/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp @@ -16,617 +16,823 @@ #include "common/Constants.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" -namespace { - -class RenderPassDescriptorValidationTest : public ValidationTest { - public: - void AssertBeginRenderPassSuccess(const dawn::RenderPassDescriptor* descriptor) { - dawn::CommandEncoder commandEncoder = TestBeginRenderPass(descriptor); - commandEncoder.Finish(); - } - void AssertBeginRenderPassError(const dawn::RenderPassDescriptor* descriptor) { - dawn::CommandEncoder commandEncoder = TestBeginRenderPass(descriptor); - ASSERT_DEVICE_ERROR(commandEncoder.Finish()); - } +#include - private: - dawn::CommandEncoder TestBeginRenderPass(const dawn::RenderPassDescriptor* descriptor) { - dawn::CommandEncoder commandEncoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(descriptor); - renderPassEncoder.EndPass(); - return commandEncoder; - } -}; - -dawn::Texture CreateTexture(dawn::Device& device, - dawn::TextureDimension dimension, - dawn::TextureFormat format, - uint32_t width, - uint32_t height, - uint32_t arrayLayerCount, - uint32_t mipLevelCount, - uint32_t sampleCount = 1, - dawn::TextureUsageBit usage = dawn::TextureUsageBit::OutputAttachment) { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dimension; - descriptor.size.width = width; - descriptor.size.height = height; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = arrayLayerCount; - descriptor.sampleCount = sampleCount; - descriptor.format = format; - descriptor.mipLevelCount = mipLevelCount; - descriptor.usage = usage; - - return device.CreateTexture(&descriptor); -} - -dawn::TextureView Create2DAttachment(dawn::Device& device, - uint32_t width, - uint32_t height, - dawn::TextureFormat format) { - dawn::Texture texture = CreateTexture( - device, dawn::TextureDimension::e2D, format, width, height, 1, 1); - return texture.CreateDefaultView(); -} - -// Using BeginRenderPass with no attachments isn't valid -TEST_F(RenderPassDescriptorValidationTest, Empty) { - utils::ComboRenderPassDescriptor renderPass({}, nullptr); - AssertBeginRenderPassError(&renderPass); -} - -// A render pass with only one color or one depth attachment is ok -TEST_F(RenderPassDescriptorValidationTest, OneAttachment) { - // One color attachment - { - dawn::TextureView color = Create2DAttachment(device, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm); - utils::ComboRenderPassDescriptor renderPass({color}); - - AssertBeginRenderPassSuccess(&renderPass); - } - // One depth-stencil attachment - { - dawn::TextureView depthStencil = Create2DAttachment(device, 1, 1, dawn::TextureFormat::D32FloatS8Uint); - utils::ComboRenderPassDescriptor renderPass({}, depthStencil); - - AssertBeginRenderPassSuccess(&renderPass); - } -} - -// Test OOB color attachment indices are handled -TEST_F(RenderPassDescriptorValidationTest, ColorAttachmentOutOfBounds) { - dawn::TextureView color1 = Create2DAttachment(device, 1, 1, - dawn::TextureFormat::R8G8B8A8Unorm); - dawn::TextureView color2 = Create2DAttachment(device, 1, 1, - dawn::TextureFormat::R8G8B8A8Unorm); - dawn::TextureView color3 = Create2DAttachment(device, 1, 1, - dawn::TextureFormat::R8G8B8A8Unorm); - dawn::TextureView color4 = Create2DAttachment(device, 1, 1, - dawn::TextureFormat::R8G8B8A8Unorm); - // For setting the color attachment, control case - { - utils::ComboRenderPassDescriptor renderPass({color1, color2, color3, color4}); - AssertBeginRenderPassSuccess(&renderPass); - } - // For setting the color attachment, OOB - { - // We cannot use utils::ComboRenderPassDescriptor here because it only supports at most - // kMaxColorAttachments(4) color attachments. - dawn::RenderPassColorAttachmentDescriptor colorAttachment1; - colorAttachment1.attachment = color1; - colorAttachment1.resolveTarget = nullptr; - colorAttachment1.clearColor = {0.0f, 0.0f, 0.0f, 0.0f}; - colorAttachment1.loadOp = dawn::LoadOp::Clear; - colorAttachment1.storeOp = dawn::StoreOp::Store; - - dawn::RenderPassColorAttachmentDescriptor colorAttachment2 = colorAttachment1; - dawn::RenderPassColorAttachmentDescriptor colorAttachment3 = colorAttachment1; - dawn::RenderPassColorAttachmentDescriptor colorAttachment4 = colorAttachment1; - colorAttachment2.attachment = color2; - colorAttachment3.attachment = color3; - colorAttachment4.attachment = color4; - - dawn::TextureView color5 = Create2DAttachment(device, 1, 1, - dawn::TextureFormat::R8G8B8A8Unorm); - dawn::RenderPassColorAttachmentDescriptor colorAttachment5 = colorAttachment1; - colorAttachment5.attachment = color5; - - dawn::RenderPassColorAttachmentDescriptor* colorAttachments[] = {&colorAttachment1, - &colorAttachment2, - &colorAttachment3, - &colorAttachment4, - &colorAttachment5}; - dawn::RenderPassDescriptor renderPass; - renderPass.colorAttachmentCount = kMaxColorAttachments + 1; - renderPass.colorAttachments = colorAttachments; - renderPass.depthStencilAttachment = nullptr; - AssertBeginRenderPassError(&renderPass); - } -} - -// Attachments must have the same size -TEST_F(RenderPassDescriptorValidationTest, SizeMustMatch) { - dawn::TextureView color1x1A = Create2DAttachment(device, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm); - dawn::TextureView color1x1B = Create2DAttachment(device, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm); - dawn::TextureView color2x2 = Create2DAttachment(device, 2, 2, dawn::TextureFormat::R8G8B8A8Unorm); - - dawn::TextureView depthStencil1x1 = Create2DAttachment(device, 1, 1, dawn::TextureFormat::D32FloatS8Uint); - dawn::TextureView depthStencil2x2 = Create2DAttachment(device, 2, 2, dawn::TextureFormat::D32FloatS8Uint); - - // Control case: all the same size (1x1) - { - utils::ComboRenderPassDescriptor renderPass({color1x1A, color1x1B}, depthStencil1x1); - AssertBeginRenderPassSuccess(&renderPass); - } - - // One of the color attachments has a different size - { - utils::ComboRenderPassDescriptor renderPass({color1x1A, color2x2}); - AssertBeginRenderPassError(&renderPass); - } +namespace { - // The depth stencil attachment has a different size - { - utils::ComboRenderPassDescriptor renderPass({color1x1A, color1x1B}, depthStencil2x2); + class RenderPassDescriptorValidationTest : public ValidationTest { + public: + void AssertBeginRenderPassSuccess(const wgpu::RenderPassDescriptor* descriptor) { + wgpu::CommandEncoder commandEncoder = TestBeginRenderPass(descriptor); + commandEncoder.Finish(); + } + void AssertBeginRenderPassError(const wgpu::RenderPassDescriptor* descriptor) { + wgpu::CommandEncoder commandEncoder = TestBeginRenderPass(descriptor); + ASSERT_DEVICE_ERROR(commandEncoder.Finish()); + } + + private: + wgpu::CommandEncoder TestBeginRenderPass(const wgpu::RenderPassDescriptor* descriptor) { + wgpu::CommandEncoder commandEncoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = commandEncoder.BeginRenderPass(descriptor); + renderPassEncoder.EndPass(); + return commandEncoder; + } + }; + + wgpu::Texture CreateTexture(wgpu::Device& device, + wgpu::TextureDimension dimension, + wgpu::TextureFormat format, + uint32_t width, + uint32_t height, + uint32_t arrayLayerCount, + uint32_t mipLevelCount, + uint32_t sampleCount = 1, + wgpu::TextureUsage usage = wgpu::TextureUsage::OutputAttachment) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = dimension; + descriptor.size.width = width; + descriptor.size.height = height; + descriptor.size.depth = arrayLayerCount; + descriptor.sampleCount = sampleCount; + descriptor.format = format; + descriptor.mipLevelCount = mipLevelCount; + descriptor.usage = usage; + + return device.CreateTexture(&descriptor); + } + + wgpu::TextureView Create2DAttachment(wgpu::Device& device, + uint32_t width, + uint32_t height, + wgpu::TextureFormat format) { + wgpu::Texture texture = + CreateTexture(device, wgpu::TextureDimension::e2D, format, width, height, 1, 1); + return texture.CreateView(); + } + + // Using BeginRenderPass with no attachments isn't valid + TEST_F(RenderPassDescriptorValidationTest, Empty) { + utils::ComboRenderPassDescriptor renderPass({}, nullptr); AssertBeginRenderPassError(&renderPass); } -} - -// Attachments formats must match whether they are used for color or depth-stencil -TEST_F(RenderPassDescriptorValidationTest, FormatMismatch) { - dawn::TextureView color = Create2DAttachment(device, 1, 1, dawn::TextureFormat::R8G8B8A8Unorm); - dawn::TextureView depthStencil = Create2DAttachment(device, 1, 1, dawn::TextureFormat::D32FloatS8Uint); - // Using depth-stencil for color - { - utils::ComboRenderPassDescriptor renderPass({depthStencil}); - AssertBeginRenderPassError(&renderPass); - } + // A render pass with only one color or one depth attachment is ok + TEST_F(RenderPassDescriptorValidationTest, OneAttachment) { + // One color attachment + { + wgpu::TextureView color = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + utils::ComboRenderPassDescriptor renderPass({color}); + + AssertBeginRenderPassSuccess(&renderPass); + } + // One depth-stencil attachment + { + wgpu::TextureView depthStencil = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); + utils::ComboRenderPassDescriptor renderPass({}, depthStencil); + + AssertBeginRenderPassSuccess(&renderPass); + } + } + + // Test OOB color attachment indices are handled + TEST_F(RenderPassDescriptorValidationTest, ColorAttachmentOutOfBounds) { + wgpu::TextureView color0 = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + wgpu::TextureView color1 = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + wgpu::TextureView color2 = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + wgpu::TextureView color3 = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + // For setting the color attachment, control case + { + utils::ComboRenderPassDescriptor renderPass({color0, color1, color2, color3}); + AssertBeginRenderPassSuccess(&renderPass); + } + // For setting the color attachment, OOB + { + // We cannot use utils::ComboRenderPassDescriptor here because it only supports at most + // kMaxColorAttachments(4) color attachments. + std::array colorAttachments; + colorAttachments[0].attachment = color0; + colorAttachments[0].resolveTarget = nullptr; + colorAttachments[0].clearColor = {0.0f, 0.0f, 0.0f, 0.0f}; + colorAttachments[0].loadOp = wgpu::LoadOp::Clear; + colorAttachments[0].storeOp = wgpu::StoreOp::Store; + + colorAttachments[1] = colorAttachments[0]; + colorAttachments[1].attachment = color1; + + colorAttachments[2] = colorAttachments[0]; + colorAttachments[2].attachment = color2; + + colorAttachments[3] = colorAttachments[0]; + colorAttachments[3].attachment = color3; + + colorAttachments[4] = colorAttachments[0]; + colorAttachments[4].attachment = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + + wgpu::RenderPassDescriptor renderPass; + renderPass.colorAttachmentCount = 5; + renderPass.colorAttachments = colorAttachments.data(); + renderPass.depthStencilAttachment = nullptr; + AssertBeginRenderPassError(&renderPass); + } + } + + // Attachments must have the same size + TEST_F(RenderPassDescriptorValidationTest, SizeMustMatch) { + wgpu::TextureView color1x1A = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + wgpu::TextureView color1x1B = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + wgpu::TextureView color2x2 = + Create2DAttachment(device, 2, 2, wgpu::TextureFormat::RGBA8Unorm); + + wgpu::TextureView depthStencil1x1 = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); + wgpu::TextureView depthStencil2x2 = + Create2DAttachment(device, 2, 2, wgpu::TextureFormat::Depth24PlusStencil8); + + // Control case: all the same size (1x1) + { + utils::ComboRenderPassDescriptor renderPass({color1x1A, color1x1B}, depthStencil1x1); + AssertBeginRenderPassSuccess(&renderPass); + } + + // One of the color attachments has a different size + { + utils::ComboRenderPassDescriptor renderPass({color1x1A, color2x2}); + AssertBeginRenderPassError(&renderPass); + } + + // The depth stencil attachment has a different size + { + utils::ComboRenderPassDescriptor renderPass({color1x1A, color1x1B}, depthStencil2x2); + AssertBeginRenderPassError(&renderPass); + } + } + + // Attachments formats must match whether they are used for color or depth-stencil + TEST_F(RenderPassDescriptorValidationTest, FormatMismatch) { + wgpu::TextureView color = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + wgpu::TextureView depthStencil = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); + + // Using depth-stencil for color + { + utils::ComboRenderPassDescriptor renderPass({depthStencil}); + AssertBeginRenderPassError(&renderPass); + } + + // Using color for depth-stencil + { + utils::ComboRenderPassDescriptor renderPass({}, color); + AssertBeginRenderPassError(&renderPass); + } + } + + // Depth and stencil storeOps must match + TEST_F(RenderPassDescriptorValidationTest, DepthStencilStoreOpMismatch) { + constexpr uint32_t kArrayLayers = 1; + constexpr uint32_t kLevelCount = 1; + constexpr uint32_t kSize = 32; + constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; + constexpr wgpu::TextureFormat kDepthStencilFormat = + wgpu::TextureFormat::Depth24PlusStencil8; + + wgpu::Texture colorTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount); + wgpu::Texture depthStencilTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, + kArrayLayers, kLevelCount); + + wgpu::TextureViewDescriptor descriptor; + descriptor.dimension = wgpu::TextureViewDimension::e2D; + descriptor.baseArrayLayer = 0; + descriptor.arrayLayerCount = kArrayLayers; + descriptor.baseMipLevel = 0; + descriptor.mipLevelCount = kLevelCount; + wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); + wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); + + // StoreOps mismatch causing the render pass to error + { + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; + AssertBeginRenderPassError(&renderPass); + } + + // StoreOps match so render pass is a success + { + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + AssertBeginRenderPassSuccess(&renderPass); + } + + // StoreOps match so render pass is a success + { + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; + AssertBeginRenderPassSuccess(&renderPass); + } + } + + // Currently only texture views with arrayLayerCount == 1 are allowed to be color and depth + // stencil attachments + TEST_F(RenderPassDescriptorValidationTest, TextureViewLayerCountForColorAndDepthStencil) { + constexpr uint32_t kLevelCount = 1; + constexpr uint32_t kSize = 32; + constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; + constexpr wgpu::TextureFormat kDepthStencilFormat = + wgpu::TextureFormat::Depth24PlusStencil8; + + constexpr uint32_t kArrayLayers = 10; + + wgpu::Texture colorTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount); + wgpu::Texture depthStencilTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, + kArrayLayers, kLevelCount); + + wgpu::TextureViewDescriptor baseDescriptor; + baseDescriptor.dimension = wgpu::TextureViewDimension::e2DArray; + baseDescriptor.baseArrayLayer = 0; + baseDescriptor.arrayLayerCount = kArrayLayers; + baseDescriptor.baseMipLevel = 0; + baseDescriptor.mipLevelCount = kLevelCount; + + // Using 2D array texture view with arrayLayerCount > 1 is not allowed for color + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kColorFormat; + descriptor.arrayLayerCount = 5; + + wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({colorTextureView}); + AssertBeginRenderPassError(&renderPass); + } + + // Using 2D array texture view with arrayLayerCount > 1 is not allowed for depth stencil + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kDepthStencilFormat; + descriptor.arrayLayerCount = 5; + + wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + AssertBeginRenderPassError(&renderPass); + } + + // Using 2D array texture view that covers the first layer of the texture is OK for color + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kColorFormat; + descriptor.baseArrayLayer = 0; + descriptor.arrayLayerCount = 1; + + wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({colorTextureView}); + AssertBeginRenderPassSuccess(&renderPass); + } + + // Using 2D array texture view that covers the first layer is OK for depth stencil + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kDepthStencilFormat; + descriptor.baseArrayLayer = 0; + descriptor.arrayLayerCount = 1; + + wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + AssertBeginRenderPassSuccess(&renderPass); + } + + // Using 2D array texture view that covers the last layer is OK for color + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kColorFormat; + descriptor.baseArrayLayer = kArrayLayers - 1; + descriptor.arrayLayerCount = 1; + + wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({colorTextureView}); + AssertBeginRenderPassSuccess(&renderPass); + } + + // Using 2D array texture view that covers the last layer is OK for depth stencil + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kDepthStencilFormat; + descriptor.baseArrayLayer = kArrayLayers - 1; + descriptor.arrayLayerCount = 1; + + wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + AssertBeginRenderPassSuccess(&renderPass); + } + } + + // Only 2D texture views with mipLevelCount == 1 are allowed to be color attachments + TEST_F(RenderPassDescriptorValidationTest, TextureViewLevelCountForColorAndDepthStencil) { + constexpr uint32_t kArrayLayers = 1; + constexpr uint32_t kSize = 32; + constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; + constexpr wgpu::TextureFormat kDepthStencilFormat = + wgpu::TextureFormat::Depth24PlusStencil8; + + constexpr uint32_t kLevelCount = 4; + + wgpu::Texture colorTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount); + wgpu::Texture depthStencilTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, + kArrayLayers, kLevelCount); + + wgpu::TextureViewDescriptor baseDescriptor; + baseDescriptor.dimension = wgpu::TextureViewDimension::e2D; + baseDescriptor.baseArrayLayer = 0; + baseDescriptor.arrayLayerCount = kArrayLayers; + baseDescriptor.baseMipLevel = 0; + baseDescriptor.mipLevelCount = kLevelCount; + + // Using 2D texture view with mipLevelCount > 1 is not allowed for color + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kColorFormat; + descriptor.mipLevelCount = 2; + + wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({colorTextureView}); + AssertBeginRenderPassError(&renderPass); + } + + // Using 2D texture view with mipLevelCount > 1 is not allowed for depth stencil + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kDepthStencilFormat; + descriptor.mipLevelCount = 2; + + wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + AssertBeginRenderPassError(&renderPass); + } + + // Using 2D texture view that covers the first level of the texture is OK for color + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kColorFormat; + descriptor.baseMipLevel = 0; + descriptor.mipLevelCount = 1; + + wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({colorTextureView}); + AssertBeginRenderPassSuccess(&renderPass); + } + + // Using 2D texture view that covers the first level is OK for depth stencil + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kDepthStencilFormat; + descriptor.baseMipLevel = 0; + descriptor.mipLevelCount = 1; + + wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + AssertBeginRenderPassSuccess(&renderPass); + } + + // Using 2D texture view that covers the last level is OK for color + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kColorFormat; + descriptor.baseMipLevel = kLevelCount - 1; + descriptor.mipLevelCount = 1; + + wgpu::TextureView colorTextureView = colorTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({colorTextureView}); + AssertBeginRenderPassSuccess(&renderPass); + } + + // Using 2D texture view that covers the last level is OK for depth stencil + { + wgpu::TextureViewDescriptor descriptor = baseDescriptor; + descriptor.format = kDepthStencilFormat; + descriptor.baseMipLevel = kLevelCount - 1; + descriptor.mipLevelCount = 1; + + wgpu::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); + utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + AssertBeginRenderPassSuccess(&renderPass); + } + } + + // It is not allowed to set resolve target when the color attachment is non-multisampled. + TEST_F(RenderPassDescriptorValidationTest, NonMultisampledColorWithResolveTarget) { + static constexpr uint32_t kArrayLayers = 1; + static constexpr uint32_t kLevelCount = 1; + static constexpr uint32_t kSize = 32; + static constexpr uint32_t kSampleCount = 1; + static constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::Texture colorTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount, kSampleCount); + wgpu::Texture resolveTargetTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount, kSampleCount); + wgpu::TextureView colorTextureView = colorTexture.CreateView(); + wgpu::TextureView resolveTargetTextureView = resolveTargetTexture.CreateView(); - // Using color for depth-stencil - { - utils::ComboRenderPassDescriptor renderPass({}, color); - AssertBeginRenderPassError(&renderPass); - } -} - -// Currently only texture views with arrayLayerCount == 1 are allowed to be color and depth stencil -// attachments -TEST_F(RenderPassDescriptorValidationTest, TextureViewLayerCountForColorAndDepthStencil) { - constexpr uint32_t kLevelCount = 1; - constexpr uint32_t kSize = 32; - constexpr dawn::TextureFormat kColorFormat = dawn::TextureFormat::R8G8B8A8Unorm; - constexpr dawn::TextureFormat kDepthStencilFormat = dawn::TextureFormat::D32FloatS8Uint; - - constexpr uint32_t kArrayLayers = 10; - - dawn::Texture colorTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, kLevelCount); - dawn::Texture depthStencilTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, kArrayLayers, - kLevelCount); - - dawn::TextureViewDescriptor baseDescriptor; - baseDescriptor.dimension = dawn::TextureViewDimension::e2DArray; - baseDescriptor.baseArrayLayer = 0; - baseDescriptor.arrayLayerCount = kArrayLayers; - baseDescriptor.baseMipLevel = 0; - baseDescriptor.mipLevelCount = kLevelCount; - - // Using 2D array texture view with arrayLayerCount > 1 is not allowed for color - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kColorFormat; - descriptor.arrayLayerCount = 5; - - dawn::TextureView colorTextureView = colorTexture.CreateView(&descriptor); utils::ComboRenderPassDescriptor renderPass({colorTextureView}); + renderPass.cColorAttachments[0].resolveTarget = resolveTargetTextureView; AssertBeginRenderPassError(&renderPass); } - // Using 2D array texture view with arrayLayerCount > 1 is not allowed for depth stencil - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kDepthStencilFormat; - descriptor.arrayLayerCount = 5; + class MultisampledRenderPassDescriptorValidationTest + : public RenderPassDescriptorValidationTest { + public: + utils::ComboRenderPassDescriptor CreateMultisampledRenderPass() { + return utils::ComboRenderPassDescriptor({CreateMultisampledColorTextureView()}); + } + + wgpu::TextureView CreateMultisampledColorTextureView() { + return CreateColorTextureView(kSampleCount); + } + + wgpu::TextureView CreateNonMultisampledColorTextureView() { + return CreateColorTextureView(1); + } + + static constexpr uint32_t kArrayLayers = 1; + static constexpr uint32_t kLevelCount = 1; + static constexpr uint32_t kSize = 32; + static constexpr uint32_t kSampleCount = 4; + static constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; + + private: + wgpu::TextureView CreateColorTextureView(uint32_t sampleCount) { + wgpu::Texture colorTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount, sampleCount); + + return colorTexture.CreateView(); + } + }; + + // Tests on the use of multisampled textures as color attachments + TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledColorAttachments) { + wgpu::TextureView colorTextureView = CreateNonMultisampledColorTextureView(); + wgpu::TextureView resolveTargetTextureView = CreateNonMultisampledColorTextureView(); + wgpu::TextureView multisampledColorTextureView = CreateMultisampledColorTextureView(); + + // It is allowed to use a multisampled color attachment without setting resolve target. + { + utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); + AssertBeginRenderPassSuccess(&renderPass); + } + + // It is not allowed to use multiple color attachments with different sample counts. + { + utils::ComboRenderPassDescriptor renderPass( + {multisampledColorTextureView, colorTextureView}); + AssertBeginRenderPassError(&renderPass); + } + } + + // It is not allowed to use a multisampled resolve target. + TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledResolveTarget) { + wgpu::TextureView multisampledResolveTargetView = CreateMultisampledColorTextureView(); - dawn::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); + renderPass.cColorAttachments[0].resolveTarget = multisampledResolveTargetView; AssertBeginRenderPassError(&renderPass); } - // Using 2D array texture view that covers the first layer of the texture is OK for color - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kColorFormat; - descriptor.baseArrayLayer = 0; - descriptor.arrayLayerCount = 1; - - dawn::TextureView colorTextureView = colorTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({colorTextureView}); - AssertBeginRenderPassSuccess(&renderPass); - } - - // Using 2D array texture view that covers the first layer is OK for depth stencil - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kDepthStencilFormat; - descriptor.baseArrayLayer = 0; - descriptor.arrayLayerCount = 1; - - dawn::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); - AssertBeginRenderPassSuccess(&renderPass); - } - - // Using 2D array texture view that covers the last layer is OK for color - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kColorFormat; - descriptor.baseArrayLayer = kArrayLayers - 1; - descriptor.arrayLayerCount = 1; + // It is not allowed to use a resolve target with array layer count > 1. + TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetArrayLayerMoreThanOne) { + constexpr uint32_t kArrayLayers2 = 2; + wgpu::Texture resolveTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers2, kLevelCount); + wgpu::TextureView resolveTextureView = resolveTexture.CreateView(); - dawn::TextureView colorTextureView = colorTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({colorTextureView}); - AssertBeginRenderPassSuccess(&renderPass); - } - - // Using 2D array texture view that covers the last layer is OK for depth stencil - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kDepthStencilFormat; - descriptor.baseArrayLayer = kArrayLayers - 1; - descriptor.arrayLayerCount = 1; - - dawn::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); - AssertBeginRenderPassSuccess(&renderPass); - } -} - -// Only 2D texture views with mipLevelCount == 1 are allowed to be color attachments -TEST_F(RenderPassDescriptorValidationTest, TextureViewLevelCountForColorAndDepthStencil) { - constexpr uint32_t kArrayLayers = 1; - constexpr uint32_t kSize = 32; - constexpr dawn::TextureFormat kColorFormat = dawn::TextureFormat::R8G8B8A8Unorm; - constexpr dawn::TextureFormat kDepthStencilFormat = dawn::TextureFormat::D32FloatS8Uint; - - constexpr uint32_t kLevelCount = 4; - - dawn::Texture colorTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, kLevelCount); - dawn::Texture depthStencilTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, kArrayLayers, - kLevelCount); - - dawn::TextureViewDescriptor baseDescriptor; - baseDescriptor.dimension = dawn::TextureViewDimension::e2D; - baseDescriptor.baseArrayLayer = 0; - baseDescriptor.arrayLayerCount = kArrayLayers; - baseDescriptor.baseMipLevel = 0; - baseDescriptor.mipLevelCount = kLevelCount; - - // Using 2D texture view with mipLevelCount > 1 is not allowed for color - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kColorFormat; - descriptor.mipLevelCount = 2; - - dawn::TextureView colorTextureView = colorTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({colorTextureView}); + utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); + renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; AssertBeginRenderPassError(&renderPass); } - // Using 2D texture view with mipLevelCount > 1 is not allowed for depth stencil - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kDepthStencilFormat; - descriptor.mipLevelCount = 2; + // It is not allowed to use a resolve target with mipmap level count > 1. + TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetMipmapLevelMoreThanOne) { + constexpr uint32_t kLevelCount2 = 2; + wgpu::Texture resolveTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount2); + wgpu::TextureView resolveTextureView = resolveTexture.CreateView(); - dawn::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); + utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); + renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; AssertBeginRenderPassError(&renderPass); } - // Using 2D texture view that covers the first level of the texture is OK for color - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kColorFormat; - descriptor.baseMipLevel = 0; - descriptor.mipLevelCount = 1; + // It is not allowed to use a resolve target which is created from a texture whose usage does + // not include wgpu::TextureUsage::OutputAttachment. + TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetUsageNoOutputAttachment) { + constexpr wgpu::TextureUsage kUsage = + wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + wgpu::Texture nonColorUsageResolveTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount, 1, kUsage); + wgpu::TextureView nonColorUsageResolveTextureView = + nonColorUsageResolveTexture.CreateView(); - dawn::TextureView colorTextureView = colorTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({colorTextureView}); - AssertBeginRenderPassSuccess(&renderPass); - } - - // Using 2D texture view that covers the first level is OK for depth stencil - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kDepthStencilFormat; - descriptor.baseMipLevel = 0; - descriptor.mipLevelCount = 1; - - dawn::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); - AssertBeginRenderPassSuccess(&renderPass); - } - - // Using 2D texture view that covers the last level is OK for color - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kColorFormat; - descriptor.baseMipLevel = kLevelCount - 1; - descriptor.mipLevelCount = 1; - - dawn::TextureView colorTextureView = colorTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({colorTextureView}); - AssertBeginRenderPassSuccess(&renderPass); - } - - // Using 2D texture view that covers the last level is OK for depth stencil - { - dawn::TextureViewDescriptor descriptor = baseDescriptor; - descriptor.format = kDepthStencilFormat; - descriptor.baseMipLevel = kLevelCount - 1; - descriptor.mipLevelCount = 1; - - dawn::TextureView depthStencilView = depthStencilTexture.CreateView(&descriptor); - utils::ComboRenderPassDescriptor renderPass({}, depthStencilView); - AssertBeginRenderPassSuccess(&renderPass); - } -} - -// It is not allowed to set resolve target when the color attachment is non-multisampled. -TEST_F(RenderPassDescriptorValidationTest, NonMultisampledColorWithResolveTarget) { - static constexpr uint32_t kArrayLayers = 1; - static constexpr uint32_t kLevelCount = 1; - static constexpr uint32_t kSize = 32; - static constexpr uint32_t kSampleCount = 1; - static constexpr dawn::TextureFormat kColorFormat = dawn::TextureFormat::R8G8B8A8Unorm; - - dawn::Texture colorTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, - kLevelCount, kSampleCount); - dawn::Texture resolveTargetTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, - kLevelCount, kSampleCount); - dawn::TextureView colorTextureView = colorTexture.CreateDefaultView(); - dawn::TextureView resolveTargetTextureView = resolveTargetTexture.CreateDefaultView(); - - utils::ComboRenderPassDescriptor renderPass({colorTextureView}); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = resolveTargetTextureView; - AssertBeginRenderPassError(&renderPass); -} - -class MultisampledRenderPassDescriptorValidationTest : public RenderPassDescriptorValidationTest { - public: - utils::ComboRenderPassDescriptor CreateMultisampledRenderPass() { - return utils::ComboRenderPassDescriptor({CreateMultisampledColorTextureView()}); - } - - dawn::TextureView CreateMultisampledColorTextureView() { - return CreateColorTextureView(kSampleCount); - } - - dawn::TextureView CreateNonMultisampledColorTextureView() { - return CreateColorTextureView(1); - } - - static constexpr uint32_t kArrayLayers = 1; - static constexpr uint32_t kLevelCount = 1; - static constexpr uint32_t kSize = 32; - static constexpr uint32_t kSampleCount = 4; - static constexpr dawn::TextureFormat kColorFormat = dawn::TextureFormat::R8G8B8A8Unorm; - - private: - dawn::TextureView CreateColorTextureView(uint32_t sampleCount) { - dawn::Texture colorTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, - kLevelCount, sampleCount); - - return colorTexture.CreateDefaultView(); - } -}; - -// Tests on the use of multisampled textures as color attachments -TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledColorAttachments) { - dawn::TextureView colorTextureView = CreateNonMultisampledColorTextureView(); - dawn::TextureView resolveTargetTextureView = CreateNonMultisampledColorTextureView(); - dawn::TextureView multisampledColorTextureView = CreateMultisampledColorTextureView(); - - // It is allowed to use a multisampled color attachment without setting resolve target. - { utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - AssertBeginRenderPassSuccess(&renderPass); - } - - // It is not allowed to use multiple color attachments with different sample counts. - { - utils::ComboRenderPassDescriptor renderPass( - {multisampledColorTextureView, colorTextureView}); + renderPass.cColorAttachments[0].resolveTarget = nonColorUsageResolveTextureView; AssertBeginRenderPassError(&renderPass); } -} - -// It is not allowed to use a multisampled resolve target. -TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledResolveTarget) { - dawn::TextureView multisampledResolveTargetView = CreateMultisampledColorTextureView(); - - utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = multisampledResolveTargetView; - AssertBeginRenderPassError(&renderPass); -} - -// It is not allowed to use a resolve target with array layer count > 1. -TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetArrayLayerMoreThanOne) { - constexpr uint32_t kArrayLayers2 = 2; - dawn::Texture resolveTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers2, - kLevelCount); - dawn::TextureView resolveTextureView = resolveTexture.CreateDefaultView(); - - utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = resolveTextureView; - AssertBeginRenderPassError(&renderPass); -} - -// It is not allowed to use a resolve target with mipmap level count > 1. -TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetMipmapLevelMoreThanOne) { - constexpr uint32_t kLevelCount2 = 2; - dawn::Texture resolveTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, - kLevelCount2); - dawn::TextureView resolveTextureView = resolveTexture.CreateDefaultView(); - - utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = resolveTextureView; - AssertBeginRenderPassError(&renderPass); -} - -// It is not allowed to use a resolve target which is created from a texture whose usage does not -// include dawn::TextureUsageBit::OutputAttachment. -TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetUsageNoOutputAttachment) { - constexpr dawn::TextureUsageBit kUsage = - dawn::TextureUsageBit::TransferDst | dawn::TextureUsageBit::TransferSrc; - dawn::Texture nonColorUsageResolveTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, - kLevelCount, 1, kUsage); - dawn::TextureView nonColorUsageResolveTextureView = - nonColorUsageResolveTexture.CreateDefaultView(); - - utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = nonColorUsageResolveTextureView; - AssertBeginRenderPassError(&renderPass); -} - -// It is not allowed to use a resolve target which is in error state. -TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetInErrorState) { - dawn::Texture resolveTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize, kSize, kArrayLayers, - kLevelCount); - dawn::TextureViewDescriptor errorTextureView; - errorTextureView.dimension = dawn::TextureViewDimension::e2D; - errorTextureView.format = kColorFormat; - errorTextureView.baseArrayLayer = kArrayLayers + 1; - ASSERT_DEVICE_ERROR( - dawn::TextureView errorResolveTarget = - resolveTexture.CreateView(&errorTextureView)); - - utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = errorResolveTarget; - AssertBeginRenderPassError(&renderPass); -} - -// It is allowed to use a multisampled color attachment and a non-multisampled resolve target. -TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledColorWithResolveTarget) { - dawn::TextureView resolveTargetTextureView = CreateNonMultisampledColorTextureView(); - - utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = resolveTargetTextureView; - AssertBeginRenderPassSuccess(&renderPass); -} - -// It is not allowed to use a resolve target in a format different from the color attachment. -TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetDifferentFormat) { - constexpr dawn::TextureFormat kColorFormat2 = dawn::TextureFormat::B8G8R8A8Unorm; - dawn::Texture resolveTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat2, kSize, kSize, kArrayLayers, - kLevelCount); - dawn::TextureView resolveTextureView = resolveTexture.CreateDefaultView(); - - utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = resolveTextureView; - AssertBeginRenderPassError(&renderPass); -} - -// Tests on the size of the resolve target. -TEST_F(MultisampledRenderPassDescriptorValidationTest, ColorAttachmentResolveTargetCompatibility) { - constexpr uint32_t kSize2 = kSize * 2; - dawn::Texture resolveTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kColorFormat, kSize2, kSize2, kArrayLayers, - kLevelCount + 1); - - dawn::TextureViewDescriptor textureViewDescriptor; - textureViewDescriptor.nextInChain = nullptr; - textureViewDescriptor.dimension = dawn::TextureViewDimension::e2D; - textureViewDescriptor.format = kColorFormat; - textureViewDescriptor.mipLevelCount = 1; - textureViewDescriptor.baseArrayLayer = 0; - textureViewDescriptor.arrayLayerCount = 1; - - { - dawn::TextureViewDescriptor firstMipLevelDescriptor = textureViewDescriptor; - firstMipLevelDescriptor.baseMipLevel = 0; - - dawn::TextureView resolveTextureView = - resolveTexture.CreateView(&firstMipLevelDescriptor); + + // It is not allowed to use a resolve target which is in error state. + TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetInErrorState) { + wgpu::Texture resolveTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize, kSize, + kArrayLayers, kLevelCount); + wgpu::TextureViewDescriptor errorTextureView; + errorTextureView.dimension = wgpu::TextureViewDimension::e2D; + errorTextureView.format = kColorFormat; + errorTextureView.baseArrayLayer = kArrayLayers + 1; + ASSERT_DEVICE_ERROR(wgpu::TextureView errorResolveTarget = + resolveTexture.CreateView(&errorTextureView)); utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = resolveTextureView; + renderPass.cColorAttachments[0].resolveTarget = errorResolveTarget; AssertBeginRenderPassError(&renderPass); } - { - dawn::TextureViewDescriptor secondMipLevelDescriptor = textureViewDescriptor; - secondMipLevelDescriptor.baseMipLevel = 1; - - dawn::TextureView resolveTextureView = - resolveTexture.CreateView(&secondMipLevelDescriptor); + // It is allowed to use a multisampled color attachment and a non-multisampled resolve target. + TEST_F(MultisampledRenderPassDescriptorValidationTest, MultisampledColorWithResolveTarget) { + wgpu::TextureView resolveTargetTextureView = CreateNonMultisampledColorTextureView(); utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); - renderPass.cColorAttachmentsInfoPtr[0]->resolveTarget = resolveTextureView; + renderPass.cColorAttachments[0].resolveTarget = resolveTargetTextureView; AssertBeginRenderPassSuccess(&renderPass); } -} - -// Tests on the sample count of depth stencil attachment. -TEST_F(MultisampledRenderPassDescriptorValidationTest, DepthStencilAttachmentSampleCount) { - constexpr dawn::TextureFormat kDepthStencilFormat = dawn::TextureFormat::D32FloatS8Uint; - dawn::Texture multisampledDepthStencilTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, kArrayLayers, - kLevelCount, kSampleCount); - dawn::TextureView multisampledDepthStencilTextureView = - multisampledDepthStencilTexture.CreateDefaultView(); - - // It is not allowed to use a depth stencil attachment whose sample count is different from the - // one of the color attachment. - { - dawn::Texture depthStencilTexture = CreateTexture( - device, dawn::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, kArrayLayers, - kLevelCount); - dawn::TextureView depthStencilTextureView = depthStencilTexture.CreateDefaultView(); - - utils::ComboRenderPassDescriptor renderPass( - {CreateMultisampledColorTextureView()}, depthStencilTextureView); - AssertBeginRenderPassError(&renderPass); - } - - { - utils::ComboRenderPassDescriptor renderPass( - {CreateNonMultisampledColorTextureView()}, multisampledDepthStencilTextureView); - AssertBeginRenderPassError(&renderPass); - } - // It is allowed to use a multisampled depth stencil attachment whose sample count is equal to - // the one of the color attachment. - { - utils::ComboRenderPassDescriptor renderPass( - {CreateMultisampledColorTextureView()}, multisampledDepthStencilTextureView); - AssertBeginRenderPassSuccess(&renderPass); - } + // It is not allowed to use a resolve target in a format different from the color attachment. + TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetDifferentFormat) { + constexpr wgpu::TextureFormat kColorFormat2 = wgpu::TextureFormat::BGRA8Unorm; + wgpu::Texture resolveTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat2, kSize, kSize, + kArrayLayers, kLevelCount); + wgpu::TextureView resolveTextureView = resolveTexture.CreateView(); - // It is allowed to use a multisampled depth stencil attachment while there is no color - // attachment. - { - utils::ComboRenderPassDescriptor renderPass({}, multisampledDepthStencilTextureView); - AssertBeginRenderPassSuccess(&renderPass); + utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); + renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; + AssertBeginRenderPassError(&renderPass); } -} - -// TODO(cwallez@chromium.org): Constraints on attachment aliasing? -} // anonymous namespace + // Tests on the size of the resolve target. + TEST_F(MultisampledRenderPassDescriptorValidationTest, + ColorAttachmentResolveTargetCompatibility) { + constexpr uint32_t kSize2 = kSize * 2; + wgpu::Texture resolveTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kColorFormat, kSize2, kSize2, + kArrayLayers, kLevelCount + 1); + + wgpu::TextureViewDescriptor textureViewDescriptor; + textureViewDescriptor.nextInChain = nullptr; + textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D; + textureViewDescriptor.format = kColorFormat; + textureViewDescriptor.mipLevelCount = 1; + textureViewDescriptor.baseArrayLayer = 0; + textureViewDescriptor.arrayLayerCount = 1; + + { + wgpu::TextureViewDescriptor firstMipLevelDescriptor = textureViewDescriptor; + firstMipLevelDescriptor.baseMipLevel = 0; + + wgpu::TextureView resolveTextureView = + resolveTexture.CreateView(&firstMipLevelDescriptor); + + utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); + renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; + AssertBeginRenderPassError(&renderPass); + } + + { + wgpu::TextureViewDescriptor secondMipLevelDescriptor = textureViewDescriptor; + secondMipLevelDescriptor.baseMipLevel = 1; + + wgpu::TextureView resolveTextureView = + resolveTexture.CreateView(&secondMipLevelDescriptor); + + utils::ComboRenderPassDescriptor renderPass = CreateMultisampledRenderPass(); + renderPass.cColorAttachments[0].resolveTarget = resolveTextureView; + AssertBeginRenderPassSuccess(&renderPass); + } + } + + // Tests on the sample count of depth stencil attachment. + TEST_F(MultisampledRenderPassDescriptorValidationTest, DepthStencilAttachmentSampleCount) { + constexpr wgpu::TextureFormat kDepthStencilFormat = + wgpu::TextureFormat::Depth24PlusStencil8; + wgpu::Texture multisampledDepthStencilTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, kSize, + kArrayLayers, kLevelCount, kSampleCount); + wgpu::TextureView multisampledDepthStencilTextureView = + multisampledDepthStencilTexture.CreateView(); + + // It is not allowed to use a depth stencil attachment whose sample count is different from + // the one of the color attachment. + { + wgpu::Texture depthStencilTexture = + CreateTexture(device, wgpu::TextureDimension::e2D, kDepthStencilFormat, kSize, + kSize, kArrayLayers, kLevelCount); + wgpu::TextureView depthStencilTextureView = depthStencilTexture.CreateView(); + + utils::ComboRenderPassDescriptor renderPass({CreateMultisampledColorTextureView()}, + depthStencilTextureView); + AssertBeginRenderPassError(&renderPass); + } + + { + utils::ComboRenderPassDescriptor renderPass({CreateNonMultisampledColorTextureView()}, + multisampledDepthStencilTextureView); + AssertBeginRenderPassError(&renderPass); + } + + // It is allowed to use a multisampled depth stencil attachment whose sample count is equal + // to the one of the color attachment. + { + utils::ComboRenderPassDescriptor renderPass({CreateMultisampledColorTextureView()}, + multisampledDepthStencilTextureView); + AssertBeginRenderPassSuccess(&renderPass); + } + + // It is allowed to use a multisampled depth stencil attachment while there is no color + // attachment. + { + utils::ComboRenderPassDescriptor renderPass({}, multisampledDepthStencilTextureView); + AssertBeginRenderPassSuccess(&renderPass); + } + } + + // Tests that NaN cannot be accepted as a valid color or depth clear value and INFINITY is valid + // in both color and depth clear values. + TEST_F(RenderPassDescriptorValidationTest, UseNaNOrINFINITYAsColorOrDepthClearValue) { + wgpu::TextureView color = Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + + // Tests that NaN cannot be used in clearColor. + { + utils::ComboRenderPassDescriptor renderPass({color}, nullptr); + renderPass.cColorAttachments[0].clearColor.r = NAN; + AssertBeginRenderPassError(&renderPass); + } + + { + utils::ComboRenderPassDescriptor renderPass({color}, nullptr); + renderPass.cColorAttachments[0].clearColor.g = NAN; + AssertBeginRenderPassError(&renderPass); + } + + { + utils::ComboRenderPassDescriptor renderPass({color}, nullptr); + renderPass.cColorAttachments[0].clearColor.b = NAN; + AssertBeginRenderPassError(&renderPass); + } + + { + utils::ComboRenderPassDescriptor renderPass({color}, nullptr); + renderPass.cColorAttachments[0].clearColor.a = NAN; + AssertBeginRenderPassError(&renderPass); + } + + // Tests that INFINITY can be used in clearColor. + { + utils::ComboRenderPassDescriptor renderPass({color}, nullptr); + renderPass.cColorAttachments[0].clearColor.r = INFINITY; + AssertBeginRenderPassSuccess(&renderPass); + } + + { + utils::ComboRenderPassDescriptor renderPass({color}, nullptr); + renderPass.cColorAttachments[0].clearColor.g = INFINITY; + AssertBeginRenderPassSuccess(&renderPass); + } + + { + utils::ComboRenderPassDescriptor renderPass({color}, nullptr); + renderPass.cColorAttachments[0].clearColor.b = INFINITY; + AssertBeginRenderPassSuccess(&renderPass); + } + + { + utils::ComboRenderPassDescriptor renderPass({color}, nullptr); + renderPass.cColorAttachments[0].clearColor.a = INFINITY; + AssertBeginRenderPassSuccess(&renderPass); + } + + // Tests that NaN cannot be used in clearDepth. + { + wgpu::TextureView depth = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); + utils::ComboRenderPassDescriptor renderPass({color}, depth); + renderPass.cDepthStencilAttachmentInfo.clearDepth = NAN; + AssertBeginRenderPassError(&renderPass); + } + + // Tests that INFINITY can be used in clearDepth. + { + wgpu::TextureView depth = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); + utils::ComboRenderPassDescriptor renderPass({color}, depth); + renderPass.cDepthStencilAttachmentInfo.clearDepth = INFINITY; + AssertBeginRenderPassSuccess(&renderPass); + } + } + + TEST_F(RenderPassDescriptorValidationTest, ValidateDepthStencilReadOnly) { + wgpu::TextureView colorView = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::RGBA8Unorm); + wgpu::TextureView depthStencilView = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24PlusStencil8); + wgpu::TextureView depthStencilViewNoStencil = + Create2DAttachment(device, 1, 1, wgpu::TextureFormat::Depth24Plus); + + // Tests that a read-only pass with depthReadOnly set to true succeeds. + { + utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilView); + renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; + renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = true; + AssertBeginRenderPassSuccess(&renderPass); + } + + // Tests that a pass with mismatched depthReadOnly and stencilReadOnly values passes when + // there is no stencil component in the format. + { + utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilViewNoStencil); + renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; + renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = false; + AssertBeginRenderPassSuccess(&renderPass); + } + + // Tests that a pass with mismatched depthReadOnly and stencilReadOnly values fails when + // both depth and stencil components exist. + { + utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilView); + renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; + renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = false; + AssertBeginRenderPassError(&renderPass); + } + + // Tests that a pass with loadOp set to clear and readOnly set to true fails. + { + utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilView); + renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; + renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = true; + AssertBeginRenderPassError(&renderPass); + } + + // Tests that a pass with storeOp set to clear and readOnly set to true fails. + { + utils::ComboRenderPassDescriptor renderPass({colorView}, depthStencilView); + renderPass.cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Load; + renderPass.cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Clear; + renderPass.cDepthStencilAttachmentInfo.depthReadOnly = true; + renderPass.cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Load; + renderPass.cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Clear; + renderPass.cDepthStencilAttachmentInfo.stencilReadOnly = true; + AssertBeginRenderPassError(&renderPass); + } + } + + // TODO(cwallez@chromium.org): Constraints on attachment aliasing? + +} // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/validation/RenderPipelineValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/RenderPipelineValidationTests.cpp index 0bb87fd7b85..19cc817e812 100644 --- a/third_party/dawn/src/tests/unittests/validation/RenderPipelineValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/RenderPipelineValidationTests.cpp @@ -16,46 +16,113 @@ #include "common/Constants.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" + +#include +#include class RenderPipelineValidationTest : public ValidationTest { - protected: - void SetUp() override { - ValidationTest::SetUp(); + protected: + void SetUp() override { + ValidationTest::SetUp(); - vsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"( + vsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( #version 450 void main() { gl_Position = vec4(0.0, 0.0, 0.0, 1.0); - })" - ); + })"); - fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + fsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); })"); - } + } - dawn::ShaderModule vsModule; - dawn::ShaderModule fsModule; + wgpu::ShaderModule vsModule; + wgpu::ShaderModule fsModule; }; // Test cases where creation should succeed TEST_F(RenderPipelineValidationTest, CreationSuccess) { - utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; - descriptor.cFragmentStage.module = fsModule; + { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; - device.CreateRenderPipeline(&descriptor); + device.CreateRenderPipeline(&descriptor); + } + { + // Vertex input should be optional + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.vertexState = nullptr; + + device.CreateRenderPipeline(&descriptor); + } + { + // Rasterization state should be optional + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.rasterizationState = nullptr; + device.CreateRenderPipeline(&descriptor); + } } -TEST_F(RenderPipelineValidationTest, ColorState) { +// Tests that depth bias parameters must not be NaN. +TEST_F(RenderPipelineValidationTest, DepthBiasParameterNotBeNaN) { + // Control case, depth bias parameters in ComboRenderPipeline default to 0 which is finite + { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + device.CreateRenderPipeline(&descriptor); + } + + // Infinite depth bias clamp is valid + { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cRasterizationState.depthBiasClamp = INFINITY; + device.CreateRenderPipeline(&descriptor); + } + // NAN depth bias clamp is invalid + { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cRasterizationState.depthBiasClamp = NAN; + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + + // Infinite depth bias slope is valid + { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cRasterizationState.depthBiasSlopeScale = INFINITY; + device.CreateRenderPipeline(&descriptor); + } + // NAN depth bias slope is invalid + { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cRasterizationState.depthBiasSlopeScale = NAN; + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } +} + +// Tests that at least one color state is required. +TEST_F(RenderPipelineValidationTest, ColorStateRequired) { { // This one succeeds because attachment 0 is the color attachment utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; descriptor.colorStateCount = 1; @@ -64,7 +131,7 @@ TEST_F(RenderPipelineValidationTest, ColorState) { { // Fail because lack of color states (and depth/stencil state) utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; descriptor.colorStateCount = 0; @@ -72,11 +139,67 @@ TEST_F(RenderPipelineValidationTest, ColorState) { } } +// Tests that the color formats must be renderable. +TEST_F(RenderPipelineValidationTest, NonRenderableFormat) { + { + // Succeeds because RGBA8Unorm is renderable + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; + + device.CreateRenderPipeline(&descriptor); + } + + { + // Fails because RG11B10Float is non-renderable + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.cColorStates[0].format = wgpu::TextureFormat::RG11B10Float; + + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } +} + +// Tests that the format of the color state descriptor must match the output of the fragment shader. +TEST_F(RenderPipelineValidationTest, FragmentOutputFormatCompatibility) { + constexpr uint32_t kNumTextureFormatBaseType = 3u; + std::array kVecPreFix = {{"", "i", "u"}}; + std::array kColorFormats = { + {wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8Sint, + wgpu::TextureFormat::RGBA8Uint}}; + + for (size_t i = 0; i < kNumTextureFormatBaseType; ++i) { + for (size_t j = 0; j < kNumTextureFormatBaseType; ++j) { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cColorStates[0].format = kColorFormats[j]; + + std::ostringstream stream; + stream << R"( + #version 450 + layout(location = 0) out )" + << kVecPreFix[i] << R"(vec4 fragColor; + void main() { + })"; + descriptor.cFragmentStage.module = utils::CreateShaderModule( + device, utils::SingleShaderStage::Fragment, stream.str().c_str()); + + if (i == j) { + device.CreateRenderPipeline(&descriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + } + } +} + /// Tests that the sample count of the render pipeline must be valid. TEST_F(RenderPipelineValidationTest, SampleCount) { { utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; descriptor.sampleCount = 4; @@ -85,7 +208,7 @@ TEST_F(RenderPipelineValidationTest, SampleCount) { { utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; + descriptor.vertexStage.module = vsModule; descriptor.cFragmentStage.module = fsModule; descriptor.sampleCount = 3; @@ -97,56 +220,55 @@ TEST_F(RenderPipelineValidationTest, SampleCount) { // in the render pass. TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) { constexpr uint32_t kMultisampledCount = 4; - constexpr dawn::TextureFormat kColorFormat = dawn::TextureFormat::R8G8B8A8Unorm; - constexpr dawn::TextureFormat kDepthStencilFormat = dawn::TextureFormat::D32FloatS8Uint; + constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm; + constexpr wgpu::TextureFormat kDepthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8; - dawn::TextureDescriptor baseTextureDescriptor; + wgpu::TextureDescriptor baseTextureDescriptor; baseTextureDescriptor.size.width = 4; baseTextureDescriptor.size.height = 4; baseTextureDescriptor.size.depth = 1; - baseTextureDescriptor.arrayLayerCount = 1; baseTextureDescriptor.mipLevelCount = 1; - baseTextureDescriptor.dimension = dawn::TextureDimension::e2D; - baseTextureDescriptor.usage = dawn::TextureUsageBit::OutputAttachment; + baseTextureDescriptor.dimension = wgpu::TextureDimension::e2D; + baseTextureDescriptor.usage = wgpu::TextureUsage::OutputAttachment; utils::ComboRenderPipelineDescriptor nonMultisampledPipelineDescriptor(device); nonMultisampledPipelineDescriptor.sampleCount = 1; - nonMultisampledPipelineDescriptor.cVertexStage.module = vsModule; + nonMultisampledPipelineDescriptor.vertexStage.module = vsModule; nonMultisampledPipelineDescriptor.cFragmentStage.module = fsModule; - dawn::RenderPipeline nonMultisampledPipeline = + wgpu::RenderPipeline nonMultisampledPipeline = device.CreateRenderPipeline(&nonMultisampledPipelineDescriptor); nonMultisampledPipelineDescriptor.colorStateCount = 0; nonMultisampledPipelineDescriptor.depthStencilState = &nonMultisampledPipelineDescriptor.cDepthStencilState; - dawn::RenderPipeline nonMultisampledPipelineWithDepthStencilOnly = + wgpu::RenderPipeline nonMultisampledPipelineWithDepthStencilOnly = device.CreateRenderPipeline(&nonMultisampledPipelineDescriptor); utils::ComboRenderPipelineDescriptor multisampledPipelineDescriptor(device); multisampledPipelineDescriptor.sampleCount = kMultisampledCount; - multisampledPipelineDescriptor.cVertexStage.module = vsModule; + multisampledPipelineDescriptor.vertexStage.module = vsModule; multisampledPipelineDescriptor.cFragmentStage.module = fsModule; - dawn::RenderPipeline multisampledPipeline = + wgpu::RenderPipeline multisampledPipeline = device.CreateRenderPipeline(&multisampledPipelineDescriptor); multisampledPipelineDescriptor.colorStateCount = 0; multisampledPipelineDescriptor.depthStencilState = &multisampledPipelineDescriptor.cDepthStencilState; - dawn::RenderPipeline multisampledPipelineWithDepthStencilOnly = + wgpu::RenderPipeline multisampledPipelineWithDepthStencilOnly = device.CreateRenderPipeline(&multisampledPipelineDescriptor); // It is not allowed to use multisampled render pass and non-multisampled render pipeline. { { - dawn::TextureDescriptor textureDescriptor = baseTextureDescriptor; + wgpu::TextureDescriptor textureDescriptor = baseTextureDescriptor; textureDescriptor.format = kColorFormat; textureDescriptor.sampleCount = kMultisampledCount; - dawn::Texture multisampledColorTexture = device.CreateTexture(&textureDescriptor); + wgpu::Texture multisampledColorTexture = device.CreateTexture(&textureDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor( - {multisampledColorTexture.CreateDefaultView()}); + {multisampledColorTexture.CreateView()}); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); renderPass.SetPipeline(nonMultisampledPipeline); renderPass.EndPass(); @@ -154,16 +276,16 @@ TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) { } { - dawn::TextureDescriptor textureDescriptor = baseTextureDescriptor; + wgpu::TextureDescriptor textureDescriptor = baseTextureDescriptor; textureDescriptor.sampleCount = kMultisampledCount; textureDescriptor.format = kDepthStencilFormat; - dawn::Texture multisampledDepthStencilTexture = + wgpu::Texture multisampledDepthStencilTexture = device.CreateTexture(&textureDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor( - {}, multisampledDepthStencilTexture.CreateDefaultView()); + {}, multisampledDepthStencilTexture.CreateView()); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); renderPass.SetPipeline(nonMultisampledPipelineWithDepthStencilOnly); renderPass.EndPass(); @@ -174,15 +296,15 @@ TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) { // It is allowed to use multisampled render pass and multisampled render pipeline. { { - dawn::TextureDescriptor textureDescriptor = baseTextureDescriptor; + wgpu::TextureDescriptor textureDescriptor = baseTextureDescriptor; textureDescriptor.format = kColorFormat; textureDescriptor.sampleCount = kMultisampledCount; - dawn::Texture multisampledColorTexture = device.CreateTexture(&textureDescriptor); + wgpu::Texture multisampledColorTexture = device.CreateTexture(&textureDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor( - {multisampledColorTexture.CreateDefaultView()}); + {multisampledColorTexture.CreateView()}); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); renderPass.SetPipeline(multisampledPipeline); renderPass.EndPass(); @@ -190,16 +312,16 @@ TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) { } { - dawn::TextureDescriptor textureDescriptor = baseTextureDescriptor; + wgpu::TextureDescriptor textureDescriptor = baseTextureDescriptor; textureDescriptor.sampleCount = kMultisampledCount; textureDescriptor.format = kDepthStencilFormat; - dawn::Texture multisampledDepthStencilTexture = + wgpu::Texture multisampledDepthStencilTexture = device.CreateTexture(&textureDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor( - {}, multisampledDepthStencilTexture.CreateDefaultView()); + {}, multisampledDepthStencilTexture.CreateView()); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); renderPass.SetPipeline(multisampledPipelineWithDepthStencilOnly); renderPass.EndPass(); @@ -210,15 +332,15 @@ TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) { // It is not allowed to use non-multisampled render pass and multisampled render pipeline. { { - dawn::TextureDescriptor textureDescriptor = baseTextureDescriptor; + wgpu::TextureDescriptor textureDescriptor = baseTextureDescriptor; textureDescriptor.format = kColorFormat; textureDescriptor.sampleCount = 1; - dawn::Texture nonMultisampledColorTexture = device.CreateTexture(&textureDescriptor); + wgpu::Texture nonMultisampledColorTexture = device.CreateTexture(&textureDescriptor); utils::ComboRenderPassDescriptor nonMultisampledRenderPassDescriptor( - { nonMultisampledColorTexture.CreateDefaultView() }); + {nonMultisampledColorTexture.CreateView()}); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPass = + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&nonMultisampledRenderPassDescriptor); renderPass.SetPipeline(multisampledPipeline); renderPass.EndPass(); @@ -227,16 +349,16 @@ TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) { } { - dawn::TextureDescriptor textureDescriptor = baseTextureDescriptor; + wgpu::TextureDescriptor textureDescriptor = baseTextureDescriptor; textureDescriptor.sampleCount = 1; textureDescriptor.format = kDepthStencilFormat; - dawn::Texture multisampledDepthStencilTexture = + wgpu::Texture multisampledDepthStencilTexture = device.CreateTexture(&textureDescriptor); utils::ComboRenderPassDescriptor renderPassDescriptor( - {}, multisampledDepthStencilTexture.CreateDefaultView()); + {}, multisampledDepthStencilTexture.CreateView()); - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - dawn::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPass = encoder.BeginRenderPass(&renderPassDescriptor); renderPass.SetPipeline(multisampledPipelineWithDepthStencilOnly); renderPass.EndPass(); @@ -244,3 +366,139 @@ TEST_F(RenderPipelineValidationTest, SampleCountCompatibilityWithRenderPass) { } } } + +// Tests that the texture component type in shader must match the bind group layout. +TEST_F(RenderPipelineValidationTest, TextureComponentTypeCompatibility) { + constexpr uint32_t kNumTextureComponentType = 3u; + std::array kTexturePrefix = {{"", "i", "u"}}; + std::array kTextureComponentTypes = {{ + wgpu::TextureComponentType::Float, + wgpu::TextureComponentType::Sint, + wgpu::TextureComponentType::Uint, + }}; + + for (size_t i = 0; i < kNumTextureComponentType; ++i) { + for (size_t j = 0; j < kNumTextureComponentType; ++j) { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + + std::ostringstream stream; + stream << R"( + #version 450 + layout(set = 0, binding = 0) uniform )" + << kTexturePrefix[i] << R"(texture2D tex; + void main() { + })"; + descriptor.cFragmentStage.module = utils::CreateShaderModule( + device, utils::SingleShaderStage::Fragment, stream.str().c_str()); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, + 0, false, wgpu::TextureViewDimension::e2D, kTextureComponentTypes[j]}}); + descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); + + if (i == j) { + device.CreateRenderPipeline(&descriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + } + } +} + +// Tests that the texture view dimension in shader must match the bind group layout. +TEST_F(RenderPipelineValidationTest, TextureViewDimensionCompatibility) { + constexpr uint32_t kNumTextureViewDimensions = 6u; + std::array kTextureKeywords = {{ + "texture1D", + "texture2D", + "texture2DArray", + "textureCube", + "textureCubeArray", + "texture3D", + }}; + + std::array kTextureViewDimensions = {{ + wgpu::TextureViewDimension::e1D, + wgpu::TextureViewDimension::e2D, + wgpu::TextureViewDimension::e2DArray, + wgpu::TextureViewDimension::Cube, + wgpu::TextureViewDimension::CubeArray, + wgpu::TextureViewDimension::e3D, + }}; + + for (size_t i = 0; i < kNumTextureViewDimensions; ++i) { + for (size_t j = 0; j < kNumTextureViewDimensions; ++j) { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + + std::ostringstream stream; + stream << R"( + #version 450 + layout(set = 0, binding = 0) uniform )" + << kTextureKeywords[i] << R"( tex; + void main() { + })"; + descriptor.cFragmentStage.module = utils::CreateShaderModule( + device, utils::SingleShaderStage::Fragment, stream.str().c_str()); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, + 0, false, kTextureViewDimensions[j], wgpu::TextureComponentType::Float}}); + descriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl); + + if (i == j) { + device.CreateRenderPipeline(&descriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + } + } +} + +// Test that declaring a storage buffer in the vertex shader without setting pipeline layout won't +// cause crash. +TEST_F(RenderPipelineValidationTest, StorageBufferInVertexShaderNoLayout) { + wgpu::ShaderModule vsModuleWithStorageBuffer = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + #define kNumValues 100 + layout(std430, set = 0, binding = 0) buffer Dst { uint dst[kNumValues]; }; + void main() { + uint index = gl_VertexIndex; + dst[index] = 0x1234; + gl_Position = vec4(1.f, 0.f, 0.f, 1.f); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModuleWithStorageBuffer; + descriptor.cFragmentStage.module = fsModule; + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); +} + +// Test that a pipeline with defaulted layout may not have multisampled array textures +// TODO(enga): Also test multisampled cube, cube array, and 3D. These have no GLSL keywords. +TEST_F(RenderPipelineValidationTest, MultisampledTexture) { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.cFragmentStage.module = fsModule; + + // Base case works. + descriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2DMS texture; + void main() { + })"); + device.CreateRenderPipeline(&descriptor); + + // texture2DMSArray invalid + descriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0) uniform texture2DMSArray texture; + void main() { + })"); + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); +} diff --git a/third_party/dawn/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp b/third_party/dawn/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp new file mode 100644 index 00000000000..846a8746a5d --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/ResourceUsageTrackingTests.cpp @@ -0,0 +1,1668 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +namespace { + + class ResourceUsageTrackingTest : public ValidationTest { + protected: + wgpu::Buffer CreateBuffer(uint64_t size, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage; + + return device.CreateBuffer(&descriptor); + } + + wgpu::Texture CreateTexture(wgpu::TextureUsage usage) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size = {1, 1, 1}; + descriptor.sampleCount = 1; + descriptor.mipLevelCount = 1; + descriptor.usage = usage; + descriptor.format = kFormat; + + return device.CreateTexture(&descriptor); + } + + // Note that it is valid to bind any bind groups for indices that the pipeline doesn't use. + // We create a no-op render or compute pipeline without any bindings, and set bind groups + // in the caller, so it is always correct for binding validation between bind groups and + // pipeline. But those bind groups in caller can be used for validation for other purposes. + wgpu::RenderPipeline CreateNoOpRenderPipeline() { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + void main() { + })"); + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, nullptr); + return device.CreateRenderPipeline(&pipelineDescriptor); + } + + wgpu::ComputePipeline CreateNoOpComputePipeline() { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + void main() { + })"); + wgpu::ComputePipelineDescriptor pipelineDescriptor; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, nullptr); + pipelineDescriptor.computeStage.module = csModule; + pipelineDescriptor.computeStage.entryPoint = "main"; + return device.CreateComputePipeline(&pipelineDescriptor); + } + + static constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + }; + + // Test that using a single buffer in multiple read usages in the same pass is allowed. + TEST_F(ResourceUsageTrackingTest, BufferWithMultipleReadUsage) { + // Test render pass + { + // Create a buffer, and use the buffer as both vertex and index buffer. + wgpu::Buffer buffer = + CreateBuffer(4, wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Index); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetIndexBuffer(buffer); + pass.SetVertexBuffer(0, buffer); + pass.EndPass(); + encoder.Finish(); + } + + // Test compute pass + { + // Create buffer and bind group + wgpu::Buffer buffer = + CreateBuffer(4, wgpu::BufferUsage::Uniform | wgpu::BufferUsage::Storage); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}}); + + // Use the buffer as both uniform and readonly storage buffer in compute pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + } + + // Test that it is invalid to use the same buffer as both readable and writable in the same + // render pass. It is invalid in the same dispatch in compute pass. + TEST_F(ResourceUsageTrackingTest, BufferWithReadAndWriteUsage) { + // test render pass + { + // Create buffer and bind group + wgpu::Buffer buffer = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}}); + + // It is invalid to use the buffer as both index and storage in render pass + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetIndexBuffer(buffer); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // test compute pass + { + // Create buffer and bind group + wgpu::Buffer buffer = CreateBuffer(512, wgpu::BufferUsage::Storage); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroup bg = + utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}, {1, buffer, 256, 4}}); + + // Create a no-op compute pipeline + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // It is valid to use the buffer as both storage and readonly storage in a single + // compute pass if dispatch command is not called. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + + // It is invalid to use the buffer as both storage and readonly storage in a single + // dispatch. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add buffer usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + } + + // Test using multiple writable usages on the same buffer in a single pass/dispatch + TEST_F(ResourceUsageTrackingTest, BufferWithMultipleWriteUsage) { + // Create buffer and bind group + wgpu::Buffer buffer = CreateBuffer(512, wgpu::BufferUsage::Storage); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute, + wgpu::BindingType::StorageBuffer}, + {1, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute, + wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg = + utils::MakeBindGroup(device, bgl, {{0, buffer, 0, 4}, {1, buffer, 256, 4}}); + + // test render pass + { + // It is valid to use multiple storage usages on the same buffer in render pass + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + + // test compute pass + { + // Create a no-op compute pipeline + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // It is valid to use the same buffer as multiple writeable usages in a single compute + // pass if dispatch command is not called. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + + // It is invalid to use the same buffer as multiple writeable usages in a single + // dispatch + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add buffer usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + } + + // Test that using the same buffer as both readable and writable in different passes is allowed + TEST_F(ResourceUsageTrackingTest, BufferWithReadAndWriteUsageInDifferentPasses) { + // Test render pass + { + // Create buffers that will be used as index and storage buffers + wgpu::Buffer buffer0 = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index); + wgpu::Buffer buffer1 = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index); + + // Create bind groups to use the buffer as storage + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl, {{0, buffer0}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, buffer1}}); + + // Use these two buffers as both index and storage in different render passes + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + + wgpu::RenderPassEncoder pass0 = encoder.BeginRenderPass(&dummyRenderPass); + pass0.SetIndexBuffer(buffer0); + pass0.SetBindGroup(0, bg1); + pass0.EndPass(); + + wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&dummyRenderPass); + pass1.SetIndexBuffer(buffer1); + pass1.SetBindGroup(0, bg0); + pass1.EndPass(); + + encoder.Finish(); + } + + // Test compute pass + { + // Create buffer and bind groups that will be used as storage and uniform bindings + wgpu::Buffer buffer = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Uniform); + + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::UniformBuffer}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}}); + + // Use the buffer as both storage and uniform in different compute passes + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass(); + pass0.SetBindGroup(0, bg0); + pass0.EndPass(); + + wgpu::ComputePassEncoder pass1 = encoder.BeginComputePass(); + pass1.SetBindGroup(1, bg1); + pass1.EndPass(); + + encoder.Finish(); + } + + // Test render pass and compute pass mixed together with resource dependency. + { + // Create buffer and bind groups that will be used as storage and uniform bindings + wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage); + + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}}); + + // Use the buffer as storage and uniform in render pass and compute pass respectively + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass(); + pass0.SetBindGroup(0, bg0); + pass0.EndPass(); + + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&dummyRenderPass); + pass1.SetBindGroup(1, bg1); + pass1.EndPass(); + + encoder.Finish(); + } + } + + // Test that it is invalid to use the same buffer as both readable and writable in different + // draws in a single render pass. But it is valid in different dispatches in a single compute + // pass. + TEST_F(ResourceUsageTrackingTest, BufferWithReadAndWriteUsageInDifferentDrawsOrDispatches) { + // Test render pass + { + // Create a buffer and a bind group + wgpu::Buffer buffer = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}}); + + // Create a no-op render pipeline. + wgpu::RenderPipeline rp = CreateNoOpRenderPipeline(); + + // It is not allowed to use the same buffer as both readable and writable in different + // draws within the same render pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetPipeline(rp); + + pass.SetIndexBuffer(buffer); + pass.Draw(3); + + pass.SetBindGroup(0, bg); + pass.Draw(3); + + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // test compute pass + { + // Create a buffer and bind groups + wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage); + + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // It is valid to use the same buffer as both readable and writable in different + // dispatches within the same compute pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + + pass.SetBindGroup(0, bg0); + pass.Dispatch(1); + + pass.SetBindGroup(0, bg1); + pass.Dispatch(1); + + pass.EndPass(); + encoder.Finish(); + } + } + + // Test that it is invalid to use the same buffer as both readable and writable in a single + // draw or dispatch. + TEST_F(ResourceUsageTrackingTest, BufferWithReadAndWriteUsageInSingleDrawOrDispatch) { + // Test render pass + { + // Create a buffer and a bind group + wgpu::Buffer buffer = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index); + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, buffer}}); + + // Create a no-op render pipeline. + wgpu::RenderPipeline rp = CreateNoOpRenderPipeline(); + + // It is invalid to use the same buffer as both readable and writable usages in a single + // draw + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetPipeline(rp); + + pass.SetIndexBuffer(buffer); + pass.SetBindGroup(0, writeBG); + pass.Draw(3); + + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // test compute pass + { + // Create a buffer and bind groups + wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage); + + wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, buffer}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, buffer}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // It is invalid to use the same buffer as both readable and writable usages in a single + // dispatch + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + + pass.SetBindGroup(0, readBG); + pass.SetBindGroup(1, writeBG); + pass.Dispatch(1); + + pass.EndPass(); + // TODO (yunchao.he@intel.com): add buffer usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + + // Test that using the same buffer as copy src/dst and writable/readable usage is allowed. + TEST_F(ResourceUsageTrackingTest, BufferCopyAndBufferUsageInPass) { + // Create buffers that will be used as both a copy src/dst buffer and a storage buffer + wgpu::Buffer bufferSrc = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc); + wgpu::Buffer bufferDst = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst); + + // Create the bind group to use the buffer as storage + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, bufferSrc}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, bufferDst}}); + + // Use the buffer as both copy src and storage in render pass + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(bufferSrc, 0, bufferDst, 0, 4); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetBindGroup(0, bg0); + pass.EndPass(); + encoder.Finish(); + } + + // Use the buffer as both copy dst and readonly storage in compute pass + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyBufferToBuffer(bufferSrc, 0, bufferDst, 0, 4); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg1); + pass.EndPass(); + encoder.Finish(); + } + } + + // Test that all index buffers and vertex buffers take effect even though some buffers are + // not used because they are overwritten by another consecutive call. + TEST_F(ResourceUsageTrackingTest, BufferWithMultipleSetIndexOrVertexBuffer) { + // Create buffers that will be used as both vertex and index buffer. + wgpu::Buffer buffer0 = CreateBuffer( + 4, wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Index | wgpu::BufferUsage::Storage); + wgpu::Buffer buffer1 = + CreateBuffer(4, wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Index); + + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer0}}); + + DummyRenderPass dummyRenderPass(device); + + // Set index buffer twice. The second one overwrites the first one. No buffer is used as + // both read and write in the same pass. But the overwritten index buffer (buffer0) still + // take effect during resource tracking. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetIndexBuffer(buffer0); + pass.SetIndexBuffer(buffer1); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Set index buffer twice. The second one overwrites the first one. buffer0 is used as both + // read and write in the same pass + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetIndexBuffer(buffer1); + pass.SetIndexBuffer(buffer0); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Set vertex buffer on the same index twice. The second one overwrites the first one. No + // buffer is used as both read and write in the same pass. But the overwritten vertex buffer + // (buffer0) still take effect during resource tracking. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetVertexBuffer(0, buffer0); + pass.SetVertexBuffer(0, buffer1); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Set vertex buffer on the same index twice. The second one overwrites the first one. + // buffer0 is used as both read and write in the same pass + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetVertexBuffer(0, buffer1); + pass.SetVertexBuffer(0, buffer0); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + } + + // Test that all consecutive SetBindGroup()s take effect even though some bind groups are not + // used because they are overwritten by a consecutive call. + TEST_F(ResourceUsageTrackingTest, BufferWithMultipleSetBindGroupsOnSameIndex) { + // test render pass + { + // Create buffers that will be used as index and storage buffers + wgpu::Buffer buffer0 = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index); + wgpu::Buffer buffer1 = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index); + + // Create the bind group to use the buffer as storage + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl, {{0, buffer0}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, buffer1}}); + + DummyRenderPass dummyRenderPass(device); + + // Set bind group on the same index twice. The second one overwrites the first one. + // No buffer is used as both read and write in the same pass. But the overwritten + // bind group still take effect during resource tracking. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetIndexBuffer(buffer0); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(0, bg1); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Set bind group on the same index twice. The second one overwrites the first one. + // buffer0 is used as both read and write in the same pass + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetIndexBuffer(buffer0); + pass.SetBindGroup(0, bg1); + pass.SetBindGroup(0, bg0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + } + + // test compute pass + { + // Create buffers that will be used as index and storage buffers + wgpu::Buffer buffer0 = CreateBuffer(512, wgpu::BufferUsage::Storage); + wgpu::Buffer buffer1 = CreateBuffer(4, wgpu::BufferUsage::Storage); + + // Create the bind group to use the buffer as storage + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroup writeBG0 = utils::MakeBindGroup(device, writeBGL, {{0, buffer0, 0, 4}}); + wgpu::BindGroup readBG0 = utils::MakeBindGroup(device, readBGL, {{0, buffer0, 256, 4}}); + wgpu::BindGroup readBG1 = utils::MakeBindGroup(device, readBGL, {{0, buffer1, 0, 4}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // Set bind group against the same index twice. The second one overwrites the first one. + // Then no buffer is used as both read and write in the same pass. But the overwritten + // bind group still take effect. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, writeBG0); + pass.SetBindGroup(1, readBG0); + pass.SetBindGroup(1, readBG1); + pass.SetPipeline(cp); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add buffer usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + + // Set bind group against the same index twice. The second one overwrites the first one. + // Then buffer0 is used as both read and write in the same pass + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, writeBG0); + pass.SetBindGroup(1, readBG1); + pass.SetBindGroup(1, readBG0); + pass.SetPipeline(cp); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add buffer usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + } + + // Test that it is invalid to have resource usage conflicts even when all bindings are not + // visible to the programmable pass where it is used. + TEST_F(ResourceUsageTrackingTest, BufferUsageConflictBetweenInvisibleStagesInBindGroup) { + wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage); + + // Test render pass for bind group. The conflict of readonly storage and storage usage + // doesn't reside in render related stages at all + { + // Create a bind group whose bindings are not visible in render pass + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}, + {1, wgpu::ShaderStage::None, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}}); + + // These two bindings are invisible in render pass. But we still track these bindings. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass for bind group. The conflict of readonly storage and storage usage + // doesn't reside in compute related stage at all + { + // Create a bind group whose bindings are not visible in compute pass + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer}, + {1, wgpu::ShaderStage::None, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // These two bindings are invisible in compute pass. But we still track these bindings. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add buffer usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + + // Test that it is invalid to have resource usage conflicts even when one of the bindings is not + // visible to the programmable pass where it is used. + TEST_F(ResourceUsageTrackingTest, BufferUsageConflictWithInvisibleStageInBindGroup) { + // Test render pass for bind group and index buffer. The conflict of storage and index + // buffer usage resides between fragment stage and compute stage. But the compute stage + // binding is not visible in render pass. + { + wgpu::Buffer buffer = + CreateBuffer(4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::Index); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}}); + + // Buffer usage in compute stage in bind group conflicts with index buffer. And binding + // for compute stage is not visible in render pass. But we still track this binding. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetIndexBuffer(buffer); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass for bind group. The conflict of readonly storage and storage buffer + // usage resides between compute stage and fragment stage. But the fragment stage binding is + // not visible in compute pass. + { + wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage); + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, buffer}, {1, buffer}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // Buffer usage in compute stage conflicts with buffer usage in fragment stage. And + // binding for fragment stage is not visible in compute pass. But we still track this + // invisible binding. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add buffer usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + + // Test that it is invalid to have resource usage conflicts even when one of the bindings is not + // used in the pipeline. + TEST_F(ResourceUsageTrackingTest, BufferUsageConflictWithUnusedPipelineBindings) { + wgpu::Buffer buffer = CreateBuffer(4, wgpu::BufferUsage::Storage); + + // Test render pass for bind groups with unused bindings. The conflict of readonly storage + // and storage usages resides in different bind groups, although some bindings may not be + // used because its bind group layout is not designated in pipeline layout. + { + // Create bind groups. The bindings are visible for render pass. + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}}); + + // Create a passthrough render pipeline with a readonly buffer + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(std140, set = 0, binding = 0) readonly buffer RBuffer { + readonly float value; + } rBuffer; + void main() { + })"); + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl0); + wgpu::RenderPipeline rp = device.CreateRenderPipeline(&pipelineDescriptor); + + // Resource in bg1 conflicts with resources used in bg0. However, bindings in bg1 is + // not used in pipeline. But we still track this binding. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.SetPipeline(rp); + pass.Draw(3); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass for bind groups with unused bindings. The conflict of readonly storage + // and storage usages resides in different bind groups, although some bindings may not be + // used because its bind group layout is not designated in pipeline layout. + { + // Create bind groups. The bindings are visible for compute pass. + wgpu::BindGroupLayout bgl0 = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageBuffer}}); + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::StorageBuffer}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl0, {{0, buffer}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl1, {{0, buffer}}); + + // Create a passthrough compute pipeline with a readonly buffer + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(std140, set = 0, binding = 0) readonly buffer RBuffer { + readonly float value; + } rBuffer; + void main() { + })"); + wgpu::ComputePipelineDescriptor pipelineDescriptor; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &bgl0); + pipelineDescriptor.computeStage.module = csModule; + pipelineDescriptor.computeStage.entryPoint = "main"; + wgpu::ComputePipeline cp = device.CreateComputePipeline(&pipelineDescriptor); + + // Resource in bg1 conflicts with resources used in bg0. However, the binding in bg1 is + // not used in pipeline. But we still track this binding and read/write usage in one + // dispatch is not allowed. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(1, bg1); + pass.SetPipeline(cp); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add resource tracking per dispatch for compute pass + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + + // Test that using a single texture in multiple read usages in the same pass is allowed. + TEST_F(ResourceUsageTrackingTest, TextureWithMultipleReadUsages) { + // Create a texture that will be used as both sampled and readonly storage texture + wgpu::Texture texture = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Create a bind group to use the texture as sampled and readonly storage bindings + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute, + wgpu::BindingType::SampledTexture}, + {1, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute, + wgpu::BindingType::ReadonlyStorageTexture, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}}); + + // Test render pass + { + // Use the texture as both sampled and readonly storage in the same render pass + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + + // Test compute pass + { + // Use the texture as both sampled and readonly storage in the same compute pass + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + } + + // Test that it is invalid to use the same texture as both readable and writable in the same + // render pass. It is invalid in the same dispatch in compute pass. + TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsage) { + // Test render pass + { + // Create a texture + wgpu::Texture texture = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment); + wgpu::TextureView view = texture.CreateView(); + + // Create a bind group to use the texture as sampled binding + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::SampledTexture}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}}); + + // Create a render pass to use the texture as a render target + utils::ComboRenderPassDescriptor renderPass({view}); + + // It is invalid to use the texture as both sampled and render target in the same pass + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass + { + // Create a texture + wgpu::Texture texture = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Create a bind group to use the texture as sampled and writeonly bindings + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}}); + + // Create a no-op compute pipeline + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // It is valid to use the texture as both sampled and writeonly storage in a single + // compute pass if dispatch command is not called. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + + // It is invalid to use the texture as both sampled and writeonly storage in a single + // dispatch + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add texture usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + } + + // Test using multiple writable usages on the same texture in a single pass/dispatch + TEST_F(ResourceUsageTrackingTest, TextureWithMultipleWriteUsage) { + // Test render pass + { + // Create a texture + wgpu::Texture texture = + CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::OutputAttachment); + wgpu::TextureView view = texture.CreateView(); + + // Create a bind group to use the texture as writeonly storage binding + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::WriteonlyStorageTexture, false, + 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}}); + + // It is invalid to use the texture as both writeonly storage and render target in + // the same pass + { + utils::ComboRenderPassDescriptor renderPass({view}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // It is valid to use multiple writeonly storage usages on the same texture in render + // pass + { + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, view}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetBindGroup(0, bg); + pass.SetBindGroup(1, bg1); + pass.EndPass(); + encoder.Finish(); + } + } + + // Test compute pass + { + // Create a texture + wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Create a bind group to use the texture as sampled and writeonly bindings + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}}); + + // Create a no-op compute pipeline + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // It is valid to use the texture as multiple writeonly storage usages in a single + // compute pass if dispatch command is not called. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + + // It is invalid to use the texture as multiple writeonly storage usages in a single + // dispatch + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add texture usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + } + + // Test that using the same texture as both readable and writable in different passes is + // allowed + TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsageInDifferentPasses) { + // Test render pass + { + // Create textures that will be used as both a sampled texture and a render target + wgpu::Texture t0 = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment); + wgpu::TextureView v0 = t0.CreateView(); + wgpu::Texture t1 = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment); + wgpu::TextureView v1 = t1.CreateView(); + + // Create bind groups to use the texture as sampled + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::SampledTexture}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl, {{0, v0}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, v1}}); + + // Create render passes that will use the textures as output attachments + utils::ComboRenderPassDescriptor renderPass0({v1}); + utils::ComboRenderPassDescriptor renderPass1({v0}); + + // Use the textures as both sampled and output attachments in different passes + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::RenderPassEncoder pass0 = encoder.BeginRenderPass(&renderPass0); + pass0.SetBindGroup(0, bg0); + pass0.EndPass(); + + wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPass1); + pass1.SetBindGroup(0, bg1); + pass1.EndPass(); + + encoder.Finish(); + } + + // Test compute pass + { + // Create a texture that will be used storage texture + wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Create bind groups to use the texture as readonly and writeonly bindings + wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}}); + + // Use the textures as both readonly and writeonly storages in different passes + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass(); + pass0.SetBindGroup(0, readBG); + pass0.EndPass(); + + wgpu::ComputePassEncoder pass1 = encoder.BeginComputePass(); + pass1.SetBindGroup(0, writeBG); + pass1.EndPass(); + + encoder.Finish(); + } + + // Test compute pass and render pass mixed together with resource dependency + { + // Create a texture that will be used a storage texture + wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Create bind groups to use the texture as readonly and writeonly bindings + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}}); + wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}}); + + // Use the texture as writeonly and readonly storage in compute pass and render + // pass respectively + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + wgpu::ComputePassEncoder pass0 = encoder.BeginComputePass(); + pass0.SetBindGroup(0, writeBG); + pass0.EndPass(); + + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&dummyRenderPass); + pass1.SetBindGroup(0, readBG); + pass1.EndPass(); + + encoder.Finish(); + } + } + + // Test that it is invalid to use the same texture as both readable and writable in different + // draws in a single render pass. But it is valid in different dispatches in a single compute + // pass. + TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsageOnDifferentDrawsOrDispatches) { + // Create a texture that will be used both as a sampled texture and a storage texture + wgpu::Texture texture = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Test render pass + { + // Create bind groups to use the texture as sampled and writeonly storage bindings + wgpu::BindGroupLayout sampledBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}}); + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::WriteonlyStorageTexture, false, + 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup sampledBG = utils::MakeBindGroup(device, sampledBGL, {{0, view}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}}); + + // Create a no-op render pipeline. + wgpu::RenderPipeline rp = CreateNoOpRenderPipeline(); + + // It is not allowed to use the same texture as both readable and writable in different + // draws within the same render pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetPipeline(rp); + + pass.SetBindGroup(0, sampledBG); + pass.Draw(3); + + pass.SetBindGroup(0, writeBG); + pass.Draw(3); + + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass + { + // Create bind groups to use the texture as readonly and writeonly storage bindings + wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // It is valid to use the same texture as both readable and writable in different + // dispatches within the same compute pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + + pass.SetBindGroup(0, readBG); + pass.Dispatch(1); + + pass.SetBindGroup(0, writeBG); + pass.Dispatch(1); + + pass.EndPass(); + encoder.Finish(); + } + } + + // Test that it is invalid to use the same texture as both readable and writable in a single + // draw or dispatch. + TEST_F(ResourceUsageTrackingTest, TextureWithReadAndWriteUsageInSingleDrawOrDispatch) { + // Create a texture that will be used both as a sampled texture and a storage texture + wgpu::Texture texture = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Test render pass + { + // Create the bind group to use the texture as sampled and writeonly storage bindings + wgpu::BindGroupLayout sampledBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture}}); + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::WriteonlyStorageTexture, false, + 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup sampledBG = utils::MakeBindGroup(device, sampledBGL, {{0, view}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}}); + + // Create a no-op render pipeline. + wgpu::RenderPipeline rp = CreateNoOpRenderPipeline(); + + // It is invalid to use the same texture as both readable and writable usages in a + // single draw + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetPipeline(rp); + + pass.SetBindGroup(0, sampledBG); + pass.SetBindGroup(1, writeBG); + pass.Draw(3); + + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass + { + // Create the bind group to use the texture as readonly and writeonly storage bindings + wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // It is invalid to use the same texture as both readable and writable usages in a + // single dispatch + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + + pass.SetBindGroup(0, readBG); + pass.SetBindGroup(1, writeBG); + pass.Dispatch(1); + + pass.EndPass(); + // TODO (yunchao.he@intel.com): add texture usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + + // Test that using a single texture as copy src/dst and writable/readable usage in pass is + // allowed. + TEST_F(ResourceUsageTrackingTest, TextureCopyAndTextureUsageInPass) { + // Create textures that will be used as both a sampled texture and a render target + wgpu::Texture texture0 = CreateTexture(wgpu::TextureUsage::CopySrc); + wgpu::Texture texture1 = + CreateTexture(wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::Sampled | + wgpu::TextureUsage::OutputAttachment); + wgpu::TextureView view0 = texture0.CreateView(); + wgpu::TextureView view1 = texture1.CreateView(); + + wgpu::TextureCopyView srcView = utils::CreateTextureCopyView(texture0, 0, {0, 0, 0}); + wgpu::TextureCopyView dstView = utils::CreateTextureCopyView(texture1, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {1, 1, 1}; + + // Use the texture as both copy dst and output attachment in render pass + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcView, &dstView, ©Size); + utils::ComboRenderPassDescriptor renderPass({view1}); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.EndPass(); + encoder.Finish(); + } + + // Use the texture as both copy dst and readable usage in compute pass + { + // Create the bind group to use the texture as sampled + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view1}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToTexture(&srcView, &dstView, ©Size); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, bg); + pass.EndPass(); + encoder.Finish(); + } + } + + // Test that all consecutive SetBindGroup()s take effect even though some bind groups are not + // used because they are overwritten by a consecutive call. + TEST_F(ResourceUsageTrackingTest, TextureWithMultipleSetBindGroupsOnSameIndex) { + // Test render pass + { + // Create textures that will be used as both a sampled texture and a render target + wgpu::Texture texture0 = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment); + wgpu::TextureView view0 = texture0.CreateView(); + wgpu::Texture texture1 = + CreateTexture(wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment); + wgpu::TextureView view1 = texture1.CreateView(); + + // Create the bind group to use the texture as sampled + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::SampledTexture}}); + wgpu::BindGroup bg0 = utils::MakeBindGroup(device, bgl, {{0, view0}}); + wgpu::BindGroup bg1 = utils::MakeBindGroup(device, bgl, {{0, view1}}); + + // Create the render pass that will use the texture as an output attachment + utils::ComboRenderPassDescriptor renderPass({view0}); + + // Set bind group on the same index twice. The second one overwrites the first one. + // No texture is used as both sampled and output attachment in the same pass. But the + // overwritten texture still take effect during resource tracking. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetBindGroup(0, bg0); + pass.SetBindGroup(0, bg1); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Set bind group on the same index twice. The second one overwrites the first one. + // texture0 is used as both sampled and output attachment in the same pass + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetBindGroup(0, bg1); + pass.SetBindGroup(0, bg0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + } + + // Test compute pass + { + // Create a texture that will be used both as storage texture + wgpu::Texture texture0 = CreateTexture(wgpu::TextureUsage::Storage); + wgpu::TextureView view0 = texture0.CreateView(); + wgpu::Texture texture1 = CreateTexture(wgpu::TextureUsage::Storage); + wgpu::TextureView view1 = texture1.CreateView(); + + // Create the bind group to use the texture as readonly and writeonly bindings + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + + wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + + wgpu::BindGroup writeBG0 = utils::MakeBindGroup(device, writeBGL, {{0, view0}}); + wgpu::BindGroup readBG0 = utils::MakeBindGroup(device, readBGL, {{0, view0}}); + wgpu::BindGroup readBG1 = utils::MakeBindGroup(device, readBGL, {{0, view1}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // Set bind group on the same index twice. The second one overwrites the first one. + // No texture is used as both readonly and writeonly storage in the same pass. But the + // overwritten texture still take effect during resource tracking. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, writeBG0); + pass.SetBindGroup(1, readBG0); + pass.SetBindGroup(1, readBG1); + pass.SetPipeline(cp); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add texture usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + + // Set bind group on the same index twice. The second one overwrites the first one. + // texture0 is used as both writeonly and readonly storage in the same pass. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, writeBG0); + pass.SetBindGroup(1, readBG1); + pass.SetBindGroup(1, readBG0); + pass.SetPipeline(cp); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add texture usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + } + + // Test that it is invalid to have resource usage conflicts even when all bindings are not + // visible to the programmable pass where it is used. + TEST_F(ResourceUsageTrackingTest, TextureUsageConflictBetweenInvisibleStagesInBindGroup) { + // Create texture and texture view + wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Test render pass for bind group. The conflict of readonly storage and writeonly storage + // usage doesn't reside in render related stages at all + { + // Create a bind group whose bindings are not visible in render pass + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}, + {1, wgpu::ShaderStage::None, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}}); + + // These two bindings are invisible in render pass. But we still track these bindings. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass for bind group. The conflict of readonly storage and writeonly storage + // usage doesn't reside in compute related stage at all + { + // Create a bind group whose bindings are not visible in compute pass + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}, + {1, wgpu::ShaderStage::None, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // These two bindings are invisible in compute pass. But we still track these bindings. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add texture usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + + // Test that it is invalid to have resource usage conflicts even when one of the bindings is not + // visible to the programmable pass where it is used. + TEST_F(ResourceUsageTrackingTest, TextureUsageConflictWithInvisibleStageInBindGroup) { + // Create texture and texture view + wgpu::Texture texture = + CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::OutputAttachment); + wgpu::TextureView view = texture.CreateView(); + + // Test render pass + { + // Create the render pass that will use the texture as an output attachment + utils::ComboRenderPassDescriptor renderPass({view}); + + // Create a bind group which use the texture as readonly storage in compute stage + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}}); + + // Texture usage in compute stage in bind group conflicts with render target. And + // binding for compute stage is not visible in render pass. But we still track this + // binding. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetBindGroup(0, bg); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass + { + // Create a bind group which contains both fragment and compute stages + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bg = utils::MakeBindGroup(device, bgl, {{0, view}, {1, view}}); + + // Create a no-op compute pipeline. + wgpu::ComputePipeline cp = CreateNoOpComputePipeline(); + + // Texture usage in compute stage conflicts with texture usage in fragment stage. And + // binding for fragment stage is not visible in compute pass. But we still track this + // invisible binding. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetPipeline(cp); + pass.SetBindGroup(0, bg); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add texture usage tracking for compute + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + + // Test that it is invalid to have resource usage conflicts even when one of the bindings is not + // used in the pipeline. + TEST_F(ResourceUsageTrackingTest, TextureUsageConflictWithUnusedPipelineBindings) { + // Create texture and texture view + wgpu::Texture texture = CreateTexture(wgpu::TextureUsage::Storage); + wgpu::TextureView view = texture.CreateView(); + + // Create bind groups. + wgpu::BindGroupLayout readBGL = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute, + wgpu::BindingType::ReadonlyStorageTexture, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroupLayout writeBGL = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment | wgpu::ShaderStage::Compute, + wgpu::BindingType::WriteonlyStorageTexture, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup readBG = utils::MakeBindGroup(device, readBGL, {{0, view}}); + wgpu::BindGroup writeBG = utils::MakeBindGroup(device, writeBGL, {{0, view}}); + + // Test render pass + { + // Create a passthrough render pipeline with a readonly storage texture + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + })"); + + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform readonly image2D image; + void main() { + })"); + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.cFragmentStage.module = fsModule; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &readBGL); + wgpu::RenderPipeline rp = device.CreateRenderPipeline(&pipelineDescriptor); + + // Texture binding in readBG conflicts with texture binding in writeBG. The binding + // in writeBG is not used in pipeline. But we still track this binding. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + DummyRenderPass dummyRenderPass(device); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&dummyRenderPass); + pass.SetBindGroup(0, readBG); + pass.SetBindGroup(1, writeBG); + pass.SetPipeline(rp); + pass.Draw(3); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Test compute pass + { + // Create a passthrough compute pipeline with a readonly storage texture + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform readonly image2D image; + void main() { + })"); + wgpu::ComputePipelineDescriptor pipelineDescriptor; + pipelineDescriptor.layout = utils::MakeBasicPipelineLayout(device, &readBGL); + pipelineDescriptor.computeStage.module = csModule; + pipelineDescriptor.computeStage.entryPoint = "main"; + wgpu::ComputePipeline cp = device.CreateComputePipeline(&pipelineDescriptor); + + // Texture binding in readBG conflicts with texture binding in writeBG. The binding + // in writeBG is not used in pipeline. But we still track this binding. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder pass = encoder.BeginComputePass(); + pass.SetBindGroup(0, readBG); + pass.SetBindGroup(1, writeBG); + pass.SetPipeline(cp); + pass.Dispatch(1); + pass.EndPass(); + // TODO (yunchao.he@intel.com): add resource tracking per dispatch for compute pass + // ASSERT_DEVICE_ERROR(encoder.Finish()); + encoder.Finish(); + } + } + + // TODO (yunchao.he@intel.com): + // + // * Add tests for multiple encoders upon the same resource simultaneously. This situation fits + // some cases like VR, multi-threading, etc. + // + // * Add tests for indirect buffer + // + // * Add tests for bundle + +} // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/validation/SamplerValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/SamplerValidationTests.cpp index 82bf2b25dbd..96f91877083 100644 --- a/third_party/dawn/src/tests/unittests/validation/SamplerValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/SamplerValidationTests.cpp @@ -14,7 +14,7 @@ #include "tests/unittests/validation/ValidationTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" #include @@ -25,24 +25,29 @@ namespace { // Test NaN and INFINITY values are not allowed TEST_F(SamplerValidationTest, InvalidLOD) { { - dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + device.CreateSampler(&samplerDesc); + } + { + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); samplerDesc.lodMinClamp = NAN; ASSERT_DEVICE_ERROR(device.CreateSampler(&samplerDesc)); } { - dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); samplerDesc.lodMaxClamp = NAN; ASSERT_DEVICE_ERROR(device.CreateSampler(&samplerDesc)); } { - dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); - samplerDesc.lodMinClamp = INFINITY; - ASSERT_DEVICE_ERROR(device.CreateSampler(&samplerDesc)); + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + samplerDesc.lodMaxClamp = INFINITY; + device.CreateSampler(&samplerDesc); } { - dawn::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); samplerDesc.lodMaxClamp = INFINITY; - ASSERT_DEVICE_ERROR(device.CreateSampler(&samplerDesc)); + samplerDesc.lodMinClamp = INFINITY; + device.CreateSampler(&samplerDesc); } } diff --git a/third_party/dawn/src/tests/unittests/validation/ShaderModuleValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/ShaderModuleValidationTests.cpp index b1bda010d97..1e570eb4216 100644 --- a/third_party/dawn/src/tests/unittests/validation/ShaderModuleValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/ShaderModuleValidationTests.cpp @@ -12,12 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "common/Constants.h" + #include "tests/unittests/validation/ValidationTest.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" + +#include -class ShaderModuleValidationTest : public ValidationTest { -}; +class ShaderModuleValidationTest : public ValidationTest {}; // Test case with a simpler shader that should successfully be created TEST_F(ShaderModuleValidationTest, CreationSuccess) { @@ -89,3 +92,38 @@ TEST_F(ShaderModuleValidationTest, DISABLED_OpUndef) { std::string error = GetLastDeviceErrorMessage(); ASSERT_NE(error.find("OpUndef"), std::string::npos); } + +// Tests that if the output location exceeds kMaxColorAttachments the fragment shader will fail to +// be compiled. +TEST_F(ShaderModuleValidationTest, FragmentOutputLocationExceedsMaxColorAttachments) { + std::ostringstream stream; + stream << R"(#version 450 + layout(location = )" + << kMaxColorAttachments << R"() out vec4 fragColor; + void main() { + fragColor = vec4(0.0, 1.0, 0.0, 1.0); + })"; + + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, + stream.str().c_str())); +} + +// Test that it is invalid to create a shader module with no chained descriptor. (It must be +// WGSL or SPIRV, not empty) +TEST_F(ShaderModuleValidationTest, NoChainedDescriptor) { + wgpu::ShaderModuleDescriptor desc = {}; + ASSERT_DEVICE_ERROR(device.CreateShaderModule(&desc)); +} + +// Test that it is not allowed to use combined texture and sampler. +// TODO(jiawei.shao@intel.com): support extracting combined texture and sampler in spvc. +TEST_F(ShaderModuleValidationTest, CombinedTextureAndSampler) { + const char* shader = R"( + #version 450 + layout (set = 0, binding = 0) uniform sampler2D texture; + void main() { + })"; + + ASSERT_DEVICE_ERROR( + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, shader)); +} diff --git a/third_party/dawn/src/tests/unittests/validation/StorageTextureValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/StorageTextureValidationTests.cpp new file mode 100644 index 00000000000..ca3e3b289c4 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/StorageTextureValidationTests.cpp @@ -0,0 +1,1016 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Assert.h" +#include "tests/unittests/validation/ValidationTest.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" + +class StorageTextureValidationTests : public ValidationTest { + protected: + static const char* GetGLSLFloatImageTypeDeclaration(wgpu::TextureViewDimension dimension) { + switch (dimension) { + case wgpu::TextureViewDimension::e1D: + return "image1D"; + case wgpu::TextureViewDimension::e2D: + return "image2D"; + case wgpu::TextureViewDimension::e2DArray: + return "image2DArray"; + case wgpu::TextureViewDimension::Cube: + return "imageCube"; + case wgpu::TextureViewDimension::CubeArray: + return "imageCubeArray"; + case wgpu::TextureViewDimension::e3D: + return "image3D"; + case wgpu::TextureViewDimension::Undefined: + default: + UNREACHABLE(); + return ""; + } + } + + static std::string CreateComputeShaderWithStorageTexture( + wgpu::BindingType storageTextureBindingType, + wgpu::TextureFormat textureFormat, + wgpu::TextureViewDimension textureViewDimension = wgpu::TextureViewDimension::e2D) { + const char* glslImageFormatQualifier = utils::GetGLSLImageFormatQualifier(textureFormat); + const char* textureComponentTypePrefix = + utils::GetColorTextureComponentTypePrefix(textureFormat); + return CreateComputeShaderWithStorageTexture( + storageTextureBindingType, glslImageFormatQualifier, textureComponentTypePrefix, + GetGLSLFloatImageTypeDeclaration(textureViewDimension)); + } + + static std::string CreateComputeShaderWithStorageTexture( + wgpu::BindingType storageTextureBindingType, + const char* glslImageFormatQualifier, + const char* textureComponentTypePrefix, + const char* glslImageTypeDeclaration = "image2D") { + const char* memoryQualifier = ""; + switch (storageTextureBindingType) { + case wgpu::BindingType::ReadonlyStorageTexture: + memoryQualifier = "readonly"; + break; + case wgpu::BindingType::WriteonlyStorageTexture: + memoryQualifier = "writeonly"; + break; + default: + UNREACHABLE(); + break; + } + + std::ostringstream ostream; + ostream << "#version 450\n" + "layout (set = 0, binding = 0, " + << glslImageFormatQualifier << ") uniform " << memoryQualifier << " " + << textureComponentTypePrefix << glslImageTypeDeclaration + << " image0;\n" + "void main() {\n" + "}\n"; + + return ostream.str(); + } + + wgpu::Texture CreateTexture(wgpu::TextureUsage usage, + wgpu::TextureFormat format, + uint32_t sampleCount = 1, + uint32_t arrayLayerCount = 1) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size = {16, 16, arrayLayerCount}; + descriptor.sampleCount = sampleCount; + descriptor.format = format; + descriptor.mipLevelCount = 1; + descriptor.usage = usage; + return device.CreateTexture(&descriptor); + } + + const wgpu::ShaderModule mDefaultVSModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + })"); + const wgpu::ShaderModule mDefaultFSModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(1.f, 0.f, 0.f, 1.f); + })"); + + const std::array kSupportedStorageTextureBindingTypes = { + wgpu::BindingType::ReadonlyStorageTexture, wgpu::BindingType::WriteonlyStorageTexture}; +}; + +// Validate read-only storage textures can be declared in vertex and fragment shaders, while +// writeonly storage textures cannot be used in vertex shaders. +TEST_F(StorageTextureValidationTests, RenderPipeline) { + // Readonly storage texture can be declared in a vertex shader. + { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform readonly image2D image0; + void main() { + gl_Position = imageLoad(image0, ivec2(gl_VertexIndex, 0)); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = mDefaultFSModule; + device.CreateRenderPipeline(&descriptor); + } + + // Read-only storage textures can be declared in a fragment shader. + { + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform readonly image2D image0; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = imageLoad(image0, ivec2(gl_FragCoord.xy)); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = mDefaultVSModule; + descriptor.cFragmentStage.module = fsModule; + device.CreateRenderPipeline(&descriptor); + } + + // Write-only storage textures cannot be declared in a vertex shader. + { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0; + void main() { + imageStore(image0, ivec2(gl_VertexIndex, 0), vec4(1.f, 0.f, 0.f, 1.f)); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = mDefaultFSModule; + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + + // Write-only storage textures can be declared in a fragment shader. + { + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0; + void main() { + imageStore(image0, ivec2(gl_FragCoord.xy), vec4(1.f, 0.f, 0.f, 1.f)); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = mDefaultVSModule; + descriptor.cFragmentStage.module = fsModule; + device.CreateRenderPipeline(&descriptor); + } +} + +// Validate both read-only and write-only storage textures can be declared in +// compute shaders. +TEST_F(StorageTextureValidationTests, ComputePipeline) { + // Read-only storage textures can be declared in a compute shader. + { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform readonly image2D image0; + layout(std430, set = 0, binding = 1) buffer Buf { uint buf; }; + void main() { + vec4 pixel = imageLoad(image0, ivec2(gl_LocalInvocationID.xy)); + buf = uint(pixel.x); + })"); + + wgpu::ComputePipelineDescriptor descriptor; + descriptor.layout = nullptr; + descriptor.computeStage.module = csModule; + descriptor.computeStage.entryPoint = "main"; + + device.CreateComputePipeline(&descriptor); + } + + // Write-only storage textures can be declared in a compute shader. + { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform writeonly image2D image0; + void main() { + imageStore(image0, ivec2(gl_LocalInvocationID.xy), vec4(0.f, 0.f, 0.f, 0.f)); + })"); + + wgpu::ComputePipelineDescriptor descriptor; + descriptor.layout = nullptr; + descriptor.computeStage.module = csModule; + descriptor.computeStage.entryPoint = "main"; + + device.CreateComputePipeline(&descriptor); + } +} + +// Validate read-write storage textures have not been supported yet. +TEST_F(StorageTextureValidationTests, ReadWriteStorageTexture) { + // Read-write storage textures cannot be declared in a vertex shader by default. + { + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform image2D image0; + void main() { + vec4 pixel = imageLoad(image0, ivec2(gl_VertexIndex, 0)); + imageStore(image0, ivec2(gl_VertexIndex, 0), pixel * 2); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = mDefaultFSModule; + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + + // Read-write storage textures cannot be declared in a fragment shader by default. + { + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform image2D image0; + void main() { + vec4 pixel = imageLoad(image0, ivec2(gl_FragCoord.xy)); + imageStore(image0, ivec2(gl_FragCoord.xy), pixel * 2); + })"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.layout = nullptr; + descriptor.vertexStage.module = mDefaultVSModule; + descriptor.cFragmentStage.module = fsModule; + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } + + // Read-write storage textures cannot be declared in a compute shader by default. + { + wgpu::ShaderModule csModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, R"( + #version 450 + layout(set = 0, binding = 0, rgba8) uniform image2D image0; + void main() { + vec4 pixel = imageLoad(image0, ivec2(gl_LocalInvocationID.xy)); + imageStore(image0, ivec2(gl_LocalInvocationID.xy), pixel * 2); + })"); + + wgpu::ComputePipelineDescriptor descriptor; + descriptor.layout = nullptr; + descriptor.computeStage.module = csModule; + descriptor.computeStage.entryPoint = "main"; + + ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&descriptor)); + } +} + +// Test that using read-only storage texture and write-only storage texture in +// BindGroupLayout is valid, while using read-write storage texture is not allowed now. +TEST_F(StorageTextureValidationTests, BindGroupLayoutWithStorageTextureBindingType) { + struct TestSpec { + wgpu::ShaderStage stage; + wgpu::BindingType type; + bool valid; + }; + constexpr std::array kTestSpecs = { + {{wgpu::ShaderStage::Vertex, wgpu::BindingType::ReadonlyStorageTexture, true}, + {wgpu::ShaderStage::Vertex, wgpu::BindingType::WriteonlyStorageTexture, false}, + {wgpu::ShaderStage::Vertex, wgpu::BindingType::StorageTexture, false}, + {wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageTexture, true}, + {wgpu::ShaderStage::Fragment, wgpu::BindingType::WriteonlyStorageTexture, true}, + {wgpu::ShaderStage::Fragment, wgpu::BindingType::StorageTexture, false}, + {wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, true}, + {wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, true}, + {wgpu::ShaderStage::Compute, wgpu::BindingType::StorageTexture, false}}}; + + for (const auto& testSpec : kTestSpecs) { + wgpu::BindGroupLayoutEntry entry = {0, testSpec.stage, testSpec.type}; + entry.storageTextureFormat = wgpu::TextureFormat::R32Uint; + + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = 1; + descriptor.entries = &entry; + + if (testSpec.valid) { + device.CreateBindGroupLayout(&descriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateBindGroupLayout(&descriptor)); + } + } +} + +// Validate it is an error to declare a read-only or write-only storage texture in shaders with any +// format that doesn't support TextureUsage::Storage texture usages. +TEST_F(StorageTextureValidationTests, StorageTextureFormatInShaders) { + // Not include RGBA8UnormSrgb, BGRA8Unorm, BGRA8UnormSrgb because they are not related to any + // SPIR-V Image Formats. + constexpr std::array kWGPUTextureFormatSupportedAsSPIRVImageFormats = { + wgpu::TextureFormat::R32Uint, wgpu::TextureFormat::R32Sint, + wgpu::TextureFormat::R32Float, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureFormat::RGBA8Snorm, wgpu::TextureFormat::RGBA8Uint, + wgpu::TextureFormat::RGBA8Sint, wgpu::TextureFormat::RG32Uint, + wgpu::TextureFormat::RG32Sint, wgpu::TextureFormat::RG32Float, + wgpu::TextureFormat::RGBA16Uint, wgpu::TextureFormat::RGBA16Sint, + wgpu::TextureFormat::RGBA16Float, wgpu::TextureFormat::RGBA32Uint, + wgpu::TextureFormat::RGBA32Sint, wgpu::TextureFormat::RGBA32Float, + wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Snorm, + wgpu::TextureFormat::R8Uint, wgpu::TextureFormat::R8Sint, + wgpu::TextureFormat::R16Uint, wgpu::TextureFormat::R16Sint, + wgpu::TextureFormat::R16Float, wgpu::TextureFormat::RG8Unorm, + wgpu::TextureFormat::RG8Snorm, wgpu::TextureFormat::RG8Uint, + wgpu::TextureFormat::RG8Sint, wgpu::TextureFormat::RG16Uint, + wgpu::TextureFormat::RG16Sint, wgpu::TextureFormat::RG16Float, + wgpu::TextureFormat::RGB10A2Unorm, wgpu::TextureFormat::RG11B10Float}; + + for (wgpu::BindingType storageTextureBindingType : kSupportedStorageTextureBindingTypes) { + for (wgpu::TextureFormat format : kWGPUTextureFormatSupportedAsSPIRVImageFormats) { + std::string computeShader = + CreateComputeShaderWithStorageTexture(storageTextureBindingType, format); + if (utils::TextureFormatSupportsStorageTexture(format)) { + utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, + computeShader.c_str()); + } else { + ASSERT_DEVICE_ERROR(utils::CreateShaderModule( + device, utils::SingleShaderStage::Compute, computeShader.c_str())); + } + } + } +} + +// Verify that declaring a storage texture format that is not supported in WebGPU causes validation +// error. +TEST_F(StorageTextureValidationTests, UnsupportedSPIRVStorageTextureFormat) { + struct TextureFormatInfo { + const char* name; + const char* componentTypePrefix; + }; + + constexpr std::array kUnsupportedTextureFormats = {{{"rgba16", ""}, + {"rg16", ""}, + {"r16", ""}, + {"rgba16_snorm", ""}, + {"rg16_snorm", ""}, + {"r16_snorm", ""}, + {"rgb10_a2ui", "u"}}}; + + for (wgpu::BindingType bindingType : kSupportedStorageTextureBindingTypes) { + for (const TextureFormatInfo& formatInfo : kUnsupportedTextureFormats) { + std::string computeShader = CreateComputeShaderWithStorageTexture( + bindingType, formatInfo.name, formatInfo.componentTypePrefix); + ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, + computeShader.c_str())); + } + } +} + +// Verify that declaring a storage texture dimension that is not supported in WebGPU in shader +// causes validation error at the creation of PSO. WebGPU doesn't support using cube map texture +// views and cube map array texture views as storage textures. +TEST_F(StorageTextureValidationTests, UnsupportedTextureViewDimensionInShader) { + constexpr std::array kUnsupportedTextureViewDimensions = { + wgpu::TextureViewDimension::Cube, wgpu::TextureViewDimension::CubeArray}; + constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::R32Float; + + for (wgpu::BindingType bindingType : kSupportedStorageTextureBindingTypes) { + for (wgpu::TextureViewDimension dimension : kUnsupportedTextureViewDimensions) { + std::string computeShader = + CreateComputeShaderWithStorageTexture(bindingType, kFormat, dimension); + wgpu::ShaderModule csModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Compute, computeShader.c_str()); + + wgpu::ComputePipelineDescriptor computePipelineDescriptor; + computePipelineDescriptor.computeStage.module = csModule; + computePipelineDescriptor.computeStage.entryPoint = "main"; + computePipelineDescriptor.layout = nullptr; + ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor)); + } + } +} + +// Verify that declaring a texture view dimension that is not supported to be used as storage +// textures in WebGPU in bind group layout causes validation error. WebGPU doesn't support using +// cube map texture views and cube map array texture views as storage textures. +TEST_F(StorageTextureValidationTests, UnsupportedTextureViewDimensionInBindGroupLayout) { + constexpr std::array kUnsupportedTextureViewDimensions = { + wgpu::TextureViewDimension::Cube, wgpu::TextureViewDimension::CubeArray}; + constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::R32Float; + + for (wgpu::BindingType bindingType : kSupportedStorageTextureBindingTypes) { + for (wgpu::TextureViewDimension dimension : kUnsupportedTextureViewDimensions) { + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Compute, bindingType, false, 0, false, dimension, + wgpu::TextureComponentType::Float, kFormat}})); + } + } +} + +// Verify when we create and use a bind group layout with storage textures in the creation of +// render and compute pipeline, the binding type in the bind group layout must match the +// declaration in the shader. +TEST_F(StorageTextureValidationTests, BindGroupLayoutEntryTypeMatchesShaderDeclaration) { + constexpr std::array kSupportedBindingTypes = { + wgpu::BindingType::UniformBuffer, wgpu::BindingType::StorageBuffer, + wgpu::BindingType::ReadonlyStorageBuffer, wgpu::BindingType::Sampler, + wgpu::BindingType::SampledTexture, wgpu::BindingType::ReadonlyStorageTexture, + wgpu::BindingType::WriteonlyStorageTexture}; + constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float; + + for (wgpu::BindingType bindingTypeInShader : kSupportedStorageTextureBindingTypes) { + // Create the compute shader with the given binding type. + std::string computeShader = + CreateComputeShaderWithStorageTexture(bindingTypeInShader, kStorageTextureFormat); + wgpu::ShaderModule csModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Compute, computeShader.c_str()); + + // Set common fields of compute pipeline descriptor. + wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor; + defaultComputePipelineDescriptor.computeStage.module = csModule; + defaultComputePipelineDescriptor.computeStage.entryPoint = "main"; + + // Set common fileds of bind group layout binding. + wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry; + defaultBindGroupLayoutEntry.binding = 0; + defaultBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute; + defaultBindGroupLayoutEntry.storageTextureFormat = kStorageTextureFormat; + + for (wgpu::BindingType bindingTypeInBindgroupLayout : kSupportedBindingTypes) { + wgpu::ComputePipelineDescriptor computePipelineDescriptor = + defaultComputePipelineDescriptor; + + // Create bind group layout with different binding types. + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry; + bindGroupLayoutBinding.type = bindingTypeInBindgroupLayout; + wgpu::BindGroupLayout bindGroupLayout = + utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}); + computePipelineDescriptor.layout = + utils::MakeBasicPipelineLayout(device, &bindGroupLayout); + + // The binding type in the bind group layout must the same as the related image object + // declared in shader. + if (bindingTypeInBindgroupLayout == bindingTypeInShader) { + device.CreateComputePipeline(&computePipelineDescriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor)); + } + } + } +} + +// Verify it is invalid not to set a valid texture format in a bind group layout when the binding +// type is read-only or write-only storage texture. +TEST_F(StorageTextureValidationTests, UndefinedStorageTextureFormatInBindGroupLayout) { + wgpu::BindGroupLayoutEntry errorBindGroupLayoutEntry; + errorBindGroupLayoutEntry.binding = 0; + errorBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute; + errorBindGroupLayoutEntry.storageTextureFormat = wgpu::TextureFormat::Undefined; + + for (wgpu::BindingType bindingType : kSupportedStorageTextureBindingTypes) { + errorBindGroupLayoutEntry.type = bindingType; + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(device, {errorBindGroupLayoutEntry})); + } +} + +// Verify it is invalid to create a bind group layout with storage textures and an unsupported +// storage texture format. +TEST_F(StorageTextureValidationTests, StorageTextureFormatInBindGroupLayout) { + wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry; + defaultBindGroupLayoutEntry.binding = 0; + defaultBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute; + + for (wgpu::BindingType bindingType : kSupportedStorageTextureBindingTypes) { + for (wgpu::TextureFormat textureFormat : utils::kAllTextureFormats) { + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry; + bindGroupLayoutBinding.type = bindingType; + bindGroupLayoutBinding.storageTextureFormat = textureFormat; + if (utils::TextureFormatSupportsStorageTexture(textureFormat)) { + utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}); + } else { + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding})); + } + } + } +} + +// Verify the storage texture format in the bind group layout must match the declaration in shader. +TEST_F(StorageTextureValidationTests, BindGroupLayoutStorageTextureFormatMatchesShaderDeclaration) { + for (wgpu::BindingType bindingType : kSupportedStorageTextureBindingTypes) { + for (wgpu::TextureFormat storageTextureFormatInShader : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(storageTextureFormatInShader)) { + continue; + } + + // Create the compute shader module with the given binding type and storage texture + // format. + std::string computeShader = + CreateComputeShaderWithStorageTexture(bindingType, storageTextureFormatInShader); + wgpu::ShaderModule csModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Compute, computeShader.c_str()); + + // Set common fields of compute pipeline descriptor. + wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor; + defaultComputePipelineDescriptor.computeStage.module = csModule; + defaultComputePipelineDescriptor.computeStage.entryPoint = "main"; + + // Set common fileds of bind group layout binding. + wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry = {0, wgpu::ShaderStage::Compute, + bindingType}; + + for (wgpu::TextureFormat storageTextureFormatInBindGroupLayout : + utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture( + storageTextureFormatInBindGroupLayout)) { + continue; + } + + // Create the bind group layout with the given storage texture format. + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry; + bindGroupLayoutBinding.storageTextureFormat = storageTextureFormatInBindGroupLayout; + wgpu::BindGroupLayout bindGroupLayout = + utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}); + + // Create the compute pipeline with the bind group layout. + wgpu::ComputePipelineDescriptor computePipelineDescriptor = + defaultComputePipelineDescriptor; + computePipelineDescriptor.layout = + utils::MakeBasicPipelineLayout(device, &bindGroupLayout); + + // The storage texture format in the bind group layout must be the same as the one + // declared in the shader. + if (storageTextureFormatInShader == storageTextureFormatInBindGroupLayout) { + device.CreateComputePipeline(&computePipelineDescriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor)); + } + } + } + } +} + +// Verify the dimension of the bind group layout with storage textures must match the one declared +// in shader. +TEST_F(StorageTextureValidationTests, BindGroupLayoutViewDimensionMatchesShaderDeclaration) { + constexpr std::array kSupportedDimensions = { + wgpu::TextureViewDimension::e1D, wgpu::TextureViewDimension::e2D, + wgpu::TextureViewDimension::e2DArray, wgpu::TextureViewDimension::e3D}; + constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float; + + for (wgpu::BindingType bindingType : kSupportedStorageTextureBindingTypes) { + for (wgpu::TextureViewDimension dimensionInShader : kSupportedDimensions) { + // Create the compute shader with the given texture view dimension. + std::string computeShader = CreateComputeShaderWithStorageTexture( + bindingType, kStorageTextureFormat, dimensionInShader); + wgpu::ShaderModule csModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Compute, computeShader.c_str()); + + // Set common fields of compute pipeline descriptor. + wgpu::ComputePipelineDescriptor defaultComputePipelineDescriptor; + defaultComputePipelineDescriptor.computeStage.module = csModule; + defaultComputePipelineDescriptor.computeStage.entryPoint = "main"; + + // Set common fileds of bind group layout binding. + wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry = {0, wgpu::ShaderStage::Compute, + bindingType}; + defaultBindGroupLayoutEntry.storageTextureFormat = kStorageTextureFormat; + + for (wgpu::TextureViewDimension dimensionInBindGroupLayout : kSupportedDimensions) { + // Create the bind group layout with the given texture view dimension. + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry; + bindGroupLayoutBinding.viewDimension = dimensionInBindGroupLayout; + wgpu::BindGroupLayout bindGroupLayout = + utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}); + + // Create the compute pipeline with the bind group layout. + wgpu::ComputePipelineDescriptor computePipelineDescriptor = + defaultComputePipelineDescriptor; + computePipelineDescriptor.layout = + utils::MakeBasicPipelineLayout(device, &bindGroupLayout); + + // The texture dimension in the bind group layout must be the same as the one + // declared in the shader. + if (dimensionInShader == dimensionInBindGroupLayout) { + device.CreateComputePipeline(&computePipelineDescriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&computePipelineDescriptor)); + } + } + } + } +} + +// Verify that in a bind group layout binding neither read-only nor write-only storage textures +// are allowed to have dynamic offsets. +TEST_F(StorageTextureValidationTests, StorageTextureCannotHaveDynamicOffsets) { + for (wgpu::BindingType storageBindingType : kSupportedStorageTextureBindingTypes) { + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding; + bindGroupLayoutBinding.binding = 0; + bindGroupLayoutBinding.visibility = wgpu::ShaderStage::Compute; + bindGroupLayoutBinding.type = storageBindingType; + bindGroupLayoutBinding.storageTextureFormat = wgpu::TextureFormat::R32Float; + + bindGroupLayoutBinding.hasDynamicOffset = true; + ASSERT_DEVICE_ERROR(utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding})); + } +} + +// Verify that only a texture view can be used as a read-only or write-only storage texture in a +// bind group. +TEST_F(StorageTextureValidationTests, StorageTextureBindingTypeInBindGroup) { + constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float; + for (wgpu::BindingType storageBindingType : kSupportedStorageTextureBindingTypes) { + // Create a bind group layout. + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding; + bindGroupLayoutBinding.binding = 0; + bindGroupLayoutBinding.visibility = wgpu::ShaderStage::Compute; + bindGroupLayoutBinding.type = storageBindingType; + bindGroupLayoutBinding.storageTextureFormat = kStorageTextureFormat; + wgpu::BindGroupLayout bindGroupLayout = + utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}); + + // Buffers are not allowed to be used as storage textures in a bind group. + { + wgpu::BufferDescriptor descriptor; + descriptor.size = 1024; + descriptor.usage = wgpu::BufferUsage::Uniform; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bindGroupLayout, {{0, buffer}})); + } + + // Samplers are not allowed to be used as storage textures in a bind group. + { + wgpu::SamplerDescriptor descriptor = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&descriptor); + ASSERT_DEVICE_ERROR(utils::MakeBindGroup(device, bindGroupLayout, {{0, sampler}})); + } + + // Texture views are allowed to be used as storage textures in a bind group. + { + wgpu::TextureView textureView = + CreateTexture(wgpu::TextureUsage::Storage, kStorageTextureFormat).CreateView(); + utils::MakeBindGroup(device, bindGroupLayout, {{0, textureView}}); + } + } +} + +// Verify that a texture used as read-only or write-only storage texture in a bind group must be +// created with the texture usage wgpu::TextureUsage::STORAGE. +TEST_F(StorageTextureValidationTests, StorageTextureUsageInBindGroup) { + constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float; + constexpr std::array kTextureUsages = { + wgpu::TextureUsage::CopySrc, wgpu::TextureUsage::CopyDst, + wgpu::TextureUsage::Sampled, wgpu::TextureUsage::Storage, + wgpu::TextureUsage::OutputAttachment, wgpu::TextureUsage::Present}; + + for (wgpu::BindingType storageBindingType : kSupportedStorageTextureBindingTypes) { + // Create a bind group layout. + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding; + bindGroupLayoutBinding.binding = 0; + bindGroupLayoutBinding.visibility = wgpu::ShaderStage::Compute; + bindGroupLayoutBinding.type = storageBindingType; + bindGroupLayoutBinding.storageTextureFormat = wgpu::TextureFormat::R32Float; + wgpu::BindGroupLayout bindGroupLayout = + utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}); + + for (wgpu::TextureUsage usage : kTextureUsages) { + // Create texture views with different texture usages + wgpu::TextureView textureView = + CreateTexture(usage, kStorageTextureFormat).CreateView(); + + // Verify that the texture used as storage texture must be created with the texture + // usage wgpu::TextureUsage::STORAGE. + if (usage & wgpu::TextureUsage::Storage) { + utils::MakeBindGroup(device, bindGroupLayout, {{0, textureView}}); + } else { + ASSERT_DEVICE_ERROR( + utils::MakeBindGroup(device, bindGroupLayout, {{0, textureView}})); + } + } + } +} + +// Verify that the format of a texture used as read-only or write-only storage texture in a bind +// group must match the corresponding bind group binding. +TEST_F(StorageTextureValidationTests, StorageTextureFormatInBindGroup) { + for (wgpu::BindingType storageBindingType : kSupportedStorageTextureBindingTypes) { + wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry; + defaultBindGroupLayoutEntry.binding = 0; + defaultBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute; + defaultBindGroupLayoutEntry.type = storageBindingType; + + for (wgpu::TextureFormat formatInBindGroupLayout : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(formatInBindGroupLayout)) { + continue; + } + + // Create a bind group layout with given storage texture format. + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry; + bindGroupLayoutBinding.storageTextureFormat = formatInBindGroupLayout; + wgpu::BindGroupLayout bindGroupLayout = + utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}); + + for (wgpu::TextureFormat textureViewFormat : utils::kAllTextureFormats) { + if (!utils::TextureFormatSupportsStorageTexture(textureViewFormat)) { + continue; + } + + // Create texture views with different texture formats. + wgpu::TextureView storageTextureView = + CreateTexture(wgpu::TextureUsage::Storage, textureViewFormat).CreateView(); + + // Verify that the format of the texture view used as storage texture in a bind + // group must match the storage texture format declaration in the bind group layout. + if (textureViewFormat == formatInBindGroupLayout) { + utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTextureView}}); + } else { + ASSERT_DEVICE_ERROR( + utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTextureView}})); + } + } + } + } +} + +// Verify that the dimension of a texture view used as read-only or write-only storage texture in a +// bind group must match the corresponding bind group binding. +TEST_F(StorageTextureValidationTests, StorageTextureViewDimensionInBindGroup) { + constexpr wgpu::TextureFormat kStorageTextureFormat = wgpu::TextureFormat::R32Float; + constexpr uint32_t kArrayLayerCount = 6u; + + // Currently we only support creating 2D-compatible texture view dimensions. + // TODO(jiawei.shao@intel.com): test the use of 1D and 3D texture view dimensions when they are + // supported in Dawn. + constexpr std::array kSupportedDimensions = { + wgpu::TextureViewDimension::e2D, wgpu::TextureViewDimension::e2DArray}; + + wgpu::Texture texture = + CreateTexture(wgpu::TextureUsage::Storage, kStorageTextureFormat, 1, kArrayLayerCount); + + wgpu::TextureViewDescriptor kDefaultTextureViewDescriptor; + kDefaultTextureViewDescriptor.format = kStorageTextureFormat; + kDefaultTextureViewDescriptor.baseMipLevel = 0; + kDefaultTextureViewDescriptor.mipLevelCount = 1; + kDefaultTextureViewDescriptor.baseArrayLayer = 0; + kDefaultTextureViewDescriptor.arrayLayerCount = 1u; + + for (wgpu::BindingType storageBindingType : kSupportedStorageTextureBindingTypes) { + wgpu::BindGroupLayoutEntry defaultBindGroupLayoutEntry; + defaultBindGroupLayoutEntry.binding = 0; + defaultBindGroupLayoutEntry.visibility = wgpu::ShaderStage::Compute; + defaultBindGroupLayoutEntry.type = storageBindingType; + defaultBindGroupLayoutEntry.storageTextureFormat = kStorageTextureFormat; + + for (wgpu::TextureViewDimension dimensionInBindGroupLayout : kSupportedDimensions) { + // Create a bind group layout with given texture view dimension. + wgpu::BindGroupLayoutEntry bindGroupLayoutBinding = defaultBindGroupLayoutEntry; + bindGroupLayoutBinding.viewDimension = dimensionInBindGroupLayout; + wgpu::BindGroupLayout bindGroupLayout = + utils::MakeBindGroupLayout(device, {bindGroupLayoutBinding}); + + for (wgpu::TextureViewDimension dimensionOfTextureView : kSupportedDimensions) { + // Create a texture view with given texture view dimension. + wgpu::TextureViewDescriptor textureViewDescriptor = kDefaultTextureViewDescriptor; + textureViewDescriptor.dimension = dimensionOfTextureView; + wgpu::TextureView storageTextureView = texture.CreateView(&textureViewDescriptor); + + // Verify that the dimension of the texture view used as storage texture in a bind + // group must match the texture view dimension declaration in the bind group layout. + if (dimensionInBindGroupLayout == dimensionOfTextureView) { + utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTextureView}}); + } else { + ASSERT_DEVICE_ERROR( + utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTextureView}})); + } + } + } + } +} + +// Verify multisampled storage textures cannot be supported now. +TEST_F(StorageTextureValidationTests, MultisampledStorageTexture) { + for (wgpu::BindingType bindingType : kSupportedStorageTextureBindingTypes) { + std::string computeShader = + CreateComputeShaderWithStorageTexture(bindingType, "rgba8", "", "image2DMS"); + wgpu::ShaderModule csModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Compute, computeShader.c_str()); + + wgpu::ComputePipelineDescriptor descriptor; + descriptor.layout = nullptr; + descriptor.computeStage.module = csModule; + descriptor.computeStage.entryPoint = "main"; + + ASSERT_DEVICE_ERROR(device.CreateComputePipeline(&descriptor)); + } +} + +// Verify it is valid to use a texture as either read-only storage texture or write-only storage +// texture in a render pass. +TEST_F(StorageTextureValidationTests, StorageTextureInRenderPass) { + constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture storageTexture = CreateTexture(wgpu::TextureUsage::Storage, kFormat); + + wgpu::Texture outputAttachment = CreateTexture(wgpu::TextureUsage::OutputAttachment, kFormat); + utils::ComboRenderPassDescriptor renderPassDescriptor({outputAttachment.CreateView()}); + + for (wgpu::BindingType storageTextureType : kSupportedStorageTextureBindingTypes) { + // Create a bind group that contains a storage texture. + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, storageTextureType, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}}); + + wgpu::BindGroup bindGroupWithStorageTexture = + utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTexture.CreateView()}}); + + // It is valid to use a texture as read-only or write-only storage texture in the render + // pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor); + renderPassEncoder.SetBindGroup(0, bindGroupWithStorageTexture); + renderPassEncoder.EndPass(); + encoder.Finish(); + } +} + +// Verify it is valid to use a a texture as both read-only storage texture and sampled texture in +// one render pass, while it is invalid to use a texture as both write-only storage texture and +// sampled texture in one render pass. +TEST_F(StorageTextureValidationTests, StorageTextureAndSampledTextureInOneRenderPass) { + constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture storageTexture = + CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::Sampled, kFormat); + + wgpu::Texture outputAttachment = CreateTexture(wgpu::TextureUsage::OutputAttachment, kFormat); + utils::ComboRenderPassDescriptor renderPassDescriptor({outputAttachment.CreateView()}); + + // Create a bind group that contains a storage texture and a sampled texture. + for (wgpu::BindingType storageTextureType : kSupportedStorageTextureBindingTypes) { + // Create a bind group that binds the same texture as both storage texture and sampled + // texture. + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, storageTextureType, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}, + {1, wgpu::ShaderStage::Fragment, wgpu::BindingType::SampledTexture, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, bindGroupLayout, + {{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}}); + + // It is valid to use a a texture as both read-only storage texture and sampled texture in + // one render pass, while it is invalid to use a texture as both write-only storage + // texture an sampled texture in one render pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor); + renderPassEncoder.SetBindGroup(0, bindGroup); + renderPassEncoder.EndPass(); + switch (storageTextureType) { + case wgpu::BindingType::ReadonlyStorageTexture: + encoder.Finish(); + break; + case wgpu::BindingType::WriteonlyStorageTexture: + ASSERT_DEVICE_ERROR(encoder.Finish()); + break; + default: + UNREACHABLE(); + break; + } + } +} + +// Verify it is invalid to use a a texture as both storage texture (either read-only or write-only) +// and output attachment in one render pass. +TEST_F(StorageTextureValidationTests, StorageTextureAndOutputAttachmentInOneRenderPass) { + constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture storageTexture = + CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::OutputAttachment, kFormat); + utils::ComboRenderPassDescriptor renderPassDescriptor({storageTexture.CreateView()}); + + for (wgpu::BindingType storageTextureType : kSupportedStorageTextureBindingTypes) { + // Create a bind group that contains a storage texture. + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, storageTextureType, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bindGroupWithStorageTexture = + utils::MakeBindGroup(device, bindGroupLayout, {{0, storageTexture.CreateView()}}); + + // It is invalid to use a texture as both storage texture and output attachment in one + // render pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor); + renderPassEncoder.SetBindGroup(0, bindGroupWithStorageTexture); + renderPassEncoder.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Verify it is invalid to use a a texture as both read-only storage texture and write-only storage +// texture in one render pass. +TEST_F(StorageTextureValidationTests, ReadOnlyAndWriteOnlyStorageTextureInOneRenderPass) { + constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture storageTexture = CreateTexture(wgpu::TextureUsage::Storage, kFormat); + + // Create a bind group that uses the same texture as both read-only and write-only storage + // texture. + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageTexture, false, 0, + false, wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}, + {1, wgpu::ShaderStage::Fragment, wgpu::BindingType::WriteonlyStorageTexture, false, 0, + false, wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, + kFormat}}); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, bindGroupLayout, + {{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}}); + + // It is invalid to use a texture as both read-only storage texture and write-only storage + // texture in one render pass. + wgpu::Texture outputAttachment = CreateTexture(wgpu::TextureUsage::OutputAttachment, kFormat); + utils::ComboRenderPassDescriptor renderPassDescriptor({outputAttachment.CreateView()}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor); + renderPassEncoder.SetBindGroup(0, bindGroup); + renderPassEncoder.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); +} + +// Verify it is valid to use a texture as both storage texture (read-only or write-only) and +// sampled texture in one compute pass. +TEST_F(StorageTextureValidationTests, StorageTextureAndSampledTextureInOneComputePass) { + constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture storageTexture = + CreateTexture(wgpu::TextureUsage::Storage | wgpu::TextureUsage::Sampled, kFormat); + + for (wgpu::BindingType storageTextureType : kSupportedStorageTextureBindingTypes) { + // Create a bind group that binds the same texture as both storage texture and sampled + // texture. + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Compute, storageTextureType, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::SampledTexture, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, bindGroupLayout, + {{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}}); + + // It is valid to use a a texture as both storage texture (read-only or write-only) and + // sampled texture in one compute pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup); + computePassEncoder.EndPass(); + encoder.Finish(); + } +} + +// Verify it is valid to use a texture as both read-only storage texture and write-only storage +// texture in one compute pass. +TEST_F(StorageTextureValidationTests, ReadOnlyAndWriteOnlyStorageTextureInOneComputePass) { + constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + wgpu::Texture storageTexture = CreateTexture(wgpu::TextureUsage::Storage, kFormat); + + // Create a bind group that uses the same texture as both read-only and write-only storage + // texture. + wgpu::BindGroupLayout bindGroupLayout = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Compute, wgpu::BindingType::ReadonlyStorageTexture, false, 0, false, + wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, kFormat}, + {1, wgpu::ShaderStage::Compute, wgpu::BindingType::WriteonlyStorageTexture, false, 0, + false, wgpu::TextureViewDimension::Undefined, wgpu::TextureComponentType::Float, + kFormat}}); + wgpu::BindGroup bindGroup = + utils::MakeBindGroup(device, bindGroupLayout, + {{0, storageTexture.CreateView()}, {1, storageTexture.CreateView()}}); + + // It is valid to use a texture as both read-only storage texture and write-only storage + // texture in one compute pass. + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::ComputePassEncoder computePassEncoder = encoder.BeginComputePass(); + computePassEncoder.SetBindGroup(0, bindGroup); + computePassEncoder.EndPass(); + encoder.Finish(); +} diff --git a/third_party/dawn/src/tests/unittests/validation/TextureSubresourceTests.cpp b/third_party/dawn/src/tests/unittests/validation/TextureSubresourceTests.cpp new file mode 100644 index 00000000000..da6d21057d0 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/TextureSubresourceTests.cpp @@ -0,0 +1,164 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/WGPUHelpers.h" + +#include "tests/unittests/validation/ValidationTest.h" + +namespace { + + class TextureSubresourceTest : public ValidationTest { + public: + static constexpr uint32_t kSize = 32u; + static constexpr wgpu::TextureFormat kFormat = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::Texture CreateTexture(uint32_t mipLevelCount, + uint32_t arrayLayerCount, + wgpu::TextureUsage usage) { + wgpu::TextureDescriptor texDesc; + texDesc.dimension = wgpu::TextureDimension::e2D; + texDesc.size = {kSize, kSize, arrayLayerCount}; + texDesc.sampleCount = 1; + texDesc.mipLevelCount = mipLevelCount; + texDesc.usage = usage; + texDesc.format = kFormat; + return device.CreateTexture(&texDesc); + } + + wgpu::TextureView CreateTextureView(wgpu::Texture texture, + uint32_t baseMipLevel, + uint32_t baseArrayLayer) { + wgpu::TextureViewDescriptor viewDesc; + viewDesc.format = kFormat; + viewDesc.baseArrayLayer = baseArrayLayer; + viewDesc.arrayLayerCount = 1; + viewDesc.baseMipLevel = baseMipLevel; + viewDesc.mipLevelCount = 1; + viewDesc.dimension = wgpu::TextureViewDimension::e2D; + return texture.CreateView(&viewDesc); + } + + void TestRenderPass(const wgpu::TextureView& renderView, + const wgpu::TextureView& samplerView) { + // Create bind group + wgpu::BindGroupLayout bgl = utils::MakeBindGroupLayout( + device, {{0, wgpu::ShaderStage::Vertex, wgpu::BindingType::SampledTexture}}); + + utils::ComboRenderPassDescriptor renderPassDesc({renderView}); + + // It is valid to read from and write into different subresources of the same texture + { + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, samplerView}}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetBindGroup(0, bindGroup); + pass.EndPass(); + encoder.Finish(); + } + + // It is valid to has multiple read from a subresource and one single write into another + // subresource + { + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, samplerView}}); + + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::ReadonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + + wgpu::BindGroup bindGroup1 = utils::MakeBindGroup(device, bgl1, {{0, samplerView}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetBindGroup(0, bindGroup); + pass.SetBindGroup(1, bindGroup1); + pass.EndPass(); + encoder.Finish(); + } + + // It is invalid to read and write into the same subresources + { + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, renderView}}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetBindGroup(0, bindGroup); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // It is valid to write into and then read from the same level of a texture in different + // render passes + { + wgpu::BindGroup bindGroup = utils::MakeBindGroup(device, bgl, {{0, samplerView}}); + + wgpu::BindGroupLayout bgl1 = utils::MakeBindGroupLayout( + device, + {{0, wgpu::ShaderStage::Fragment, wgpu::BindingType::WriteonlyStorageTexture, + false, 0, false, wgpu::TextureViewDimension::Undefined, + wgpu::TextureComponentType::Float, kFormat}}); + wgpu::BindGroup bindGroup1 = utils::MakeBindGroup(device, bgl1, {{0, samplerView}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass1 = encoder.BeginRenderPass(&renderPassDesc); + pass1.SetBindGroup(0, bindGroup1); + pass1.EndPass(); + + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDesc); + pass.SetBindGroup(0, bindGroup); + pass.EndPass(); + + encoder.Finish(); + } + } + }; + + // Test different mipmap levels + TEST_F(TextureSubresourceTest, MipmapLevelsTest) { + // Create texture with 2 mipmap levels and 1 layer + wgpu::Texture texture = + CreateTexture(2, 1, + wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::Storage); + + // Create two views on different mipmap levels. + wgpu::TextureView samplerView = CreateTextureView(texture, 0, 0); + wgpu::TextureView renderView = CreateTextureView(texture, 1, 0); + TestRenderPass(samplerView, renderView); + } + + // Test different array layers + TEST_F(TextureSubresourceTest, ArrayLayersTest) { + // Create texture with 1 mipmap level and 2 layers + wgpu::Texture texture = + CreateTexture(1, 2, + wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::Storage); + + // Create two views on different layers. + wgpu::TextureView samplerView = CreateTextureView(texture, 0, 0); + wgpu::TextureView renderView = CreateTextureView(texture, 0, 1); + + TestRenderPass(samplerView, renderView); + } + + // TODO (yunchao.he@intel.com): + // * Add tests for compute, in which texture subresource is traced per dispatch. + // + // * Add tests for multiple encoders upon the same resource simultaneously. This situation fits + // some cases like VR, multi-threading, etc. + // + // * Add tests for conflicts between usages in two render bundles used in the same pass. + +} // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/validation/TextureValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/TextureValidationTests.cpp index 206914bf5ad..52dc1597fa3 100644 --- a/third_party/dawn/src/tests/unittests/validation/TextureValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/TextureValidationTests.cpp @@ -16,332 +16,481 @@ #include "common/Constants.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/TextureFormatUtils.h" +#include "utils/WGPUHelpers.h" namespace { -class TextureValidationTest : public ValidationTest { - protected: - dawn::TextureDescriptor CreateDefaultTextureDescriptor() { - dawn::TextureDescriptor descriptor; - descriptor.size.width = kWidth; - descriptor.size.height = kHeight; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = kDefaultArraySize; - descriptor.mipLevelCount = kDefaultMipLevels; - descriptor.sampleCount = kDefaultSampleCount; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.format = kDefaultTextureFormat; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment | dawn::TextureUsageBit::Sampled; - return descriptor; - } + class TextureValidationTest : public ValidationTest { + protected: + void SetUp() override { + queue = device.GetDefaultQueue(); + } - dawn::Queue queue = device.CreateQueue(); + wgpu::TextureDescriptor CreateDefaultTextureDescriptor() { + wgpu::TextureDescriptor descriptor; + descriptor.size.width = kWidth; + descriptor.size.height = kHeight; + descriptor.size.depth = kDefaultDepth; + descriptor.mipLevelCount = kDefaultMipLevels; + descriptor.sampleCount = kDefaultSampleCount; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.format = kDefaultTextureFormat; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::Sampled; + return descriptor; + } - private: - static constexpr uint32_t kWidth = 32; - static constexpr uint32_t kHeight = 32; - static constexpr uint32_t kDefaultArraySize = 1; - static constexpr uint32_t kDefaultMipLevels = 1; - static constexpr uint32_t kDefaultSampleCount = 1; + wgpu::Queue queue; - static constexpr dawn::TextureFormat kDefaultTextureFormat = dawn::TextureFormat::R8G8B8A8Unorm; -}; + private: + static constexpr uint32_t kWidth = 32; + static constexpr uint32_t kHeight = 32; + static constexpr uint32_t kDefaultDepth = 1; + static constexpr uint32_t kDefaultMipLevels = 1; + static constexpr uint32_t kDefaultSampleCount = 1; -// Test the validation of sample count -TEST_F(TextureValidationTest, SampleCount) { - dawn::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); + static constexpr wgpu::TextureFormat kDefaultTextureFormat = + wgpu::TextureFormat::RGBA8Unorm; + }; - // sampleCount == 1 is allowed. - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.sampleCount = 1; + // Test the validation of sample count + TEST_F(TextureValidationTest, SampleCount) { + wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); - device.CreateTexture(&descriptor); - } + // sampleCount == 1 is allowed. + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.sampleCount = 1; - // sampleCount == 4 is allowed. - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.sampleCount = 4; + device.CreateTexture(&descriptor); + } - device.CreateTexture(&descriptor); - } + // sampleCount == 4 is allowed. + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.sampleCount = 4; - // It is an error to create a texture with an invalid sampleCount. - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.sampleCount = 3; + device.CreateTexture(&descriptor); + } - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); - } + // It is an error to create a texture with an invalid sampleCount. + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.sampleCount = 3; - // It is an error to create a multisampled texture with mipLevelCount > 1. - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.sampleCount = 4; - descriptor.mipLevelCount = 2; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); - } + // It is an error to create a multisampled texture with mipLevelCount > 1. + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.sampleCount = 4; + descriptor.mipLevelCount = 2; - // Currently we do not support multisampled 2D array textures. - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.sampleCount = 4; - descriptor.arrayLayerCount = 2; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); - } -} + // Currently we do not support multisampled 2D array textures. + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.sampleCount = 4; + descriptor.size.depth = 2; -// Test the validation of the mip level count -TEST_F(TextureValidationTest, MipLevelCount) { - dawn::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } - // mipLevelCount == 1 is allowed - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.size.width = 32; - descriptor.size.height = 32; - descriptor.mipLevelCount = 1; + // It is an error to set TextureUsage::Storage when sampleCount > 1. + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.sampleCount = 4; + descriptor.usage |= wgpu::TextureUsage::Storage; - device.CreateTexture(&descriptor); + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } } - // mipLevelCount == 0 is an error - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.size.width = 32; - descriptor.size.height = 32; - descriptor.mipLevelCount = 0; + // Test the validation of the mip level count + TEST_F(TextureValidationTest, MipLevelCount) { + wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); - } + // mipLevelCount == 1 is allowed + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = 32; + descriptor.size.height = 32; + descriptor.mipLevelCount = 1; - // Full mip chains are allowed - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.size.width = 32; - descriptor.size.height = 32; - // Mip level sizes: 32, 16, 8, 4, 2, 1 - descriptor.mipLevelCount = 6; + device.CreateTexture(&descriptor); + } - device.CreateTexture(&descriptor); - } + // mipLevelCount == 0 is an error + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = 32; + descriptor.size.height = 32; + descriptor.mipLevelCount = 0; - // Too big mip chains on width are disallowed - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.size.width = 31; - descriptor.size.height = 32; - // Mip level width: 31, 15, 7, 3, 1, 1 - descriptor.mipLevelCount = 7; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); - } + // Full mip chains are allowed + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = 32; + descriptor.size.height = 32; + // Mip level sizes: 32, 16, 8, 4, 2, 1 + descriptor.mipLevelCount = 6; - // Too big mip chains on height are disallowed - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.size.width = 32; - descriptor.size.height = 31; - // Mip level height: 31, 15, 7, 3, 1, 1 - descriptor.mipLevelCount = 7; + device.CreateTexture(&descriptor); + } - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); - } + // Too big mip chains on width are disallowed + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = 31; + descriptor.size.height = 32; + // Mip level width: 31, 15, 7, 3, 1, 1 + descriptor.mipLevelCount = 7; - // Undefined shift check if miplevel is bigger than the integer bit width. - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.size.width = 32; - descriptor.size.height = 32; - descriptor.mipLevelCount = 100; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); - } + // Too big mip chains on height are disallowed + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = 32; + descriptor.size.height = 31; + // Mip level height: 31, 15, 7, 3, 1, 1 + descriptor.mipLevelCount = 7; - // Non square mip map halves the resolution until a 1x1 dimension. - { - dawn::TextureDescriptor descriptor = defaultDescriptor; - descriptor.size.width = 32; - descriptor.size.height = 8; - // Mip maps: 32 * 8, 16 * 4, 8 * 2, 4 * 1, 2 * 1, 1 * 1 - descriptor.mipLevelCount = 6; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } - device.CreateTexture(&descriptor); - } -} - -// Test that it is valid to destroy a texture -TEST_F(TextureValidationTest, DestroyTexture) { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - dawn::Texture texture = device.CreateTexture(&descriptor); - texture.Destroy(); -} - -// Test that it's valid to destroy a destroyed texture -TEST_F(TextureValidationTest, DestroyDestroyedTexture) { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - dawn::Texture texture = device.CreateTexture(&descriptor); - texture.Destroy(); - texture.Destroy(); -} - -// Test that it's invalid to submit a destroyed texture in a queue -// in the case of destroy, encode, submit -TEST_F(TextureValidationTest, DestroyEncodeSubmit) { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - dawn::Texture texture = device.CreateTexture(&descriptor); - dawn::TextureView textureView = texture.CreateDefaultView(); - - utils::ComboRenderPassDescriptor renderPass({textureView}); - - // Destroy the texture - texture.Destroy(); - - dawn::CommandEncoder encoder_post_destroy = device.CreateCommandEncoder(); - { - dawn::RenderPassEncoder pass = encoder_post_destroy.BeginRenderPass(&renderPass); - pass.EndPass(); - } - dawn::CommandBuffer commands = encoder_post_destroy.Finish(); + // Undefined shift check if miplevel is bigger than the integer bit width. + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = 32; + descriptor.size.height = 32; + descriptor.mipLevelCount = 100; - // Submit should fail due to destroyed texture - ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); -} + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } -// Test that it's invalid to submit a destroyed texture in a queue -// in the case of encode, destroy, submit -TEST_F(TextureValidationTest, EncodeDestroySubmit) { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - dawn::Texture texture = device.CreateTexture(&descriptor); - dawn::TextureView textureView = texture.CreateDefaultView(); + // Non square mip map halves the resolution until a 1x1 dimension. + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = 32; + descriptor.size.height = 8; + // Mip maps: 32 * 8, 16 * 4, 8 * 2, 4 * 1, 2 * 1, 1 * 1 + descriptor.mipLevelCount = 6; - utils::ComboRenderPassDescriptor renderPass({textureView}); + device.CreateTexture(&descriptor); + } - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); - { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); - pass.EndPass(); - } - dawn::CommandBuffer commands = encoder.Finish(); - - // Destroy the texture - texture.Destroy(); - - // Submit should fail due to destroyed texture - ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); -} - -// TODO(jiawei.shao@intel.com): use compressed texture formats as extensions. -// TODO(jiawei.shao@intel.com): add tests to verify we cannot create 1D or 3D textures with -// compressed texture formats. -class CompressedTextureFormatsValidationTests : public TextureValidationTest { - protected: - dawn::TextureDescriptor CreateDefaultTextureDescriptor() { - dawn::TextureDescriptor descriptor = - TextureValidationTest::CreateDefaultTextureDescriptor(); - descriptor.usage = dawn::TextureUsageBit::TransferSrc | dawn::TextureUsageBit::TransferDst | - dawn::TextureUsageBit::Sampled; - return descriptor; + // Mip level exceeding kMaxTexture2DMipLevels not allowed + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = 1 >> kMaxTexture2DMipLevels; + descriptor.size.height = 1 >> kMaxTexture2DMipLevels; + descriptor.mipLevelCount = kMaxTexture2DMipLevels + 1u; + + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } } + // Test the validation of array layer count + TEST_F(TextureValidationTest, ArrayLayerCount) { + wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); - const std::array kBCFormats = { - dawn::TextureFormat::BC1RGBAUnorm, dawn::TextureFormat::BC1RGBAUnormSrgb, - dawn::TextureFormat::BC2RGBAUnorm, dawn::TextureFormat::BC2RGBAUnormSrgb, - dawn::TextureFormat::BC3RGBAUnorm, dawn::TextureFormat::BC3RGBAUnormSrgb, - dawn::TextureFormat::BC4RUnorm, dawn::TextureFormat::BC4RSnorm, - dawn::TextureFormat::BC5RGUnorm, dawn::TextureFormat::BC5RGSnorm, - dawn::TextureFormat::BC6HRGBUfloat, dawn::TextureFormat::BC6HRGBSfloat, - dawn::TextureFormat::BC7RGBAUnorm, dawn::TextureFormat::BC7RGBAUnormSrgb}; -}; - -// Test the validation of texture size when creating textures in compressed texture formats. -TEST_F(CompressedTextureFormatsValidationTests, TextureSize) { - // Test that it is invalid to use a number that is not a multiple of 4 (the compressed block - // width and height of all BC formats) as the width or height of textures in BC formats. - for (dawn::TextureFormat format : kBCFormats) { + // Array layer count exceeding kMaxTexture2DArrayLayers is not allowed { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - descriptor.format = format; - ASSERT_TRUE(descriptor.size.width % 4 == 0 && descriptor.size.height % 4 == 0); + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.depth = kMaxTexture2DArrayLayers + 1u; + + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + // Array layer count less than kMaxTexture2DArrayLayers is allowed; + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.depth = kMaxTexture2DArrayLayers >> 1; + device.CreateTexture(&descriptor); } + // Array layer count equal to kMaxTexture2DArrayLayers is allowed; { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - descriptor.format = format; - descriptor.size.width = 31; - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.depth = kMaxTexture2DArrayLayers; + + device.CreateTexture(&descriptor); } + } + + // Test the validation of texture size + TEST_F(TextureValidationTest, TextureSize) { + wgpu::TextureDescriptor defaultDescriptor = CreateDefaultTextureDescriptor(); + // Texture size exceeding kMaxTextureSize is not allowed { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - descriptor.format = format; - descriptor.size.height = 31; + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = kMaxTextureSize + 1u; + descriptor.size.height = kMaxTextureSize + 1u; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); } + // Texture size less than kMaxTextureSize is allowed { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - descriptor.format = format; - descriptor.size.width = 12; - descriptor.size.height = 32; + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = kMaxTextureSize >> 1; + descriptor.size.height = kMaxTextureSize >> 1; + + device.CreateTexture(&descriptor); + } + + // Texture equal to kMaxTextureSize is allowed + { + wgpu::TextureDescriptor descriptor = defaultDescriptor; + descriptor.size.width = kMaxTextureSize; + descriptor.size.height = kMaxTextureSize; + device.CreateTexture(&descriptor); } } -} -// Test the validation of texture usages when creating textures in compressed texture formats. -TEST_F(CompressedTextureFormatsValidationTests, TextureUsage) { - // Test that only TransferSrc, TransferDst and Sampled are accepted as the texture usage of the - // textures in BC formats. - for (dawn::TextureFormat format : kBCFormats) { + // Test that it is valid to destroy a texture + TEST_F(TextureValidationTest, DestroyTexture) { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + wgpu::Texture texture = device.CreateTexture(&descriptor); + texture.Destroy(); + } + + // Test that it's valid to destroy a destroyed texture + TEST_F(TextureValidationTest, DestroyDestroyedTexture) { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + wgpu::Texture texture = device.CreateTexture(&descriptor); + texture.Destroy(); + texture.Destroy(); + } + + // Test that it's invalid to submit a destroyed texture in a queue + // in the case of destroy, encode, submit + TEST_F(TextureValidationTest, DestroyEncodeSubmit) { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + wgpu::Texture texture = device.CreateTexture(&descriptor); + wgpu::TextureView textureView = texture.CreateView(); + + utils::ComboRenderPassDescriptor renderPass({textureView}); + + // Destroy the texture + texture.Destroy(); + + wgpu::CommandEncoder encoder_post_destroy = device.CreateCommandEncoder(); { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - descriptor.format = format; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment; - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + wgpu::RenderPassEncoder pass = encoder_post_destroy.BeginRenderPass(&renderPass); + pass.EndPass(); } + wgpu::CommandBuffer commands = encoder_post_destroy.Finish(); + + // Submit should fail due to destroyed texture + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + } + + // Test that it's invalid to submit a destroyed texture in a queue + // in the case of encode, destroy, submit + TEST_F(TextureValidationTest, EncodeDestroySubmit) { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + wgpu::Texture texture = device.CreateTexture(&descriptor); + wgpu::TextureView textureView = texture.CreateView(); + + utils::ComboRenderPassDescriptor renderPass({textureView}); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.EndPass(); + } + wgpu::CommandBuffer commands = encoder.Finish(); + + // Destroy the texture + texture.Destroy(); + + // Submit should fail due to destroyed texture + ASSERT_DEVICE_ERROR(queue.Submit(1, &commands)); + } + + // Test it is an error to create an OutputAttachment texture with a non-renderable format. + TEST_F(TextureValidationTest, NonRenderableAndOutputAttachment) { + wgpu::TextureDescriptor descriptor; + descriptor.size = {1, 1, 1}; + descriptor.usage = wgpu::TextureUsage::OutputAttachment; + + // Succeeds because RGBA8Unorm is renderable + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + device.CreateTexture(&descriptor); + + wgpu::TextureFormat nonRenderableFormats[] = { + wgpu::TextureFormat::RG11B10Float, + wgpu::TextureFormat::R8Snorm, + wgpu::TextureFormat::RG8Snorm, + wgpu::TextureFormat::RGBA8Snorm, + }; + + for (wgpu::TextureFormat format : nonRenderableFormats) { + // Fails because `format` is non-renderable descriptor.format = format; - descriptor.usage = dawn::TextureUsageBit::Storage; ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); } + } - { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + // Test it is an error to create a Storage texture with any format that doesn't support + // TextureUsage::Storage texture usages. + TEST_F(TextureValidationTest, TextureFormatNotSupportTextureUsageStorage) { + wgpu::TextureDescriptor descriptor; + descriptor.size = {1, 1, 1}; + descriptor.usage = wgpu::TextureUsage::Storage; + + for (wgpu::TextureFormat format : utils::kAllTextureFormats) { descriptor.format = format; - descriptor.usage = dawn::TextureUsageBit::Present; - ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + if (utils::TextureFormatSupportsStorageTexture(format)) { + device.CreateTexture(&descriptor); + } else { + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } } } -} - -// Test the validation of sample count when creating textures in compressed texture formats. -TEST_F(CompressedTextureFormatsValidationTests, SampleCount) { - // Test that it is invalid to specify SampleCount > 1 when we create a texture in BC formats. - for (dawn::TextureFormat format : kBCFormats) { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - descriptor.format = format; - descriptor.sampleCount = 4; + + // Test it is an error to create a texture with format "Undefined". + TEST_F(TextureValidationTest, TextureFormatUndefined) { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = wgpu::TextureFormat::Undefined; ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); } -} - -// Test the validation of creating 2D array textures in compressed texture formats. -TEST_F(CompressedTextureFormatsValidationTests, 2DArrayTexture) { - // Test that it is allowed to create a 2D array texture in BC formats. - for (dawn::TextureFormat format : kBCFormats) { - dawn::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); - descriptor.format = format; - descriptor.arrayLayerCount = 6; - device.CreateTexture(&descriptor); + + // TODO(jiawei.shao@intel.com): add tests to verify we cannot create 1D or 3D textures with + // compressed texture formats. + class CompressedTextureFormatsValidationTests : public TextureValidationTest { + public: + CompressedTextureFormatsValidationTests() : TextureValidationTest() { + device = CreateDeviceFromAdapter(adapter, {"texture_compression_bc"}); + } + + protected: + wgpu::TextureDescriptor CreateDefaultTextureDescriptor() { + wgpu::TextureDescriptor descriptor = + TextureValidationTest::CreateDefaultTextureDescriptor(); + descriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst | + wgpu::TextureUsage::Sampled; + return descriptor; + } + + const std::array kBCFormats = { + wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb, + wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb, + wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb, + wgpu::TextureFormat::BC4RUnorm, wgpu::TextureFormat::BC4RSnorm, + wgpu::TextureFormat::BC5RGUnorm, wgpu::TextureFormat::BC5RGSnorm, + wgpu::TextureFormat::BC6HRGBUfloat, wgpu::TextureFormat::BC6HRGBSfloat, + wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb}; + }; + + // Test the validation of texture size when creating textures in compressed texture formats. + TEST_F(CompressedTextureFormatsValidationTests, TextureSize) { + // Test that it is invalid to use a number that is not a multiple of 4 (the compressed block + // width and height of all BC formats) as the width or height of textures in BC formats. + for (wgpu::TextureFormat format : kBCFormats) { + { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + ASSERT_TRUE(descriptor.size.width % 4 == 0 && descriptor.size.height % 4 == 0); + device.CreateTexture(&descriptor); + } + + { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.size.width = 31; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.size.height = 31; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.size.width = 12; + descriptor.size.height = 32; + device.CreateTexture(&descriptor); + } + } + } + + // Test the creation of a texture with BC format will fail when the extension + // textureCompressionBC is not enabled. + TEST_F(CompressedTextureFormatsValidationTests, UseBCFormatWithoutEnablingExtension) { + const std::vector kEmptyVector; + wgpu::Device deviceWithoutExtension = CreateDeviceFromAdapter(adapter, kEmptyVector); + for (wgpu::TextureFormat format : kBCFormats) { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + ASSERT_DEVICE_ERROR(deviceWithoutExtension.CreateTexture(&descriptor)); + } + } + + // Test the validation of texture usages when creating textures in compressed texture formats. + TEST_F(CompressedTextureFormatsValidationTests, TextureUsage) { + // Test that only CopySrc, CopyDst and Sampled are accepted as the texture usage of the + // textures in BC formats. + for (wgpu::TextureFormat format : kBCFormats) { + { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.usage = wgpu::TextureUsage::OutputAttachment; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.usage = wgpu::TextureUsage::Storage; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + + { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.usage = wgpu::TextureUsage::Present; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + } + } + + // Test the validation of sample count when creating textures in compressed texture formats. + TEST_F(CompressedTextureFormatsValidationTests, SampleCount) { + // Test that it is invalid to specify SampleCount > 1 when we create a texture in BC + // formats. + for (wgpu::TextureFormat format : kBCFormats) { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.sampleCount = 4; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); + } + } + + // Test the validation of creating 2D array textures in compressed texture formats. + TEST_F(CompressedTextureFormatsValidationTests, 2DArrayTexture) { + // Test that it is allowed to create a 2D array texture in BC formats. + for (wgpu::TextureFormat format : kBCFormats) { + wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor(); + descriptor.format = format; + descriptor.size.depth = 6; + device.CreateTexture(&descriptor); + } } -} } // namespace diff --git a/third_party/dawn/src/tests/unittests/validation/TextureViewValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/TextureViewValidationTests.cpp index b7ddfc56ee5..c5c324220ca 100644 --- a/third_party/dawn/src/tests/unittests/validation/TextureViewValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/TextureViewValidationTests.cpp @@ -16,214 +16,348 @@ namespace { -class TextureViewValidationTest : public ValidationTest { -}; - -constexpr uint32_t kWidth = 32u; -constexpr uint32_t kHeight = 32u; -constexpr uint32_t kDefaultMipLevels = 6u; - -constexpr dawn::TextureFormat kDefaultTextureFormat = dawn::TextureFormat::R8G8B8A8Unorm; - -dawn::Texture Create2DArrayTexture(dawn::Device& device, - uint32_t arrayLayerCount, - uint32_t width = kWidth, - uint32_t height = kHeight, - uint32_t mipLevelCount = kDefaultMipLevels, - uint32_t sampleCount = 1) { - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; - descriptor.size.width = width; - descriptor.size.height = height; - descriptor.size.depth = 1; - descriptor.arrayLayerCount = arrayLayerCount; - descriptor.sampleCount = sampleCount; - descriptor.format = kDefaultTextureFormat; - descriptor.mipLevelCount = mipLevelCount; - descriptor.usage = dawn::TextureUsageBit::Sampled; - return device.CreateTexture(&descriptor); -} - -dawn::TextureViewDescriptor CreateDefaultViewDescriptor(dawn::TextureViewDimension dimension) { - dawn::TextureViewDescriptor descriptor; - descriptor.format = kDefaultTextureFormat; - descriptor.dimension = dimension; - descriptor.baseMipLevel = 0; - descriptor.mipLevelCount = kDefaultMipLevels; - descriptor.baseArrayLayer = 0; - descriptor.arrayLayerCount = 1; - return descriptor; -} - -// Test creating texture view on a 2D non-array texture -TEST_F(TextureViewValidationTest, CreateTextureViewOnTexture2D) { - dawn::Texture texture = Create2DArrayTexture(device, 1); - - dawn::TextureViewDescriptor base2DTextureViewDescriptor = - CreateDefaultViewDescriptor(dawn::TextureViewDimension::e2D); - - // It is OK to create a 2D texture view on a 2D texture. - { - dawn::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; - descriptor.arrayLayerCount = 1; - texture.CreateView(&descriptor); - } - - // It is an error to specify the layer count of the texture view > 1 when texture view dimension - // is 2D. - { - dawn::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; - descriptor.arrayLayerCount = 2; - ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + class TextureViewValidationTest : public ValidationTest {}; + + constexpr uint32_t kWidth = 32u; + constexpr uint32_t kHeight = 32u; + constexpr uint32_t kDefaultMipLevels = 6u; + + constexpr wgpu::TextureFormat kDefaultTextureFormat = wgpu::TextureFormat::RGBA8Unorm; + + wgpu::Texture Create2DArrayTexture(wgpu::Device& device, + uint32_t arrayLayerCount, + uint32_t width = kWidth, + uint32_t height = kHeight, + uint32_t mipLevelCount = kDefaultMipLevels, + uint32_t sampleCount = 1) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = width; + descriptor.size.height = height; + descriptor.size.depth = arrayLayerCount; + descriptor.sampleCount = sampleCount; + descriptor.format = kDefaultTextureFormat; + descriptor.mipLevelCount = mipLevelCount; + descriptor.usage = wgpu::TextureUsage::Sampled; + return device.CreateTexture(&descriptor); } - // It is OK to create a 1-layer 2D array texture view on a 2D texture. - { - dawn::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::e2DArray; + wgpu::TextureViewDescriptor CreateDefaultViewDescriptor(wgpu::TextureViewDimension dimension) { + wgpu::TextureViewDescriptor descriptor; + descriptor.format = kDefaultTextureFormat; + descriptor.dimension = dimension; + descriptor.baseMipLevel = 0; + descriptor.mipLevelCount = kDefaultMipLevels; + descriptor.baseArrayLayer = 0; descriptor.arrayLayerCount = 1; - texture.CreateView(&descriptor); + return descriptor; } - // It is an error to specify mipLevelCount == 0. - { - dawn::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; - descriptor.mipLevelCount = 0; - ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + // Test creating texture view on a 2D non-array texture + TEST_F(TextureViewValidationTest, CreateTextureViewOnTexture2D) { + wgpu::Texture texture = Create2DArrayTexture(device, 1); + + wgpu::TextureViewDescriptor base2DTextureViewDescriptor = + CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D); + + // It is OK to create a 2D texture view on a 2D texture. + { + wgpu::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; + descriptor.arrayLayerCount = 1; + texture.CreateView(&descriptor); + } + + // It is an error to view a layer past the end of the texture. + { + wgpu::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; + descriptor.arrayLayerCount = 2; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + + // It is OK to create a 1-layer 2D array texture view on a 2D texture. + { + wgpu::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::e2DArray; + descriptor.arrayLayerCount = 1; + texture.CreateView(&descriptor); + } + + // baseMipLevel == k && mipLevelCount == 0 means to use levels k..end. + { + wgpu::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; + descriptor.mipLevelCount = 0; + + descriptor.baseMipLevel = 0; + texture.CreateView(&descriptor); + descriptor.baseMipLevel = 1; + texture.CreateView(&descriptor); + descriptor.baseMipLevel = kDefaultMipLevels - 1; + texture.CreateView(&descriptor); + descriptor.baseMipLevel = kDefaultMipLevels; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + + // It is an error to make the mip level out of range. + { + wgpu::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; + descriptor.baseMipLevel = 0; + descriptor.mipLevelCount = kDefaultMipLevels + 1; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + descriptor.baseMipLevel = 1; + descriptor.mipLevelCount = kDefaultMipLevels; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + descriptor.baseMipLevel = kDefaultMipLevels - 1; + descriptor.mipLevelCount = 2; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + descriptor.baseMipLevel = kDefaultMipLevels; + descriptor.mipLevelCount = 1; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } } - // It is an error to make the mip level out of range. - { - dawn::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; - descriptor.baseMipLevel = kDefaultMipLevels - 1; - descriptor.mipLevelCount = 2; - ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + // Test creating texture view on a 2D array texture + TEST_F(TextureViewValidationTest, CreateTextureViewOnTexture2DArray) { + constexpr uint32_t kDefaultArrayLayers = 6; + + wgpu::Texture texture = Create2DArrayTexture(device, kDefaultArrayLayers); + + wgpu::TextureViewDescriptor base2DArrayTextureViewDescriptor = + CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2DArray); + + // It is OK to create a 2D texture view on a 2D array texture. + { + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::e2D; + descriptor.arrayLayerCount = 1; + texture.CreateView(&descriptor); + } + + // It is OK to create a 2D array texture view on a 2D array texture. + { + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.arrayLayerCount = kDefaultArrayLayers; + texture.CreateView(&descriptor); + } + + // baseArrayLayer == k && arrayLayerCount == 0 means to use layers k..end. + { + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.arrayLayerCount = 0; + + descriptor.baseArrayLayer = 0; + texture.CreateView(&descriptor); + descriptor.baseArrayLayer = 1; + texture.CreateView(&descriptor); + descriptor.baseArrayLayer = kDefaultArrayLayers - 1; + texture.CreateView(&descriptor); + descriptor.baseArrayLayer = kDefaultArrayLayers; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + + // It is an error for the array layer range of the view to exceed that of the texture. + { + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.baseArrayLayer = 0; + descriptor.arrayLayerCount = kDefaultArrayLayers + 1; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + descriptor.baseArrayLayer = 1; + descriptor.arrayLayerCount = kDefaultArrayLayers; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + descriptor.baseArrayLayer = kDefaultArrayLayers - 1; + descriptor.arrayLayerCount = 2; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + descriptor.baseArrayLayer = kDefaultArrayLayers; + descriptor.arrayLayerCount = 1; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } } -} - -// Test creating texture view on a 2D array texture -TEST_F(TextureViewValidationTest, CreateTextureViewOnTexture2DArray) { - constexpr uint32_t kDefaultArrayLayers = 6; - dawn::Texture texture = Create2DArrayTexture(device, kDefaultArrayLayers); - - dawn::TextureViewDescriptor base2DArrayTextureViewDescriptor = - CreateDefaultViewDescriptor(dawn::TextureViewDimension::e2DArray); - - // It is OK to create a 2D texture view on a 2D array texture. - { - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::e2D; - descriptor.arrayLayerCount = 1; - texture.CreateView(&descriptor); + // Using the "none" ("default") values validates the same as explicitly + // specifying the values they're supposed to default to. + // Variant for a texture with more than 1 array layer. + TEST_F(TextureViewValidationTest, TextureViewDescriptorDefaultsArray) { + constexpr uint32_t kDefaultArrayLayers = 6; + wgpu::Texture texture = Create2DArrayTexture(device, kDefaultArrayLayers); + + { texture.CreateView(); } + { + wgpu::TextureViewDescriptor descriptor; + descriptor.format = wgpu::TextureFormat::Undefined; + texture.CreateView(&descriptor); + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + texture.CreateView(&descriptor); + descriptor.format = wgpu::TextureFormat::R8Unorm; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + { + wgpu::TextureViewDescriptor descriptor; + descriptor.dimension = wgpu::TextureViewDimension::Undefined; + texture.CreateView(&descriptor); + descriptor.dimension = wgpu::TextureViewDimension::e2DArray; + texture.CreateView(&descriptor); + descriptor.dimension = wgpu::TextureViewDimension::e2D; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + { + wgpu::TextureViewDescriptor descriptor; + + // Setting array layers to non-0 means the dimensionality will + // default to 2D so by itself it causes an error. + descriptor.arrayLayerCount = kDefaultArrayLayers; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + descriptor.dimension = wgpu::TextureViewDimension::e2DArray; + texture.CreateView(&descriptor); + + descriptor.mipLevelCount = kDefaultMipLevels; + texture.CreateView(&descriptor); + } } - // It is OK to create a 2D array texture view on a 2D array texture. - { - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.arrayLayerCount = kDefaultArrayLayers; - texture.CreateView(&descriptor); - } - - // It is an error to specify arrayLayerCount == 0. - { - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.arrayLayerCount = 0; - ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + // Using the "none" ("default") values validates the same as explicitly + // specifying the values they're supposed to default to. + // Variant for a texture with only 1 array layer. + TEST_F(TextureViewValidationTest, TextureViewDescriptorDefaultsNonArray) { + constexpr uint32_t kDefaultArrayLayers = 1; + wgpu::Texture texture = Create2DArrayTexture(device, kDefaultArrayLayers); + + { texture.CreateView(); } + { + wgpu::TextureViewDescriptor descriptor; + descriptor.format = wgpu::TextureFormat::Undefined; + texture.CreateView(&descriptor); + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + texture.CreateView(&descriptor); + descriptor.format = wgpu::TextureFormat::R8Unorm; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + { + wgpu::TextureViewDescriptor descriptor; + descriptor.dimension = wgpu::TextureViewDimension::Undefined; + texture.CreateView(&descriptor); + descriptor.dimension = wgpu::TextureViewDimension::e2D; + texture.CreateView(&descriptor); + descriptor.dimension = wgpu::TextureViewDimension::e2DArray; + texture.CreateView(&descriptor); + } + { + wgpu::TextureViewDescriptor descriptor; + descriptor.arrayLayerCount = 0; + texture.CreateView(&descriptor); + descriptor.arrayLayerCount = 1; + texture.CreateView(&descriptor); + descriptor.arrayLayerCount = 2; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + { + wgpu::TextureViewDescriptor descriptor; + descriptor.mipLevelCount = kDefaultMipLevels; + texture.CreateView(&descriptor); + descriptor.arrayLayerCount = kDefaultArrayLayers; + texture.CreateView(&descriptor); + } } - // It is an error to make the array layer out of range. - { - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.arrayLayerCount = kDefaultArrayLayers + 1; - ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + // Test creating cube map texture view + TEST_F(TextureViewValidationTest, CreateCubeMapTextureView) { + constexpr uint32_t kDefaultArrayLayers = 16; + + wgpu::Texture texture = Create2DArrayTexture(device, kDefaultArrayLayers); + + wgpu::TextureViewDescriptor base2DArrayTextureViewDescriptor = + CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2DArray); + + // It is OK to create a cube map texture view with arrayLayerCount == 6. + { + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::Cube; + descriptor.arrayLayerCount = 6; + texture.CreateView(&descriptor); + } + + // It is an error to create a cube map texture view with arrayLayerCount != 6. + { + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::Cube; + descriptor.arrayLayerCount = 3; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + + // It is OK to create a cube map array texture view with arrayLayerCount % 6 == 0. + { + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::CubeArray; + descriptor.arrayLayerCount = 12; + texture.CreateView(&descriptor); + } + + // It is an error to create a cube map array texture view with arrayLayerCount % 6 != 0. + { + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::CubeArray; + descriptor.arrayLayerCount = 11; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } + + // It is an error to create a cube map texture view with width != height. + { + wgpu::Texture nonSquareTexture = Create2DArrayTexture(device, 18, 32, 16, 5); + + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::Cube; + descriptor.arrayLayerCount = 6; + ASSERT_DEVICE_ERROR(nonSquareTexture.CreateView(&descriptor)); + } + + // It is an error to create a cube map array texture view with width != height. + { + wgpu::Texture nonSquareTexture = Create2DArrayTexture(device, 18, 32, 16, 5); + + wgpu::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; + descriptor.dimension = wgpu::TextureViewDimension::CubeArray; + descriptor.arrayLayerCount = 12; + ASSERT_DEVICE_ERROR(nonSquareTexture.CreateView(&descriptor)); + } } -} - -// Test creating cube map texture view -TEST_F(TextureViewValidationTest, CreateCubeMapTextureView) { - constexpr uint32_t kDefaultArrayLayers = 16; - dawn::Texture texture = Create2DArrayTexture(device, kDefaultArrayLayers); + // Test the format compatibility rules when creating a texture view. + // TODO(jiawei.shao@intel.com): add more tests when the rules are fully implemented. + TEST_F(TextureViewValidationTest, TextureViewFormatCompatibility) { + wgpu::Texture texture = Create2DArrayTexture(device, 1); - dawn::TextureViewDescriptor base2DArrayTextureViewDescriptor = - CreateDefaultViewDescriptor(dawn::TextureViewDimension::e2DArray); + wgpu::TextureViewDescriptor base2DTextureViewDescriptor = + CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D); - // It is OK to create a cube map texture view with arrayLayerCount == 6. - { - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::Cube; - descriptor.arrayLayerCount = 6; - texture.CreateView(&descriptor); + // It is an error to create a texture view in depth-stencil format on a RGBA texture. + { + wgpu::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; + descriptor.format = wgpu::TextureFormat::Depth24PlusStencil8; + ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); + } } - // It is an error to create a cube map texture view with arrayLayerCount != 6. - { - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::Cube; - descriptor.arrayLayerCount = 3; + // Test that it's invalid to create a texture view from a destroyed texture + TEST_F(TextureViewValidationTest, DestroyCreateTextureView) { + wgpu::Texture texture = Create2DArrayTexture(device, 1); + wgpu::TextureViewDescriptor descriptor = + CreateDefaultViewDescriptor(wgpu::TextureViewDimension::e2D); + texture.Destroy(); ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); } - // It is OK to create a cube map array texture view with arrayLayerCount % 6 == 0. - { - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::CubeArray; - descriptor.arrayLayerCount = 12; - texture.CreateView(&descriptor); - } + // Test that only TextureAspect::All is supported + TEST_F(TextureViewValidationTest, AspectMustBeAll) { + wgpu::TextureDescriptor descriptor = {}; + descriptor.size = {1, 1, 1}; + descriptor.format = wgpu::TextureFormat::Depth32Float; + descriptor.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment; + wgpu::Texture texture = device.CreateTexture(&descriptor); - // It is an error to create a cube map array texture view with arrayLayerCount % 6 != 0. - { - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::CubeArray; - descriptor.arrayLayerCount = 11; - ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); - } - - // It is an error to create a cube map texture view with width != height. - { - dawn::Texture nonSquareTexture = Create2DArrayTexture(device, 18, 32, 16, 5); + wgpu::TextureViewDescriptor viewDescriptor = {}; + viewDescriptor.aspect = wgpu::TextureAspect::All; + texture.CreateView(&viewDescriptor); - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::Cube; - descriptor.arrayLayerCount = 6; - ASSERT_DEVICE_ERROR(nonSquareTexture.CreateView(&descriptor)); + viewDescriptor.aspect = wgpu::TextureAspect::DepthOnly; + ASSERT_DEVICE_ERROR(texture.CreateView(&viewDescriptor)); } - // It is an error to create a cube map array texture view with width != height. - { - dawn::Texture nonSquareTexture = Create2DArrayTexture(device, 18, 32, 16, 5); - - dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor; - descriptor.dimension = dawn::TextureViewDimension::CubeArray; - descriptor.arrayLayerCount = 12; - ASSERT_DEVICE_ERROR(nonSquareTexture.CreateView(&descriptor)); - } -} - -// Test the format compatibility rules when creating a texture view. -// TODO(jiawei.shao@intel.com): add more tests when the rules are fully implemented. -TEST_F(TextureViewValidationTest, TextureViewFormatCompatibility) { - dawn::Texture texture = Create2DArrayTexture(device, 1); - - dawn::TextureViewDescriptor base2DTextureViewDescriptor = - CreateDefaultViewDescriptor(dawn::TextureViewDimension::e2D); - - // It is an error to create a texture view in depth-stencil format on a RGBA texture. - { - dawn::TextureViewDescriptor descriptor = base2DTextureViewDescriptor; - descriptor.format = dawn::TextureFormat::D32FloatS8Uint; - ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); - } -} - -// Test that it's invalid to create a texture view from a destroyed texture -TEST_F(TextureViewValidationTest, DestroyCreateTextureView) { - dawn::Texture texture = Create2DArrayTexture(device, 1); - dawn::TextureViewDescriptor descriptor = - CreateDefaultViewDescriptor(dawn::TextureViewDimension::e2D); - texture.Destroy(); - ASSERT_DEVICE_ERROR(texture.CreateView(&descriptor)); -} -} +} // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/validation/ToggleValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/ToggleValidationTests.cpp index 9ebedfb72f3..b0f489f5fed 100644 --- a/third_party/dawn/src/tests/unittests/validation/ToggleValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/ToggleValidationTests.cpp @@ -16,38 +16,71 @@ namespace { -class ToggleValidationTest : public ValidationTest { -}; + class ToggleValidationTest : public ValidationTest {}; -// Tests querying the detail of a toggle from dawn_native::InstanceBase works correctly. -TEST_F(ToggleValidationTest, QueryToggleInfo) { - // Query with a valid toggle name - { - const char* kValidToggleName = "emulate_store_and_msaa_resolve"; - const dawn_native::ToggleInfo* toggleInfo = instance->GetToggleInfo(kValidToggleName); - ASSERT_NE(nullptr, toggleInfo); - ASSERT_NE(nullptr, toggleInfo->name); - ASSERT_NE(nullptr, toggleInfo->description); - ASSERT_NE(nullptr, toggleInfo->url); + // Tests querying the detail of a toggle from dawn_native::InstanceBase works correctly. + TEST_F(ToggleValidationTest, QueryToggleInfo) { + // Query with a valid toggle name + { + const char* kValidToggleName = "emulate_store_and_msaa_resolve"; + const dawn_native::ToggleInfo* toggleInfo = instance->GetToggleInfo(kValidToggleName); + ASSERT_NE(nullptr, toggleInfo); + ASSERT_NE(nullptr, toggleInfo->name); + ASSERT_NE(nullptr, toggleInfo->description); + ASSERT_NE(nullptr, toggleInfo->url); + } + + // Query with an invalid toggle name + { + const char* kInvalidToggleName = "!@#$%^&*"; + const dawn_native::ToggleInfo* toggleInfo = instance->GetToggleInfo(kInvalidToggleName); + ASSERT_EQ(nullptr, toggleInfo); + } } - // Query with an invalid toggle name - { - const char* kInvalidToggleName = "!@#$%^&*"; - const dawn_native::ToggleInfo* toggleInfo = instance->GetToggleInfo(kInvalidToggleName); - ASSERT_EQ(nullptr, toggleInfo); + // Tests overriding toggles when creating a device works correctly. + TEST_F(ToggleValidationTest, OverrideToggleUsage) { + // Create device with a valid name of a toggle + { + const char* kValidToggleName = "emulate_store_and_msaa_resolve"; + dawn_native::DeviceDescriptor descriptor; + descriptor.forceEnabledToggles.push_back(kValidToggleName); + + WGPUDevice deviceWithToggle = adapter.CreateDevice(&descriptor); + std::vector toggleNames = dawn_native::GetTogglesUsed(deviceWithToggle); + bool validToggleExists = false; + for (const char* toggle : toggleNames) { + if (strcmp(toggle, kValidToggleName) == 0) { + validToggleExists = true; + } + } + ASSERT_EQ(validToggleExists, true); + } + + // Create device with an invalid toggle name + { + const char* kInvalidToggleName = "!@#$%^&*"; + dawn_native::DeviceDescriptor descriptor; + descriptor.forceEnabledToggles.push_back(kInvalidToggleName); + + WGPUDevice deviceWithToggle = adapter.CreateDevice(&descriptor); + std::vector toggleNames = dawn_native::GetTogglesUsed(deviceWithToggle); + bool InvalidToggleExists = false; + for (const char* toggle : toggleNames) { + if (strcmp(toggle, kInvalidToggleName) == 0) { + InvalidToggleExists = true; + } + } + ASSERT_EQ(InvalidToggleExists, false); + } } -} -// Tests overriding toggles when creating a device works correctly. -TEST_F(ToggleValidationTest, OverrideToggleUsage) { - // Create device with a valid name of a toggle - { - const char* kValidToggleName = "emulate_store_and_msaa_resolve"; + TEST_F(ToggleValidationTest, TurnOffVsyncWithToggle) { + const char* kValidToggleName = "turn_off_vsync"; dawn_native::DeviceDescriptor descriptor; descriptor.forceEnabledToggles.push_back(kValidToggleName); - DawnDevice deviceWithToggle = adapter.CreateDevice(&descriptor); + WGPUDevice deviceWithToggle = adapter.CreateDevice(&descriptor); std::vector toggleNames = dawn_native::GetTogglesUsed(deviceWithToggle); bool validToggleExists = false; for (const char* toggle : toggleNames) { @@ -57,22 +90,4 @@ TEST_F(ToggleValidationTest, OverrideToggleUsage) { } ASSERT_EQ(validToggleExists, true); } - - // Create device with an invalid toggle name - { - const char* kInvalidToggleName = "!@#$%^&*"; - dawn_native::DeviceDescriptor descriptor; - descriptor.forceEnabledToggles.push_back(kInvalidToggleName); - - DawnDevice deviceWithToggle = adapter.CreateDevice(&descriptor); - std::vector toggleNames = dawn_native::GetTogglesUsed(deviceWithToggle); - bool InvalidToggleExists = false; - for (const char* toggle : toggleNames) { - if (strcmp(toggle, kInvalidToggleName) == 0) { - InvalidToggleExists = true; - } - } - ASSERT_EQ(InvalidToggleExists, false); - } -} } // anonymous namespace diff --git a/third_party/dawn/src/tests/unittests/validation/ValidationTest.cpp b/third_party/dawn/src/tests/unittests/validation/ValidationTest.cpp index 0f5cf9b1078..6b533b4ef82 100644 --- a/third_party/dawn/src/tests/unittests/validation/ValidationTest.cpp +++ b/third_party/dawn/src/tests/unittests/validation/ValidationTest.cpp @@ -15,7 +15,8 @@ #include "tests/unittests/validation/ValidationTest.h" #include "common/Assert.h" -#include "dawn/dawn.h" +#include "dawn/dawn_proc.h" +#include "dawn/webgpu.h" #include "dawn_native/NullBackend.h" ValidationTest::ValidationTest() { @@ -26,8 +27,11 @@ ValidationTest::ValidationTest() { // Validation tests run against the null backend, find the corresponding adapter bool foundNullAdapter = false; - for (auto ¤tAdapter : adapters) { - if (currentAdapter.GetBackendType() == dawn_native::BackendType::Null) { + for (auto& currentAdapter : adapters) { + wgpu::AdapterProperties adapterProperties; + currentAdapter.GetProperties(&adapterProperties); + + if (adapterProperties.backendType == wgpu::BackendType::Null) { adapter = currentAdapter; foundNullAdapter = true; break; @@ -35,19 +39,36 @@ ValidationTest::ValidationTest() { } ASSERT(foundNullAdapter); - device = dawn::Device::Acquire(adapter.CreateDevice()); DawnProcTable procs = dawn_native::GetProcs(); - dawnSetProcs(&procs); + dawnProcSetProcs(&procs); + + device = CreateDeviceFromAdapter(adapter, std::vector()); +} - device.SetErrorCallback(ValidationTest::OnDeviceError, this); +wgpu::Device ValidationTest::CreateDeviceFromAdapter( + dawn_native::Adapter adapterToTest, + const std::vector& requiredExtensions) { + wgpu::Device deviceToTest; + + // Always keep this code path to test creating a device without a device descriptor. + if (requiredExtensions.empty()) { + deviceToTest = wgpu::Device::Acquire(adapterToTest.CreateDevice()); + } else { + dawn_native::DeviceDescriptor descriptor; + descriptor.requiredExtensions = requiredExtensions; + deviceToTest = wgpu::Device::Acquire(adapterToTest.CreateDevice(&descriptor)); + } + + deviceToTest.SetUncapturedErrorCallback(ValidationTest::OnDeviceError, this); + return deviceToTest; } ValidationTest::~ValidationTest() { // We need to destroy Dawn objects before setting the procs to null otherwise the dawn*Release // will call a nullptr - device = dawn::Device(); - dawnSetProcs(nullptr); + device = wgpu::Device(); + dawnProcSetProcs(nullptr); } void ValidationTest::TearDown() { @@ -66,8 +87,24 @@ std::string ValidationTest::GetLastDeviceErrorMessage() const { return mDeviceErrorMessage; } +void ValidationTest::WaitForAllOperations(const wgpu::Device& device) const { + wgpu::Queue queue = device.GetDefaultQueue(); + wgpu::Fence fence = queue.CreateFence(); + + // Force the currently submitted operations to completed. + queue.Signal(fence, 1); + while (fence.GetCompletedValue() < 1) { + device.Tick(); + } + + // TODO(cwallez@chromium.org): It's not clear why we need this additional tick. Investigate it + // once WebGPU has defined the ordering of callbacks firing. + device.Tick(); +} + // static -void ValidationTest::OnDeviceError(const char* message, void* userdata) { +void ValidationTest::OnDeviceError(WGPUErrorType type, const char* message, void* userdata) { + ASSERT(type != WGPUErrorType_NoError); auto self = static_cast(userdata); self->mDeviceErrorMessage = message; @@ -76,30 +113,27 @@ void ValidationTest::OnDeviceError(const char* message, void* userdata) { self->mError = true; } -ValidationTest::DummyRenderPass::DummyRenderPass(const dawn::Device& device) - : attachmentFormat(dawn::TextureFormat::R8G8B8A8Unorm), width(400), height(400) { - - dawn::TextureDescriptor descriptor; - descriptor.dimension = dawn::TextureDimension::e2D; +ValidationTest::DummyRenderPass::DummyRenderPass(const wgpu::Device& device) + : attachmentFormat(wgpu::TextureFormat::RGBA8Unorm), width(400), height(400) { + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; descriptor.size.width = width; descriptor.size.height = height; descriptor.size.depth = 1; - descriptor.arrayLayerCount = 1; descriptor.sampleCount = 1; descriptor.format = attachmentFormat; descriptor.mipLevelCount = 1; - descriptor.usage = dawn::TextureUsageBit::OutputAttachment; + descriptor.usage = wgpu::TextureUsage::OutputAttachment; attachment = device.CreateTexture(&descriptor); - dawn::TextureView view = attachment.CreateDefaultView(); + wgpu::TextureView view = attachment.CreateView(); mColorAttachment.attachment = view; mColorAttachment.resolveTarget = nullptr; - mColorAttachment.clearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; - mColorAttachment.loadOp = dawn::LoadOp::Clear; - mColorAttachment.storeOp = dawn::StoreOp::Store; - mColorAttachments[0] = &mColorAttachment; + mColorAttachment.clearColor = {0.0f, 0.0f, 0.0f, 0.0f}; + mColorAttachment.loadOp = wgpu::LoadOp::Clear; + mColorAttachment.storeOp = wgpu::StoreOp::Store; colorAttachmentCount = 1; - colorAttachments = mColorAttachments; + colorAttachments = &mColorAttachment; depthStencilAttachment = nullptr; } diff --git a/third_party/dawn/src/tests/unittests/validation/ValidationTest.h b/third_party/dawn/src/tests/unittests/validation/ValidationTest.h index 92aef16c70d..c922dd634a2 100644 --- a/third_party/dawn/src/tests/unittests/validation/ValidationTest.h +++ b/third_party/dawn/src/tests/unittests/validation/ValidationTest.h @@ -15,19 +15,26 @@ #ifndef TESTS_UNITTESTS_VALIDATIONTEST_H_ #define TESTS_UNITTESTS_VALIDATIONTEST_H_ -#include "gtest/gtest.h" -#include "dawn/dawncpp.h" +#include "dawn/webgpu_cpp.h" #include "dawn_native/DawnNative.h" +#include "gtest/gtest.h" -#define ASSERT_DEVICE_ERROR(statement) \ - StartExpectDeviceError(); \ - statement; \ - ASSERT_TRUE(EndExpectDeviceError()); +#define ASSERT_DEVICE_ERROR(statement) \ + StartExpectDeviceError(); \ + statement; \ + if (!EndExpectDeviceError()) { \ + FAIL() << "Expected device error in:\n " << #statement; \ + } \ + do { \ + } while (0) class ValidationTest : public testing::Test { public: ValidationTest(); - ~ValidationTest(); + ~ValidationTest() override; + + wgpu::Device CreateDeviceFromAdapter(dawn_native::Adapter adapter, + const std::vector& requiredExtensions); void TearDown() override; @@ -35,31 +42,32 @@ class ValidationTest : public testing::Test { bool EndExpectDeviceError(); std::string GetLastDeviceErrorMessage() const; + void WaitForAllOperations(const wgpu::Device& device) const; + // Helper functions to create objects to test validation. - struct DummyRenderPass : public dawn::RenderPassDescriptor { + struct DummyRenderPass : public wgpu::RenderPassDescriptor { public: - DummyRenderPass(const dawn::Device& device); - dawn::Texture attachment; - dawn::TextureFormat attachmentFormat; + DummyRenderPass(const wgpu::Device& device); + wgpu::Texture attachment; + wgpu::TextureFormat attachmentFormat; uint32_t width; uint32_t height; private: - dawn::RenderPassColorAttachmentDescriptor mColorAttachment; - dawn::RenderPassColorAttachmentDescriptor* mColorAttachments[1]; + wgpu::RenderPassColorAttachmentDescriptor mColorAttachment; }; protected: - dawn::Device device; + wgpu::Device device; dawn_native::Adapter adapter; std::unique_ptr instance; private: - static void OnDeviceError(const char* message, void* userdata); + static void OnDeviceError(WGPUErrorType type, const char* message, void* userdata); std::string mDeviceErrorMessage; bool mExpectError = false; bool mError = false; }; -#endif // TESTS_UNITTESTS_VALIDATIONTEST_H_ +#endif // TESTS_UNITTESTS_VALIDATIONTEST_H_ diff --git a/third_party/dawn/src/tests/unittests/validation/VertexBufferValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/VertexBufferValidationTests.cpp index c75854cd4df..4c8722ed58d 100644 --- a/third_party/dawn/src/tests/unittests/validation/VertexBufferValidationTests.cpp +++ b/third_party/dawn/src/tests/unittests/validation/VertexBufferValidationTests.cpp @@ -16,95 +16,92 @@ #include "tests/unittests/validation/ValidationTest.h" +#include "utils/ComboRenderBundleEncoderDescriptor.h" #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" class VertexBufferValidationTest : public ValidationTest { - protected: - void SetUp() override { - ValidationTest::SetUp(); + protected: + void SetUp() override { + ValidationTest::SetUp(); - fsModule = utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"( + fsModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( #version 450 layout(location = 0) out vec4 fragColor; void main() { fragColor = vec4(0.0, 1.0, 0.0, 1.0); })"); - } + } - template - std::array MakeVertexBuffers() { - std::array buffers; - for (auto& buffer : buffers) { - dawn::BufferDescriptor descriptor; - descriptor.size = 256; - descriptor.usage = dawn::BufferUsageBit::Vertex; + wgpu::Buffer MakeVertexBuffer() { + wgpu::BufferDescriptor descriptor; + descriptor.size = 256; + descriptor.usage = wgpu::BufferUsage::Vertex; - buffer = device.CreateBuffer(&descriptor); - } - return buffers; + return device.CreateBuffer(&descriptor); + } + + wgpu::ShaderModule MakeVertexShader(unsigned int bufferCount) { + std::ostringstream vs; + vs << "#version 450\n"; + for (unsigned int i = 0; i < bufferCount; ++i) { + vs << "layout(location = " << i << ") in vec3 a_position" << i << ";\n"; } + vs << "void main() {\n"; - dawn::ShaderModule MakeVertexShader(unsigned int bufferCount) { - std::ostringstream vs; - vs << "#version 450\n"; - for (unsigned int i = 0; i < bufferCount; ++i) { - vs << "layout(location = " << i << ") in vec3 a_position" << i << ";\n"; - } - vs << "void main() {\n"; - - vs << "gl_Position = vec4("; - for (unsigned int i = 0; i < bufferCount; ++i) { - vs << "a_position" << i; - if (i != bufferCount - 1) { - vs << " + "; - } + vs << "gl_Position = vec4("; + for (unsigned int i = 0; i < bufferCount; ++i) { + vs << "a_position" << i; + if (i != bufferCount - 1) { + vs << " + "; } - vs << ", 1.0);"; + } + vs << ", 1.0);"; - vs << "}\n"; + vs << "}\n"; - return utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, vs.str().c_str()); - } + return utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, + vs.str().c_str()); + } - dawn::RenderPipeline MakeRenderPipeline(const dawn::ShaderModule& vsModule, - unsigned int bufferCount) { - utils::ComboRenderPipelineDescriptor descriptor(device); - descriptor.cVertexStage.module = vsModule; - descriptor.cFragmentStage.module = fsModule; - - for (unsigned int i = 0; i < bufferCount; ++i) { - descriptor.cVertexInput.cBuffers[i].attributeCount = 1; - descriptor.cVertexInput.cBuffers[i].attributes = - &descriptor.cVertexInput.cAttributes[i]; - descriptor.cVertexInput.cAttributes[i].shaderLocation = i; - descriptor.cVertexInput.cAttributes[i].format = dawn::VertexFormat::Float3; - } - descriptor.cVertexInput.bufferCount = bufferCount; + wgpu::RenderPipeline MakeRenderPipeline(const wgpu::ShaderModule& vsModule, + unsigned int bufferCount) { + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; - return device.CreateRenderPipeline(&descriptor); + for (unsigned int i = 0; i < bufferCount; ++i) { + descriptor.cVertexState.cVertexBuffers[i].attributeCount = 1; + descriptor.cVertexState.cVertexBuffers[i].attributes = + &descriptor.cVertexState.cAttributes[i]; + descriptor.cVertexState.cAttributes[i].shaderLocation = i; + descriptor.cVertexState.cAttributes[i].format = wgpu::VertexFormat::Float3; } + descriptor.cVertexState.vertexBufferCount = bufferCount; - dawn::ShaderModule fsModule; + return device.CreateRenderPipeline(&descriptor); + } + + wgpu::ShaderModule fsModule; }; TEST_F(VertexBufferValidationTest, VertexBuffersInheritedBetweenPipelines) { DummyRenderPass renderPass(device); - auto vsModule2 = MakeVertexShader(2); - auto vsModule1 = MakeVertexShader(1); + wgpu::ShaderModule vsModule2 = MakeVertexShader(2); + wgpu::ShaderModule vsModule1 = MakeVertexShader(1); - auto pipeline2 = MakeRenderPipeline(vsModule2, 2); - auto pipeline1 = MakeRenderPipeline(vsModule1, 1); + wgpu::RenderPipeline pipeline2 = MakeRenderPipeline(vsModule2, 2); + wgpu::RenderPipeline pipeline1 = MakeRenderPipeline(vsModule1, 1); - auto vertexBuffers = MakeVertexBuffers<2>(); - uint64_t offsets[] = { 0, 0 }; + wgpu::Buffer vertexBuffer1 = MakeVertexBuffer(); + wgpu::Buffer vertexBuffer2 = MakeVertexBuffer(); // Check failure when vertex buffer is not set - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline1); - pass.Draw(3, 1, 0, 0); + pass.Draw(3); pass.EndPass(); } ASSERT_DEVICE_ERROR(encoder.Finish()); @@ -112,12 +109,13 @@ TEST_F(VertexBufferValidationTest, VertexBuffersInheritedBetweenPipelines) { // Check success when vertex buffer is inherited from previous pipeline encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline2); - pass.SetVertexBuffers(0, 2, vertexBuffers.data(), offsets); - pass.Draw(3, 1, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer1); + pass.SetVertexBuffer(1, vertexBuffer2); + pass.Draw(3); pass.SetPipeline(pipeline1); - pass.Draw(3, 1, 0, 0); + pass.Draw(3); pass.EndPass(); } encoder.Finish(); @@ -125,29 +123,30 @@ TEST_F(VertexBufferValidationTest, VertexBuffersInheritedBetweenPipelines) { TEST_F(VertexBufferValidationTest, VertexBuffersNotInheritedBetweenRendePasses) { DummyRenderPass renderPass(device); - auto vsModule2 = MakeVertexShader(2); - auto vsModule1 = MakeVertexShader(1); + wgpu::ShaderModule vsModule2 = MakeVertexShader(2); + wgpu::ShaderModule vsModule1 = MakeVertexShader(1); - auto pipeline2 = MakeRenderPipeline(vsModule2, 2); - auto pipeline1 = MakeRenderPipeline(vsModule1, 1); + wgpu::RenderPipeline pipeline2 = MakeRenderPipeline(vsModule2, 2); + wgpu::RenderPipeline pipeline1 = MakeRenderPipeline(vsModule1, 1); - auto vertexBuffers = MakeVertexBuffers<2>(); - uint64_t offsets[] = { 0, 0 }; + wgpu::Buffer vertexBuffer1 = MakeVertexBuffer(); + wgpu::Buffer vertexBuffer2 = MakeVertexBuffer(); // Check success when vertex buffer is set for each render pass - dawn::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline2); - pass.SetVertexBuffers(0, 2, vertexBuffers.data(), offsets); - pass.Draw(3, 1, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer1); + pass.SetVertexBuffer(1, vertexBuffer2); + pass.Draw(3); pass.EndPass(); } { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline1); - pass.SetVertexBuffers(0, 1, vertexBuffers.data(), offsets); - pass.Draw(3, 1, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer1); + pass.Draw(3); pass.EndPass(); } encoder.Finish(); @@ -155,17 +154,132 @@ TEST_F(VertexBufferValidationTest, VertexBuffersNotInheritedBetweenRendePasses) // Check failure because vertex buffer is not inherited in second subpass encoder = device.CreateCommandEncoder(); { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline2); - pass.SetVertexBuffers(0, 2, vertexBuffers.data(), offsets); - pass.Draw(3, 1, 0, 0); + pass.SetVertexBuffer(0, vertexBuffer1); + pass.SetVertexBuffer(1, vertexBuffer2); + pass.Draw(3); pass.EndPass(); } { - dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); pass.SetPipeline(pipeline1); - pass.Draw(3, 1, 0, 0); + pass.Draw(3); pass.EndPass(); } ASSERT_DEVICE_ERROR(encoder.Finish()); } + +TEST_F(VertexBufferValidationTest, VertexBufferSlotValidation) { + wgpu::Buffer buffer = MakeVertexBuffer(); + + DummyRenderPass renderPass(device); + + // Control case: using the last vertex buffer slot in render passes is ok. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetVertexBuffer(kMaxVertexBuffers - 1, buffer, 0); + pass.EndPass(); + encoder.Finish(); + } + + // Error case: using past the last vertex buffer slot in render pass fails. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetVertexBuffer(kMaxVertexBuffers, buffer, 0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 1; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + + // Control case: using the last vertex buffer slot in render bundles is ok. + { + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc); + encoder.SetVertexBuffer(kMaxVertexBuffers - 1, buffer, 0); + encoder.Finish(); + } + + // Error case: using past the last vertex buffer slot in render bundle fails. + { + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc); + encoder.SetVertexBuffer(kMaxVertexBuffers, buffer, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} + +// Test that for OOB validation of vertex buffer offset and size. +TEST_F(VertexBufferValidationTest, VertexBufferOffsetOOBValidation) { + wgpu::Buffer buffer = MakeVertexBuffer(); + + DummyRenderPass renderPass(device); + // Control case, using the full buffer, with or without an explicit size is valid. + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + // Explicit size + pass.SetVertexBuffer(0, buffer, 0, 256); + // Implicit size + pass.SetVertexBuffer(0, buffer, 0, 0); + pass.SetVertexBuffer(0, buffer, 256 - 4, 0); + pass.SetVertexBuffer(0, buffer, 4, 0); + // Implicit size of zero + pass.SetVertexBuffer(0, buffer, 256, 0); + pass.EndPass(); + encoder.Finish(); + } + + // Bad case, offset + size is larger than the buffer + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetVertexBuffer(0, buffer, 4, 256); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Bad case, size is 0 but the offset is larger than the buffer + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass); + pass.SetVertexBuffer(0, buffer, 256 + 4, 0); + pass.EndPass(); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {}; + renderBundleDesc.colorFormatsCount = 1; + renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm; + + // Control case, using the full buffer, with or without an explicit size is valid. + { + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc); + // Explicit size + encoder.SetVertexBuffer(0, buffer, 0, 256); + // Implicit size + encoder.SetVertexBuffer(0, buffer, 0, 0); + encoder.SetVertexBuffer(0, buffer, 256 - 4, 0); + encoder.SetVertexBuffer(0, buffer, 4, 0); + // Implicit size of zero + encoder.SetVertexBuffer(0, buffer, 256, 0); + encoder.Finish(); + } + + // Bad case, offset + size is larger than the buffer + { + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc); + encoder.SetVertexBuffer(0, buffer, 4, 256); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } + + // Bad case, size is 0 but the offset is larger than the buffer + { + wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc); + encoder.SetVertexBuffer(0, buffer, 256 + 4, 0); + ASSERT_DEVICE_ERROR(encoder.Finish()); + } +} diff --git a/third_party/dawn/src/tests/unittests/validation/VertexStateValidationTests.cpp b/third_party/dawn/src/tests/unittests/validation/VertexStateValidationTests.cpp new file mode 100644 index 00000000000..42d7025e901 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/validation/VertexStateValidationTests.cpp @@ -0,0 +1,472 @@ +// Copyright 2017 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/validation/ValidationTest.h" + +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +class VertexStateTest : public ValidationTest { + protected: + void CreatePipeline(bool success, + const utils::ComboVertexStateDescriptor& state, + std::string vertexSource) { + wgpu::ShaderModule vsModule = utils::CreateShaderModule( + device, utils::SingleShaderStage::Vertex, vertexSource.c_str()); + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(1.0, 0.0, 0.0, 1.0); + } + )"); + + utils::ComboRenderPipelineDescriptor descriptor(device); + descriptor.vertexStage.module = vsModule; + descriptor.cFragmentStage.module = fsModule; + descriptor.vertexState = &state; + descriptor.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm; + + if (!success) { + ASSERT_DEVICE_ERROR(device.CreateRenderPipeline(&descriptor)); + } else { + device.CreateRenderPipeline(&descriptor); + } + } +}; + +// Check an empty vertex input is valid +TEST_F(VertexStateTest, EmptyIsOk) { + utils::ComboVertexStateDescriptor state; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check null buffer is valid +TEST_F(VertexStateTest, NullBufferIsOk) { + utils::ComboVertexStateDescriptor state; + // One null buffer (buffer[0]) is OK + state.vertexBufferCount = 1; + state.cVertexBuffers[0].arrayStride = 0; + state.cVertexBuffers[0].attributeCount = 0; + state.cVertexBuffers[0].attributes = nullptr; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // One null buffer (buffer[0]) followed by a buffer (buffer[1]) is OK + state.vertexBufferCount = 2; + state.cVertexBuffers[1].arrayStride = 0; + state.cVertexBuffers[1].attributeCount = 1; + state.cVertexBuffers[1].attributes = &state.cAttributes[0]; + state.cAttributes[0].shaderLocation = 0; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Null buffer (buffer[2]) sitting between buffers (buffer[1] and buffer[3]) is OK + state.vertexBufferCount = 4; + state.cVertexBuffers[2].attributeCount = 0; + state.cVertexBuffers[2].attributes = nullptr; + state.cVertexBuffers[3].attributeCount = 1; + state.cVertexBuffers[3].attributes = &state.cAttributes[1]; + state.cAttributes[1].shaderLocation = 1; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check validation that pipeline vertex buffers are backed by attributes in the vertex input +// Check validation that pipeline vertex buffers are backed by attributes in the vertex input +TEST_F(VertexStateTest, PipelineCompatibility) { + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].arrayStride = 2 * sizeof(float); + state.cVertexBuffers[0].attributeCount = 2; + state.cAttributes[0].shaderLocation = 0; + state.cAttributes[1].shaderLocation = 1; + state.cAttributes[1].offset = sizeof(float); + + // Control case: pipeline with one input per attribute + CreatePipeline(true, state, R"( + #version 450 + layout(location = 0) in vec4 a; + layout(location = 1) in vec4 b; + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Check it is valid for the pipeline to use a subset of the VertexState + CreatePipeline(true, state, R"( + #version 450 + layout(location = 0) in vec4 a; + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Check for an error when the pipeline uses an attribute not in the vertex input + CreatePipeline(false, state, R"( + #version 450 + layout(location = 2) in vec4 a; + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Test that a arrayStride of 0 is valid +TEST_F(VertexStateTest, StrideZero) { + // Works ok without attributes + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].arrayStride = 0; + state.cVertexBuffers[0].attributeCount = 1; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Works ok with attributes at a large-ish offset + state.cAttributes[0].offset = 128; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check validation that vertex attribute offset should be within vertex buffer arrayStride, +// if vertex buffer arrayStride is not zero. +TEST_F(VertexStateTest, SetOffsetOutOfBounds) { + // Control case, setting correct arrayStride and offset + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].arrayStride = 2 * sizeof(float); + state.cVertexBuffers[0].attributeCount = 2; + state.cAttributes[0].shaderLocation = 0; + state.cAttributes[1].shaderLocation = 1; + state.cAttributes[1].offset = sizeof(float); + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test vertex attribute offset exceed vertex buffer arrayStride range + state.cVertexBuffers[0].arrayStride = sizeof(float); + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // It's OK if arrayStride is zero + state.cVertexBuffers[0].arrayStride = 0; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check out of bounds condition on total number of vertex buffers +TEST_F(VertexStateTest, SetVertexBuffersNumLimit) { + // Control case, setting max vertex buffer number + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = kMaxVertexBuffers; + for (uint32_t i = 0; i < kMaxVertexBuffers; ++i) { + state.cVertexBuffers[i].attributeCount = 1; + state.cVertexBuffers[i].attributes = &state.cAttributes[i]; + state.cAttributes[i].shaderLocation = i; + } + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test vertex buffer number exceed the limit + state.vertexBufferCount = kMaxVertexBuffers + 1; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check out of bounds condition on total number of vertex attributes +TEST_F(VertexStateTest, SetVertexAttributesNumLimit) { + // Control case, setting max vertex attribute number + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 2; + state.cVertexBuffers[0].attributeCount = kMaxVertexAttributes; + for (uint32_t i = 0; i < kMaxVertexAttributes; ++i) { + state.cAttributes[i].shaderLocation = i; + } + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test vertex attribute number exceed the limit + state.cVertexBuffers[1].attributeCount = 1; + state.cVertexBuffers[1].attributes = &state.cAttributes[kMaxVertexAttributes - 1]; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check out of bounds condition on input arrayStride +TEST_F(VertexStateTest, SetInputStrideOutOfBounds) { + // Control case, setting max input arrayStride + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].arrayStride = kMaxVertexBufferStride; + state.cVertexBuffers[0].attributeCount = 1; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test input arrayStride OOB + state.cVertexBuffers[0].arrayStride = kMaxVertexBufferStride + 1; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check multiple of 4 bytes constraint on input arrayStride +TEST_F(VertexStateTest, SetInputStrideNotAligned) { + // Control case, setting input arrayStride 4 bytes. + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].arrayStride = 4; + state.cVertexBuffers[0].attributeCount = 1; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test input arrayStride not multiple of 4 bytes + state.cVertexBuffers[0].arrayStride = 2; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Test that we cannot set an already set attribute +TEST_F(VertexStateTest, AlreadySetAttribute) { + // Control case, setting attribute 0 + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].attributeCount = 1; + state.cAttributes[0].shaderLocation = 0; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Oh no, attribute 0 is set twice + state.cVertexBuffers[0].attributeCount = 2; + state.cAttributes[0].shaderLocation = 0; + state.cAttributes[1].shaderLocation = 0; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Test that a arrayStride of 0 is valid +TEST_F(VertexStateTest, SetSameShaderLocation) { + // Control case, setting different shader locations in two attributes + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].attributeCount = 2; + state.cAttributes[0].shaderLocation = 0; + state.cAttributes[1].shaderLocation = 1; + state.cAttributes[1].offset = sizeof(float); + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test same shader location in two attributes in the same buffer + state.cAttributes[1].shaderLocation = 0; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test same shader location in two attributes in different buffers + state.vertexBufferCount = 2; + state.cVertexBuffers[0].attributeCount = 1; + state.cAttributes[0].shaderLocation = 0; + state.cVertexBuffers[1].attributeCount = 1; + state.cVertexBuffers[1].attributes = &state.cAttributes[1]; + state.cAttributes[1].shaderLocation = 0; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check out of bounds condition on attribute shader location +TEST_F(VertexStateTest, SetAttributeLocationOutOfBounds) { + // Control case, setting last attribute shader location + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].attributeCount = 1; + state.cAttributes[0].shaderLocation = kMaxVertexAttributes - 1; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test attribute location OOB + state.cAttributes[0].shaderLocation = kMaxVertexAttributes; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check attribute offset out of bounds +TEST_F(VertexStateTest, SetAttributeOffsetOutOfBounds) { + // Control case, setting max attribute offset for FloatR32 vertex format + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].attributeCount = 1; + state.cAttributes[0].offset = kMaxVertexAttributeEnd - sizeof(wgpu::VertexFormat::Float); + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test attribute offset out of bounds + state.cAttributes[0].offset = kMaxVertexAttributeEnd - 1; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check multiple of 4 bytes constraint on offset +TEST_F(VertexStateTest, SetOffsetNotAligned) { + // Control case, setting offset 4 bytes. + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].attributeCount = 1; + state.cAttributes[0].offset = 4; + CreatePipeline(true, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); + + // Test offset not multiple of 4 bytes + state.cAttributes[0].offset = 2; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check attribute offset overflow +TEST_F(VertexStateTest, SetAttributeOffsetOverflow) { + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].attributeCount = 1; + state.cAttributes[0].offset = std::numeric_limits::max(); + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} + +// Check for some potential underflow in the vertex input validation +TEST_F(VertexStateTest, VertexFormatLargerThanNonZeroStride) { + utils::ComboVertexStateDescriptor state; + state.vertexBufferCount = 1; + state.cVertexBuffers[0].arrayStride = 4; + state.cVertexBuffers[0].attributeCount = 1; + state.cAttributes[0].format = wgpu::VertexFormat::Float4; + CreatePipeline(false, state, R"( + #version 450 + void main() { + gl_Position = vec4(0.0); + } + )"); +} diff --git a/third_party/dawn/src/tests/unittests/wire/WireArgumentTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireArgumentTests.cpp index c23a53282b3..95685cf4f23 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireArgumentTests.cpp +++ b/third_party/dawn/src/tests/unittests/wire/WireArgumentTests.cpp @@ -30,15 +30,15 @@ class WireArgumentTests : public WireTest { // Test that the wire is able to send numerical values TEST_F(WireArgumentTests, ValueArgument) { - DawnCommandEncoder encoder = dawnDeviceCreateCommandEncoder(device); - DawnComputePassEncoder pass = dawnCommandEncoderBeginComputePass(encoder); - dawnComputePassEncoderDispatch(pass, 1, 2, 3); + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr); + WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr); + wgpuComputePassEncoderDispatch(pass, 1, 2, 3); - DawnCommandEncoder apiEncoder = api.GetNewCommandEncoder(); - EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice)).WillOnce(Return(apiEncoder)); + WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder)); - DawnComputePassEncoder apiPass = api.GetNewComputePassEncoder(); - EXPECT_CALL(api, CommandEncoderBeginComputePass(apiEncoder)).WillOnce(Return(apiPass)); + WGPUComputePassEncoder apiPass = api.GetNewComputePassEncoder(); + EXPECT_CALL(api, CommandEncoderBeginComputePass(apiEncoder, nullptr)).WillOnce(Return(apiPass)); EXPECT_CALL(api, ComputePassEncoderDispatch(apiPass, 1, 2, 3)).Times(1); @@ -48,41 +48,39 @@ TEST_F(WireArgumentTests, ValueArgument) { // Test that the wire is able to send arrays of numerical values TEST_F(WireArgumentTests, ValueArrayArgument) { // Create a bindgroup. - DawnBindGroupLayoutDescriptor bglDescriptor; - bglDescriptor.nextInChain = nullptr; - bglDescriptor.bindingCount = 0; - bglDescriptor.bindings = nullptr; + WGPUBindGroupLayoutDescriptor bglDescriptor = {}; + bglDescriptor.entryCount = 0; + bglDescriptor.entries = nullptr; - DawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDescriptor); - DawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); + WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor); + WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl)); - DawnBindGroupDescriptor bindGroupDescriptor; - bindGroupDescriptor.nextInChain = nullptr; + WGPUBindGroupDescriptor bindGroupDescriptor = {}; bindGroupDescriptor.layout = bgl; - bindGroupDescriptor.bindingCount = 0; - bindGroupDescriptor.bindings = nullptr; + bindGroupDescriptor.entryCount = 0; + bindGroupDescriptor.entries = nullptr; - DawnBindGroup bindGroup = dawnDeviceCreateBindGroup(device, &bindGroupDescriptor); - DawnBindGroup apiBindGroup = api.GetNewBindGroup(); + WGPUBindGroup bindGroup = wgpuDeviceCreateBindGroup(device, &bindGroupDescriptor); + WGPUBindGroup apiBindGroup = api.GetNewBindGroup(); EXPECT_CALL(api, DeviceCreateBindGroup(apiDevice, _)).WillOnce(Return(apiBindGroup)); // Use the bindgroup in SetBindGroup that takes an array of value offsets. - DawnCommandEncoder encoder = dawnDeviceCreateCommandEncoder(device); - DawnComputePassEncoder pass = dawnCommandEncoderBeginComputePass(encoder); + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr); + WGPUComputePassEncoder pass = wgpuCommandEncoderBeginComputePass(encoder, nullptr); - std::array testOffsets = {0, 42, 0xDEAD'BEEF'DEAD'BEEFu, 0xFFFF'FFFF'FFFF'FFFFu}; - dawnComputePassEncoderSetBindGroup(pass, 0, bindGroup, testOffsets.size(), testOffsets.data()); + std::array testOffsets = {0, 42, 0xDEAD'BEEFu, 0xFFFF'FFFFu}; + wgpuComputePassEncoderSetBindGroup(pass, 0, bindGroup, testOffsets.size(), testOffsets.data()); - DawnCommandEncoder apiEncoder = api.GetNewCommandEncoder(); - EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice)).WillOnce(Return(apiEncoder)); + WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder)); - DawnComputePassEncoder apiPass = api.GetNewComputePassEncoder(); - EXPECT_CALL(api, CommandEncoderBeginComputePass(apiEncoder)).WillOnce(Return(apiPass)); + WGPUComputePassEncoder apiPass = api.GetNewComputePassEncoder(); + EXPECT_CALL(api, CommandEncoderBeginComputePass(apiEncoder, nullptr)).WillOnce(Return(apiPass)); EXPECT_CALL(api, ComputePassEncoderSetBindGroup( apiPass, 0, apiBindGroup, testOffsets.size(), - MatchesLambda([testOffsets](const uint64_t* offsets) -> bool { + MatchesLambda([testOffsets](const uint32_t* offsets) -> bool { for (size_t i = 0; i < testOffsets.size(); i++) { if (offsets[i] != testOffsets[i]) { return false; @@ -97,127 +95,114 @@ TEST_F(WireArgumentTests, ValueArrayArgument) { // Test that the wire is able to send C strings TEST_F(WireArgumentTests, CStringArgument) { // Create shader module - DawnShaderModuleDescriptor vertexDescriptor; - vertexDescriptor.nextInChain = nullptr; - vertexDescriptor.codeSize = 0; - DawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor); - DawnShaderModule apiVsModule = api.GetNewShaderModule(); + WGPUShaderModuleDescriptor vertexDescriptor = {}; + WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vertexDescriptor); + WGPUShaderModule apiVsModule = api.GetNewShaderModule(); EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule)); // Create the color state descriptor - DawnBlendDescriptor blendDescriptor; - blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD; - blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE; - blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE; - DawnColorStateDescriptor colorStateDescriptor; - colorStateDescriptor.nextInChain = nullptr; - colorStateDescriptor.format = DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM; + WGPUBlendDescriptor blendDescriptor = {}; + blendDescriptor.operation = WGPUBlendOperation_Add; + blendDescriptor.srcFactor = WGPUBlendFactor_One; + blendDescriptor.dstFactor = WGPUBlendFactor_One; + WGPUColorStateDescriptor colorStateDescriptor = {}; + colorStateDescriptor.format = WGPUTextureFormat_RGBA8Unorm; colorStateDescriptor.alphaBlend = blendDescriptor; colorStateDescriptor.colorBlend = blendDescriptor; - colorStateDescriptor.writeMask = DAWN_COLOR_WRITE_MASK_ALL; + colorStateDescriptor.writeMask = WGPUColorWriteMask_All; // Create the input state - DawnVertexInputDescriptor vertexInput; - vertexInput.nextInChain = nullptr; - vertexInput.indexFormat = DAWN_INDEX_FORMAT_UINT32; - vertexInput.bufferCount = 0; - vertexInput.buffers = nullptr; + WGPUVertexStateDescriptor vertexState = {}; + vertexState.indexFormat = WGPUIndexFormat_Uint32; + vertexState.vertexBufferCount = 0; + vertexState.vertexBuffers = nullptr; // Create the rasterization state - DawnRasterizationStateDescriptor rasterizationState; - rasterizationState.nextInChain = nullptr; - rasterizationState.frontFace = DAWN_FRONT_FACE_CCW; - rasterizationState.cullMode = DAWN_CULL_MODE_NONE; + WGPURasterizationStateDescriptor rasterizationState = {}; + rasterizationState.frontFace = WGPUFrontFace_CCW; + rasterizationState.cullMode = WGPUCullMode_None; rasterizationState.depthBias = 0; rasterizationState.depthBiasSlopeScale = 0.0; rasterizationState.depthBiasClamp = 0.0; // Create the depth-stencil state - DawnStencilStateFaceDescriptor stencilFace; - stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS; - stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP; - stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP; - stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP; - - DawnDepthStencilStateDescriptor depthStencilState; - depthStencilState.nextInChain = nullptr; - depthStencilState.format = DAWN_TEXTURE_FORMAT_D32_FLOAT_S8_UINT; + WGPUStencilStateFaceDescriptor stencilFace = {}; + stencilFace.compare = WGPUCompareFunction_Always; + stencilFace.failOp = WGPUStencilOperation_Keep; + stencilFace.depthFailOp = WGPUStencilOperation_Keep; + stencilFace.passOp = WGPUStencilOperation_Keep; + + WGPUDepthStencilStateDescriptor depthStencilState = {}; + depthStencilState.format = WGPUTextureFormat_Depth24PlusStencil8; depthStencilState.depthWriteEnabled = false; - depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS; + depthStencilState.depthCompare = WGPUCompareFunction_Always; depthStencilState.stencilBack = stencilFace; depthStencilState.stencilFront = stencilFace; depthStencilState.stencilReadMask = 0xff; depthStencilState.stencilWriteMask = 0xff; // Create the pipeline layout - DawnPipelineLayoutDescriptor layoutDescriptor; - layoutDescriptor.nextInChain = nullptr; + WGPUPipelineLayoutDescriptor layoutDescriptor = {}; layoutDescriptor.bindGroupLayoutCount = 0; layoutDescriptor.bindGroupLayouts = nullptr; - DawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor); - DawnPipelineLayout apiLayout = api.GetNewPipelineLayout(); + WGPUPipelineLayout layout = wgpuDeviceCreatePipelineLayout(device, &layoutDescriptor); + WGPUPipelineLayout apiLayout = api.GetNewPipelineLayout(); EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout)); // Create pipeline - DawnRenderPipelineDescriptor pipelineDescriptor; - pipelineDescriptor.nextInChain = nullptr; + WGPURenderPipelineDescriptor pipelineDescriptor = {}; - DawnPipelineStageDescriptor vertexStage; - vertexStage.nextInChain = nullptr; - vertexStage.module = vsModule; - vertexStage.entryPoint = "main"; - pipelineDescriptor.vertexStage = &vertexStage; + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.vertexStage.entryPoint = "main"; - DawnPipelineStageDescriptor fragmentStage; - fragmentStage.nextInChain = nullptr; + WGPUProgrammableStageDescriptor fragmentStage = {}; fragmentStage.module = vsModule; fragmentStage.entryPoint = "main"; pipelineDescriptor.fragmentStage = &fragmentStage; pipelineDescriptor.colorStateCount = 1; - DawnColorStateDescriptor* colorStatesPtr[] = {&colorStateDescriptor}; - pipelineDescriptor.colorStates = colorStatesPtr; + pipelineDescriptor.colorStates = &colorStateDescriptor; pipelineDescriptor.sampleCount = 1; + pipelineDescriptor.sampleMask = 0xFFFFFFFF; + pipelineDescriptor.alphaToCoverageEnabled = false; pipelineDescriptor.layout = layout; - pipelineDescriptor.vertexInput = &vertexInput; - pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + pipelineDescriptor.vertexState = &vertexState; + pipelineDescriptor.primitiveTopology = WGPUPrimitiveTopology_TriangleList; pipelineDescriptor.rasterizationState = &rasterizationState; pipelineDescriptor.depthStencilState = &depthStencilState; - dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); + wgpuDeviceCreateRenderPipeline(device, &pipelineDescriptor); - DawnRenderPipeline apiDummyPipeline = api.GetNewRenderPipeline(); + WGPURenderPipeline apiDummyPipeline = api.GetNewRenderPipeline(); EXPECT_CALL(api, DeviceCreateRenderPipeline( - apiDevice, MatchesLambda([](const DawnRenderPipelineDescriptor* desc) -> bool { - return desc->vertexStage->entryPoint == std::string("main"); + apiDevice, MatchesLambda([](const WGPURenderPipelineDescriptor* desc) -> bool { + return desc->vertexStage.entryPoint == std::string("main"); }))) .WillOnce(Return(apiDummyPipeline)); FlushClient(); } - // Test that the wire is able to send objects as value arguments TEST_F(WireArgumentTests, ObjectAsValueArgument) { - DawnCommandEncoder cmdBufEncoder = dawnDeviceCreateCommandEncoder(device); - DawnCommandEncoder apiEncoder = api.GetNewCommandEncoder(); - EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice)).WillOnce(Return(apiEncoder)); + WGPUCommandEncoder cmdBufEncoder = wgpuDeviceCreateCommandEncoder(device, nullptr); + WGPUCommandEncoder apiEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)).WillOnce(Return(apiEncoder)); - DawnBufferDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUBufferDescriptor descriptor = {}; descriptor.size = 8; - descriptor.usage = static_cast(DAWN_BUFFER_USAGE_BIT_TRANSFER_SRC | - DAWN_BUFFER_USAGE_BIT_TRANSFER_DST); + descriptor.usage = + static_cast(WGPUBufferUsage_CopySrc | WGPUBufferUsage_CopyDst); - DawnBuffer buffer = dawnDeviceCreateBuffer(device, &descriptor); - DawnBuffer apiBuffer = api.GetNewBuffer(); + WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); + WGPUBuffer apiBuffer = api.GetNewBuffer(); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) .WillOnce(Return(apiBuffer)) .RetiresOnSaturation(); - dawnCommandEncoderCopyBufferToBuffer(cmdBufEncoder, buffer, 0, buffer, 4, 4); + wgpuCommandEncoderCopyBufferToBuffer(cmdBufEncoder, buffer, 0, buffer, 4, 4); EXPECT_CALL(api, CommandEncoderCopyBufferToBuffer(apiEncoder, apiBuffer, 0, apiBuffer, 4, 4)); FlushClient(); @@ -225,36 +210,31 @@ TEST_F(WireArgumentTests, ObjectAsValueArgument) { // Test that the wire is able to send array of objects TEST_F(WireArgumentTests, ObjectsAsPointerArgument) { - DawnCommandBuffer cmdBufs[2]; - DawnCommandBuffer apiCmdBufs[2]; + WGPUCommandBuffer cmdBufs[2]; + WGPUCommandBuffer apiCmdBufs[2]; // Create two command buffers we need to use a GMock sequence otherwise the order of the // CreateCommandEncoder might be swapped since they are equivalent in term of matchers Sequence s; for (int i = 0; i < 2; ++i) { - DawnCommandEncoder cmdBufEncoder = dawnDeviceCreateCommandEncoder(device); - cmdBufs[i] = dawnCommandEncoderFinish(cmdBufEncoder); + WGPUCommandEncoder cmdBufEncoder = wgpuDeviceCreateCommandEncoder(device, nullptr); + cmdBufs[i] = wgpuCommandEncoderFinish(cmdBufEncoder, nullptr); - DawnCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); - EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice)) + WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) .InSequence(s) .WillOnce(Return(apiCmdBufEncoder)); apiCmdBufs[i] = api.GetNewCommandBuffer(); - EXPECT_CALL(api, CommandEncoderFinish(apiCmdBufEncoder)) + EXPECT_CALL(api, CommandEncoderFinish(apiCmdBufEncoder, nullptr)) .WillOnce(Return(apiCmdBufs[i])); } - // Create queue - DawnQueue queue = dawnDeviceCreateQueue(device); - DawnQueue apiQueue = api.GetNewQueue(); - EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue)); - // Submit command buffer and check we got a call with both API-side command buffers - dawnQueueSubmit(queue, 2, cmdBufs); + wgpuQueueSubmit(queue, 2, cmdBufs); EXPECT_CALL( - api, QueueSubmit(apiQueue, 2, MatchesLambda([=](const DawnCommandBuffer* cmdBufs) -> bool { + api, QueueSubmit(apiQueue, 2, MatchesLambda([=](const WGPUCommandBuffer* cmdBufs) -> bool { return cmdBufs[0] == apiCmdBufs[0] && cmdBufs[1] == apiCmdBufs[1]; }))); @@ -263,31 +243,30 @@ TEST_F(WireArgumentTests, ObjectsAsPointerArgument) { // Test that the wire is able to send structures that contain pure values (non-objects) TEST_F(WireArgumentTests, StructureOfValuesArgument) { - DawnSamplerDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.magFilter = DAWN_FILTER_MODE_LINEAR; - descriptor.minFilter = DAWN_FILTER_MODE_NEAREST; - descriptor.mipmapFilter = DAWN_FILTER_MODE_LINEAR; - descriptor.addressModeU = DAWN_ADDRESS_MODE_CLAMP_TO_EDGE; - descriptor.addressModeV = DAWN_ADDRESS_MODE_REPEAT; - descriptor.addressModeW = DAWN_ADDRESS_MODE_MIRRORED_REPEAT; + WGPUSamplerDescriptor descriptor = {}; + descriptor.magFilter = WGPUFilterMode_Linear; + descriptor.minFilter = WGPUFilterMode_Nearest; + descriptor.mipmapFilter = WGPUFilterMode_Linear; + descriptor.addressModeU = WGPUAddressMode_ClampToEdge; + descriptor.addressModeV = WGPUAddressMode_Repeat; + descriptor.addressModeW = WGPUAddressMode_MirrorRepeat; descriptor.lodMinClamp = kLodMin; descriptor.lodMaxClamp = kLodMax; - descriptor.compareFunction = DAWN_COMPARE_FUNCTION_NEVER; + descriptor.compare = WGPUCompareFunction_Never; - dawnDeviceCreateSampler(device, &descriptor); + wgpuDeviceCreateSampler(device, &descriptor); - DawnSampler apiDummySampler = api.GetNewSampler(); + WGPUSampler apiDummySampler = api.GetNewSampler(); EXPECT_CALL(api, DeviceCreateSampler( - apiDevice, MatchesLambda([](const DawnSamplerDescriptor* desc) -> bool { + apiDevice, MatchesLambda([](const WGPUSamplerDescriptor* desc) -> bool { return desc->nextInChain == nullptr && - desc->magFilter == DAWN_FILTER_MODE_LINEAR && - desc->minFilter == DAWN_FILTER_MODE_NEAREST && - desc->mipmapFilter == DAWN_FILTER_MODE_LINEAR && - desc->addressModeU == DAWN_ADDRESS_MODE_CLAMP_TO_EDGE && - desc->addressModeV == DAWN_ADDRESS_MODE_REPEAT && - desc->addressModeW == DAWN_ADDRESS_MODE_MIRRORED_REPEAT && - desc->compareFunction == DAWN_COMPARE_FUNCTION_NEVER && + desc->magFilter == WGPUFilterMode_Linear && + desc->minFilter == WGPUFilterMode_Nearest && + desc->mipmapFilter == WGPUFilterMode_Linear && + desc->addressModeU == WGPUAddressMode_ClampToEdge && + desc->addressModeV == WGPUAddressMode_Repeat && + desc->addressModeW == WGPUAddressMode_MirrorRepeat && + desc->compare == WGPUCompareFunction_Never && desc->lodMinClamp == kLodMin && desc->lodMaxClamp == kLodMax; }))) .WillOnce(Return(apiDummySampler)); @@ -297,26 +276,24 @@ TEST_F(WireArgumentTests, StructureOfValuesArgument) { // Test that the wire is able to send structures that contain objects TEST_F(WireArgumentTests, StructureOfObjectArrayArgument) { - DawnBindGroupLayoutDescriptor bglDescriptor; - bglDescriptor.nextInChain = nullptr; - bglDescriptor.bindingCount = 0; - bglDescriptor.bindings = nullptr; + WGPUBindGroupLayoutDescriptor bglDescriptor = {}; + bglDescriptor.entryCount = 0; + bglDescriptor.entries = nullptr; - DawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDescriptor); - DawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); + WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor); + WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl)); - DawnPipelineLayoutDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUPipelineLayoutDescriptor descriptor = {}; descriptor.bindGroupLayoutCount = 1; descriptor.bindGroupLayouts = &bgl; - dawnDeviceCreatePipelineLayout(device, &descriptor); + wgpuDeviceCreatePipelineLayout(device, &descriptor); - DawnPipelineLayout apiDummyLayout = api.GetNewPipelineLayout(); + WGPUPipelineLayout apiDummyLayout = api.GetNewPipelineLayout(); EXPECT_CALL(api, DeviceCreatePipelineLayout( apiDevice, - MatchesLambda([apiBgl](const DawnPipelineLayoutDescriptor* desc) -> bool { + MatchesLambda([apiBgl](const WGPUPipelineLayoutDescriptor* desc) -> bool { return desc->nextInChain == nullptr && desc->bindGroupLayoutCount == 1 && desc->bindGroupLayouts[0] == apiBgl; @@ -329,33 +306,34 @@ TEST_F(WireArgumentTests, StructureOfObjectArrayArgument) { // Test that the wire is able to send structures that contain objects TEST_F(WireArgumentTests, StructureOfStructureArrayArgument) { static constexpr int NUM_BINDINGS = 3; - DawnBindGroupLayoutBinding bindings[NUM_BINDINGS]{ - {0, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLER}, - {1, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLED_TEXTURE}, - {2, - static_cast(DAWN_SHADER_STAGE_BIT_VERTEX | - DAWN_SHADER_STAGE_BIT_FRAGMENT), - DAWN_BINDING_TYPE_UNIFORM_BUFFER}, + WGPUBindGroupLayoutEntry entries[NUM_BINDINGS]{ + {0, WGPUShaderStage_Vertex, WGPUBindingType_Sampler, false, 0, false, + WGPUTextureViewDimension_2D, WGPUTextureComponentType_Float, WGPUTextureFormat_RGBA8Unorm}, + {1, WGPUShaderStage_Vertex, WGPUBindingType_SampledTexture, false, 0, false, + WGPUTextureViewDimension_2D, WGPUTextureComponentType_Float, WGPUTextureFormat_RGBA8Unorm}, + {2, static_cast(WGPUShaderStage_Vertex | WGPUShaderStage_Fragment), + WGPUBindingType_UniformBuffer, false, 0, false, WGPUTextureViewDimension_2D, + WGPUTextureComponentType_Float, WGPUTextureFormat_RGBA8Unorm}, }; - DawnBindGroupLayoutDescriptor bglDescriptor; - bglDescriptor.bindingCount = NUM_BINDINGS; - bglDescriptor.bindings = bindings; + WGPUBindGroupLayoutDescriptor bglDescriptor = {}; + bglDescriptor.entryCount = NUM_BINDINGS; + bglDescriptor.entries = entries; - dawnDeviceCreateBindGroupLayout(device, &bglDescriptor); - DawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); + wgpuDeviceCreateBindGroupLayout(device, &bglDescriptor); + WGPUBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); EXPECT_CALL( api, DeviceCreateBindGroupLayout( - apiDevice, MatchesLambda([bindings](const DawnBindGroupLayoutDescriptor* desc) -> bool { + apiDevice, MatchesLambda([entries](const WGPUBindGroupLayoutDescriptor* desc) -> bool { for (int i = 0; i < NUM_BINDINGS; ++i) { - const auto& a = desc->bindings[i]; - const auto& b = bindings[i]; + const auto& a = desc->entries[i]; + const auto& b = entries[i]; if (a.binding != b.binding || a.visibility != b.visibility || a.type != b.type) { return false; } } - return desc->nextInChain == nullptr && desc->bindingCount == 3; + return desc->nextInChain == nullptr && desc->entryCount == 3; }))) .WillOnce(Return(apiBgl)); @@ -364,17 +342,16 @@ TEST_F(WireArgumentTests, StructureOfStructureArrayArgument) { // Test passing nullptr instead of objects - array of objects version TEST_F(WireArgumentTests, DISABLED_NullptrInArray) { - DawnBindGroupLayout nullBGL = nullptr; + WGPUBindGroupLayout nullBGL = nullptr; - DawnPipelineLayoutDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUPipelineLayoutDescriptor descriptor = {}; descriptor.bindGroupLayoutCount = 1; descriptor.bindGroupLayouts = &nullBGL; - dawnDeviceCreatePipelineLayout(device, &descriptor); + wgpuDeviceCreatePipelineLayout(device, &descriptor); EXPECT_CALL(api, DeviceCreatePipelineLayout( - apiDevice, MatchesLambda([](const DawnPipelineLayoutDescriptor* desc) -> bool { + apiDevice, MatchesLambda([](const WGPUPipelineLayoutDescriptor* desc) -> bool { return desc->nextInChain == nullptr && desc->bindGroupLayoutCount == 1 && desc->bindGroupLayouts[0] == nullptr; }))) diff --git a/third_party/dawn/src/tests/unittests/wire/WireBasicTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireBasicTests.cpp index 38bff0bc7c2..e14a5967bfd 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireBasicTests.cpp +++ b/third_party/dawn/src/tests/unittests/wire/WireBasicTests.cpp @@ -26,10 +26,10 @@ class WireBasicTests : public WireTest { // One call gets forwarded correctly. TEST_F(WireBasicTests, CallForwarded) { - dawnDeviceCreateCommandEncoder(device); + wgpuDeviceCreateCommandEncoder(device, nullptr); - DawnCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); - EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice)) + WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) .WillOnce(Return(apiCmdBufEncoder)); FlushClient(); @@ -37,28 +37,28 @@ TEST_F(WireBasicTests, CallForwarded) { // Test that calling methods on a new object works as expected. TEST_F(WireBasicTests, CreateThenCall) { - DawnCommandEncoder encoder = dawnDeviceCreateCommandEncoder(device); - dawnCommandEncoderFinish(encoder); + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr); + wgpuCommandEncoderFinish(encoder, nullptr); - DawnCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); - EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice)) + WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) .WillOnce(Return(apiCmdBufEncoder)); - DawnCommandBuffer apiCmdBuf = api.GetNewCommandBuffer(); - EXPECT_CALL(api, CommandEncoderFinish(apiCmdBufEncoder)).WillOnce(Return(apiCmdBuf)); + WGPUCommandBuffer apiCmdBuf = api.GetNewCommandBuffer(); + EXPECT_CALL(api, CommandEncoderFinish(apiCmdBufEncoder, nullptr)).WillOnce(Return(apiCmdBuf)); FlushClient(); } // Test that client reference/release do not call the backend API. TEST_F(WireBasicTests, RefCountKeptInClient) { - DawnCommandEncoder encoder = dawnDeviceCreateCommandEncoder(device); + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr); - dawnCommandEncoderReference(encoder); - dawnCommandEncoderRelease(encoder); + wgpuCommandEncoderReference(encoder); + wgpuCommandEncoderRelease(encoder); - DawnCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); - EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice)) + WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) .WillOnce(Return(apiCmdBufEncoder)); FlushClient(); @@ -66,12 +66,12 @@ TEST_F(WireBasicTests, RefCountKeptInClient) { // Test that client reference/release do not call the backend API. TEST_F(WireBasicTests, ReleaseCalledOnRefCount0) { - DawnCommandEncoder encoder = dawnDeviceCreateCommandEncoder(device); + WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device, nullptr); - dawnCommandEncoderRelease(encoder); + wgpuCommandEncoderRelease(encoder); - DawnCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); - EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice)) + WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) .WillOnce(Return(apiCmdBufEncoder)); EXPECT_CALL(api, CommandEncoderRelease(apiCmdBufEncoder)); diff --git a/third_party/dawn/src/tests/unittests/wire/WireBufferMappingTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireBufferMappingTests.cpp index 03b939ef666..c8902c1fe34 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireBufferMappingTests.cpp +++ b/third_party/dawn/src/tests/unittests/wire/WireBufferMappingTests.cpp @@ -22,15 +22,16 @@ namespace { // Mock classes to add expectations on the wire calling callbacks class MockBufferMapReadCallback { public: - MOCK_METHOD4(Call, - void(DawnBufferMapAsyncStatus status, - const uint32_t* ptr, - uint64_t dataLength, - void* userdata)); + MOCK_METHOD(void, + Call, + (WGPUBufferMapAsyncStatus status, + const uint32_t* ptr, + uint64_t dataLength, + void* userdata)); }; std::unique_ptr> mockBufferMapReadCallback; - void ToMockBufferMapReadCallback(DawnBufferMapAsyncStatus status, + void ToMockBufferMapReadCallback(WGPUBufferMapAsyncStatus status, const void* ptr, uint64_t dataLength, void* userdata) { @@ -41,16 +42,15 @@ namespace { class MockBufferMapWriteCallback { public: - MOCK_METHOD4(Call, - void(DawnBufferMapAsyncStatus status, - uint32_t* ptr, - uint64_t dataLength, - void* userdata)); + MOCK_METHOD( + void, + Call, + (WGPUBufferMapAsyncStatus status, uint32_t* ptr, uint64_t dataLength, void* userdata)); }; std::unique_ptr> mockBufferMapWriteCallback; uint32_t* lastMapWritePointer = nullptr; - void ToMockBufferMapWriteCallback(DawnBufferMapAsyncStatus status, + void ToMockBufferMapWriteCallback(WGPUBufferMapAsyncStatus status, void* ptr, uint64_t dataLength, void* userdata) { @@ -59,6 +59,17 @@ namespace { mockBufferMapWriteCallback->Call(status, lastMapWritePointer, dataLength, userdata); } + class MockBufferCreateMappedCallback { + public: + MOCK_METHOD(void, + Call, + (WGPUBufferMapAsyncStatus status, + WGPUBuffer buffer, + uint32_t* ptr, + uint64_t dataLength, + void* userdata)); + }; + } // anonymous namespace class WireBufferMappingTests : public WireTest { @@ -73,11 +84,11 @@ class WireBufferMappingTests : public WireTest { mockBufferMapReadCallback = std::make_unique>(); mockBufferMapWriteCallback = std::make_unique>(); - DawnBufferDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUBufferDescriptor descriptor = {}; + descriptor.size = kBufferSize; apiBuffer = api.GetNewBuffer(); - buffer = dawnDeviceCreateBuffer(device, &descriptor); + buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) .WillOnce(Return(apiBuffer)) @@ -101,33 +112,34 @@ class WireBufferMappingTests : public WireTest { } protected: + static constexpr uint64_t kBufferSize = sizeof(uint32_t); // A successfully created buffer - DawnBuffer buffer; - DawnBuffer apiBuffer; + WGPUBuffer buffer; + WGPUBuffer apiBuffer; }; // MapRead-specific tests // Check mapping for reading a succesfully created buffer TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) { - dawnBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent, - sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - Pointee(Eq(bufferContent)), sizeof(uint32_t), _)) + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(bufferContent)), kBufferSize, _)) .Times(1); FlushServer(); - dawnBufferUnmap(buffer); + wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); @@ -136,16 +148,15 @@ TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) { // Check that things work correctly when a validation error happens when mapping the buffer for // reading TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) { - dawnBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); + })); FlushClient(); - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) .Times(1); FlushServer(); @@ -154,19 +165,20 @@ TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) { // Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the // request is finished TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) { - dawnBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); // Return success - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, nullptr, 0); - })); + uint32_t bufferContent = 0; + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); // Destroy before the client gets the success, so the callback is called with unknown. - EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, _)) .Times(1); - dawnBufferRelease(buffer); + wgpuBufferRelease(buffer); EXPECT_CALL(api, BufferRelease(apiBuffer)); FlushClient(); @@ -176,22 +188,21 @@ TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) { // Check the map read callback is called with UNKNOWN when the map request would have worked, but // Unmap was called TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) { - dawnBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent, - sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); // Oh no! We are calling Unmap too early! - EXPECT_CALL(*mockBufferMapReadCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, _)) .Times(1); - dawnBufferUnmap(buffer); + wgpuBufferUnmap(buffer); // The callback shouldn't get called, even when the request succeeded on the server side FlushServer(); @@ -200,34 +211,32 @@ TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) { // Check that an error map read callback gets nullptr while a buffer is already mapped TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) { // Successful map - dawnBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent, - sizeof(uint32_t)); - })) - .RetiresOnSaturation(); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - Pointee(Eq(bufferContent)), sizeof(uint32_t), _)) + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(bufferContent)), kBufferSize, _)) .Times(1); FlushServer(); // Map failure while the buffer is already mapped - dawnBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0); - })); + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); + })); FlushClient(); - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) .Times(1); FlushServer(); @@ -235,20 +244,20 @@ TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullp // Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) { - dawnBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent, - sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - Pointee(Eq(bufferContent)), sizeof(uint32_t), _)) - .WillOnce(InvokeWithoutArgs([&]() { dawnBufferUnmap(buffer); })); + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(bufferContent)), kBufferSize, _)) + .WillOnce(InvokeWithoutArgs([&]() { wgpuBufferUnmap(buffer); })); FlushServer(); @@ -260,20 +269,20 @@ TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) { // Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the // callback TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) { - dawnBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent, - sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - Pointee(Eq(bufferContent)), sizeof(uint32_t), _)) - .WillOnce(InvokeWithoutArgs([&]() { dawnBufferRelease(buffer); })); + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(bufferContent)), kBufferSize, _)) + .WillOnce(InvokeWithoutArgs([&]() { wgpuBufferRelease(buffer); })); FlushServer(); @@ -286,23 +295,23 @@ TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) { // Check mapping for writing a succesfully created buffer TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) { - dawnBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); uint32_t serverBufferContent = 31337; uint32_t updatedContent = 4242; uint32_t zero = 0; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - &serverBufferContent, sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&serverBufferContent)); FlushClient(); // The map write callback always gets a buffer full of zeroes. EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), sizeof(uint32_t), _)) + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(zero)), kBufferSize, _)) .Times(1); FlushServer(); @@ -310,7 +319,7 @@ TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) { // Write something to the mapped pointer *lastMapWritePointer = updatedContent; - dawnBufferUnmap(buffer); + wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); @@ -322,17 +331,15 @@ TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) { // Check that things work correctly when a validation error happens when mapping the buffer for // writing TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) { - dawnBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); + })); FlushClient(); - EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) .Times(1); FlushServer(); @@ -341,19 +348,20 @@ TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) { // Check that the map write callback is called with UNKNOWN when the buffer is destroyed before the // request is finished TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) { - dawnBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); // Return success - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, nullptr, 0); - })); + uint32_t bufferContent = 31337; + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); // Destroy before the client gets the success, so the callback is called with unknown. - EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, _)) .Times(1); - dawnBufferRelease(buffer); + wgpuBufferRelease(buffer); EXPECT_CALL(api, BufferRelease(apiBuffer)); FlushClient(); @@ -363,22 +371,21 @@ TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) { // Check the map read callback is called with UNKNOWN when the map request would have worked, but // Unmap was called TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) { - dawnBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - &bufferContent, sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); // Oh no! We are calling Unmap too early! - EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, _)) .Times(1); - dawnBufferUnmap(buffer); + wgpuBufferUnmap(buffer); // The callback shouldn't get called, even when the request succeeded on the server side FlushServer(); @@ -387,36 +394,33 @@ TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) { // Check that an error map read callback gets nullptr while a buffer is already mapped TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) { // Successful map - dawnBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); uint32_t bufferContent = 31337; uint32_t zero = 0; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - &bufferContent, sizeof(uint32_t)); - })) - .RetiresOnSaturation(); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), sizeof(uint32_t), _)) + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(zero)), kBufferSize, _)) .Times(1); FlushServer(); // Map failure while the buffer is already mapped - dawnBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0); - })); + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); + })); FlushClient(); - EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) .Times(1); FlushServer(); @@ -424,21 +428,21 @@ TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullp // Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) { - dawnBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); uint32_t bufferContent = 31337; uint32_t zero = 0; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - &bufferContent, sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), sizeof(uint32_t), _)) - .WillOnce(InvokeWithoutArgs([&]() { dawnBufferUnmap(buffer); })); + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(zero)), kBufferSize, _)) + .WillOnce(InvokeWithoutArgs([&]() { wgpuBufferUnmap(buffer); })); FlushServer(); @@ -450,21 +454,21 @@ TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) { // Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the // callback TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) { - dawnBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); uint32_t bufferContent = 31337; uint32_t zero = 0; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - &bufferContent, sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), sizeof(uint32_t), _)) - .WillOnce(InvokeWithoutArgs([&]() { dawnBufferRelease(buffer); })); + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(zero)), kBufferSize, _)) + .WillOnce(InvokeWithoutArgs([&]() { wgpuBufferRelease(buffer); })); FlushServer(); @@ -475,18 +479,17 @@ TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) { // Test successful CreateBufferMapped TEST_F(WireBufferMappingTests, CreateBufferMappedSuccess) { - DawnBufferDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUBufferDescriptor descriptor = {}; descriptor.size = 4; - DawnBuffer apiBuffer = api.GetNewBuffer(); - DawnCreateBufferMappedResult apiResult; + WGPUBuffer apiBuffer = api.GetNewBuffer(); + WGPUCreateBufferMappedResult apiResult; uint32_t apiBufferData = 1234; apiResult.buffer = apiBuffer; apiResult.data = reinterpret_cast(&apiBufferData); apiResult.dataLength = 4; - DawnCreateBufferMappedResult result = dawnDeviceCreateBufferMapped(device, &descriptor); + WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor); EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _)) .WillOnce(Return(apiResult)) @@ -494,7 +497,7 @@ TEST_F(WireBufferMappingTests, CreateBufferMappedSuccess) { FlushClient(); - dawnBufferUnmap(result.buffer); + wgpuBufferUnmap(result.buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); @@ -502,18 +505,17 @@ TEST_F(WireBufferMappingTests, CreateBufferMappedSuccess) { // Test that releasing after CreateBufferMapped does not call Unmap TEST_F(WireBufferMappingTests, ReleaseAfterCreateBufferMapped) { - DawnBufferDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUBufferDescriptor descriptor = {}; descriptor.size = 4; - DawnBuffer apiBuffer = api.GetNewBuffer(); - DawnCreateBufferMappedResult apiResult; + WGPUBuffer apiBuffer = api.GetNewBuffer(); + WGPUCreateBufferMappedResult apiResult; uint32_t apiBufferData = 1234; apiResult.buffer = apiBuffer; apiResult.data = reinterpret_cast(&apiBufferData); apiResult.dataLength = 4; - DawnCreateBufferMappedResult result = dawnDeviceCreateBufferMapped(device, &descriptor); + WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor); EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _)) .WillOnce(Return(apiResult)) @@ -521,7 +523,7 @@ TEST_F(WireBufferMappingTests, ReleaseAfterCreateBufferMapped) { FlushClient(); - dawnBufferRelease(result.buffer); + wgpuBufferRelease(result.buffer); EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1); FlushClient(); @@ -529,18 +531,17 @@ TEST_F(WireBufferMappingTests, ReleaseAfterCreateBufferMapped) { // Test that it is valid to map a buffer after CreateBufferMapped and Unmap TEST_F(WireBufferMappingTests, CreateBufferMappedThenMapSuccess) { - DawnBufferDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUBufferDescriptor descriptor = {}; descriptor.size = 4; - DawnBuffer apiBuffer = api.GetNewBuffer(); - DawnCreateBufferMappedResult apiResult; + WGPUBuffer apiBuffer = api.GetNewBuffer(); + WGPUCreateBufferMappedResult apiResult; uint32_t apiBufferData = 9863; apiResult.buffer = apiBuffer; apiResult.data = reinterpret_cast(&apiBufferData); apiResult.dataLength = 4; - DawnCreateBufferMappedResult result = dawnDeviceCreateBufferMapped(device, &descriptor); + WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor); EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _)) .WillOnce(Return(apiResult)) @@ -548,24 +549,24 @@ TEST_F(WireBufferMappingTests, CreateBufferMappedThenMapSuccess) { FlushClient(); - dawnBufferUnmap(result.buffer); + wgpuBufferUnmap(result.buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); - dawnBufferMapWriteAsync(result.buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(result.buffer, ToMockBufferMapWriteCallback, nullptr); uint32_t zero = 0; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, - &apiBufferData, sizeof(uint32_t)); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&apiBufferData)); FlushClient(); EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), sizeof(uint32_t), _)) + Call(WGPUBufferMapAsyncStatus_Success, Pointee(Eq(zero)), kBufferSize, _)) .Times(1); FlushServer(); @@ -573,18 +574,17 @@ TEST_F(WireBufferMappingTests, CreateBufferMappedThenMapSuccess) { // Test that it is invalid to map a buffer after CreateBufferMapped before Unmap TEST_F(WireBufferMappingTests, CreateBufferMappedThenMapFailure) { - DawnBufferDescriptor descriptor; - descriptor.nextInChain = nullptr; + WGPUBufferDescriptor descriptor = {}; descriptor.size = 4; - DawnBuffer apiBuffer = api.GetNewBuffer(); - DawnCreateBufferMappedResult apiResult; + WGPUBuffer apiBuffer = api.GetNewBuffer(); + WGPUCreateBufferMappedResult apiResult; uint32_t apiBufferData = 9863; apiResult.buffer = apiBuffer; apiResult.data = reinterpret_cast(&apiBufferData); apiResult.dataLength = 4; - DawnCreateBufferMappedResult result = dawnDeviceCreateBufferMapped(device, &descriptor); + WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor); EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _)) .WillOnce(Return(apiResult)) @@ -592,22 +592,20 @@ TEST_F(WireBufferMappingTests, CreateBufferMappedThenMapFailure) { FlushClient(); - dawnBufferMapWriteAsync(result.buffer, ToMockBufferMapWriteCallback, nullptr); + wgpuBufferMapWriteAsync(result.buffer, ToMockBufferMapWriteCallback, nullptr); - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0); - })); + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); + })); FlushClient(); - EXPECT_CALL(*mockBufferMapWriteCallback, - Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, _)) + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) .Times(1); FlushServer(); - dawnBufferUnmap(result.buffer); + wgpuBufferUnmap(result.buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); diff --git a/third_party/dawn/src/tests/unittests/wire/WireDisconnectTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireDisconnectTests.cpp new file mode 100644 index 00000000000..4e9b355f8cf --- /dev/null +++ b/third_party/dawn/src/tests/unittests/wire/WireDisconnectTests.cpp @@ -0,0 +1,128 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/wire/WireTest.h" + +#include "common/Assert.h" +#include "dawn_wire/WireClient.h" +#include "tests/MockCallback.h" + +using namespace testing; +using namespace dawn_wire; + +namespace { + + class WireDisconnectTests : public WireTest {}; + +} // anonymous namespace + +// Test that commands are not received if the client disconnects. +TEST_F(WireDisconnectTests, CommandsAfterDisconnect) { + // Sanity check that commands work at all. + wgpuDeviceCreateCommandEncoder(device, nullptr); + + WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) + .WillOnce(Return(apiCmdBufEncoder)); + FlushClient(); + + // Disconnect. + GetWireClient()->Disconnect(); + + // Command is not received because client disconnected. + wgpuDeviceCreateCommandEncoder(device, nullptr); + EXPECT_CALL(api, DeviceCreateCommandEncoder(_, _)).Times(Exactly(0)); + FlushClient(); +} + +// Test that commands that are serialized before a disconnect but flushed +// after are received. +TEST_F(WireDisconnectTests, FlushAfterDisconnect) { + // Sanity check that commands work at all. + wgpuDeviceCreateCommandEncoder(device, nullptr); + + // Disconnect. + GetWireClient()->Disconnect(); + + // Already-serialized commmands are still received. + WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); + EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) + .WillOnce(Return(apiCmdBufEncoder)); + FlushClient(); +} + +// Check that disconnecting the wire client calls the device lost callback exacty once. +TEST_F(WireDisconnectTests, CallsDeviceLostCallback) { + MockCallback mockDeviceLostCallback; + wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), + mockDeviceLostCallback.MakeUserdata(this)); + + // Disconnect the wire client. We should receive device lost only once. + EXPECT_CALL(mockDeviceLostCallback, Call(_, this)).Times(Exactly(1)); + GetWireClient()->Disconnect(); + GetWireClient()->Disconnect(); +} + +// Check that disconnecting the wire client after a device loss does not trigger the callback again. +TEST_F(WireDisconnectTests, ServerLostThenDisconnect) { + MockCallback mockDeviceLostCallback; + wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), + mockDeviceLostCallback.MakeUserdata(this)); + + api.CallDeviceLostCallback(apiDevice, "some reason"); + + // Flush the device lost return command. + EXPECT_CALL(mockDeviceLostCallback, Call(StrEq("some reason"), this)).Times(Exactly(1)); + FlushServer(); + + // Disconnect the client. We shouldn't see the lost callback again. + EXPECT_CALL(mockDeviceLostCallback, Call(_, _)).Times(Exactly(0)); + GetWireClient()->Disconnect(); +} + +// Check that disconnecting the wire client inside the device loss callback does not trigger the +// callback again. +TEST_F(WireDisconnectTests, ServerLostThenDisconnectInCallback) { + MockCallback mockDeviceLostCallback; + wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), + mockDeviceLostCallback.MakeUserdata(this)); + + api.CallDeviceLostCallback(apiDevice, "lost reason"); + + // Disconnect the client inside the lost callback. We should see the callback + // only once. + EXPECT_CALL(mockDeviceLostCallback, Call(StrEq("lost reason"), this)) + .WillOnce(InvokeWithoutArgs([&]() { + EXPECT_CALL(mockDeviceLostCallback, Call(_, _)).Times(Exactly(0)); + GetWireClient()->Disconnect(); + })); + FlushServer(); +} + +// Check that a device loss after a disconnect does not trigger the callback again. +TEST_F(WireDisconnectTests, DisconnectThenServerLost) { + MockCallback mockDeviceLostCallback; + wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), + mockDeviceLostCallback.MakeUserdata(this)); + + // Disconnect the client. We should see the callback once. + EXPECT_CALL(mockDeviceLostCallback, Call(_, this)).Times(Exactly(1)); + GetWireClient()->Disconnect(); + + // Lose the device on the server. The client callback shouldn't be + // called again. + api.CallDeviceLostCallback(apiDevice, "lost reason"); + EXPECT_CALL(mockDeviceLostCallback, Call(_, _)).Times(Exactly(0)); + FlushServer(); +} diff --git a/third_party/dawn/src/tests/unittests/wire/WireErrorCallbackTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireErrorCallbackTests.cpp index 04d81f95d8f..3fe25f01719 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireErrorCallbackTests.cpp +++ b/third_party/dawn/src/tests/unittests/wire/WireErrorCallbackTests.cpp @@ -22,12 +22,34 @@ namespace { // Mock classes to add expectations on the wire calling callbacks class MockDeviceErrorCallback { public: - MOCK_METHOD2(Call, void(const char* message, void* userdata)); + MOCK_METHOD(void, Call, (WGPUErrorType type, const char* message, void* userdata)); }; std::unique_ptr> mockDeviceErrorCallback; - void ToMockDeviceErrorCallback(const char* message, void* userdata) { - mockDeviceErrorCallback->Call(message, userdata); + void ToMockDeviceErrorCallback(WGPUErrorType type, const char* message, void* userdata) { + mockDeviceErrorCallback->Call(type, message, userdata); + } + + class MockDevicePopErrorScopeCallback { + public: + MOCK_METHOD(void, Call, (WGPUErrorType type, const char* message, void* userdata)); + }; + + std::unique_ptr> mockDevicePopErrorScopeCallback; + void ToMockDevicePopErrorScopeCallback(WGPUErrorType type, + const char* message, + void* userdata) { + mockDevicePopErrorScopeCallback->Call(type, message, userdata); + } + + class MockDeviceLostCallback { + public: + MOCK_METHOD(void, Call, (const char* message, void* userdata)); + }; + + std::unique_ptr> mockDeviceLostCallback; + void ToMockDeviceLostCallback(const char* message, void* userdata) { + mockDeviceLostCallback->Call(message, userdata); } } // anonymous namespace @@ -42,33 +64,199 @@ class WireErrorCallbackTests : public WireTest { WireTest::SetUp(); mockDeviceErrorCallback = std::make_unique>(); + mockDevicePopErrorScopeCallback = + std::make_unique>(); + mockDeviceLostCallback = std::make_unique>(); } void TearDown() override { WireTest::TearDown(); mockDeviceErrorCallback = nullptr; + mockDevicePopErrorScopeCallback = nullptr; + mockDeviceLostCallback = nullptr; } void FlushServer() { WireTest::FlushServer(); Mock::VerifyAndClearExpectations(&mockDeviceErrorCallback); + Mock::VerifyAndClearExpectations(&mockDevicePopErrorScopeCallback); } }; // Test the return wire for device error callbacks TEST_F(WireErrorCallbackTests, DeviceErrorCallback) { - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, this); + wgpuDeviceSetUncapturedErrorCallback(device, ToMockDeviceErrorCallback, this); + + // Setting the error callback should stay on the client side and do nothing + FlushClient(); + + // Calling the callback on the server side will result in the callback being called on the + // client side + api.CallDeviceErrorCallback(apiDevice, WGPUErrorType_Validation, "Some error message"); + + EXPECT_CALL(*mockDeviceErrorCallback, + Call(WGPUErrorType_Validation, StrEq("Some error message"), this)) + .Times(1); + + FlushServer(); +} + +// Test the return wire for error scopes. +TEST_F(WireErrorCallbackTests, PushPopErrorScopeCallback) { + wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation); + EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1); + + FlushClient(); + + wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this); + + WGPUErrorCallback callback; + void* userdata; + EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _)) + .WillOnce(DoAll(SaveArg<1>(&callback), SaveArg<2>(&userdata), Return(true))); + + FlushClient(); + + callback(WGPUErrorType_Validation, "Some error message", userdata); + EXPECT_CALL(*mockDevicePopErrorScopeCallback, + Call(WGPUErrorType_Validation, StrEq("Some error message"), this)) + .Times(1); + + FlushServer(); +} + +// Test the return wire for error scopes when callbacks return in a various orders. +TEST_F(WireErrorCallbackTests, PopErrorScopeCallbackOrdering) { + // Two error scopes are popped, and the first one returns first. + { + wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation); + wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation); + EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(2); + + FlushClient(); + + wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this); + wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1); + + WGPUErrorCallback callback1; + WGPUErrorCallback callback2; + void* userdata1; + void* userdata2; + EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _)) + .WillOnce(DoAll(SaveArg<1>(&callback1), SaveArg<2>(&userdata1), Return(true))) + .WillOnce(DoAll(SaveArg<1>(&callback2), SaveArg<2>(&userdata2), Return(true))); + + FlushClient(); + + callback1(WGPUErrorType_Validation, "First error message", userdata1); + EXPECT_CALL(*mockDevicePopErrorScopeCallback, + Call(WGPUErrorType_Validation, StrEq("First error message"), this)) + .Times(1); + FlushServer(); + + callback2(WGPUErrorType_Validation, "Second error message", userdata2); + EXPECT_CALL(*mockDevicePopErrorScopeCallback, + Call(WGPUErrorType_Validation, StrEq("Second error message"), this + 1)) + .Times(1); + FlushServer(); + } + + // Two error scopes are popped, and the second one returns first. + { + wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation); + wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation); + EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(2); + + FlushClient(); + + wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this); + wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1); + + WGPUErrorCallback callback1; + WGPUErrorCallback callback2; + void* userdata1; + void* userdata2; + EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _)) + .WillOnce(DoAll(SaveArg<1>(&callback1), SaveArg<2>(&userdata1), Return(true))) + .WillOnce(DoAll(SaveArg<1>(&callback2), SaveArg<2>(&userdata2), Return(true))); + + FlushClient(); + + callback2(WGPUErrorType_Validation, "Second error message", userdata2); + EXPECT_CALL(*mockDevicePopErrorScopeCallback, + Call(WGPUErrorType_Validation, StrEq("Second error message"), this + 1)) + .Times(1); + FlushServer(); + + callback1(WGPUErrorType_Validation, "First error message", userdata1); + EXPECT_CALL(*mockDevicePopErrorScopeCallback, + Call(WGPUErrorType_Validation, StrEq("First error message"), this)) + .Times(1); + FlushServer(); + } +} + +// Test the return wire for error scopes in flight when the device is destroyed. +TEST_F(WireErrorCallbackTests, PopErrorScopeDeviceDestroyed) { + wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation); + EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1); + + FlushClient(); + + EXPECT_TRUE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this)); + + EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _)).WillOnce(Return(true)); + FlushClient(); + + // Incomplete callback called in Device destructor. + EXPECT_CALL(*mockDevicePopErrorScopeCallback, + Call(WGPUErrorType_Unknown, ValidStringMessage(), this)) + .Times(1); +} + +// Test that PopErrorScope returns false if there are no error scopes. +TEST_F(WireErrorCallbackTests, PopErrorScopeEmptyStack) { + // Empty stack + { EXPECT_FALSE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this)); } + + // Pop too many times + { + wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation); + EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1); + + EXPECT_TRUE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this)); + EXPECT_FALSE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1)); + + WGPUErrorCallback callback; + void* userdata; + EXPECT_CALL(api, OnDevicePopErrorScopeCallback(apiDevice, _, _)) + .WillOnce(DoAll(SaveArg<1>(&callback), SaveArg<2>(&userdata), Return(true))); + + FlushClient(); + + callback(WGPUErrorType_Validation, "Some error message", userdata); + EXPECT_CALL(*mockDevicePopErrorScopeCallback, + Call(WGPUErrorType_Validation, StrEq("Some error message"), this)) + .Times(1); + + FlushServer(); + } +} + +// Test the return wire for device lost callback +TEST_F(WireErrorCallbackTests, DeviceLostCallback) { + wgpuDeviceSetDeviceLostCallback(device, ToMockDeviceLostCallback, this); // Setting the error callback should stay on the client side and do nothing FlushClient(); // Calling the callback on the server side will result in the callback being called on the // client side - api.CallDeviceErrorCallback(apiDevice, "Some error message"); + api.CallDeviceLostCallback(apiDevice, "Some error message"); - EXPECT_CALL(*mockDeviceErrorCallback, Call(StrEq("Some error message"), this)).Times(1); + EXPECT_CALL(*mockDeviceLostCallback, Call(StrEq("Some error message"), this)).Times(1); FlushServer(); } diff --git a/third_party/dawn/src/tests/unittests/wire/WireExtensionTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireExtensionTests.cpp new file mode 100644 index 00000000000..d36fc5f7f28 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/wire/WireExtensionTests.cpp @@ -0,0 +1,213 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/wire/WireTest.h" + +using namespace testing; +using namespace dawn_wire; + +class WireExtensionTests : public WireTest { + public: + WireExtensionTests() { + } + ~WireExtensionTests() override = default; +}; + +// Serialize/Deserializes a chained struct correctly. +TEST_F(WireExtensionTests, ChainedStruct) { + WGPUSamplerDescriptorDummyAnisotropicFiltering clientExt = {}; + clientExt.chain.sType = WGPUSType_SamplerDescriptorDummyAnisotropicFiltering; + clientExt.chain.next = nullptr; + clientExt.maxAnisotropy = 3.14; + + WGPUSamplerDescriptor clientDesc = {}; + clientDesc.nextInChain = &clientExt.chain; + clientDesc.label = "sampler with anisotropic filtering"; + + wgpuDeviceCreateSampler(device, &clientDesc); + EXPECT_CALL(api, DeviceCreateSampler(apiDevice, NotNull())) + .WillOnce(Invoke([&](Unused, const WGPUSamplerDescriptor* serverDesc) -> WGPUSampler { + EXPECT_STREQ(serverDesc->label, clientDesc.label); + + const auto* ext = + reinterpret_cast( + serverDesc->nextInChain); + EXPECT_EQ(ext->chain.sType, clientExt.chain.sType); + EXPECT_EQ(ext->maxAnisotropy, clientExt.maxAnisotropy); + + EXPECT_EQ(ext->chain.next, nullptr); + + return api.GetNewSampler(); + })); + FlushClient(); +} + +// Serialize/Deserializes multiple chained structs correctly. +TEST_F(WireExtensionTests, MutlipleChainedStructs) { + WGPUSamplerDescriptorDummyAnisotropicFiltering clientExt2 = {}; + clientExt2.chain.sType = WGPUSType_SamplerDescriptorDummyAnisotropicFiltering; + clientExt2.chain.next = nullptr; + clientExt2.maxAnisotropy = 2.71828; + + WGPUSamplerDescriptorDummyAnisotropicFiltering clientExt1 = {}; + clientExt1.chain.sType = WGPUSType_SamplerDescriptorDummyAnisotropicFiltering; + clientExt1.chain.next = &clientExt2.chain; + clientExt1.maxAnisotropy = 3.14; + + WGPUSamplerDescriptor clientDesc = {}; + clientDesc.nextInChain = &clientExt1.chain; + clientDesc.label = "sampler with anisotropic filtering"; + + wgpuDeviceCreateSampler(device, &clientDesc); + EXPECT_CALL(api, DeviceCreateSampler(apiDevice, NotNull())) + .WillOnce(Invoke([&](Unused, const WGPUSamplerDescriptor* serverDesc) -> WGPUSampler { + EXPECT_STREQ(serverDesc->label, clientDesc.label); + + const auto* ext1 = + reinterpret_cast( + serverDesc->nextInChain); + EXPECT_EQ(ext1->chain.sType, clientExt1.chain.sType); + EXPECT_EQ(ext1->maxAnisotropy, clientExt1.maxAnisotropy); + + const auto* ext2 = + reinterpret_cast( + ext1->chain.next); + EXPECT_EQ(ext2->chain.sType, clientExt2.chain.sType); + EXPECT_EQ(ext2->maxAnisotropy, clientExt2.maxAnisotropy); + + EXPECT_EQ(ext2->chain.next, nullptr); + + return api.GetNewSampler(); + })); + FlushClient(); + + // Swap the order of the chained structs. + clientDesc.nextInChain = &clientExt2.chain; + clientExt2.chain.next = &clientExt1.chain; + clientExt1.chain.next = nullptr; + + wgpuDeviceCreateSampler(device, &clientDesc); + EXPECT_CALL(api, DeviceCreateSampler(apiDevice, NotNull())) + .WillOnce(Invoke([&](Unused, const WGPUSamplerDescriptor* serverDesc) -> WGPUSampler { + EXPECT_STREQ(serverDesc->label, clientDesc.label); + + const auto* ext2 = + reinterpret_cast( + serverDesc->nextInChain); + EXPECT_EQ(ext2->chain.sType, clientExt2.chain.sType); + EXPECT_EQ(ext2->maxAnisotropy, clientExt2.maxAnisotropy); + + const auto* ext1 = + reinterpret_cast( + ext2->chain.next); + EXPECT_EQ(ext1->chain.sType, clientExt1.chain.sType); + EXPECT_EQ(ext1->maxAnisotropy, clientExt1.maxAnisotropy); + + EXPECT_EQ(ext1->chain.next, nullptr); + + return api.GetNewSampler(); + })); + FlushClient(); +} + +// Test that a chained struct with Invalid sType is an error. +TEST_F(WireExtensionTests, InvalidSType) { + WGPUSamplerDescriptorDummyAnisotropicFiltering clientExt = {}; + clientExt.chain.sType = WGPUSType_Invalid; + clientExt.chain.next = nullptr; + + WGPUSamplerDescriptor clientDesc = {}; + clientDesc.nextInChain = &clientExt.chain; + clientDesc.label = "sampler with anisotropic filtering"; + + wgpuDeviceCreateSampler(device, &clientDesc); + FlushClient(false); +} + +// Test that if both an invalid and valid stype are passed on the chain, it is an error. +TEST_F(WireExtensionTests, ValidAndInvalidSTypeInChain) { + WGPUSamplerDescriptorDummyAnisotropicFiltering clientExt2 = {}; + clientExt2.chain.sType = WGPUSType_Invalid; + clientExt2.chain.next = nullptr; + clientExt2.maxAnisotropy = 2.71828; + + WGPUSamplerDescriptorDummyAnisotropicFiltering clientExt1 = {}; + clientExt1.chain.sType = WGPUSType_SamplerDescriptorDummyAnisotropicFiltering; + clientExt1.chain.next = &clientExt2.chain; + clientExt1.maxAnisotropy = 3.14; + + WGPUSamplerDescriptor clientDesc = {}; + clientDesc.nextInChain = &clientExt1.chain; + clientDesc.label = "sampler with anisotropic filtering"; + + wgpuDeviceCreateSampler(device, &clientDesc); + FlushClient(false); + + // Swap the order of the chained structs. + clientDesc.nextInChain = &clientExt2.chain; + clientExt2.chain.next = &clientExt1.chain; + clientExt1.chain.next = nullptr; + + wgpuDeviceCreateSampler(device, &clientDesc); + FlushClient(false); +} + +// Test that (de)?serializing a chained struct with subdescriptors works. +TEST_F(WireExtensionTests, ChainedStructWithSubdescriptor) { + WGPUShaderModuleDescriptor shaderModuleDesc = {}; + + WGPUShaderModule apiShaderModule1 = api.GetNewShaderModule(); + WGPUShaderModule shaderModule1 = wgpuDeviceCreateShaderModule(device, &shaderModuleDesc); + EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiShaderModule1)); + FlushClient(); + + WGPUShaderModule apiShaderModule2 = api.GetNewShaderModule(); + WGPUShaderModule shaderModule2 = wgpuDeviceCreateShaderModule(device, &shaderModuleDesc); + EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiShaderModule2)); + FlushClient(); + + WGPUProgrammableStageDescriptor extraStageDesc = {}; + extraStageDesc.module = shaderModule1; + extraStageDesc.entryPoint = "my other module"; + + WGPURenderPipelineDescriptorDummyExtension clientExt = {}; + clientExt.chain.sType = WGPUSType_RenderPipelineDescriptorDummyExtension; + clientExt.chain.next = nullptr; + clientExt.dummyStage = extraStageDesc; + + WGPURenderPipelineDescriptor renderPipelineDesc = {}; + renderPipelineDesc.nextInChain = &clientExt.chain; + renderPipelineDesc.vertexStage.module = shaderModule2; + renderPipelineDesc.vertexStage.entryPoint = "my vertex module"; + + wgpuDeviceCreateRenderPipeline(device, &renderPipelineDesc); + EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, NotNull())) + .WillOnce(Invoke([&](Unused, + const WGPURenderPipelineDescriptor* serverDesc) -> WGPURenderPipeline { + EXPECT_EQ(serverDesc->vertexStage.module, apiShaderModule2); + EXPECT_STREQ(serverDesc->vertexStage.entryPoint, + renderPipelineDesc.vertexStage.entryPoint); + + const auto* ext = reinterpret_cast( + serverDesc->nextInChain); + EXPECT_EQ(ext->chain.sType, clientExt.chain.sType); + EXPECT_EQ(ext->dummyStage.module, apiShaderModule1); + EXPECT_STREQ(ext->dummyStage.entryPoint, extraStageDesc.entryPoint); + + EXPECT_EQ(ext->chain.next, nullptr); + + return api.GetNewRenderPipeline(); + })); + FlushClient(); +} diff --git a/third_party/dawn/src/tests/unittests/wire/WireFenceTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireFenceTests.cpp index 8acbf007b4d..1cd57708c43 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireFenceTests.cpp +++ b/third_party/dawn/src/tests/unittests/wire/WireFenceTests.cpp @@ -19,24 +19,13 @@ using namespace dawn_wire; namespace { - // Mock classes to add expectations on the wire calling callbacks - class MockDeviceErrorCallback { - public: - MOCK_METHOD2(Call, void(const char* message, void* userdata)); - }; - - std::unique_ptr> mockDeviceErrorCallback; - void ToMockDeviceErrorCallback(const char* message, void* userdata) { - mockDeviceErrorCallback->Call(message, userdata); - } - class MockFenceOnCompletionCallback { public: - MOCK_METHOD2(Call, void(DawnFenceCompletionStatus status, void* userdata)); + MOCK_METHOD(void, Call, (WGPUFenceCompletionStatus status, void* userdata)); }; std::unique_ptr> mockFenceOnCompletionCallback; - void ToMockFenceOnCompletionCallback(DawnFenceCompletionStatus status, void* userdata) { + void ToMockFenceOnCompletionCallback(WGPUFenceCompletionStatus status, void* userdata) { mockFenceOnCompletionCallback->Call(status, userdata); } @@ -51,23 +40,15 @@ class WireFenceTests : public WireTest { void SetUp() override { WireTest::SetUp(); - mockDeviceErrorCallback = std::make_unique>(); mockFenceOnCompletionCallback = std::make_unique>(); { - queue = dawnDeviceCreateQueue(device); - apiQueue = api.GetNewQueue(); - EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue)); - FlushClient(); - } - { - DawnFenceDescriptor descriptor; + WGPUFenceDescriptor descriptor = {}; descriptor.initialValue = 1; - descriptor.nextInChain = nullptr; apiFence = api.GetNewFence(); - fence = dawnQueueCreateFence(queue, &descriptor); + fence = wgpuQueueCreateFence(queue, &descriptor); EXPECT_CALL(api, QueueCreateFence(apiQueue, _)).WillOnce(Return(apiFence)); FlushClient(); @@ -77,36 +58,31 @@ class WireFenceTests : public WireTest { void TearDown() override { WireTest::TearDown(); - mockDeviceErrorCallback = nullptr; mockFenceOnCompletionCallback = nullptr; } void FlushServer() { WireTest::FlushServer(); - Mock::VerifyAndClearExpectations(&mockDeviceErrorCallback); Mock::VerifyAndClearExpectations(&mockFenceOnCompletionCallback); } protected: void DoQueueSignal(uint64_t signalValue) { - dawnQueueSignal(queue, fence, signalValue); + wgpuQueueSignal(queue, fence, signalValue); EXPECT_CALL(api, QueueSignal(apiQueue, apiFence, signalValue)).Times(1); // This callback is generated to update the completedValue of the fence // on the client EXPECT_CALL(api, OnFenceOnCompletionCallback(apiFence, signalValue, _, _)) .WillOnce(InvokeWithoutArgs([&]() { - api.CallFenceOnCompletionCallback(apiFence, DAWN_FENCE_COMPLETION_STATUS_SUCCESS); + api.CallFenceOnCompletionCallback(apiFence, WGPUFenceCompletionStatus_Success); })); } // A successfully created fence - DawnFence fence; - DawnFence apiFence; - - DawnQueue queue; - DawnQueue apiQueue; + WGPUFence fence; + WGPUFence apiFence; }; // Check that signaling a fence succeeds @@ -117,53 +93,42 @@ TEST_F(WireFenceTests, QueueSignalSuccess) { FlushServer(); } -// Without any flushes, it is valid to signal a value greater than the current -// signaled value -TEST_F(WireFenceTests, QueueSignalSynchronousValidationSuccess) { - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, nullptr); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(0); - - dawnQueueSignal(queue, fence, 2u); - dawnQueueSignal(queue, fence, 4u); - dawnQueueSignal(queue, fence, 5u); -} - -// Without any flushes, errors should be generated when signaling a value less +// Errors should be generated when signaling a value less // than or equal to the current signaled value -TEST_F(WireFenceTests, QueueSignalSynchronousValidationError) { - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, nullptr); - - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1); - dawnQueueSignal(queue, fence, 0u); // Error - EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); +TEST_F(WireFenceTests, QueueSignalValidationError) { + wgpuQueueSignal(queue, fence, 0u); // Error + EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) + .Times(1); + FlushClient(); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1); - dawnQueueSignal(queue, fence, 1u); // Error - EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); + wgpuQueueSignal(queue, fence, 1u); // Error + EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) + .Times(1); + FlushClient(); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(0); - dawnQueueSignal(queue, fence, 4u); // Success - EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); + DoQueueSignal(4u); // Success + FlushClient(); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1); - dawnQueueSignal(queue, fence, 3u); // Error - EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); + wgpuQueueSignal(queue, fence, 3u); // Error + EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) + .Times(1); + FlushClient(); } // Check that callbacks are immediately called if the fence is already finished TEST_F(WireFenceTests, OnCompletionImmediate) { // Can call on value < (initial) signaled value happens immediately { - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, _)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, _)) .Times(1); - dawnFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, nullptr); + wgpuFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, nullptr); } // Can call on value == (initial) signaled value happens immediately { - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, _)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, _)) .Times(1); - dawnFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, nullptr); + wgpuFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, nullptr); } } @@ -175,26 +140,22 @@ TEST_F(WireFenceTests, OnCompletionMultiple) { // Add callbacks in a non-monotonic order. They should still be called // in order of increasing fence value. // Add multiple callbacks for the same value. - dawnFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, this + 0); - dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, this + 1); - dawnFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, this + 2); - dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, this + 3); + wgpuFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, this + 0); + wgpuFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, this + 1); + wgpuFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, this + 2); + wgpuFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, this + 3); Sequence s1, s2; - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 1)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 1)) .Times(1) .InSequence(s1); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 3)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 3)) .Times(1) .InSequence(s2); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 2)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 2)) .Times(1) .InSequence(s1, s2); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, this + 0)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0)) .Times(1) .InSequence(s1, s2); @@ -205,30 +166,31 @@ TEST_F(WireFenceTests, OnCompletionMultiple) { // Without any flushes, it is valid to wait on a value less than or equal to // the last signaled value TEST_F(WireFenceTests, OnCompletionSynchronousValidationSuccess) { - dawnQueueSignal(queue, fence, 4u); - dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0); - dawnFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0); - dawnFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0); + wgpuQueueSignal(queue, fence, 4u); + wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0); + wgpuFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0); + wgpuFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0); - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, _)) + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Unknown, _)) .Times(3); } -// Without any flushes, errors should be generated when waiting on a value greater +// Errors should be generated when waiting on a value greater // than the last signaled value -TEST_F(WireFenceTests, OnCompletionSynchronousValidationError) { - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, this + 1); - - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, this + 0)) +TEST_F(WireFenceTests, OnCompletionValidationError) { + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Error, this + 0)) .Times(1); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, this + 1)).Times(1); - dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, this + 0); + wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, this + 0); + + EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) + .Times(1); + FlushClient(); } // Check that the fence completed value is initialized TEST_F(WireFenceTests, GetCompletedValueInitialization) { - EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u); + EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u); } // Check that the fence completed value updates after signaling the fence @@ -237,55 +199,59 @@ TEST_F(WireFenceTests, GetCompletedValueUpdate) { FlushClient(); FlushServer(); - EXPECT_EQ(dawnFenceGetCompletedValue(fence), 3u); + EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 3u); } // Check that the fence completed value does not update without a flush TEST_F(WireFenceTests, GetCompletedValueNoUpdate) { - dawnQueueSignal(queue, fence, 3u); - EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u); + wgpuQueueSignal(queue, fence, 3u); + EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u); } // Check that the callback is called with UNKNOWN when the fence is destroyed // before the completed value is updated TEST_F(WireFenceTests, DestroyBeforeOnCompletionEnd) { - dawnQueueSignal(queue, fence, 3u); - dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, nullptr); - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, _)) + wgpuQueueSignal(queue, fence, 3u); + wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, nullptr); + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Unknown, _)) .Times(1); } // Test that signaling a fence on a wrong queue is invalid -TEST_F(WireFenceTests, SignalWrongQueue) { - DawnQueue queue2 = dawnDeviceCreateQueue(device); - DawnQueue apiQueue2 = api.GetNewQueue(); - EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue2)); +// DISABLED until we have support for multiple queues. +TEST_F(WireFenceTests, DISABLED_SignalWrongQueue) { + WGPUQueue queue2 = wgpuDeviceGetDefaultQueue(device); + WGPUQueue apiQueue2 = api.GetNewQueue(); + EXPECT_CALL(api, DeviceGetDefaultQueue(apiDevice)).WillOnce(Return(apiQueue2)); FlushClient(); - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, nullptr); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1); - dawnQueueSignal(queue2, fence, 2u); // error + wgpuQueueSignal(queue2, fence, 2u); // error + EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) + .Times(1); + FlushClient(); } // Test that signaling a fence on a wrong queue does not update fence signaled value -TEST_F(WireFenceTests, SignalWrongQueueDoesNotUpdateValue) { - DawnQueue queue2 = dawnDeviceCreateQueue(device); - DawnQueue apiQueue2 = api.GetNewQueue(); - EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue2)); +// DISABLED until we have support for multiple queues. +TEST_F(WireFenceTests, DISABLED_SignalWrongQueueDoesNotUpdateValue) { + WGPUQueue queue2 = wgpuDeviceGetDefaultQueue(device); + WGPUQueue apiQueue2 = api.GetNewQueue(); + EXPECT_CALL(api, DeviceGetDefaultQueue(apiDevice)).WillOnce(Return(apiQueue2)); FlushClient(); - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, nullptr); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, _)).Times(1); - dawnQueueSignal(queue2, fence, 2u); // error + wgpuQueueSignal(queue2, fence, 2u); // error + EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) + .Times(1); + FlushClient(); // Fence value should be unchanged. FlushClient(); FlushServer(); - EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u); + EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u); // Signaling with 2 on the correct queue should succeed DoQueueSignal(2u); // success FlushClient(); FlushServer(); - EXPECT_EQ(dawnFenceGetCompletedValue(fence), 2u); + EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 2u); } diff --git a/third_party/dawn/src/tests/unittests/wire/WireInjectTextureTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireInjectTextureTests.cpp index 9cf04796b13..9e327cf5b13 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireInjectTextureTests.cpp +++ b/third_party/dawn/src/tests/unittests/wire/WireInjectTextureTests.cpp @@ -32,13 +32,13 @@ class WireInjectTextureTests : public WireTest { TEST_F(WireInjectTextureTests, CallAfterReserveInject) { ReservedTexture reservation = GetWireClient()->ReserveTexture(device); - DawnTexture apiTexture = api.GetNewTexture(); + WGPUTexture apiTexture = api.GetNewTexture(); EXPECT_CALL(api, TextureReference(apiTexture)); ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation)); - dawnTextureCreateDefaultView(reservation.texture); - DawnTextureView apiDummyView = api.GetNewTextureView(); - EXPECT_CALL(api, TextureCreateDefaultView(apiTexture)).WillOnce(Return(apiDummyView)); + wgpuTextureCreateView(reservation.texture, nullptr); + WGPUTextureView apiDummyView = api.GetNewTextureView(); + EXPECT_CALL(api, TextureCreateView(apiTexture, nullptr)).WillOnce(Return(apiDummyView)); FlushClient(); } @@ -55,7 +55,7 @@ TEST_F(WireInjectTextureTests, ReserveDifferentIDs) { TEST_F(WireInjectTextureTests, InjectExistingID) { ReservedTexture reservation = GetWireClient()->ReserveTexture(device); - DawnTexture apiTexture = api.GetNewTexture(); + WGPUTexture apiTexture = api.GetNewTexture(); EXPECT_CALL(api, TextureReference(apiTexture)); ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation)); @@ -69,12 +69,12 @@ TEST_F(WireInjectTextureTests, InjectedTextureLifetime) { ReservedTexture reservation = GetWireClient()->ReserveTexture(device); // Injecting the texture adds a reference - DawnTexture apiTexture = api.GetNewTexture(); + WGPUTexture apiTexture = api.GetNewTexture(); EXPECT_CALL(api, TextureReference(apiTexture)); ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id, reservation.generation)); // Releasing the texture removes a single reference. - dawnTextureRelease(reservation.texture); + wgpuTextureRelease(reservation.texture); EXPECT_CALL(api, TextureRelease(apiTexture)); FlushClient(); diff --git a/third_party/dawn/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp new file mode 100644 index 00000000000..6c8179b8b55 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp @@ -0,0 +1,1062 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/wire/WireTest.h" + +#include "dawn_wire/client/ClientMemoryTransferService_mock.h" +#include "dawn_wire/server/ServerMemoryTransferService_mock.h" + +using namespace testing; +using namespace dawn_wire; + +namespace { + + // Mock classes to add expectations on the wire calling callbacks + class MockBufferMapReadCallback { + public: + MOCK_METHOD(void, + Call, + (WGPUBufferMapAsyncStatus status, + const uint32_t* ptr, + uint64_t dataLength, + void* userdata)); + }; + + std::unique_ptr> mockBufferMapReadCallback; + void ToMockBufferMapReadCallback(WGPUBufferMapAsyncStatus status, + const void* ptr, + uint64_t dataLength, + void* userdata) { + // Assume the data is uint32_t to make writing matchers easier + mockBufferMapReadCallback->Call(status, static_cast(ptr), dataLength, + userdata); + } + + class MockBufferMapWriteCallback { + public: + MOCK_METHOD( + void, + Call, + (WGPUBufferMapAsyncStatus status, uint32_t* ptr, uint64_t dataLength, void* userdata)); + }; + + std::unique_ptr> mockBufferMapWriteCallback; + void ToMockBufferMapWriteCallback(WGPUBufferMapAsyncStatus status, + void* ptr, + uint64_t dataLength, + void* userdata) { + // Assume the data is uint32_t to make writing matchers easier + mockBufferMapWriteCallback->Call(status, static_cast(ptr), dataLength, userdata); + } + + class MockBufferCreateMappedCallback { + public: + MOCK_METHOD(void, + Call, + (WGPUBufferMapAsyncStatus status, + WGPUBuffer buffer, + uint32_t* ptr, + uint64_t dataLength, + void* userdata)); + }; + +} // anonymous namespace + +// WireMemoryTransferServiceTests test the MemoryTransferService with buffer mapping. +// They test the basic success and error cases for buffer mapping, and they test +// mocked failures of each fallible MemoryTransferService method that an embedder +// could implement. +// The test harness defines multiple helpers for expecting operations on Read/Write handles +// and for mocking failures. The helpers are designed such that for a given run of a test, +// a Serialization expection has a corresponding Deserialization expectation for which the +// serialized data must match. +// There are tests which check for Success for every mapping operation which mock an entire mapping +// operation from map to unmap, and add all MemoryTransferService expectations. +// Tests which check for errors perform the same mapping operations but insert mocked failures for +// various mapping or MemoryTransferService operations. +class WireMemoryTransferServiceTests : public WireTest { + public: + WireMemoryTransferServiceTests() { + } + ~WireMemoryTransferServiceTests() override = default; + + client::MemoryTransferService* GetClientMemoryTransferService() override { + return &clientMemoryTransferService; + } + + server::MemoryTransferService* GetServerMemoryTransferService() override { + return &serverMemoryTransferService; + } + + void SetUp() override { + WireTest::SetUp(); + + mockBufferMapReadCallback = std::make_unique>(); + mockBufferMapWriteCallback = std::make_unique>(); + + // TODO(enga): Make this thread-safe. + mBufferContent++; + mMappedBufferContent = 0; + mUpdatedBufferContent++; + mSerializeCreateInfo++; + mSerializeInitialDataInfo++; + mSerializeFlushInfo++; + } + + void TearDown() override { + WireTest::TearDown(); + + // Delete mocks so that expectations are checked + mockBufferMapReadCallback = nullptr; + mockBufferMapWriteCallback = nullptr; + } + + void FlushClient(bool success = true) { + WireTest::FlushClient(success); + Mock::VerifyAndClearExpectations(&serverMemoryTransferService); + } + + void FlushServer(bool success = true) { + WireTest::FlushServer(success); + + Mock::VerifyAndClearExpectations(&mockBufferMapReadCallback); + Mock::VerifyAndClearExpectations(&mockBufferMapWriteCallback); + Mock::VerifyAndClearExpectations(&clientMemoryTransferService); + } + + protected: + using ClientReadHandle = client::MockMemoryTransferService::MockReadHandle; + using ServerReadHandle = server::MockMemoryTransferService::MockReadHandle; + using ClientWriteHandle = client::MockMemoryTransferService::MockWriteHandle; + using ServerWriteHandle = server::MockMemoryTransferService::MockWriteHandle; + + std::pair CreateBuffer() { + WGPUBufferDescriptor descriptor = {}; + descriptor.size = kBufferSize; + + WGPUBuffer apiBuffer = api.GetNewBuffer(); + WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); + + EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) + .WillOnce(Return(apiBuffer)) + .RetiresOnSaturation(); + + return std::make_pair(apiBuffer, buffer); + } + + std::pair CreateBufferMapped() { + WGPUBufferDescriptor descriptor = {}; + descriptor.size = sizeof(mBufferContent); + + WGPUBuffer apiBuffer = api.GetNewBuffer(); + + WGPUCreateBufferMappedResult apiResult; + apiResult.buffer = apiBuffer; + apiResult.data = reinterpret_cast(&mMappedBufferContent); + apiResult.dataLength = sizeof(mMappedBufferContent); + + WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor); + + EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _)) + .WillOnce(Return(apiResult)) + .RetiresOnSaturation(); + + return std::make_pair(apiResult, result); + } + + ClientReadHandle* ExpectReadHandleCreation() { + // Create the handle first so we can use it in later expectations. + ClientReadHandle* handle = clientMemoryTransferService.NewReadHandle(); + + EXPECT_CALL(clientMemoryTransferService, OnCreateReadHandle(sizeof(mBufferContent))) + .WillOnce(InvokeWithoutArgs([=]() { return handle; })); + + return handle; + } + + void MockReadHandleCreationFailure() { + EXPECT_CALL(clientMemoryTransferService, OnCreateReadHandle(sizeof(mBufferContent))) + .WillOnce(InvokeWithoutArgs([=]() { return nullptr; })); + } + + void ExpectReadHandleSerialization(ClientReadHandle* handle) { + EXPECT_CALL(clientMemoryTransferService, OnReadHandleSerializeCreateSize(handle)) + .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeCreateInfo); })); + EXPECT_CALL(clientMemoryTransferService, OnReadHandleSerializeCreate(handle, _)) + .WillOnce(WithArg<1>([&](void* serializePointer) { + memcpy(serializePointer, &mSerializeCreateInfo, sizeof(mSerializeCreateInfo)); + return sizeof(mSerializeCreateInfo); + })); + } + + ServerReadHandle* ExpectServerReadHandleDeserialize() { + // Create the handle first so we can use it in later expectations. + ServerReadHandle* handle = serverMemoryTransferService.NewReadHandle(); + + EXPECT_CALL(serverMemoryTransferService, + OnDeserializeReadHandle(Pointee(Eq(mSerializeCreateInfo)), + sizeof(mSerializeCreateInfo), _)) + .WillOnce(WithArg<2>([=](server::MemoryTransferService::ReadHandle** readHandle) { + *readHandle = handle; + return true; + })); + + return handle; + } + + void MockServerReadHandleDeserializeFailure() { + EXPECT_CALL(serverMemoryTransferService, + OnDeserializeReadHandle(Pointee(Eq(mSerializeCreateInfo)), + sizeof(mSerializeCreateInfo), _)) + .WillOnce(InvokeWithoutArgs([&]() { return false; })); + } + + void ExpectServerReadHandleInitialize(ServerReadHandle* handle) { + EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeInitialDataSize(handle, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeInitialDataInfo); })); + EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeInitialData(handle, _, _, _)) + .WillOnce(WithArg<3>([&](void* serializePointer) { + memcpy(serializePointer, &mSerializeInitialDataInfo, + sizeof(mSerializeInitialDataInfo)); + return sizeof(mSerializeInitialDataInfo); + })); + } + + void ExpectClientReadHandleDeserializeInitialize(ClientReadHandle* handle, + uint32_t* mappedData) { + EXPECT_CALL(clientMemoryTransferService, OnReadHandleDeserializeInitialData( + handle, Pointee(Eq(mSerializeInitialDataInfo)), + sizeof(mSerializeInitialDataInfo), _, _)) + .WillOnce(WithArgs<3, 4>([=](const void** data, size_t* dataLength) { + *data = mappedData; + *dataLength = sizeof(*mappedData); + return true; + })); + } + + void MockClientReadHandleDeserializeInitializeFailure(ClientReadHandle* handle) { + EXPECT_CALL(clientMemoryTransferService, OnReadHandleDeserializeInitialData( + handle, Pointee(Eq(mSerializeInitialDataInfo)), + sizeof(mSerializeInitialDataInfo), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { return false; })); + } + + ClientWriteHandle* ExpectWriteHandleCreation() { + // Create the handle first so we can use it in later expectations. + ClientWriteHandle* handle = clientMemoryTransferService.NewWriteHandle(); + + EXPECT_CALL(clientMemoryTransferService, OnCreateWriteHandle(sizeof(mBufferContent))) + .WillOnce(InvokeWithoutArgs([=]() { return handle; })); + + return handle; + } + + void MockWriteHandleCreationFailure() { + EXPECT_CALL(clientMemoryTransferService, OnCreateWriteHandle(sizeof(mBufferContent))) + .WillOnce(InvokeWithoutArgs([=]() { return nullptr; })); + } + + void ExpectWriteHandleSerialization(ClientWriteHandle* handle) { + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeCreateSize(handle)) + .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeCreateInfo); })); + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeCreate(handle, _)) + .WillOnce(WithArg<1>([&](void* serializePointer) { + memcpy(serializePointer, &mSerializeCreateInfo, sizeof(mSerializeCreateInfo)); + return sizeof(mSerializeCreateInfo); + })); + } + + ServerWriteHandle* ExpectServerWriteHandleDeserialization() { + // Create the handle first so it can be used in later expectations. + ServerWriteHandle* handle = serverMemoryTransferService.NewWriteHandle(); + + EXPECT_CALL(serverMemoryTransferService, + OnDeserializeWriteHandle(Pointee(Eq(mSerializeCreateInfo)), + sizeof(mSerializeCreateInfo), _)) + .WillOnce(WithArg<2>([=](server::MemoryTransferService::WriteHandle** writeHandle) { + *writeHandle = handle; + return true; + })); + + return handle; + } + + void MockServerWriteHandleDeserializeFailure() { + EXPECT_CALL(serverMemoryTransferService, + OnDeserializeWriteHandle(Pointee(Eq(mSerializeCreateInfo)), + sizeof(mSerializeCreateInfo), _)) + .WillOnce(InvokeWithoutArgs([&]() { return false; })); + } + + void ExpectClientWriteHandleOpen(ClientWriteHandle* handle, uint32_t* mappedData) { + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleOpen(handle)) + .WillOnce(InvokeWithoutArgs( + [=]() { return std::make_pair(mappedData, sizeof(*mappedData)); })); + } + + void MockClientWriteHandleOpenFailure(ClientWriteHandle* handle) { + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleOpen(handle)) + .WillOnce(InvokeWithoutArgs([&]() { return std::make_pair(nullptr, 0); })); + } + + void ExpectClientWriteHandleSerializeFlush(ClientWriteHandle* handle) { + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeFlushSize(handle)) + .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeFlushInfo); })); + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeFlush(handle, _)) + .WillOnce(WithArg<1>([&](void* serializePointer) { + memcpy(serializePointer, &mSerializeFlushInfo, sizeof(mSerializeFlushInfo)); + return sizeof(mSerializeFlushInfo); + })); + } + + void ExpectServerWriteHandleDeserializeFlush(ServerWriteHandle* handle, uint32_t expectedData) { + EXPECT_CALL(serverMemoryTransferService, + OnWriteHandleDeserializeFlush(handle, Pointee(Eq(mSerializeFlushInfo)), + sizeof(mSerializeFlushInfo))) + .WillOnce(InvokeWithoutArgs([=]() { + // The handle data should be updated. + EXPECT_EQ(*handle->GetData(), expectedData); + return true; + })); + } + + void MockServerWriteHandleDeserializeFlushFailure(ServerWriteHandle* handle) { + EXPECT_CALL(serverMemoryTransferService, + OnWriteHandleDeserializeFlush(handle, Pointee(Eq(mSerializeFlushInfo)), + sizeof(mSerializeFlushInfo))) + .WillOnce(InvokeWithoutArgs([&]() { return false; })); + } + + // Arbitrary values used within tests to check if serialized data is correctly passed + // between the client and server. The static data changes between runs of the tests and + // test expectations will check that serialized values are passed to the respective + // deserialization function. + static uint32_t mSerializeCreateInfo; + static uint32_t mSerializeInitialDataInfo; + static uint32_t mSerializeFlushInfo; + + // Represents the buffer contents for the test. + static uint32_t mBufferContent; + + static constexpr size_t kBufferSize = sizeof(mBufferContent); + + // The client's zero-initialized buffer for writing. + uint32_t mMappedBufferContent = 0; + + // |mMappedBufferContent| should be set equal to |mUpdatedBufferContent| when the client + // performs a write. Test expectations should check that |mBufferContent == + // mUpdatedBufferContent| after all writes are flushed. + static uint32_t mUpdatedBufferContent; + + testing::StrictMock serverMemoryTransferService; + testing::StrictMock clientMemoryTransferService; +}; + +uint32_t WireMemoryTransferServiceTests::mBufferContent = 1337; +uint32_t WireMemoryTransferServiceTests::mUpdatedBufferContent = 2349; +uint32_t WireMemoryTransferServiceTests::mSerializeCreateInfo = 4242; +uint32_t WireMemoryTransferServiceTests::mSerializeInitialDataInfo = 1394; +uint32_t WireMemoryTransferServiceTests::mSerializeFlushInfo = 1235; + +// Test successful MapRead. +TEST_F(WireMemoryTransferServiceTests, BufferMapReadSuccess) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // The client should create and serialize a ReadHandle on mapReadAsync. + ClientReadHandle* clientHandle = ExpectReadHandleCreation(); + ExpectReadHandleSerialization(clientHandle); + + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + + // The server should deserialize the MapRead handle from the client and then serialize + // an initialization message. + ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); + ExpectServerReadHandleInitialize(serverHandle); + + // Mock a successful callback + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&mBufferContent)); + + FlushClient(); + + // The client receives a successful callback. + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, &mBufferContent, sizeof(mBufferContent), _)) + .Times(1); + + // The client should receive the handle initialization message from the server. + ExpectClientReadHandleDeserializeInitialize(clientHandle, &mBufferContent); + + FlushServer(); + + // The handle is destroyed once the buffer is unmapped. + EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); + wgpuBufferUnmap(buffer); + + EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + + FlushClient(); +} + +// Test unsuccessful MapRead. +TEST_F(WireMemoryTransferServiceTests, BufferMapReadError) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // The client should create and serialize a ReadHandle on mapReadAsync. + ClientReadHandle* clientHandle = ExpectReadHandleCreation(); + ExpectReadHandleSerialization(clientHandle); + + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + + // The server should deserialize the ReadHandle from the client. + ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); + + // Mock a failed callback. + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); + })); + + // Since the mapping failed, the handle is immediately destroyed. + EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); + + FlushClient(); + + // The client receives an error callback. + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) + .Times(1); + + // The client receives the map failure and destroys the handle. + EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); + + FlushServer(); + + wgpuBufferUnmap(buffer); + + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + + FlushClient(); +} + +// Test MapRead ReadHandle creation failure. +TEST_F(WireMemoryTransferServiceTests, BufferMapReadHandleCreationFailure) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // Mock a ReadHandle creation failure + MockReadHandleCreationFailure(); + + // Failed creation of a ReadHandle is a mapping failure and the client synchronously receives + // an error callback. + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) + .Times(1); + + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); +} + +// Test MapRead DeserializeReadHandle failure. +TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeReadHandleFailure) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // The client should create and serialize a ReadHandle on mapReadAsync. + ClientReadHandle* clientHandle = ExpectReadHandleCreation(); + ExpectReadHandleSerialization(clientHandle); + + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + + // Mock a Deserialization failure. + MockServerReadHandleDeserializeFailure(); + + FlushClient(false); + + // The server received a fatal failure and the client callback was never returned. + // It is called when the wire is destructed. + EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, _)) + .Times(1); + + EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); +} + +// Test MapRead DeserializeInitialData failure. +TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeInitialDataFailure) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // The client should create and serialize a ReadHandle on mapReadAsync. + ClientReadHandle* clientHandle = ExpectReadHandleCreation(); + ExpectReadHandleSerialization(clientHandle); + + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + + // The server should deserialize the MapRead handle from the client and then serialize + // an initialization message. + ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); + ExpectServerReadHandleInitialize(serverHandle); + + // Mock a successful callback + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&mBufferContent)); + + FlushClient(); + + // The client should receive the handle initialization message from the server. + // Mock a deserialization failure. + MockClientReadHandleDeserializeInitializeFailure(clientHandle); + + // Failed deserialization is a fatal failure and the client synchronously receives a + // DEVICE_LOST callback. + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _)) + .Times(1); + + // The handle will be destroyed since deserializing failed. + EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); + + FlushServer(false); + + EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); +} + +// Test MapRead destroying the buffer before unmapping on the client side. +TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroyBeforeUnmap) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // The client should create and serialize a ReadHandle on mapReadAsync. + ClientReadHandle* clientHandle = ExpectReadHandleCreation(); + ExpectReadHandleSerialization(clientHandle); + + wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); + + // The server should deserialize the MapRead handle from the client and then serialize + // an initialization message. + ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); + ExpectServerReadHandleInitialize(serverHandle); + + // Mock a successful callback + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&mBufferContent)); + + FlushClient(); + + // The client receives a successful callback. + EXPECT_CALL(*mockBufferMapReadCallback, + Call(WGPUBufferMapAsyncStatus_Success, &mBufferContent, sizeof(mBufferContent), _)) + .Times(1); + + // The client should receive the handle initialization message from the server. + ExpectClientReadHandleDeserializeInitialize(clientHandle, &mBufferContent); + + FlushServer(); + + // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping + // immediately, both in the client and server side. + { + EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); + wgpuBufferDestroy(buffer); + + EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); + EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1); + FlushClient(); + + // The handle is already destroyed so unmap only results in a server unmap call. + wgpuBufferUnmap(buffer); + + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + FlushClient(); + } +} + +// Test successful MapWrite. +TEST_F(WireMemoryTransferServiceTests, BufferMapWriteSuccess) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + ExpectWriteHandleSerialization(clientHandle); + + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + + // The server should then deserialize the WriteHandle from the client. + ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); + + // Mock a successful callback. + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&mMappedBufferContent)); + + FlushClient(); + + // The client receives a successful callback. + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(WGPUBufferMapAsyncStatus_Success, &mMappedBufferContent, + sizeof(mMappedBufferContent), _)) + .Times(1); + + // Since the mapping succeeds, the client opens the WriteHandle. + ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent); + + FlushServer(); + + // The client writes to the handle contents. + mMappedBufferContent = mUpdatedBufferContent; + + // The client will then flush and destroy the handle on Unmap() + ExpectClientWriteHandleSerializeFlush(clientHandle); + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + + wgpuBufferUnmap(buffer); + + // The server deserializes the Flush message. + ExpectServerWriteHandleDeserializeFlush(serverHandle, mUpdatedBufferContent); + + // After the handle is updated it can be destroyed. + EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + + FlushClient(); +} + +// Test unsuccessful MapWrite. +TEST_F(WireMemoryTransferServiceTests, BufferMapWriteError) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // The client should create and serialize a WriteHandle on mapWriteAsync. + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + ExpectWriteHandleSerialization(clientHandle); + + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + + // The server should then deserialize the WriteHandle from the client. + ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); + + // Mock an error callback. + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); + })); + + // Since the mapping fails, the handle is immediately destroyed because it won't be written. + EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); + + FlushClient(); + + // The client receives an error callback. + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) + .Times(1); + + // Client receives the map failure and destroys the handle. + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + + FlushServer(); + + wgpuBufferUnmap(buffer); + + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + + FlushClient(); +} + +// Test MapRead WriteHandle creation failure. +TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleCreationFailure) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // Mock a WriteHandle creation failure + MockWriteHandleCreationFailure(); + + // Failed creation of a WriteHandle is a mapping failure and the client synchronously receives + // an error callback. + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _)) + .Times(1); + + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); +} + +// Test MapWrite DeserializeWriteHandle failure. +TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeWriteHandleFailure) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + // The client should create and serialize a WriteHandle on mapWriteAsync. + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + ExpectWriteHandleSerialization(clientHandle); + + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + + // Mock a deserialization failure. + MockServerWriteHandleDeserializeFailure(); + + FlushClient(false); + + // The server hit a fatal failure and never returned the callback. The client callback is + // called when the wire is destructed. + EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, _)) + .Times(1); + + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); +} + +// Test MapWrite handle Open failure. +TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleOpenFailure) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + ExpectWriteHandleSerialization(clientHandle); + + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + + // The server should then deserialize the WriteHandle from the client. + ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); + + // Mock a successful callback. + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&mMappedBufferContent)); + + FlushClient(); + + // Since the mapping succeeds, the client opens the WriteHandle. + // Mock a failure. + MockClientWriteHandleOpenFailure(clientHandle); + + // Failing to open a handle is a fatal failure and the client receives a DEVICE_LOST callback. + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _)) + .Times(1); + + // Since opening the handle fails, it gets destroyed immediately. + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + + FlushServer(false); + + EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); +} + +// Test MapWrite DeserializeFlush failure. +TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeFlushFailure) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + ExpectWriteHandleSerialization(clientHandle); + + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + + // The server should then deserialize the WriteHandle from the client. + ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); + + // Mock a successful callback. + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&mMappedBufferContent)); + + FlushClient(); + + // The client receives a success callback. + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(WGPUBufferMapAsyncStatus_Success, &mMappedBufferContent, + sizeof(mMappedBufferContent), _)) + .Times(1); + + // Since the mapping succeeds, the client opens the WriteHandle. + ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent); + + FlushServer(); + + // The client writes to the handle contents. + mMappedBufferContent = mUpdatedBufferContent; + + // The client will then flush and destroy the handle on Unmap() + ExpectClientWriteHandleSerializeFlush(clientHandle); + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + + wgpuBufferUnmap(buffer); + + // The server deserializes the Flush message. Mock a deserialization failure. + MockServerWriteHandleDeserializeFlushFailure(serverHandle); + + FlushClient(false); + + EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); +} + +// Test MapWrite destroying the buffer before unmapping on the client side. +TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroyBeforeUnmap) { + WGPUBuffer buffer; + WGPUBuffer apiBuffer; + std::tie(apiBuffer, buffer) = CreateBuffer(); + FlushClient(); + + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + ExpectWriteHandleSerialization(clientHandle); + + wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); + + // The server should then deserialize the WriteHandle from the client. + ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); + + // Mock a successful callback. + EXPECT_CALL(api, OnBufferMapAsyncCallback(apiBuffer, _, _)).WillOnce(InvokeWithoutArgs([&]() { + api.CallMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) + .WillOnce(Return(&mMappedBufferContent)); + + FlushClient(); + + // The client receives a successful callback. + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(WGPUBufferMapAsyncStatus_Success, &mMappedBufferContent, + sizeof(mMappedBufferContent), _)) + .Times(1); + + // Since the mapping succeeds, the client opens the WriteHandle. + ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent); + + FlushServer(); + + // The client writes to the handle contents. + mMappedBufferContent = mUpdatedBufferContent; + + // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping + // immediately, both in the client and server side. + { + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + wgpuBufferDestroy(buffer); + + EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); + EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1); + FlushClient(); + + // The handle is already destroyed so unmap only results in a server unmap call. + wgpuBufferUnmap(buffer); + + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + FlushClient(); + } +} + +// Test successful CreateBufferMapped. +TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedSuccess) { + // The client should create and serialize a WriteHandle on createBufferMapped. + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + + // Staging data is immediately available so the handle is Opened. + ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent); + + ExpectWriteHandleSerialization(clientHandle); + + // The server should then deserialize the WriteHandle from the client. + ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); + + WGPUCreateBufferMappedResult result; + WGPUCreateBufferMappedResult apiResult; + std::tie(apiResult, result) = CreateBufferMapped(); + FlushClient(); + + // Update the mapped contents. + mMappedBufferContent = mUpdatedBufferContent; + + // When the client Unmaps the buffer, it will flush writes to the handle and destroy it. + ExpectClientWriteHandleSerializeFlush(clientHandle); + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + + wgpuBufferUnmap(result.buffer); + + // The server deserializes the Flush message. + ExpectServerWriteHandleDeserializeFlush(serverHandle, mUpdatedBufferContent); + + // After the handle is updated it can be destroyed. + EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); + EXPECT_CALL(api, BufferUnmap(apiResult.buffer)).Times(1); + + FlushClient(); +} + +// Test CreateBufferMapped WriteHandle creation failure. +TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedWriteHandleCreationFailure) { + // Mock a WriteHandle creation failure + MockWriteHandleCreationFailure(); + + WGPUBufferDescriptor descriptor = {}; + descriptor.size = sizeof(mBufferContent); + + WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor); + + // TODO(enga): Check that the client generated a context lost. + EXPECT_EQ(result.data, nullptr); + EXPECT_EQ(result.dataLength, 0u); +} + +// Test CreateBufferMapped DeserializeWriteHandle failure. +TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedDeserializeWriteHandleFailure) { + // The client should create and serialize a WriteHandle on createBufferMapped. + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + + // Staging data is immediately available so the handle is Opened. + ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent); + + ExpectWriteHandleSerialization(clientHandle); + + // The server should then deserialize the WriteHandle from the client. + MockServerWriteHandleDeserializeFailure(); + + WGPUCreateBufferMappedResult result; + WGPUCreateBufferMappedResult apiResult; + std::tie(apiResult, result) = CreateBufferMapped(); + FlushClient(false); + + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); +} + +// Test CreateBufferMapped handle Open failure. +TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedHandleOpenFailure) { + // The client should create a WriteHandle on createBufferMapped. + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + + // Staging data is immediately available so the handle is Opened. + // Mock a failure. + MockClientWriteHandleOpenFailure(clientHandle); + + // Since synchronous opening of the handle failed, it is destroyed immediately. + // Note: The handle is not serialized because sychronously opening it failed. + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + + WGPUBufferDescriptor descriptor = {}; + descriptor.size = sizeof(mBufferContent); + + WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor); + + // TODO(enga): Check that the client generated a context lost. + EXPECT_EQ(result.data, nullptr); + EXPECT_EQ(result.dataLength, 0u); +} + +// Test CreateBufferMapped DeserializeFlush failure. +TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedDeserializeFlushFailure) { + // The client should create and serialize a WriteHandle on createBufferMapped. + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + + // Staging data is immediately available so the handle is Opened. + ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent); + + ExpectWriteHandleSerialization(clientHandle); + + // The server should then deserialize the WriteHandle from the client. + ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); + + WGPUCreateBufferMappedResult result; + WGPUCreateBufferMappedResult apiResult; + std::tie(apiResult, result) = CreateBufferMapped(); + FlushClient(); + + // Update the mapped contents. + mMappedBufferContent = mUpdatedBufferContent; + + // When the client Unmaps the buffer, it will flush writes to the handle and destroy it. + ExpectClientWriteHandleSerializeFlush(clientHandle); + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + + wgpuBufferUnmap(result.buffer); + + // The server deserializes the Flush message. Mock a deserialization failure. + MockServerWriteHandleDeserializeFlushFailure(serverHandle); + + FlushClient(false); + + EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); +} + +// Test CreateBufferMapped destroying the buffer before unmapping on the client side. +TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedDestroyBeforeUnmap) { + // The client should create and serialize a WriteHandle on createBufferMapped. + ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(); + + // Staging data is immediately available so the handle is Opened. + ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent); + + ExpectWriteHandleSerialization(clientHandle); + + // The server should then deserialize the WriteHandle from the client. + ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); + + WGPUCreateBufferMappedResult result; + WGPUCreateBufferMappedResult apiResult; + std::tie(apiResult, result) = CreateBufferMapped(); + FlushClient(); + + // Update the mapped contents. + mMappedBufferContent = mUpdatedBufferContent; + + // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping + // immediately, both in the client and server side. + { + EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); + wgpuBufferDestroy(result.buffer); + + EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); + EXPECT_CALL(api, BufferDestroy(apiResult.buffer)).Times(1); + FlushClient(); + + // The handle is already destroyed so unmap only results in a server unmap call. + wgpuBufferUnmap(result.buffer); + + EXPECT_CALL(api, BufferUnmap(apiResult.buffer)).Times(1); + FlushClient(); + } +} diff --git a/third_party/dawn/src/tests/unittests/wire/WireMultipleDeviceTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireMultipleDeviceTests.cpp new file mode 100644 index 00000000000..3ba0da0ab4e --- /dev/null +++ b/third_party/dawn/src/tests/unittests/wire/WireMultipleDeviceTests.cpp @@ -0,0 +1,293 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/unittests/wire/WireTest.h" + +#include "common/Assert.h" +#include "dawn/dawn_proc.h" +#include "dawn_wire/WireClient.h" +#include "dawn_wire/WireServer.h" +#include "tests/MockCallback.h" +#include "utils/TerribleCommandBuffer.h" + +#include + +using namespace testing; +using namespace dawn_wire; + +class WireMultipleDeviceTests : public testing::Test { + protected: + void SetUp() override { + DawnProcTable procs = dawn_wire::WireClient::GetProcs(); + dawnProcSetProcs(&procs); + } + + void TearDown() override { + dawnProcSetProcs(nullptr); + } + + class WireHolder { + public: + WireHolder() { + DawnProcTable mockProcs; + mApi.GetProcTableAndDevice(&mockProcs, &mServerDevice); + + // Ignore Tick() + EXPECT_CALL(mApi, DeviceTick(_)).Times(AnyNumber()); + + // This SetCallback call cannot be ignored because it is done as soon as we start the + // server + EXPECT_CALL(mApi, OnDeviceSetUncapturedErrorCallback(_, _, _)).Times(Exactly(1)); + EXPECT_CALL(mApi, OnDeviceSetDeviceLostCallback(_, _, _)).Times(Exactly(1)); + + mS2cBuf = std::make_unique(); + mC2sBuf = std::make_unique(); + + WireServerDescriptor serverDesc = {}; + serverDesc.device = mServerDevice; + serverDesc.procs = &mockProcs; + serverDesc.serializer = mS2cBuf.get(); + + mWireServer.reset(new WireServer(serverDesc)); + mC2sBuf->SetHandler(mWireServer.get()); + + WireClientDescriptor clientDesc = {}; + clientDesc.serializer = mC2sBuf.get(); + + mWireClient.reset(new WireClient(clientDesc)); + mS2cBuf->SetHandler(mWireClient.get()); + + mClientDevice = mWireClient->GetDevice(); + + // The GetDefaultQueue is done on WireClient startup so we expect it now. + mClientQueue = wgpuDeviceGetDefaultQueue(mClientDevice); + mServerQueue = mApi.GetNewQueue(); + EXPECT_CALL(mApi, DeviceGetDefaultQueue(mServerDevice)).WillOnce(Return(mServerQueue)); + FlushClient(); + } + + ~WireHolder() { + mApi.IgnoreAllReleaseCalls(); + mWireClient = nullptr; + mWireServer = nullptr; + } + + void FlushClient(bool success = true) { + ASSERT_EQ(mC2sBuf->Flush(), success); + } + + void FlushServer(bool success = true) { + ASSERT_EQ(mS2cBuf->Flush(), success); + } + + testing::StrictMock* Api() { + return &mApi; + } + + WGPUDevice ClientDevice() { + return mClientDevice; + } + + WGPUDevice ServerDevice() { + return mServerDevice; + } + + WGPUQueue ClientQueue() { + return mClientQueue; + } + + WGPUQueue ServerQueue() { + return mServerQueue; + } + + private: + testing::StrictMock mApi; + std::unique_ptr mWireServer; + std::unique_ptr mWireClient; + std::unique_ptr mS2cBuf; + std::unique_ptr mC2sBuf; + WGPUDevice mServerDevice; + WGPUDevice mClientDevice; + WGPUQueue mServerQueue; + WGPUQueue mClientQueue; + }; + + void ExpectInjectedError(WireHolder* wire) { + std::string errorMessage; + EXPECT_CALL(*wire->Api(), + DeviceInjectError(wire->ServerDevice(), WGPUErrorType_Validation, _)) + .WillOnce(Invoke([&](WGPUDevice device, WGPUErrorType type, const char* message) { + errorMessage = message; + // Mock the call to the error callback. + wire->Api()->CallDeviceErrorCallback(device, type, message); + })); + wire->FlushClient(); + + // The error callback should be forwarded to the client. + StrictMock> mockErrorCallback; + wgpuDeviceSetUncapturedErrorCallback(wire->ClientDevice(), mockErrorCallback.Callback(), + mockErrorCallback.MakeUserdata(this)); + + EXPECT_CALL(mockErrorCallback, Call(WGPUErrorType_Validation, StrEq(errorMessage), this)) + .Times(1); + wire->FlushServer(); + } +}; + +// Test that using objects from a different device is a validation error. +TEST_F(WireMultipleDeviceTests, ValidatesSameDevice) { + WireHolder wireA; + WireHolder wireB; + + // Create the fence + WGPUFenceDescriptor desc = {}; + WGPUFence fenceA = wgpuQueueCreateFence(wireA.ClientQueue(), &desc); + + // Signal with a fence from a different wire. + wgpuQueueSignal(wireB.ClientQueue(), fenceA, 1u); + + // We should inject an error into the server. + ExpectInjectedError(&wireB); +} + +// Test that objects created from mixed devices are an error to use. +TEST_F(WireMultipleDeviceTests, DifferentDeviceObjectCreationIsError) { + WireHolder wireA; + WireHolder wireB; + + // Create a bind group layout on wire A. + WGPUBindGroupLayoutDescriptor bglDesc = {}; + WGPUBindGroupLayout bglA = wgpuDeviceCreateBindGroupLayout(wireA.ClientDevice(), &bglDesc); + EXPECT_CALL(*wireA.Api(), DeviceCreateBindGroupLayout(wireA.ServerDevice(), _)) + .WillOnce(Return(wireA.Api()->GetNewBindGroupLayout())); + + wireA.FlushClient(); + + std::array entries = {}; + + // Create a buffer on wire A. + WGPUBufferDescriptor bufferDesc = {}; + entries[0].buffer = wgpuDeviceCreateBuffer(wireA.ClientDevice(), &bufferDesc); + EXPECT_CALL(*wireA.Api(), DeviceCreateBuffer(wireA.ServerDevice(), _)) + .WillOnce(Return(wireA.Api()->GetNewBuffer())); + + wireA.FlushClient(); + + // Create a sampler on wire B. + WGPUSamplerDescriptor samplerDesc = {}; + entries[1].sampler = wgpuDeviceCreateSampler(wireB.ClientDevice(), &samplerDesc); + EXPECT_CALL(*wireB.Api(), DeviceCreateSampler(wireB.ServerDevice(), _)) + .WillOnce(Return(wireB.Api()->GetNewSampler())); + + wireB.FlushClient(); + + // Create a bind group on wire A using the bgl (A), buffer (A), and sampler (B). + WGPUBindGroupDescriptor bgDesc = {}; + bgDesc.layout = bglA; + bgDesc.entryCount = entries.size(); + bgDesc.entries = entries.data(); + WGPUBindGroup bindGroupA = wgpuDeviceCreateBindGroup(wireA.ClientDevice(), &bgDesc); + + // It should inject an error because the sampler is from a different device. + ExpectInjectedError(&wireA); + + // The bind group was never created on a server because it failed device validation. + // Any commands that use it should error. + wgpuBindGroupRelease(bindGroupA); + wireA.FlushClient(false); +} + +// Test that using objects, included in an extension struct, +// from a difference device is a validation error. +TEST_F(WireMultipleDeviceTests, ValidatesSameDeviceInExtensionStruct) { + WireHolder wireA; + WireHolder wireB; + + WGPUShaderModuleDescriptor shaderModuleDesc = {}; + WGPUShaderModule shaderModuleA = + wgpuDeviceCreateShaderModule(wireA.ClientDevice(), &shaderModuleDesc); + + // Flush on wire A. We should see the shader module created. + EXPECT_CALL(*wireA.Api(), DeviceCreateShaderModule(wireA.ServerDevice(), _)) + .WillOnce(Return(wireA.Api()->GetNewShaderModule())); + wireA.FlushClient(); + + WGPURenderPipelineDescriptorDummyExtension extDesc = {}; + extDesc.chain.sType = WGPUSType_RenderPipelineDescriptorDummyExtension; + extDesc.dummyStage.entryPoint = "main"; + extDesc.dummyStage.module = shaderModuleA; + + WGPURenderPipelineDescriptor pipelineDesc = {}; + pipelineDesc.nextInChain = &extDesc.chain; + WGPURenderPipeline pipelineB = + wgpuDeviceCreateRenderPipeline(wireB.ClientDevice(), &pipelineDesc); + + // We should inject an error into the server. + ExpectInjectedError(&wireB); + + // The pipeline was never created on a server because it failed device validation. + // Any commands that use it should error. + wgpuRenderPipelineRelease(pipelineB); + wireB.FlushClient(false); +} + +// Test that using objects, included in a chained extension struct, +// from a different device is a validation error. +TEST_F(WireMultipleDeviceTests, ValidatesSameDeviceSecondInExtensionStructChain) { + WireHolder wireA; + WireHolder wireB; + + WGPUShaderModuleDescriptor shaderModuleDesc = {}; + WGPUShaderModule shaderModuleA = + wgpuDeviceCreateShaderModule(wireA.ClientDevice(), &shaderModuleDesc); + + // Flush on wire A. We should see the shader module created. + EXPECT_CALL(*wireA.Api(), DeviceCreateShaderModule(wireA.ServerDevice(), _)) + .WillOnce(Return(wireA.Api()->GetNewShaderModule())); + wireA.FlushClient(); + + WGPUShaderModule shaderModuleB = + wgpuDeviceCreateShaderModule(wireB.ClientDevice(), &shaderModuleDesc); + + // Flush on wire B. We should see the shader module created. + EXPECT_CALL(*wireB.Api(), DeviceCreateShaderModule(wireB.ServerDevice(), _)) + .WillOnce(Return(wireB.Api()->GetNewShaderModule())); + wireB.FlushClient(); + + WGPURenderPipelineDescriptorDummyExtension extDescA = {}; + extDescA.chain.sType = WGPUSType_RenderPipelineDescriptorDummyExtension; + extDescA.dummyStage.entryPoint = "main"; + extDescA.dummyStage.module = shaderModuleA; + + WGPURenderPipelineDescriptorDummyExtension extDescB = {}; + extDescB.chain.sType = WGPUSType_RenderPipelineDescriptorDummyExtension; + extDescB.chain.next = &extDescA.chain; + extDescB.dummyStage.entryPoint = "main"; + extDescB.dummyStage.module = shaderModuleB; + + // The first extension struct is from Device B, and the second is from A. + // We should validate the second struct, is from the same device. + WGPURenderPipelineDescriptor pipelineDesc = {}; + pipelineDesc.nextInChain = &extDescB.chain; + WGPURenderPipeline pipelineB = + wgpuDeviceCreateRenderPipeline(wireB.ClientDevice(), &pipelineDesc); + + // We should inject an error into the server. + ExpectInjectedError(&wireB); + + // The pipeline was never created on a server because it failed device validation. + // Any commands that use it should error. + wgpuRenderPipelineRelease(pipelineB); + wireB.FlushClient(false); +} diff --git a/third_party/dawn/src/tests/unittests/wire/WireOptionalTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireOptionalTests.cpp index f56b99c41b2..503d91d9fe3 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireOptionalTests.cpp +++ b/third_party/dawn/src/tests/unittests/wire/WireOptionalTests.cpp @@ -26,38 +26,36 @@ class WireOptionalTests : public WireTest { // Test passing nullptr instead of objects - object as value version TEST_F(WireOptionalTests, OptionalObjectValue) { - DawnBindGroupLayoutDescriptor bglDesc; - bglDesc.nextInChain = nullptr; - bglDesc.bindingCount = 0; - DawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDesc); + WGPUBindGroupLayoutDescriptor bglDesc = {}; + bglDesc.entryCount = 0; + WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device, &bglDesc); - DawnBindGroupLayout apiBindGroupLayout = api.GetNewBindGroupLayout(); + WGPUBindGroupLayout apiBindGroupLayout = api.GetNewBindGroupLayout(); EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)) .WillOnce(Return(apiBindGroupLayout)); // The `sampler`, `textureView` and `buffer` members of a binding are optional. - DawnBindGroupBinding binding; - binding.binding = 0; - binding.sampler = nullptr; - binding.textureView = nullptr; - binding.buffer = nullptr; - - DawnBindGroupDescriptor bgDesc; - bgDesc.nextInChain = nullptr; + WGPUBindGroupEntry entry; + entry.binding = 0; + entry.sampler = nullptr; + entry.textureView = nullptr; + entry.buffer = nullptr; + + WGPUBindGroupDescriptor bgDesc = {}; bgDesc.layout = bgl; - bgDesc.bindingCount = 1; - bgDesc.bindings = &binding; + bgDesc.entryCount = 1; + bgDesc.entries = &entry; - dawnDeviceCreateBindGroup(device, &bgDesc); + wgpuDeviceCreateBindGroup(device, &bgDesc); - DawnBindGroup apiDummyBindGroup = api.GetNewBindGroup(); + WGPUBindGroup apiDummyBindGroup = api.GetNewBindGroup(); EXPECT_CALL(api, DeviceCreateBindGroup( - apiDevice, MatchesLambda([](const DawnBindGroupDescriptor* desc) -> bool { - return desc->nextInChain == nullptr && desc->bindingCount == 1 && - desc->bindings[0].binding == 0 && - desc->bindings[0].sampler == nullptr && - desc->bindings[0].buffer == nullptr && - desc->bindings[0].textureView == nullptr; + apiDevice, MatchesLambda([](const WGPUBindGroupDescriptor* desc) -> bool { + return desc->nextInChain == nullptr && desc->entryCount == 1 && + desc->entries[0].binding == 0 && + desc->entries[0].sampler == nullptr && + desc->entries[0].buffer == nullptr && + desc->entries[0].textureView == nullptr; }))) .WillOnce(Return(apiDummyBindGroup)); @@ -67,120 +65,106 @@ TEST_F(WireOptionalTests, OptionalObjectValue) { // Test that the wire is able to send optional pointers to structures TEST_F(WireOptionalTests, OptionalStructPointer) { // Create shader module - DawnShaderModuleDescriptor vertexDescriptor; - vertexDescriptor.nextInChain = nullptr; - vertexDescriptor.codeSize = 0; - DawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor); - DawnShaderModule apiVsModule = api.GetNewShaderModule(); + WGPUShaderModuleDescriptor vertexDescriptor = {}; + WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vertexDescriptor); + WGPUShaderModule apiVsModule = api.GetNewShaderModule(); EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule)); // Create the color state descriptor - DawnBlendDescriptor blendDescriptor; - blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD; - blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE; - blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE; - DawnColorStateDescriptor colorStateDescriptor; - colorStateDescriptor.nextInChain = nullptr; - colorStateDescriptor.format = DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM; + WGPUBlendDescriptor blendDescriptor = {}; + blendDescriptor.operation = WGPUBlendOperation_Add; + blendDescriptor.srcFactor = WGPUBlendFactor_One; + blendDescriptor.dstFactor = WGPUBlendFactor_One; + WGPUColorStateDescriptor colorStateDescriptor = {}; + colorStateDescriptor.format = WGPUTextureFormat_RGBA8Unorm; colorStateDescriptor.alphaBlend = blendDescriptor; colorStateDescriptor.colorBlend = blendDescriptor; - colorStateDescriptor.writeMask = DAWN_COLOR_WRITE_MASK_ALL; + colorStateDescriptor.writeMask = WGPUColorWriteMask_All; // Create the input state - DawnVertexInputDescriptor vertexInput; - vertexInput.nextInChain = nullptr; - vertexInput.indexFormat = DAWN_INDEX_FORMAT_UINT32; - vertexInput.bufferCount = 0; - vertexInput.buffers = nullptr; + WGPUVertexStateDescriptor vertexState = {}; + vertexState.indexFormat = WGPUIndexFormat_Uint32; + vertexState.vertexBufferCount = 0; + vertexState.vertexBuffers = nullptr; // Create the rasterization state - DawnRasterizationStateDescriptor rasterizationState; - rasterizationState.nextInChain = nullptr; - rasterizationState.frontFace = DAWN_FRONT_FACE_CCW; - rasterizationState.cullMode = DAWN_CULL_MODE_NONE; + WGPURasterizationStateDescriptor rasterizationState = {}; + rasterizationState.frontFace = WGPUFrontFace_CCW; + rasterizationState.cullMode = WGPUCullMode_None; rasterizationState.depthBias = 0; rasterizationState.depthBiasSlopeScale = 0.0; rasterizationState.depthBiasClamp = 0.0; // Create the depth-stencil state - DawnStencilStateFaceDescriptor stencilFace; - stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS; - stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP; - stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP; - stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP; - - DawnDepthStencilStateDescriptor depthStencilState; - depthStencilState.nextInChain = nullptr; - depthStencilState.format = DAWN_TEXTURE_FORMAT_D32_FLOAT_S8_UINT; + WGPUStencilStateFaceDescriptor stencilFace = {}; + stencilFace.compare = WGPUCompareFunction_Always; + stencilFace.failOp = WGPUStencilOperation_Keep; + stencilFace.depthFailOp = WGPUStencilOperation_Keep; + stencilFace.passOp = WGPUStencilOperation_Keep; + + WGPUDepthStencilStateDescriptor depthStencilState = {}; + depthStencilState.format = WGPUTextureFormat_Depth24PlusStencil8; depthStencilState.depthWriteEnabled = false; - depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS; + depthStencilState.depthCompare = WGPUCompareFunction_Always; depthStencilState.stencilBack = stencilFace; depthStencilState.stencilFront = stencilFace; depthStencilState.stencilReadMask = 0xff; depthStencilState.stencilWriteMask = 0xff; // Create the pipeline layout - DawnPipelineLayoutDescriptor layoutDescriptor; - layoutDescriptor.nextInChain = nullptr; + WGPUPipelineLayoutDescriptor layoutDescriptor = {}; layoutDescriptor.bindGroupLayoutCount = 0; layoutDescriptor.bindGroupLayouts = nullptr; - DawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor); - DawnPipelineLayout apiLayout = api.GetNewPipelineLayout(); + WGPUPipelineLayout layout = wgpuDeviceCreatePipelineLayout(device, &layoutDescriptor); + WGPUPipelineLayout apiLayout = api.GetNewPipelineLayout(); EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout)); // Create pipeline - DawnRenderPipelineDescriptor pipelineDescriptor; - pipelineDescriptor.nextInChain = nullptr; + WGPURenderPipelineDescriptor pipelineDescriptor = {}; - DawnPipelineStageDescriptor vertexStage; - vertexStage.nextInChain = nullptr; - vertexStage.module = vsModule; - vertexStage.entryPoint = "main"; - pipelineDescriptor.vertexStage = &vertexStage; + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.vertexStage.entryPoint = "main"; - DawnPipelineStageDescriptor fragmentStage; - fragmentStage.nextInChain = nullptr; + WGPUProgrammableStageDescriptor fragmentStage = {}; fragmentStage.module = vsModule; fragmentStage.entryPoint = "main"; pipelineDescriptor.fragmentStage = &fragmentStage; pipelineDescriptor.colorStateCount = 1; - DawnColorStateDescriptor* colorStatesPtr[] = {&colorStateDescriptor}; - pipelineDescriptor.colorStates = colorStatesPtr; + pipelineDescriptor.colorStates = &colorStateDescriptor; pipelineDescriptor.sampleCount = 1; + pipelineDescriptor.sampleMask = 0xFFFFFFFF; + pipelineDescriptor.alphaToCoverageEnabled = false; pipelineDescriptor.layout = layout; - pipelineDescriptor.vertexInput = &vertexInput; - pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + pipelineDescriptor.vertexState = &vertexState; + pipelineDescriptor.primitiveTopology = WGPUPrimitiveTopology_TriangleList; pipelineDescriptor.rasterizationState = &rasterizationState; // First case: depthStencilState is not null. pipelineDescriptor.depthStencilState = &depthStencilState; - dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); + wgpuDeviceCreateRenderPipeline(device, &pipelineDescriptor); - DawnRenderPipeline apiDummyPipeline = api.GetNewRenderPipeline(); + WGPURenderPipeline apiDummyPipeline = api.GetNewRenderPipeline(); EXPECT_CALL( api, DeviceCreateRenderPipeline( - apiDevice, MatchesLambda([](const DawnRenderPipelineDescriptor* desc) -> bool { + apiDevice, MatchesLambda([](const WGPURenderPipelineDescriptor* desc) -> bool { return desc->depthStencilState != nullptr && desc->depthStencilState->nextInChain == nullptr && desc->depthStencilState->depthWriteEnabled == false && - desc->depthStencilState->depthCompare == DAWN_COMPARE_FUNCTION_ALWAYS && - desc->depthStencilState->stencilBack.compare == - DAWN_COMPARE_FUNCTION_ALWAYS && - desc->depthStencilState->stencilBack.failOp == DAWN_STENCIL_OPERATION_KEEP && + desc->depthStencilState->depthCompare == WGPUCompareFunction_Always && + desc->depthStencilState->stencilBack.compare == WGPUCompareFunction_Always && + desc->depthStencilState->stencilBack.failOp == WGPUStencilOperation_Keep && desc->depthStencilState->stencilBack.depthFailOp == - DAWN_STENCIL_OPERATION_KEEP && - desc->depthStencilState->stencilBack.passOp == DAWN_STENCIL_OPERATION_KEEP && + WGPUStencilOperation_Keep && + desc->depthStencilState->stencilBack.passOp == WGPUStencilOperation_Keep && desc->depthStencilState->stencilFront.compare == - DAWN_COMPARE_FUNCTION_ALWAYS && - desc->depthStencilState->stencilFront.failOp == - DAWN_STENCIL_OPERATION_KEEP && + WGPUCompareFunction_Always && + desc->depthStencilState->stencilFront.failOp == WGPUStencilOperation_Keep && desc->depthStencilState->stencilFront.depthFailOp == - DAWN_STENCIL_OPERATION_KEEP && - desc->depthStencilState->stencilFront.passOp == - DAWN_STENCIL_OPERATION_KEEP && + WGPUStencilOperation_Keep && + desc->depthStencilState->stencilFront.passOp == WGPUStencilOperation_Keep && desc->depthStencilState->stencilReadMask == 0xff && desc->depthStencilState->stencilWriteMask == 0xff; }))) @@ -190,10 +174,10 @@ TEST_F(WireOptionalTests, OptionalStructPointer) { // Second case: depthStencilState is null. pipelineDescriptor.depthStencilState = nullptr; - dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); + wgpuDeviceCreateRenderPipeline(device, &pipelineDescriptor); EXPECT_CALL(api, DeviceCreateRenderPipeline( - apiDevice, MatchesLambda([](const DawnRenderPipelineDescriptor* desc) -> bool { + apiDevice, MatchesLambda([](const WGPURenderPipelineDescriptor* desc) -> bool { return desc->depthStencilState == nullptr; }))) .WillOnce(Return(apiDummyPipeline)); diff --git a/third_party/dawn/src/tests/unittests/wire/WireTest.cpp b/third_party/dawn/src/tests/unittests/wire/WireTest.cpp index 94aa7cde47b..bdac99f2f74 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireTest.cpp +++ b/third_party/dawn/src/tests/unittests/wire/WireTest.cpp @@ -14,6 +14,7 @@ #include "tests/unittests/wire/WireTest.h" +#include "dawn/dawn_proc.h" #include "dawn_wire/WireClient.h" #include "dawn_wire/WireServer.h" #include "utils/TerribleCommandBuffer.h" @@ -27,33 +28,58 @@ WireTest::WireTest() { WireTest::~WireTest() { } +client::MemoryTransferService* WireTest::GetClientMemoryTransferService() { + return nullptr; +} + +server::MemoryTransferService* WireTest::GetServerMemoryTransferService() { + return nullptr; +} + void WireTest::SetUp() { DawnProcTable mockProcs; - DawnDevice mockDevice; + WGPUDevice mockDevice; api.GetProcTableAndDevice(&mockProcs, &mockDevice); // This SetCallback call cannot be ignored because it is done as soon as we start the server - EXPECT_CALL(api, OnDeviceSetErrorCallback(_, _, _)).Times(Exactly(1)); + EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(_, _, _)).Times(Exactly(1)); + EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(_, _, _)).Times(Exactly(1)); SetupIgnoredCallExpectations(); mS2cBuf = std::make_unique(); mC2sBuf = std::make_unique(mWireServer.get()); - mWireServer.reset(new WireServer(mockDevice, mockProcs, mS2cBuf.get())); + WireServerDescriptor serverDesc = {}; + serverDesc.device = mockDevice; + serverDesc.procs = &mockProcs; + serverDesc.serializer = mS2cBuf.get(); + serverDesc.memoryTransferService = GetServerMemoryTransferService(); + + mWireServer.reset(new WireServer(serverDesc)); mC2sBuf->SetHandler(mWireServer.get()); - mWireClient.reset(new WireClient(mC2sBuf.get())); + WireClientDescriptor clientDesc = {}; + clientDesc.serializer = mC2sBuf.get(); + clientDesc.memoryTransferService = GetClientMemoryTransferService(); + + mWireClient.reset(new WireClient(clientDesc)); mS2cBuf->SetHandler(mWireClient.get()); device = mWireClient->GetDevice(); - DawnProcTable clientProcs = mWireClient->GetProcs(); - dawnSetProcs(&clientProcs); + DawnProcTable clientProcs = dawn_wire::WireClient::GetProcs(); + dawnProcSetProcs(&clientProcs); apiDevice = mockDevice; + + // The GetDefaultQueue is done on WireClient startup so we expect it now. + queue = wgpuDeviceGetDefaultQueue(device); + apiQueue = api.GetNewQueue(); + EXPECT_CALL(api, DeviceGetDefaultQueue(apiDevice)).WillOnce(Return(apiQueue)); + FlushClient(); } void WireTest::TearDown() { - dawnSetProcs(nullptr); + dawnProcSetProcs(nullptr); // Derived classes should call the base TearDown() first. The client must // be reset before any mocks are deleted. @@ -61,17 +87,18 @@ void WireTest::TearDown() { // cannot be null. api.IgnoreAllReleaseCalls(); mWireClient = nullptr; + mWireServer = nullptr; } -void WireTest::FlushClient() { - ASSERT_TRUE(mC2sBuf->Flush()); +void WireTest::FlushClient(bool success) { + ASSERT_EQ(mC2sBuf->Flush(), success); Mock::VerifyAndClearExpectations(&api); SetupIgnoredCallExpectations(); } -void WireTest::FlushServer() { - ASSERT_TRUE(mS2cBuf->Flush()); +void WireTest::FlushServer(bool success) { + ASSERT_EQ(mS2cBuf->Flush(), success); } dawn_wire::WireServer* WireTest::GetWireServer() { @@ -83,6 +110,7 @@ dawn_wire::WireClient* WireTest::GetWireClient() { } void WireTest::DeleteServer() { + EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1); mWireServer = nullptr; } diff --git a/third_party/dawn/src/tests/unittests/wire/WireTest.h b/third_party/dawn/src/tests/unittests/wire/WireTest.h index 3173292acf0..d4537d67e04 100644 --- a/third_party/dawn/src/tests/unittests/wire/WireTest.h +++ b/third_party/dawn/src/tests/unittests/wire/WireTest.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "dawn/mock_webgpu.h" #include "gtest/gtest.h" -#include "mock/mock_dawn.h" #include @@ -66,9 +66,46 @@ inline testing::Matcher> MatchesLambda(Lambda lamb return MakeMatcher(new LambdaMatcherImpl>(lambda)); } +class StringMessageMatcher : public testing::MatcherInterface { + public: + explicit StringMessageMatcher() { + } + + bool MatchAndExplain(const char* message, + testing::MatchResultListener* listener) const override { + if (message == nullptr) { + *listener << "missing error message"; + return false; + } + if (std::strlen(message) <= 1) { + *listener << "message is truncated"; + return false; + } + return true; + } + + void DescribeTo(std::ostream* os) const override { + *os << "valid error message"; + } + + void DescribeNegationTo(std::ostream* os) const override { + *os << "invalid error message"; + } +}; + +inline testing::Matcher ValidStringMessage() { + return MakeMatcher(new StringMessageMatcher()); +} + namespace dawn_wire { class WireClient; class WireServer; + namespace client { + class MemoryTransferService; + } // namespace client + namespace server { + class MemoryTransferService; + } // namespace server } // namespace dawn_wire namespace utils { @@ -82,12 +119,15 @@ class WireTest : public testing::Test { void SetUp() override; void TearDown() override; - void FlushClient(); - void FlushServer(); + + void FlushClient(bool success = true); + void FlushServer(bool success = true); testing::StrictMock api; - DawnDevice apiDevice; - DawnDevice device; + WGPUDevice apiDevice; + WGPUQueue apiQueue; + WGPUDevice device; + WGPUQueue queue; dawn_wire::WireServer* GetWireServer(); dawn_wire::WireClient* GetWireClient(); @@ -97,6 +137,9 @@ class WireTest : public testing::Test { private: void SetupIgnoredCallExpectations(); + virtual dawn_wire::client::MemoryTransferService* GetClientMemoryTransferService(); + virtual dawn_wire::server::MemoryTransferService* GetServerMemoryTransferService(); + std::unique_ptr mWireServer; std::unique_ptr mWireClient; std::unique_ptr mS2cBuf; diff --git a/third_party/dawn/src/tests/unittests/wire/WireWGPUDevicePropertiesTests.cpp b/third_party/dawn/src/tests/unittests/wire/WireWGPUDevicePropertiesTests.cpp new file mode 100644 index 00000000000..120e4a9aba8 --- /dev/null +++ b/third_party/dawn/src/tests/unittests/wire/WireWGPUDevicePropertiesTests.cpp @@ -0,0 +1,41 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_wire/Wire.h" +#include "gtest/gtest.h" + +#include + +class WireWGPUDevicePropertiesTests : public testing::Test {}; + +// Test that the serialization and deserialization of WGPUDeviceProperties can work correctly. +TEST_F(WireWGPUDevicePropertiesTests, SerializeWGPUDeviceProperties) { + WGPUDeviceProperties sentWGPUDeviceProperties; + sentWGPUDeviceProperties.textureCompressionBC = true; + // Set false to test that the serialization can handle both true and false correctly. + sentWGPUDeviceProperties.pipelineStatisticsQuery = false; + sentWGPUDeviceProperties.timestampQuery = true; + + size_t sentWGPUDevicePropertiesSize = + dawn_wire::SerializedWGPUDevicePropertiesSize(&sentWGPUDeviceProperties); + std::vector buffer; + buffer.resize(sentWGPUDevicePropertiesSize); + dawn_wire::SerializeWGPUDeviceProperties(&sentWGPUDeviceProperties, buffer.data()); + + WGPUDeviceProperties receivedWGPUDeviceProperties; + dawn_wire::DeserializeWGPUDeviceProperties(&receivedWGPUDeviceProperties, buffer.data()); + ASSERT_TRUE(receivedWGPUDeviceProperties.textureCompressionBC); + ASSERT_FALSE(receivedWGPUDeviceProperties.pipelineStatisticsQuery); + ASSERT_TRUE(receivedWGPUDeviceProperties.timestampQuery); +} diff --git a/third_party/dawn/src/tests/white_box/D3D12DescriptorHeapTests.cpp b/third_party/dawn/src/tests/white_box/D3D12DescriptorHeapTests.cpp new file mode 100644 index 00000000000..6750586c768 --- /dev/null +++ b/third_party/dawn/src/tests/white_box/D3D12DescriptorHeapTests.cpp @@ -0,0 +1,931 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "dawn_native/Toggles.h" +#include "dawn_native/d3d12/BindGroupLayoutD3D12.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" +#include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +constexpr uint32_t kRTSize = 4; + +// Pooling tests are required to advance the GPU completed serial to reuse heaps. +// This requires Tick() to be called at-least |kFrameDepth| times. This constant +// should be updated if the internals of Tick() change. +constexpr uint32_t kFrameDepth = 2; + +using namespace dawn_native::d3d12; + +class D3D12DescriptorHeapTests : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + mD3DDevice = reinterpret_cast(device.Get()); + + mSimpleVSModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); + gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); + })"); + + mSimpleFSModule = utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (location = 0) out vec4 fragColor; + layout (set = 0, binding = 0) uniform colorBuffer { + vec4 color; + }; + void main() { + fragColor = color; + })"); + } + + utils::BasicRenderPass MakeRenderPass(uint32_t width, + uint32_t height, + wgpu::TextureFormat format) { + DAWN_ASSERT(width > 0 && height > 0); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = width; + descriptor.size.height = height; + descriptor.size.depth = 1; + descriptor.arrayLayerCount = 1; + descriptor.sampleCount = 1; + descriptor.format = format; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + wgpu::Texture color = device.CreateTexture(&descriptor); + + return utils::BasicRenderPass(width, height, color); + } + + std::array GetSolidColor(uint32_t n) const { + ASSERT(n >> 24 == 0); + float b = (n & 0xFF) / 255.0f; + float g = ((n >> 8) & 0xFF) / 255.0f; + float r = ((n >> 16) & 0xFF) / 255.0f; + return {r, g, b, 1}; + } + + Device* mD3DDevice = nullptr; + + wgpu::ShaderModule mSimpleVSModule; + wgpu::ShaderModule mSimpleFSModule; +}; + +class DummyStagingDescriptorAllocator { + public: + DummyStagingDescriptorAllocator(Device* device, + uint32_t descriptorCount, + uint32_t allocationsPerHeap) + : mAllocator(device, + descriptorCount, + allocationsPerHeap * descriptorCount, + D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER) { + } + + CPUDescriptorHeapAllocation AllocateCPUDescriptors() { + dawn_native::ResultOrError result = + mAllocator.AllocateCPUDescriptors(); + return (result.IsSuccess()) ? result.AcquireSuccess() : CPUDescriptorHeapAllocation{}; + } + + void Deallocate(CPUDescriptorHeapAllocation& allocation) { + mAllocator.Deallocate(&allocation); + } + + private: + StagingDescriptorAllocator mAllocator; +}; + +// Verify the shader visible view heaps switch over within a single submit. +TEST_P(D3D12DescriptorHeapTests, SwitchOverViewHeap) { + DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled( + dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); + + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + + // Fill in a view heap with "view only" bindgroups (1x view per group) by creating a + // view bindgroup each draw. After HEAP_SIZE + 1 draws, the heaps must switch over. + renderPipelineDescriptor.vertexStage.module = mSimpleVSModule; + renderPipelineDescriptor.cFragmentStage.module = mSimpleFSModule; + + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + Device* d3dDevice = reinterpret_cast(device.Get()); + ShaderVisibleDescriptorAllocator* allocator = + d3dDevice->GetViewShaderVisibleDescriptorAllocator(); + const uint64_t heapSize = allocator->GetShaderVisibleHeapSizeForTesting(); + + const Serial heapSerial = allocator->GetShaderVisibleHeapSerialForTesting(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(renderPipeline); + + std::array redColor = {1, 0, 0, 1}; + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform); + + for (uint32_t i = 0; i < heapSize + 1; ++i) { + pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, uniformBuffer, 0, sizeof(redColor)}})); + pass.Draw(3); + } + + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_EQ(allocator->GetShaderVisibleHeapSerialForTesting(), heapSerial + 1); +} + +// Verify the shader visible sampler heaps does not switch over within a single submit. +TEST_P(D3D12DescriptorHeapTests, NoSwitchOverSamplerHeap) { + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + + // Fill in a sampler heap with "sampler only" bindgroups (1x sampler per group) by creating a + // sampler bindgroup each draw. After HEAP_SIZE + 1 draws, the heaps WILL NOT switch over + // because the sampler heap allocations are de-duplicated. + renderPipelineDescriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + })"); + + renderPipelineDescriptor.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"(#version 450 + layout(set = 0, binding = 0) uniform sampler sampler0; + layout(location = 0) out vec4 fragColor; + void main() { + fragColor = vec4(0.0, 0.0, 0.0, 0.0); + })"); + + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + Device* d3dDevice = reinterpret_cast(device.Get()); + ShaderVisibleDescriptorAllocator* allocator = + d3dDevice->GetSamplerShaderVisibleDescriptorAllocator(); + const uint64_t samplerHeapSize = allocator->GetShaderVisibleHeapSizeForTesting(); + + const Serial heapSerial = allocator->GetShaderVisibleHeapSerialForTesting(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(renderPipeline); + + for (uint32_t i = 0; i < samplerHeapSize + 1; ++i) { + pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, sampler}})); + pass.Draw(3); + } + + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + EXPECT_EQ(allocator->GetShaderVisibleHeapSerialForTesting(), heapSerial); +} + +// Verify shader-visible heaps can be recycled for multiple submits. +TEST_P(D3D12DescriptorHeapTests, PoolHeapsInMultipleSubmits) { + ShaderVisibleDescriptorAllocator* allocator = + mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator(); + + std::list> heaps = {allocator->GetShaderVisibleHeap()}; + + EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), 0u); + + // Allocate + Tick() up to |kFrameDepth| and ensure heaps are always unique. + for (uint32_t i = 0; i < kFrameDepth; i++) { + EXPECT_TRUE(allocator->AllocateAndSwitchShaderVisibleHeap().IsSuccess()); + ComPtr heap = allocator->GetShaderVisibleHeap(); + EXPECT_TRUE(std::find(heaps.begin(), heaps.end(), heap) == heaps.end()); + heaps.push_back(heap); + mD3DDevice->Tick(); + } + + // Repeat up to |kFrameDepth| again but ensure heaps are the same in the expected order + // (oldest heaps are recycled first). The "+ 1" is so we also include the very first heap in the + // check. + for (uint32_t i = 0; i < kFrameDepth + 1; i++) { + EXPECT_TRUE(allocator->AllocateAndSwitchShaderVisibleHeap().IsSuccess()); + ComPtr heap = allocator->GetShaderVisibleHeap(); + EXPECT_TRUE(heaps.front() == heap); + heaps.pop_front(); + mD3DDevice->Tick(); + } + + EXPECT_TRUE(heaps.empty()); + EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), kFrameDepth); +} + +// Verify shader-visible heaps do not recycle in a pending submit. +TEST_P(D3D12DescriptorHeapTests, PoolHeapsInPendingSubmit) { + constexpr uint32_t kNumOfSwitches = 5; + + ShaderVisibleDescriptorAllocator* allocator = + mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator(); + + const Serial heapSerial = allocator->GetShaderVisibleHeapSerialForTesting(); + + std::set> heaps = {allocator->GetShaderVisibleHeap()}; + + EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), 0u); + + // Switch-over |kNumOfSwitches| and ensure heaps are always unique. + for (uint32_t i = 0; i < kNumOfSwitches; i++) { + EXPECT_TRUE(allocator->AllocateAndSwitchShaderVisibleHeap().IsSuccess()); + ComPtr heap = allocator->GetShaderVisibleHeap(); + EXPECT_TRUE(std::find(heaps.begin(), heaps.end(), heap) == heaps.end()); + heaps.insert(heap); + } + + // After |kNumOfSwitches|, no heaps are recycled. + EXPECT_EQ(allocator->GetShaderVisibleHeapSerialForTesting(), heapSerial + kNumOfSwitches); + EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), kNumOfSwitches); +} + +// Verify switching shader-visible heaps do not recycle in a pending submit but do so +// once no longer pending. +TEST_P(D3D12DescriptorHeapTests, PoolHeapsInPendingAndMultipleSubmits) { + constexpr uint32_t kNumOfSwitches = 5; + + ShaderVisibleDescriptorAllocator* allocator = + mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator(); + const Serial heapSerial = allocator->GetShaderVisibleHeapSerialForTesting(); + + std::set> heaps = {allocator->GetShaderVisibleHeap()}; + + EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), 0u); + + // Switch-over |kNumOfSwitches| to create a pool of unique heaps. + for (uint32_t i = 0; i < kNumOfSwitches; i++) { + EXPECT_TRUE(allocator->AllocateAndSwitchShaderVisibleHeap().IsSuccess()); + ComPtr heap = allocator->GetShaderVisibleHeap(); + EXPECT_TRUE(std::find(heaps.begin(), heaps.end(), heap) == heaps.end()); + heaps.insert(heap); + } + + EXPECT_EQ(allocator->GetShaderVisibleHeapSerialForTesting(), heapSerial + kNumOfSwitches); + EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), kNumOfSwitches); + + // Ensure switched-over heaps can be recycled by advancing the GPU by at-least |kFrameDepth|. + for (uint32_t i = 0; i < kFrameDepth; i++) { + mD3DDevice->Tick(); + } + + // Switch-over |kNumOfSwitches| again reusing the same heaps. + for (uint32_t i = 0; i < kNumOfSwitches; i++) { + EXPECT_TRUE(allocator->AllocateAndSwitchShaderVisibleHeap().IsSuccess()); + ComPtr heap = allocator->GetShaderVisibleHeap(); + EXPECT_TRUE(std::find(heaps.begin(), heaps.end(), heap) != heaps.end()); + heaps.erase(heap); + } + + // After switching-over |kNumOfSwitches| x 2, ensure no additional heaps exist. + EXPECT_EQ(allocator->GetShaderVisibleHeapSerialForTesting(), heapSerial + kNumOfSwitches * 2); + EXPECT_EQ(allocator->GetShaderVisiblePoolSizeForTesting(), kNumOfSwitches); +} + +// Verify encoding multiple heaps worth of bindgroups. +// Shader-visible heaps will switch out |kNumOfHeaps| times. +TEST_P(D3D12DescriptorHeapTests, EncodeManyUBO) { + // This test draws a solid color triangle |heapSize| times. Each draw uses a new bindgroup that + // has its own UBO with a "color value" in the range [1... heapSize]. After |heapSize| draws, + // the result is the arithmetic sum of the sequence after the framebuffer is blended by + // accumulation. By checking for this sum, we ensure each bindgroup was encoded correctly. + DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled( + dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); + + utils::BasicRenderPass renderPass = + MakeRenderPass(kRTSize, kRTSize, wgpu::TextureFormat::R32Float); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = mSimpleVSModule; + + pipelineDescriptor.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (location = 0) out float fragColor; + layout (set = 0, binding = 0) uniform buffer0 { + float heapSize; + }; + void main() { + fragColor = heapSize; + })"); + + pipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::R32Float; + pipelineDescriptor.cColorStates[0].colorBlend.operation = wgpu::BlendOperation::Add; + pipelineDescriptor.cColorStates[0].colorBlend.srcFactor = wgpu::BlendFactor::One; + pipelineDescriptor.cColorStates[0].colorBlend.dstFactor = wgpu::BlendFactor::One; + pipelineDescriptor.cColorStates[0].alphaBlend.operation = wgpu::BlendOperation::Add; + pipelineDescriptor.cColorStates[0].alphaBlend.srcFactor = wgpu::BlendFactor::One; + pipelineDescriptor.cColorStates[0].alphaBlend.dstFactor = wgpu::BlendFactor::One; + + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&pipelineDescriptor); + + const uint32_t heapSize = + mD3DDevice->GetViewShaderVisibleDescriptorAllocator()->GetShaderVisibleHeapSizeForTesting(); + + constexpr uint32_t kNumOfHeaps = 2; + + const uint32_t numOfEncodedBindGroups = kNumOfHeaps * heapSize; + + std::vector bindGroups; + for (uint32_t i = 0; i < numOfEncodedBindGroups; i++) { + const float color = i + 1; + wgpu::Buffer uniformBuffer = + utils::CreateBufferFromData(device, &color, sizeof(color), wgpu::BufferUsage::Uniform); + bindGroups.push_back(utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, uniformBuffer}})); + } + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(renderPipeline); + + for (uint32_t i = 0; i < numOfEncodedBindGroups; ++i) { + pass.SetBindGroup(0, bindGroups[i]); + pass.Draw(3); + } + + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + float colorSum = numOfEncodedBindGroups * (numOfEncodedBindGroups + 1) / 2; + EXPECT_PIXEL_FLOAT_EQ(colorSum, renderPass.color, 0, 0); +} + +// Verify encoding one bindgroup then a heaps worth in different submits. +// Shader-visible heaps should switch out once upon encoding 1 + |heapSize| descriptors. +// The first descriptor's memory will be reused when the second submit encodes |heapSize| +// descriptors. +TEST_P(D3D12DescriptorHeapTests, EncodeUBOOverflowMultipleSubmit) { + DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled( + dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); + + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = mSimpleVSModule; + pipelineDescriptor.cFragmentStage.module = mSimpleFSModule; + pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&pipelineDescriptor); + + // Encode the first descriptor and submit. + { + std::array greenColor = {0, 1, 0, 1}; + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, &greenColor, sizeof(greenColor), wgpu::BufferUsage::Uniform); + + wgpu::BindGroup bindGroup = utils::MakeBindGroup( + device, renderPipeline.GetBindGroupLayout(0), {{0, uniformBuffer}}); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(renderPipeline); + pass.SetBindGroup(0, bindGroup); + pass.Draw(3); + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0); + + // Encode a heap worth of descriptors. + { + const uint32_t heapSize = mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator() + ->GetShaderVisibleHeapSizeForTesting(); + + std::vector bindGroups; + for (uint32_t i = 0; i < heapSize - 1; i++) { + std::array fillColor = GetSolidColor(i + 1); // Avoid black + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, &fillColor, sizeof(fillColor), wgpu::BufferUsage::Uniform); + + bindGroups.push_back(utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, uniformBuffer}})); + } + + std::array redColor = {1, 0, 0, 1}; + wgpu::Buffer lastUniformBuffer = utils::CreateBufferFromData( + device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform); + + bindGroups.push_back(utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, lastUniformBuffer, 0, sizeof(redColor)}})); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(renderPipeline); + + for (uint32_t i = 0; i < heapSize; ++i) { + pass.SetBindGroup(0, bindGroups[i]); + pass.Draw(3); + } + + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0); +} + +// Verify encoding a heaps worth of bindgroups plus one more then reuse the first +// bindgroup in the same submit. +// Shader-visible heaps should switch out once then re-encode the first descriptor at a new offset +// in the heap. +TEST_P(D3D12DescriptorHeapTests, EncodeReuseUBOOverflow) { + DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled( + dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = mSimpleVSModule; + pipelineDescriptor.cFragmentStage.module = mSimpleFSModule; + pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); + + std::array redColor = {1, 0, 0, 1}; + wgpu::Buffer firstUniformBuffer = utils::CreateBufferFromData( + device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform); + + std::vector bindGroups = {utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), {{0, firstUniformBuffer, 0, sizeof(redColor)}})}; + + const uint32_t heapSize = + mD3DDevice->GetViewShaderVisibleDescriptorAllocator()->GetShaderVisibleHeapSizeForTesting(); + + for (uint32_t i = 0; i < heapSize; i++) { + const std::array& fillColor = GetSolidColor(i + 1); // Avoid black + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, &fillColor, sizeof(fillColor), wgpu::BufferUsage::Uniform); + bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, uniformBuffer, 0, sizeof(fillColor)}})); + } + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(pipeline); + + // Encode a heap worth of descriptors plus one more. + for (uint32_t i = 0; i < heapSize + 1; ++i) { + pass.SetBindGroup(0, bindGroups[i]); + pass.Draw(3); + } + + // Re-encode the first bindgroup again. + pass.SetBindGroup(0, bindGroups[0]); + pass.Draw(3); + + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Make sure the first bindgroup was encoded correctly. + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kRed, renderPass.color, 0, 0); +} + +// Verify encoding a heaps worth of bindgroups plus one more in the first submit then reuse the +// first bindgroup again in the second submit. +// Shader-visible heaps should switch out once then re-encode the +// first descriptor at the same offset in the heap. +TEST_P(D3D12DescriptorHeapTests, EncodeReuseUBOMultipleSubmits) { + DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled( + dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + pipelineDescriptor.vertexStage.module = mSimpleVSModule; + pipelineDescriptor.cFragmentStage.module = mSimpleFSModule; + pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); + + // Encode heap worth of descriptors plus one more. + std::array redColor = {1, 0, 0, 1}; + + wgpu::Buffer firstUniformBuffer = utils::CreateBufferFromData( + device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform); + + std::vector bindGroups = {utils::MakeBindGroup( + device, pipeline.GetBindGroupLayout(0), {{0, firstUniformBuffer, 0, sizeof(redColor)}})}; + + const uint32_t heapSize = + mD3DDevice->GetViewShaderVisibleDescriptorAllocator()->GetShaderVisibleHeapSizeForTesting(); + + for (uint32_t i = 0; i < heapSize; i++) { + std::array fillColor = GetSolidColor(i + 1); // Avoid black + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, &fillColor, sizeof(fillColor), wgpu::BufferUsage::Uniform); + + bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, uniformBuffer, 0, sizeof(fillColor)}})); + } + + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(pipeline); + + for (uint32_t i = 0; i < heapSize + 1; ++i) { + pass.SetBindGroup(0, bindGroups[i]); + pass.Draw(3); + } + + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + // Re-encode the first bindgroup again. + { + std::array greenColor = {0, 1, 0, 1}; + queue.WriteBuffer(firstUniformBuffer, 0, &greenColor, sizeof(greenColor)); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(pipeline); + + pass.SetBindGroup(0, bindGroups[0]); + pass.Draw(3); + + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + // Make sure the first bindgroup was re-encoded correctly. + EXPECT_PIXEL_RGBA8_EQ(RGBA8::kGreen, renderPass.color, 0, 0); +} + +// Verify encoding many sampler and ubo worth of bindgroups. +// Shader-visible heaps should switch out |kNumOfViewHeaps| times. +TEST_P(D3D12DescriptorHeapTests, EncodeManyUBOAndSamplers) { + DAWN_SKIP_TEST_IF(!mD3DDevice->IsToggleEnabled( + dawn_native::Toggle::UseD3D12SmallShaderVisibleHeapForTesting)); + + // Create a solid filled texture. + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = kRTSize; + descriptor.size.height = kRTSize; + descriptor.size.depth = 1; + descriptor.arrayLayerCount = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::Sampled | wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopySrc; + wgpu::Texture texture = device.CreateTexture(&descriptor); + wgpu::TextureView textureView = texture.CreateView(); + + { + utils::BasicRenderPass renderPass = utils::BasicRenderPass(kRTSize, kRTSize, texture); + + utils::ComboRenderPassDescriptor renderPassDesc({textureView}); + renderPassDesc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + renderPassDesc.cColorAttachments[0].clearColor = {0.0f, 1.0f, 0.0f, 1.0f}; + renderPass.renderPassInfo.cColorAttachments[0].attachment = textureView; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + auto pass = encoder.BeginRenderPass(&renderPassDesc); + pass.EndPass(); + + wgpu::CommandBuffer commandBuffer = encoder.Finish(); + queue.Submit(1, &commandBuffer); + + RGBA8 filled(0, 255, 0, 255); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 0, 0); + } + + { + utils::ComboRenderPipelineDescriptor pipelineDescriptor(device); + + pipelineDescriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + layout (set = 0, binding = 0) uniform vertexUniformBuffer { + mat2 transform; + }; + void main() { + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); + gl_Position = vec4(transform * pos[gl_VertexIndex], 0.f, 1.f); + })"); + + pipelineDescriptor.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (set = 0, binding = 1) uniform sampler sampler0; + layout (set = 0, binding = 2) uniform texture2D texture0; + layout (set = 0, binding = 3) uniform buffer0 { + vec4 color; + }; + layout (location = 0) out vec4 fragColor; + void main() { + fragColor = texture(sampler2D(texture0, sampler0), gl_FragCoord.xy); + fragColor += color; + })"); + + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize); + pipelineDescriptor.cColorStates[0].format = renderPass.colorFormat; + + wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&pipelineDescriptor); + + // Encode a heap worth of descriptors |kNumOfHeaps| times. + constexpr float dummy = 0.0f; + constexpr float transform[] = {1.f, 0.f, dummy, dummy, 0.f, 1.f, dummy, dummy}; + wgpu::Buffer transformBuffer = utils::CreateBufferFromData( + device, &transform, sizeof(transform), wgpu::BufferUsage::Uniform); + + wgpu::SamplerDescriptor samplerDescriptor; + wgpu::Sampler sampler = device.CreateSampler(&samplerDescriptor); + + ShaderVisibleDescriptorAllocator* viewAllocator = + mD3DDevice->GetViewShaderVisibleDescriptorAllocator(); + + ShaderVisibleDescriptorAllocator* samplerAllocator = + mD3DDevice->GetSamplerShaderVisibleDescriptorAllocator(); + + const Serial viewHeapSerial = viewAllocator->GetShaderVisibleHeapSerialForTesting(); + const Serial samplerHeapSerial = samplerAllocator->GetShaderVisibleHeapSerialForTesting(); + + const uint32_t viewHeapSize = viewAllocator->GetShaderVisibleHeapSizeForTesting(); + + // "Small" view heap is always 2 x sampler heap size and encodes 3x the descriptors per + // group. This means the count of heaps switches is determined by the total number of views + // to encode. Compute the number of bindgroups to encode by counting the required views for + // |kNumOfViewHeaps| heaps worth. + constexpr uint32_t kViewsPerBindGroup = 3; + constexpr uint32_t kNumOfViewHeaps = 5; + + const uint32_t numOfEncodedBindGroups = + (viewHeapSize * kNumOfViewHeaps) / kViewsPerBindGroup; + + std::vector bindGroups; + for (uint32_t i = 0; i < numOfEncodedBindGroups - 1; i++) { + std::array fillColor = GetSolidColor(i + 1); // Avoid black + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, &fillColor, sizeof(fillColor), wgpu::BufferUsage::Uniform); + + bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, transformBuffer, 0, sizeof(transform)}, + {1, sampler}, + {2, textureView}, + {3, uniformBuffer, 0, sizeof(fillColor)}})); + } + + std::array redColor = {1, 0, 0, 1}; + wgpu::Buffer lastUniformBuffer = utils::CreateBufferFromData( + device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform); + + bindGroups.push_back(utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), + {{0, transformBuffer, 0, sizeof(transform)}, + {1, sampler}, + {2, textureView}, + {3, lastUniformBuffer, 0, sizeof(redColor)}})); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(pipeline); + + for (uint32_t i = 0; i < numOfEncodedBindGroups; ++i) { + pass.SetBindGroup(0, bindGroups[i]); + pass.Draw(3); + } + + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Final accumulated color is result of sampled + UBO color. + RGBA8 filled(255, 255, 0, 255); + RGBA8 notFilled(0, 0, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 0, 0); + EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, kRTSize - 1, 0); + + EXPECT_EQ(viewAllocator->GetShaderVisiblePoolSizeForTesting(), kNumOfViewHeaps); + EXPECT_EQ(viewAllocator->GetShaderVisibleHeapSerialForTesting(), + viewHeapSerial + kNumOfViewHeaps); + + EXPECT_EQ(samplerAllocator->GetShaderVisiblePoolSizeForTesting(), 0u); + EXPECT_EQ(samplerAllocator->GetShaderVisibleHeapSerialForTesting(), samplerHeapSerial); + } +} + +// Verify a single allocate/deallocate. +// One non-shader visible heap will be created. +TEST_P(D3D12DescriptorHeapTests, Single) { + constexpr uint32_t kDescriptorCount = 4; + constexpr uint32_t kAllocationsPerHeap = 3; + DummyStagingDescriptorAllocator allocator(mD3DDevice, kDescriptorCount, kAllocationsPerHeap); + + CPUDescriptorHeapAllocation allocation = allocator.AllocateCPUDescriptors(); + EXPECT_EQ(allocation.GetHeapIndex(), 0u); + EXPECT_NE(allocation.OffsetFrom(0, 0).ptr, 0u); + + allocator.Deallocate(allocation); + EXPECT_FALSE(allocation.IsValid()); +} + +// Verify allocating many times causes the pool to increase in size. +// Creates |kNumOfHeaps| non-shader visible heaps. +TEST_P(D3D12DescriptorHeapTests, Sequential) { + constexpr uint32_t kDescriptorCount = 4; + constexpr uint32_t kAllocationsPerHeap = 3; + DummyStagingDescriptorAllocator allocator(mD3DDevice, kDescriptorCount, kAllocationsPerHeap); + + // Allocate |kNumOfHeaps| worth. + constexpr uint32_t kNumOfHeaps = 2; + + std::set allocatedHeaps; + + std::vector allocations; + for (uint32_t i = 0; i < kAllocationsPerHeap * kNumOfHeaps; i++) { + CPUDescriptorHeapAllocation allocation = allocator.AllocateCPUDescriptors(); + EXPECT_EQ(allocation.GetHeapIndex(), i / kAllocationsPerHeap); + EXPECT_NE(allocation.OffsetFrom(0, 0).ptr, 0u); + allocations.push_back(allocation); + allocatedHeaps.insert(allocation.GetHeapIndex()); + } + + EXPECT_EQ(allocatedHeaps.size(), kNumOfHeaps); + + // Deallocate all. + for (CPUDescriptorHeapAllocation& allocation : allocations) { + allocator.Deallocate(allocation); + EXPECT_FALSE(allocation.IsValid()); + } +} + +// Verify that re-allocating a number of allocations < pool size, all heaps are reused. +// Creates and reuses |kNumofHeaps| non-shader visible heaps. +TEST_P(D3D12DescriptorHeapTests, ReuseFreedHeaps) { + constexpr uint32_t kDescriptorCount = 4; + constexpr uint32_t kAllocationsPerHeap = 25; + DummyStagingDescriptorAllocator allocator(mD3DDevice, kDescriptorCount, kAllocationsPerHeap); + + constexpr uint32_t kNumofHeaps = 10; + + std::list allocations; + std::set allocationPtrs; + + // Allocate |kNumofHeaps| heaps worth. + for (uint32_t i = 0; i < kAllocationsPerHeap * kNumofHeaps; i++) { + CPUDescriptorHeapAllocation allocation = allocator.AllocateCPUDescriptors(); + allocations.push_back(allocation); + EXPECT_TRUE(allocationPtrs.insert(allocation.OffsetFrom(0, 0).ptr).second); + } + + // Deallocate all. + for (CPUDescriptorHeapAllocation& allocation : allocations) { + allocator.Deallocate(allocation); + EXPECT_FALSE(allocation.IsValid()); + } + + allocations.clear(); + + // Re-allocate all again. + std::set reallocatedPtrs; + for (uint32_t i = 0; i < kAllocationsPerHeap * kNumofHeaps; i++) { + CPUDescriptorHeapAllocation allocation = allocator.AllocateCPUDescriptors(); + allocations.push_back(allocation); + EXPECT_TRUE(reallocatedPtrs.insert(allocation.OffsetFrom(0, 0).ptr).second); + EXPECT_TRUE(std::find(allocationPtrs.begin(), allocationPtrs.end(), + allocation.OffsetFrom(0, 0).ptr) != allocationPtrs.end()); + } + + // Deallocate all again. + for (CPUDescriptorHeapAllocation& allocation : allocations) { + allocator.Deallocate(allocation); + EXPECT_FALSE(allocation.IsValid()); + } +} + +// Verify allocating then deallocating many times. +TEST_P(D3D12DescriptorHeapTests, AllocateDeallocateMany) { + constexpr uint32_t kDescriptorCount = 4; + constexpr uint32_t kAllocationsPerHeap = 25; + DummyStagingDescriptorAllocator allocator(mD3DDevice, kDescriptorCount, kAllocationsPerHeap); + + std::list list3; + std::list list5; + std::list allocations; + + constexpr uint32_t kNumofHeaps = 2; + + // Allocate |kNumofHeaps| heaps worth. + for (uint32_t i = 0; i < kAllocationsPerHeap * kNumofHeaps; i++) { + CPUDescriptorHeapAllocation allocation = allocator.AllocateCPUDescriptors(); + EXPECT_NE(allocation.OffsetFrom(0, 0).ptr, 0u); + if (i % 3 == 0) { + list3.push_back(allocation); + } else { + allocations.push_back(allocation); + } + } + + // Deallocate every 3rd allocation. + for (auto it = list3.begin(); it != list3.end(); it = list3.erase(it)) { + allocator.Deallocate(*it); + } + + // Allocate again. + for (uint32_t i = 0; i < kAllocationsPerHeap * kNumofHeaps; i++) { + CPUDescriptorHeapAllocation allocation = allocator.AllocateCPUDescriptors(); + EXPECT_NE(allocation.OffsetFrom(0, 0).ptr, 0u); + if (i % 5 == 0) { + list5.push_back(allocation); + } else { + allocations.push_back(allocation); + } + } + + // Deallocate every 5th allocation. + for (auto it = list5.begin(); it != list5.end(); it = list5.erase(it)) { + allocator.Deallocate(*it); + } + + // Allocate again. + for (uint32_t i = 0; i < kAllocationsPerHeap * kNumofHeaps; i++) { + CPUDescriptorHeapAllocation allocation = allocator.AllocateCPUDescriptors(); + EXPECT_NE(allocation.OffsetFrom(0, 0).ptr, 0u); + allocations.push_back(allocation); + } + + // Deallocate remaining. + for (CPUDescriptorHeapAllocation& allocation : allocations) { + allocator.Deallocate(allocation); + EXPECT_FALSE(allocation.IsValid()); + } +} + +DAWN_INSTANTIATE_TEST(D3D12DescriptorHeapTests, + D3D12Backend(), + D3D12Backend({"use_d3d12_small_shader_visible_heap"})); diff --git a/third_party/dawn/src/tests/white_box/D3D12ResidencyTests.cpp b/third_party/dawn/src/tests/white_box/D3D12ResidencyTests.cpp new file mode 100644 index 00000000000..8d958ab5e58 --- /dev/null +++ b/third_party/dawn/src/tests/white_box/D3D12ResidencyTests.cpp @@ -0,0 +1,423 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "dawn_native/D3D12Backend.h" +#include "dawn_native/d3d12/BufferD3D12.h" +#include "dawn_native/d3d12/DeviceD3D12.h" +#include "dawn_native/d3d12/ResidencyManagerD3D12.h" +#include "dawn_native/d3d12/ShaderVisibleDescriptorAllocatorD3D12.h" +#include "tests/DawnTest.h" +#include "utils/ComboRenderPipelineDescriptor.h" +#include "utils/WGPUHelpers.h" + +#include + +constexpr uint32_t kRestrictedBudgetSize = 100000000; // 100MB +constexpr uint32_t kDirectlyAllocatedResourceSize = 5000000; // 5MB +constexpr uint32_t kSuballocatedResourceSize = 1000000; // 1MB +constexpr uint32_t kSourceBufferSize = 4; // 4B + +constexpr wgpu::BufferUsage kMapReadBufferUsage = + wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead; +constexpr wgpu::BufferUsage kMapWriteBufferUsage = + wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite; +constexpr wgpu::BufferUsage kNonMappableBufferUsage = wgpu::BufferUsage::CopyDst; + +class D3D12ResidencyTestBase : public DawnTest { + protected: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + + // Restrict Dawn's budget to create an artificial budget. + dawn_native::d3d12::Device* d3dDevice = + reinterpret_cast(device.Get()); + d3dDevice->GetResidencyManager()->RestrictBudgetForTesting(kRestrictedBudgetSize); + + // Initialize a source buffer on the GPU to serve as a source to quickly copy data to other + // buffers. + constexpr uint32_t one = 1; + mSourceBuffer = + utils::CreateBufferFromData(device, &one, sizeof(one), wgpu::BufferUsage::CopySrc); + } + + std::vector AllocateBuffers(uint32_t bufferSize, + uint32_t numberOfBuffers, + wgpu::BufferUsage usage) { + std::vector buffers; + + for (uint64_t i = 0; i < numberOfBuffers; i++) { + buffers.push_back(CreateBuffer(bufferSize, usage)); + } + + return buffers; + } + + wgpu::Buffer CreateBuffer(uint32_t bufferSize, wgpu::BufferUsage usage) { + wgpu::BufferDescriptor descriptor; + + descriptor.size = bufferSize; + descriptor.usage = usage; + + return device.CreateBuffer(&descriptor); + } + + void TouchBuffers(uint32_t beginIndex, + uint32_t numBuffers, + const std::vector& bufferSet) { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + + // Perform a copy on the range of buffers to ensure the are moved to dedicated GPU memory. + for (uint32_t i = beginIndex; i < beginIndex + numBuffers; i++) { + encoder.CopyBufferToBuffer(mSourceBuffer, 0, bufferSet[i], 0, kSourceBufferSize); + } + wgpu::CommandBuffer copy = encoder.Finish(); + queue.Submit(1, ©); + } + + wgpu::Buffer mSourceBuffer; + void* mMappedWriteData = nullptr; + const void* mMappedReadData = nullptr; +}; + +class D3D12ResourceResidencyTests : public D3D12ResidencyTestBase { + protected: + bool CheckAllocationMethod(wgpu::Buffer buffer, + dawn_native::AllocationMethod allocationMethod) const { + dawn_native::d3d12::Buffer* d3dBuffer = + reinterpret_cast(buffer.Get()); + return d3dBuffer->CheckAllocationMethodForTesting(allocationMethod); + } + + bool CheckIfBufferIsResident(wgpu::Buffer buffer) const { + dawn_native::d3d12::Buffer* d3dBuffer = + reinterpret_cast(buffer.Get()); + return d3dBuffer->CheckIsResidentForTesting(); + } + + bool IsUMA() const { + return reinterpret_cast(device.Get())->GetDeviceInfo().isUMA; + } + + static void MapReadCallback(WGPUBufferMapAsyncStatus status, + const void* data, + uint64_t, + void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + ASSERT_NE(nullptr, data); + + static_cast(userdata)->mMappedReadData = data; + } + + static void MapWriteCallback(WGPUBufferMapAsyncStatus status, + void* data, + uint64_t, + void* userdata) { + ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status); + ASSERT_NE(nullptr, data); + + static_cast(userdata)->mMappedWriteData = data; + } +}; + +class D3D12DescriptorResidencyTests : public D3D12ResidencyTestBase {}; + +// Check that resources existing on suballocated heaps are made resident and evicted correctly. +TEST_P(D3D12ResourceResidencyTests, OvercommitSmallResources) { + // TODO(http://crbug.com/dawn/416): Tests fails on Intel HD 630 bot. + DAWN_SKIP_TEST_IF(IsIntel() && IsBackendValidationEnabled()); + + // Create suballocated buffers to fill half the budget. + std::vector bufferSet1 = AllocateBuffers( + kSuballocatedResourceSize, ((kRestrictedBudgetSize / 2) / kSuballocatedResourceSize), + kNonMappableBufferUsage); + + // Check that all the buffers allocated are resident. Also make sure they were suballocated + // internally. + for (uint32_t i = 0; i < bufferSet1.size(); i++) { + EXPECT_TRUE(CheckIfBufferIsResident(bufferSet1[i])); + EXPECT_TRUE( + CheckAllocationMethod(bufferSet1[i], dawn_native::AllocationMethod::kSubAllocated)); + } + + // Create enough directly-allocated buffers to use the entire budget. + std::vector bufferSet2 = AllocateBuffers( + kDirectlyAllocatedResourceSize, kRestrictedBudgetSize / kDirectlyAllocatedResourceSize, + kNonMappableBufferUsage); + + // Check that everything in bufferSet1 is now evicted. + for (uint32_t i = 0; i < bufferSet1.size(); i++) { + EXPECT_FALSE(CheckIfBufferIsResident(bufferSet1[i])); + } + + // Touch one of the non-resident buffers. This should cause the buffer to become resident. + constexpr uint32_t indexOfBufferInSet1 = 5; + TouchBuffers(indexOfBufferInSet1, 1, bufferSet1); + // Check that this buffer is now resident. + EXPECT_TRUE(CheckIfBufferIsResident(bufferSet1[indexOfBufferInSet1])); + + // Touch everything in bufferSet2 again to evict the buffer made resident in the previous + // operation. + TouchBuffers(0, bufferSet2.size(), bufferSet2); + // Check that indexOfBufferInSet1 was evicted. + EXPECT_FALSE(CheckIfBufferIsResident(bufferSet1[indexOfBufferInSet1])); +} + +// Check that resources existing on directly allocated heaps are made resident and evicted +// correctly. +TEST_P(D3D12ResourceResidencyTests, OvercommitLargeResources) { + // Create directly-allocated buffers to fill half the budget. + std::vector bufferSet1 = AllocateBuffers( + kDirectlyAllocatedResourceSize, + ((kRestrictedBudgetSize / 2) / kDirectlyAllocatedResourceSize), kNonMappableBufferUsage); + + // Check that all the allocated buffers are resident. Also make sure they were directly + // allocated internally. + for (uint32_t i = 0; i < bufferSet1.size(); i++) { + EXPECT_TRUE(CheckIfBufferIsResident(bufferSet1[i])); + EXPECT_TRUE(CheckAllocationMethod(bufferSet1[i], dawn_native::AllocationMethod::kDirect)); + } + + // Create enough directly-allocated buffers to use the entire budget. + std::vector bufferSet2 = AllocateBuffers( + kDirectlyAllocatedResourceSize, kRestrictedBudgetSize / kDirectlyAllocatedResourceSize, + kNonMappableBufferUsage); + + // Check that everything in bufferSet1 is now evicted. + for (uint32_t i = 0; i < bufferSet1.size(); i++) { + EXPECT_FALSE(CheckIfBufferIsResident(bufferSet1[i])); + } + + // Touch one of the non-resident buffers. This should cause the buffer to become resident. + constexpr uint32_t indexOfBufferInSet1 = 1; + TouchBuffers(indexOfBufferInSet1, 1, bufferSet1); + EXPECT_TRUE(CheckIfBufferIsResident(bufferSet1[indexOfBufferInSet1])); + + // Touch everything in bufferSet2 again to evict the buffer made resident in the previous + // operation. + TouchBuffers(0, bufferSet2.size(), bufferSet2); + // Check that indexOfBufferInSet1 was evicted. + EXPECT_FALSE(CheckIfBufferIsResident(bufferSet1[indexOfBufferInSet1])); +} + +// Check that calling MapReadAsync makes the buffer resident and keeps it locked resident. +TEST_P(D3D12ResourceResidencyTests, AsyncMappedBufferRead) { + // Create a mappable buffer. + wgpu::Buffer buffer = CreateBuffer(4, kMapReadBufferUsage); + + uint32_t data = 12345; + queue.WriteBuffer(buffer, 0, &data, sizeof(uint32_t)); + + // The mappable buffer should be resident. + EXPECT_TRUE(CheckIfBufferIsResident(buffer)); + + // Create and touch enough buffers to use the entire budget. + std::vector bufferSet = AllocateBuffers( + kDirectlyAllocatedResourceSize, kRestrictedBudgetSize / kDirectlyAllocatedResourceSize, + kMapReadBufferUsage); + TouchBuffers(0, bufferSet.size(), bufferSet); + + // The mappable buffer should have been evicted. + EXPECT_FALSE(CheckIfBufferIsResident(buffer)); + + // Calling MapReadAsync should make the buffer resident. + buffer.MapReadAsync(MapReadCallback, this); + EXPECT_TRUE(CheckIfBufferIsResident(buffer)); + + while (mMappedReadData == nullptr) { + WaitABit(); + } + + // Touch enough resources such that the entire budget is used. The mappable buffer should remain + // locked resident. + TouchBuffers(0, bufferSet.size(), bufferSet); + EXPECT_TRUE(CheckIfBufferIsResident(buffer)); + + // Unmap the buffer, allocate and touch enough resources such that the entire budget is used. + // This should evict the mappable buffer. + buffer.Unmap(); + std::vector bufferSet2 = AllocateBuffers( + kDirectlyAllocatedResourceSize, kRestrictedBudgetSize / kDirectlyAllocatedResourceSize, + kMapReadBufferUsage); + TouchBuffers(0, bufferSet2.size(), bufferSet2); + EXPECT_FALSE(CheckIfBufferIsResident(buffer)); +} + +// Check that calling MapWriteAsync makes the buffer resident and keeps it locked resident. +TEST_P(D3D12ResourceResidencyTests, AsyncMappedBufferWrite) { + // Create a mappable buffer. + wgpu::Buffer buffer = CreateBuffer(4, kMapWriteBufferUsage); + // The mappable buffer should be resident. + EXPECT_TRUE(CheckIfBufferIsResident(buffer)); + + // Create and touch enough buffers to use the entire budget. + std::vector bufferSet1 = AllocateBuffers( + kDirectlyAllocatedResourceSize, kRestrictedBudgetSize / kDirectlyAllocatedResourceSize, + kMapReadBufferUsage); + TouchBuffers(0, bufferSet1.size(), bufferSet1); + + // The mappable buffer should have been evicted. + EXPECT_FALSE(CheckIfBufferIsResident(buffer)); + + // Calling MapWriteAsync should make the buffer resident. + buffer.MapWriteAsync(MapWriteCallback, this); + EXPECT_TRUE(CheckIfBufferIsResident(buffer)); + + while (mMappedWriteData == nullptr) { + WaitABit(); + } + + // Touch enough resources such that the entire budget is used. The mappable buffer should remain + // locked resident. + TouchBuffers(0, bufferSet1.size(), bufferSet1); + EXPECT_TRUE(CheckIfBufferIsResident(buffer)); + + // Unmap the buffer, allocate and touch enough resources such that the entire budget is used. + // This should evict the mappable buffer. + buffer.Unmap(); + std::vector bufferSet2 = AllocateBuffers( + kDirectlyAllocatedResourceSize, kRestrictedBudgetSize / kDirectlyAllocatedResourceSize, + kMapReadBufferUsage); + TouchBuffers(0, bufferSet2.size(), bufferSet2); + EXPECT_FALSE(CheckIfBufferIsResident(buffer)); +} + +// Check that overcommitting in a single submit works, then make sure the budget is enforced after. +TEST_P(D3D12ResourceResidencyTests, OvercommitInASingleSubmit) { + // Create enough buffers to exceed the budget + constexpr uint32_t numberOfBuffersToOvercommit = 5; + std::vector bufferSet1 = AllocateBuffers( + kDirectlyAllocatedResourceSize, + (kRestrictedBudgetSize / kDirectlyAllocatedResourceSize) + numberOfBuffersToOvercommit, + kNonMappableBufferUsage); + // Touch the buffers, which creates an overcommitted command list. + TouchBuffers(0, bufferSet1.size(), bufferSet1); + // Ensure that all of these buffers are resident, even though we're exceeding the budget. + for (uint32_t i = 0; i < bufferSet1.size(); i++) { + EXPECT_TRUE(CheckIfBufferIsResident(bufferSet1[i])); + } + + // Allocate another set of buffers that exceeds the budget. + std::vector bufferSet2 = AllocateBuffers( + kDirectlyAllocatedResourceSize, + (kRestrictedBudgetSize / kDirectlyAllocatedResourceSize) + numberOfBuffersToOvercommit, + kNonMappableBufferUsage); + // Ensure the first buffers in the second buffer set were evicted, + // since they shouldn't fit in the budget. + for (uint32_t i = 0; i < numberOfBuffersToOvercommit; i++) { + EXPECT_FALSE(CheckIfBufferIsResident(bufferSet2[i])); + } +} + +TEST_P(D3D12ResourceResidencyTests, SetExternalReservation) { + // Set an external reservation of 20% the budget. We should succesfully reserve the amount we + // request. + uint64_t amountReserved = dawn_native::d3d12::SetExternalMemoryReservation( + device.Get(), kRestrictedBudgetSize * .2, dawn_native::d3d12::MemorySegment::Local); + EXPECT_EQ(amountReserved, kRestrictedBudgetSize * .2); + + // If we're on a non-UMA device, we should also check the NON_LOCAL memory segment. + if (!IsUMA()) { + amountReserved = dawn_native::d3d12::SetExternalMemoryReservation( + device.Get(), kRestrictedBudgetSize * .2, dawn_native::d3d12::MemorySegment::NonLocal); + EXPECT_EQ(amountReserved, kRestrictedBudgetSize * .2); + } +} + +// Checks that when a descriptor heap is bound, it is locked resident. Also checks that when a +// previous descriptor heap becomes unbound, it is unlocked, placed in the LRU and can be evicted. +TEST_P(D3D12DescriptorResidencyTests, SwitchedViewHeapResidency) { + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + + // Fill in a view heap with "view only" bindgroups (1x view per group) by creating a + // view bindgroup each draw. After HEAP_SIZE + 1 draws, the heaps must switch over. + renderPipelineDescriptor.vertexStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, R"( + #version 450 + void main() { + const vec2 pos[3] = vec2[3](vec2(-1.f, 1.f), vec2(1.f, 1.f), vec2(-1.f, -1.f)); + gl_Position = vec4(pos[gl_VertexIndex], 0.f, 1.f); + })"); + + renderPipelineDescriptor.cFragmentStage.module = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, R"( + #version 450 + layout (location = 0) out vec4 fragColor; + layout (set = 0, binding = 0) uniform colorBuffer { + vec4 color; + }; + void main() { + fragColor = color; + })"); + + wgpu::RenderPipeline renderPipeline = device.CreateRenderPipeline(&renderPipelineDescriptor); + constexpr uint32_t kSize = 512; + utils::BasicRenderPass renderPass = utils::CreateBasicRenderPass(device, kSize, kSize); + + wgpu::SamplerDescriptor samplerDesc = utils::GetDefaultSamplerDescriptor(); + wgpu::Sampler sampler = device.CreateSampler(&samplerDesc); + + dawn_native::d3d12::Device* d3dDevice = + reinterpret_cast(device.Get()); + + dawn_native::d3d12::ShaderVisibleDescriptorAllocator* allocator = + d3dDevice->GetViewShaderVisibleDescriptorAllocator(); + const uint64_t heapSize = allocator->GetShaderVisibleHeapSizeForTesting(); + + const Serial heapSerial = allocator->GetShaderVisibleHeapSerialForTesting(); + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + { + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo); + + pass.SetPipeline(renderPipeline); + + std::array redColor = {1, 0, 0, 1}; + wgpu::Buffer uniformBuffer = utils::CreateBufferFromData( + device, &redColor, sizeof(redColor), wgpu::BufferUsage::Uniform); + + for (uint32_t i = 0; i < heapSize + 1; ++i) { + pass.SetBindGroup(0, utils::MakeBindGroup(device, renderPipeline.GetBindGroupLayout(0), + {{0, uniformBuffer, 0, sizeof(redColor)}})); + pass.Draw(3); + } + + pass.EndPass(); + } + + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Check the heap serial to ensure the heap has switched. + EXPECT_EQ(allocator->GetShaderVisibleHeapSerialForTesting(), heapSerial + 1); + + // Check that currrently bound ShaderVisibleHeap is locked resident. + EXPECT_TRUE(allocator->IsShaderVisibleHeapLockedResidentForTesting()); + // Check that the previously bound ShaderVisibleHeap was unlocked and was placed in the LRU + // cache. + EXPECT_TRUE(allocator->IsLastShaderVisibleHeapInLRUForTesting()); + // Allocate enough buffers to exceed the budget, which will purge everything from the Residency + // LRU. + AllocateBuffers(kDirectlyAllocatedResourceSize, + kRestrictedBudgetSize / kDirectlyAllocatedResourceSize, + kNonMappableBufferUsage); + // Check that currrently bound ShaderVisibleHeap remained locked resident. + EXPECT_TRUE(allocator->IsShaderVisibleHeapLockedResidentForTesting()); + // Check that the previously bound ShaderVisibleHeap has been evicted from the LRU cache. + EXPECT_FALSE(allocator->IsLastShaderVisibleHeapInLRUForTesting()); +} + +DAWN_INSTANTIATE_TEST(D3D12ResourceResidencyTests, D3D12Backend()); +DAWN_INSTANTIATE_TEST(D3D12DescriptorResidencyTests, + D3D12Backend({"use_d3d12_small_shader_visible_heap"})); diff --git a/third_party/dawn/src/tests/white_box/D3D12SmallTextureTests.cpp b/third_party/dawn/src/tests/white_box/D3D12SmallTextureTests.cpp new file mode 100644 index 00000000000..5566e158bcc --- /dev/null +++ b/third_party/dawn/src/tests/white_box/D3D12SmallTextureTests.cpp @@ -0,0 +1,77 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "dawn_native/d3d12/TextureD3D12.h" + +using namespace dawn_native::d3d12; + +class D3D12SmallTextureTests : public DawnTest { + protected: + std::vector GetRequiredExtensions() override { + mIsBCFormatSupported = SupportsExtensions({"texture_compression_bc"}); + if (!mIsBCFormatSupported) { + return {}; + } + + return {"texture_compression_bc"}; + } + + bool IsBCFormatSupported() const { + return mIsBCFormatSupported; + } + + private: + bool mIsBCFormatSupported = false; +}; + +// Verify that creating a small compressed textures will be 4KB aligned. +TEST_P(D3D12SmallTextureTests, AlignSmallCompressedTexture) { + DAWN_SKIP_TEST_IF(UsesWire()); + DAWN_SKIP_TEST_IF(!IsBCFormatSupported()); + + // TODO(http://crbug.com/dawn/282): Investigate GPU/driver rejections of small alignment. + DAWN_SKIP_TEST_IF(IsIntel() || IsNvidia() || IsWARP()); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = 8; + descriptor.size.height = 8; + descriptor.size.depth = 1; + descriptor.arrayLayerCount = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::BC1RGBAUnorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::Sampled; + + // Create a smaller one that allows use of the smaller alignment. + wgpu::Texture texture = device.CreateTexture(&descriptor); + Texture* d3dTexture = reinterpret_cast(texture.Get()); + + EXPECT_EQ(d3dTexture->GetD3D12Resource()->GetDesc().Alignment, + static_cast(D3D12_SMALL_RESOURCE_PLACEMENT_ALIGNMENT)); + + // Create a larger one (>64KB) that forbids use the smaller alignment. + descriptor.size.width = 4096; + descriptor.size.height = 4096; + + texture = device.CreateTexture(&descriptor); + d3dTexture = reinterpret_cast(texture.Get()); + + EXPECT_EQ(d3dTexture->GetD3D12Resource()->GetDesc().Alignment, + static_cast(D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT)); +} + +DAWN_INSTANTIATE_TEST(D3D12SmallTextureTests, D3D12Backend()); diff --git a/third_party/dawn/src/tests/white_box/InternalResourceUsageTests.cpp b/third_party/dawn/src/tests/white_box/InternalResourceUsageTests.cpp new file mode 100644 index 00000000000..46382f24d33 --- /dev/null +++ b/third_party/dawn/src/tests/white_box/InternalResourceUsageTests.cpp @@ -0,0 +1,45 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "dawn_native/dawn_platform.h" + +class InternalResourceUsageTests : public DawnTest {}; + +// Verify it is an error to create a buffer with a buffer usage that should only be used +// internally. +TEST_P(InternalResourceUsageTests, InternalBufferUsage) { + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + + wgpu::BufferDescriptor descriptor; + descriptor.size = 4; + descriptor.usage = dawn_native::kReadOnlyStorageBuffer; + + ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); +} + +// Verify it is an error to create a texture with a texture usage that should only be used +// internally. +TEST_P(InternalResourceUsageTests, InternalTextureUsage) { + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + + wgpu::TextureDescriptor descriptor; + descriptor.format = wgpu::TextureFormat::RGBA8Unorm; + descriptor.size = {1, 1, 1}; + descriptor.usage = dawn_native::kReadonlyStorageTexture; + ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor)); +} + +DAWN_INSTANTIATE_TEST(InternalResourceUsageTests, NullBackend()); diff --git a/third_party/dawn/src/tests/white_box/MetalAutoreleasePoolTests.mm b/third_party/dawn/src/tests/white_box/MetalAutoreleasePoolTests.mm new file mode 100644 index 00000000000..6c0a850ba29 --- /dev/null +++ b/third_party/dawn/src/tests/white_box/MetalAutoreleasePoolTests.mm @@ -0,0 +1,62 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "dawn_native/metal/DeviceMTL.h" + +using namespace dawn_native::metal; + +class MetalAutoreleasePoolTests : public DawnTest { + private: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + + mMtlDevice = reinterpret_cast(device.Get()); + } + + protected: + Device* mMtlDevice = nullptr; +}; + +// Test that the MTLCommandBuffer owned by the pending command context can +// outlive an autoreleasepool block. +TEST_P(MetalAutoreleasePoolTests, CommandBufferOutlivesAutorelease) { + @autoreleasepool { + // Get the recording context which will allocate a MTLCommandBuffer. + // It will get autoreleased at the end of this block. + mMtlDevice->GetPendingCommandContext(); + } + + // Submitting the command buffer should succeed. + mMtlDevice->SubmitPendingCommandBuffer(); +} + +// Test that the MTLBlitCommandEncoder owned by the pending command context +// can outlive an autoreleasepool block. +TEST_P(MetalAutoreleasePoolTests, EncoderOutlivesAutorelease) { + @autoreleasepool { + // Get the recording context which will allocate a MTLCommandBuffer. + // Begin a blit encoder. + // Both will get autoreleased at the end of this block. + mMtlDevice->GetPendingCommandContext()->EnsureBlit(); + } + + // Submitting the command buffer should succeed. + mMtlDevice->GetPendingCommandContext()->EndBlit(); + mMtlDevice->SubmitPendingCommandBuffer(); +} + +DAWN_INSTANTIATE_TEST(MetalAutoreleasePoolTests, MetalBackend()); diff --git a/third_party/dawn/src/tests/white_box/VulkanErrorInjectorTests.cpp b/third_party/dawn/src/tests/white_box/VulkanErrorInjectorTests.cpp new file mode 100644 index 00000000000..0a5bde64379 --- /dev/null +++ b/third_party/dawn/src/tests/white_box/VulkanErrorInjectorTests.cpp @@ -0,0 +1,124 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Math.h" +#include "common/vulkan_platform.h" +#include "dawn_native/ErrorData.h" +#include "dawn_native/VulkanBackend.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/VulkanError.h" + +namespace { + + class VulkanErrorInjectorTests : public DawnTest { + public: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + + mDeviceVk = reinterpret_cast(device.Get()); + } + + protected: + dawn_native::vulkan::Device* mDeviceVk; + }; + +} // anonymous namespace + +TEST_P(VulkanErrorInjectorTests, InjectErrorOnCreateBuffer) { + VkBufferCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + createInfo.pNext = nullptr; + createInfo.size = 16; + createInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + // Check that making a buffer works. + { + VkBuffer buffer = VK_NULL_HANDLE; + EXPECT_EQ( + mDeviceVk->fn.CreateBuffer(mDeviceVk->GetVkDevice(), &createInfo, nullptr, &buffer), + VK_SUCCESS); + mDeviceVk->fn.DestroyBuffer(mDeviceVk->GetVkDevice(), buffer, nullptr); + } + + auto CreateTestBuffer = [&]() -> bool { + VkBuffer buffer = VK_NULL_HANDLE; + dawn_native::MaybeError err = CheckVkSuccess( + mDeviceVk->fn.CreateBuffer(mDeviceVk->GetVkDevice(), &createInfo, nullptr, &buffer), + "vkCreateBuffer"); + if (err.IsError()) { + // The handle should never be written to, even for mock failures. + EXPECT_EQ(buffer, VK_NULL_HANDLE); + err.AcquireError(); + return false; + } + EXPECT_NE(buffer, VK_NULL_HANDLE); + + // We never use the buffer, only test mocking errors on creation. Cleanup now. + mDeviceVk->fn.DestroyBuffer(mDeviceVk->GetVkDevice(), buffer, nullptr); + + return true; + }; + + // Check that making a buffer inside CheckVkSuccess works. + { + EXPECT_TRUE(CreateTestBuffer()); + + // The error injector call count should be empty + EXPECT_EQ(dawn_native::AcquireErrorInjectorCallCount(), 0u); + } + + // Test error injection works. + dawn_native::EnableErrorInjector(); + { + EXPECT_TRUE(CreateTestBuffer()); + EXPECT_TRUE(CreateTestBuffer()); + + // The error injector call count should be two. + EXPECT_EQ(dawn_native::AcquireErrorInjectorCallCount(), 2u); + + // Inject an error at index 0. The first should fail, the second succeed. + { + dawn_native::InjectErrorAt(0u); + EXPECT_FALSE(CreateTestBuffer()); + EXPECT_TRUE(CreateTestBuffer()); + + dawn_native::ClearErrorInjector(); + } + + // Inject an error at index 1. The second should fail, the first succeed. + { + dawn_native::InjectErrorAt(1u); + EXPECT_TRUE(CreateTestBuffer()); + EXPECT_FALSE(CreateTestBuffer()); + + dawn_native::ClearErrorInjector(); + } + + // Inject an error and then clear the injector. Calls should be successful. + { + dawn_native::InjectErrorAt(0u); + dawn_native::DisableErrorInjector(); + + EXPECT_TRUE(CreateTestBuffer()); + EXPECT_TRUE(CreateTestBuffer()); + + dawn_native::ClearErrorInjector(); + } + } +} + +DAWN_INSTANTIATE_TEST(VulkanErrorInjectorTests, VulkanBackend()); diff --git a/third_party/dawn/src/tests/white_box/VulkanImageWrappingTestsDmaBuf.cpp b/third_party/dawn/src/tests/white_box/VulkanImageWrappingTestsDmaBuf.cpp new file mode 100644 index 00000000000..f52b365560b --- /dev/null +++ b/third_party/dawn/src/tests/white_box/VulkanImageWrappingTestsDmaBuf.cpp @@ -0,0 +1,832 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "tests/DawnTest.h" + +#include "common/Math.h" +#include "common/vulkan_platform.h" +#include "dawn_native/VulkanBackend.h" +#include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" +#include "dawn_native/vulkan/TextureVk.h" +#include "utils/SystemUtils.h" +#include "utils/WGPUHelpers.h" + +#include +#include + +namespace dawn_native { namespace vulkan { + + namespace { + + class VulkanImageWrappingTestBase : public DawnTest { + public: + void SetUp() override { + DAWN_SKIP_TEST_IF(UsesWire()); + + gbmDevice = CreateGbmDevice(); + deviceVk = reinterpret_cast(device.Get()); + + defaultGbmBo = CreateGbmBo(1, 1, true /* linear */); + defaultStride = gbm_bo_get_stride_for_plane(defaultGbmBo, 0); + defaultModifier = gbm_bo_get_modifier(defaultGbmBo); + defaultFd = gbm_bo_get_fd(defaultGbmBo); + + defaultDescriptor.dimension = wgpu::TextureDimension::e2D; + defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + defaultDescriptor.size = {1, 1, 1}; + defaultDescriptor.sampleCount = 1; + defaultDescriptor.mipLevelCount = 1; + defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + } + + void TearDown() override { + if (UsesWire()) + return; + + gbm_bo_destroy(defaultGbmBo); + gbm_device_destroy(gbmDevice); + } + + gbm_device* CreateGbmDevice() { + // Render nodes [1] are the primary interface for communicating with the GPU on + // devices that support DRM. The actual filename of the render node is + // implementation-specific, so we must scan through all possible filenames to find + // one that we can use [2]. + // + // [1] https://dri.freedesktop.org/docs/drm/gpu/drm-uapi.html#render-nodes + // [2] + // https://cs.chromium.org/chromium/src/ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.cc + const uint32_t kRenderNodeStart = 128; + const uint32_t kRenderNodeEnd = kRenderNodeStart + 16; + const std::string kRenderNodeTemplate = "/dev/dri/renderD"; + + int renderNodeFd = -1; + for (uint32_t i = kRenderNodeStart; i < kRenderNodeEnd; i++) { + std::string renderNode = kRenderNodeTemplate + std::to_string(i); + renderNodeFd = open(renderNode.c_str(), O_RDWR); + if (renderNodeFd >= 0) + break; + } + EXPECT_GE(renderNodeFd, 0) << "Failed to get file descriptor for render node"; + + gbm_device* gbmDevice = gbm_create_device(renderNodeFd); + EXPECT_NE(gbmDevice, nullptr) << "Failed to create GBM device"; + return gbmDevice; + } + + gbm_bo* CreateGbmBo(uint32_t width, uint32_t height, bool linear) { + uint32_t flags = GBM_BO_USE_RENDERING; + if (linear) + flags |= GBM_BO_USE_LINEAR; + gbm_bo* gbmBo = gbm_bo_create(gbmDevice, width, height, GBM_FORMAT_XBGR8888, flags); + EXPECT_NE(gbmBo, nullptr) << "Failed to create GBM buffer object"; + return gbmBo; + } + + wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice, + const wgpu::TextureDescriptor* textureDescriptor, + int memoryFd, + uint32_t stride, + uint64_t drmModifier, + std::vector waitFDs, + bool isCleared = true, + bool expectValid = true) { + dawn_native::vulkan::ExternalImageDescriptorDmaBuf descriptor; + descriptor.cTextureDescriptor = + reinterpret_cast(textureDescriptor); + descriptor.isCleared = isCleared; + descriptor.stride = stride; + descriptor.drmModifier = drmModifier; + descriptor.memoryFD = memoryFd; + descriptor.waitFDs = waitFDs; + + WGPUTexture texture = + dawn_native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor); + + if (expectValid) { + EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / " + "semaphore extensions supported?"; + } else { + EXPECT_EQ(texture, nullptr); + } + + return wgpu::Texture::Acquire(texture); + } + + // Exports the signal from a wrapped texture and ignores it + // We have to export the signal before destroying the wrapped texture else it's an + // assertion failure + void IgnoreSignalSemaphore(wgpu::Device dawnDevice, wgpu::Texture wrappedTexture) { + int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(dawnDevice.Get(), + wrappedTexture.Get()); + ASSERT_NE(fd, -1); + close(fd); + } + + protected: + dawn_native::vulkan::Device* deviceVk; + gbm_device* gbmDevice; + wgpu::TextureDescriptor defaultDescriptor; + gbm_bo* defaultGbmBo; + int defaultFd; + uint32_t defaultStride; + uint64_t defaultModifier; + }; + + } // anonymous namespace + + using VulkanImageWrappingValidationTests = VulkanImageWrappingTestBase; + + // Test no error occurs if the import is valid + TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) { + wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}, true, true); + EXPECT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(device, texture); + } + + // Test an error occurs if the texture descriptor is missing + TEST_P(VulkanImageWrappingValidationTests, MissingTextureDescriptor) { + ASSERT_DEVICE_ERROR(wgpu::Texture texture = + WrapVulkanImage(device, nullptr, defaultFd, defaultStride, + defaultModifier, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + close(defaultFd); + } + + // Test an error occurs if the texture descriptor is invalid + TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) { + wgpu::ChainedStruct chainedDescriptor; + defaultDescriptor.nextInChain = &chainedDescriptor; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + close(defaultFd); + } + + // Test an error occurs if the descriptor dimension isn't 2D + TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) { + defaultDescriptor.dimension = wgpu::TextureDimension::e1D; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + close(defaultFd); + } + + // Test an error occurs if the descriptor mip level count isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) { + defaultDescriptor.mipLevelCount = 2; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + close(defaultFd); + } + + // Test an error occurs if the descriptor depth isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidDepth) { + defaultDescriptor.size.depth = 2; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + close(defaultFd); + } + + // Test an error occurs if the descriptor sample count isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) { + defaultDescriptor.sampleCount = 4; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + close(defaultFd); + } + + // Test an error occurs if we try to export the signal semaphore twice + TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) { + wgpu::Texture texture = WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}, true, true); + ASSERT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(device, texture); + ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); + } + + // Test an error occurs if we try to export the signal semaphore from a normal texture + TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) { + close(defaultFd); + + wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); + ASSERT_NE(texture.Get(), nullptr); + ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); + } + + // Test an error occurs if we try to export the signal semaphore from a destroyed texture + TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) { + close(defaultFd); + + wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); + ASSERT_NE(texture.Get(), nullptr); + texture.Destroy(); + ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); + } + + // Fixture to test using external memory textures through different usages. + // These tests are skipped if the harness is using the wire. + class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase { + public: + void SetUp() override { + VulkanImageWrappingTestBase::SetUp(); + if (UsesWire()) { + return; + } + + // Create another device based on the original + backendAdapter = + reinterpret_cast(deviceVk->GetAdapter()); + deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds; + deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds; + + secondDeviceVk = reinterpret_cast( + backendAdapter->CreateDevice(&deviceDescriptor)); + secondDevice = wgpu::Device::Acquire(reinterpret_cast(secondDeviceVk)); + } + + protected: + dawn_native::vulkan::Adapter* backendAdapter; + dawn_native::DeviceDescriptor deviceDescriptor; + + wgpu::Device secondDevice; + dawn_native::vulkan::Device* secondDeviceVk; + + // Clear a texture on a given device + void ClearImage(wgpu::Device dawnDevice, + wgpu::Texture wrappedTexture, + wgpu::Color clearColor) { + wgpu::TextureView wrappedView = wrappedTexture.CreateView(); + + // Submit a clear operation + utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {}); + renderPassDescriptor.cColorAttachments[0].clearColor = clearColor; + renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + + wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = dawnDevice.GetDefaultQueue(); + queue.Submit(1, &commands); + } + + // Submits a 1x1x1 copy from source to destination + void SimpleCopyTextureToTexture(wgpu::Device dawnDevice, + wgpu::Queue dawnQueue, + wgpu::Texture source, + wgpu::Texture destination) { + wgpu::TextureCopyView copySrc = utils::CreateTextureCopyView(source, 0, {0, 0, 0}); + wgpu::TextureCopyView copyDst = utils::CreateTextureCopyView(destination, 0, {0, 0, 0}); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder(); + encoder.CopyTextureToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + + dawnQueue.Submit(1, &commands); + } + }; + + // Clear an image in |secondDevice| + // Verify clear color is visible in |device| + TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on signalFd + int nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Verify |device| sees the changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + // Import texture to |device| and |secondDevice| + // Clear image in |secondDevice| + // Verify clear color is visible in |device| + // Verify the very first import into |device| also sees the change, since it should + // alias the same memory + TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevicesAliased) { + // Import the image on |device| + wgpu::Texture wrappedTextureAlias = WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}); + + // Import the image on |secondDevice| + int nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, nextFd, + defaultStride, defaultModifier, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on signalFd + nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Verify |device| sees the changes from |secondDevice| (waits) + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + // Verify aliased texture sees changes from |secondDevice| (without waiting!) + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), wrappedTextureAlias, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + IgnoreSignalSemaphore(device, wrappedTextureAlias); + } + + // Clear an image in |secondDevice| + // Verify clear color is not visible in |device| if we import the texture as not cleared + TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on signalFd + int nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}, false); + + // Verify |device| doesn't see the changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + // Import a texture into |secondDevice| + // Clear the texture on |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstTexture| + // Verify the clear color from |secondDevice| is visible in |copyDstTexture| + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on |signalFd| + int nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture deviceWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Create a second texture on |device| + wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Copy |deviceWrappedTexture| into |copyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); + + // Verify |copyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); + } + + // Import a texture into |device| + // Clear texture with color A on |device| + // Import same texture into |secondDevice|, waiting on the copy signal + // Clear the new texture with color B on |secondDevice| + // Copy color B using Texture to Texture copy on |secondDevice| + // Import texture back into |device|, waiting on color B signal + // Verify texture contains color B + // If texture destination isn't synchronized, |secondDevice| could copy color B + // into the texture first, then |device| writes color A + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) { + // Import the image on |device| + wgpu::Texture wrappedTexture = WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}); + + // Clear |wrappedTexture| on |device| + ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); + + int signalFd = + dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); + + // Import the image to |secondDevice|, making sure we wait on |signalFd| + int nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture secondDeviceWrappedTexture = WrapVulkanImage( + secondDevice, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Create a texture with color B on |secondDevice| + wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor); + ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + // Copy color B on |secondDevice| + wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); + SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture, + secondDeviceWrappedTexture); + + // Re-import back into |device|, waiting on |secondDevice|'s signal + signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + secondDevice.Get(), secondDeviceWrappedTexture.Get()); + nextFd = gbm_bo_get_fd(defaultGbmBo); + + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Verify |nextWrappedTexture| contains the color from our copy + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + // Import a texture from |secondDevice| + // Clear the texture on |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstBuffer| + // Verify the clear color from |secondDevice| is visible in |copyDstBuffer| + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on |signalFd| + int nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture deviceWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Create a destination buffer on |device| + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = 4; + bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; + wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc); + + // Copy |deviceWrappedTexture| into |copyDstBuffer| + wgpu::TextureCopyView copySrc = + utils::CreateTextureCopyView(deviceWrappedTexture, 0, {0, 0, 0}); + wgpu::BufferCopyView copyDst = utils::CreateBufferCopyView(copyDstBuffer, 0, 256, 0); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Verify |copyDstBuffer| sees changes from |secondDevice| + uint32_t expected = 1; + EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); + } + + // Import a texture into |device| + // Clear texture with color A on |device| + // Import same texture into |secondDevice|, waiting on the copy signal + // Copy color B using Buffer to Texture copy on |secondDevice| + // Import texture back into |device|, waiting on color B signal + // Verify texture contains color B + // If texture destination isn't synchronized, |secondDevice| could copy color B + // into the texture first, then |device| writes color A + TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) { + // Import the image on |device| + wgpu::Texture wrappedTexture = WrapVulkanImage(device, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}); + + // Clear |wrappedTexture| on |device| + ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); + + int signalFd = + dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); + + // Import the image to |secondDevice|, making sure we wait on |signalFd| + int nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture secondDeviceWrappedTexture = WrapVulkanImage( + secondDevice, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Copy color B on |secondDevice| + wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); + + // Create a buffer on |secondDevice| + wgpu::Buffer copySrcBuffer = + utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201}); + + // Copy |copySrcBuffer| into |secondDeviceWrappedTexture| + wgpu::BufferCopyView copySrc = utils::CreateBufferCopyView(copySrcBuffer, 0, 256, 0); + wgpu::TextureCopyView copyDst = + utils::CreateTextureCopyView(secondDeviceWrappedTexture, 0, {0, 0, 0}); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); + encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + secondDeviceQueue.Submit(1, &commands); + + // Re-import back into |device|, waiting on |secondDevice|'s signal + signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + secondDevice.Get(), secondDeviceWrappedTexture.Get()); + nextFd = gbm_bo_get_fd(defaultGbmBo); + + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Verify |nextWrappedTexture| contains the color from our copy + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + // Import a texture from |secondDevice| + // Clear the texture on |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstTexture| + // Issue second copy to |secondCopyDstTexture| + // Verify the clear color from |secondDevice| is visible in both copies + TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) { + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, + defaultStride, defaultModifier, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on |signalFd| + int nextFd = gbm_bo_get_fd(defaultGbmBo); + wgpu::Texture deviceWrappedTexture = WrapVulkanImage( + device, &defaultDescriptor, nextFd, defaultStride, defaultModifier, {signalFd}); + + // Create a second texture on |device| + wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Create a third texture on |device| + wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Copy |deviceWrappedTexture| into |copyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); + + // Copy |deviceWrappedTexture| into |secondCopyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture); + + // Verify |copyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); + + // Verify |secondCopyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); + } + + // Tex A on device 3 (external export) + // Tex B on device 2 (external export) + // Tex C on device 1 (external export) + // Clear color for A on device 3 + // Copy A->B on device 3 + // Copy B->C on device 2 (wait on B from previous op) + // Copy C->D on device 1 (wait on C from previous op) + // Verify D has same color as A + TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) { + // Close |defaultFd| since this test doesn't import it anywhere + close(defaultFd); + + // device 1 = |device| + // device 2 = |secondDevice| + // Create device 3 + dawn_native::vulkan::Device* thirdDeviceVk = reinterpret_cast( + backendAdapter->CreateDevice(&deviceDescriptor)); + wgpu::Device thirdDevice = + wgpu::Device::Acquire(reinterpret_cast(thirdDeviceVk)); + + // Make queue for device 2 and 3 + wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); + wgpu::Queue thirdDeviceQueue = thirdDevice.GetDefaultQueue(); + + // Create BOs for A, B, C + gbm_bo* gbmBoA = CreateGbmBo(1, 1, true /* linear */); + uint32_t fdA = gbm_bo_get_fd(gbmBoA); + uint32_t strideA = gbm_bo_get_stride_for_plane(gbmBoA, 0); + uint64_t modifierA = gbm_bo_get_modifier(gbmBoA); + + gbm_bo* gbmBoB = CreateGbmBo(1, 1, true /* linear */); + uint32_t fdB = gbm_bo_get_fd(gbmBoB); + uint32_t strideB = gbm_bo_get_stride_for_plane(gbmBoB, 0); + uint64_t modifierB = gbm_bo_get_modifier(gbmBoB); + + gbm_bo* gbmBoC = CreateGbmBo(1, 1, true /* linear */); + uint32_t fdC = gbm_bo_get_fd(gbmBoC); + uint32_t strideC = gbm_bo_get_stride_for_plane(gbmBoC, 0); + uint64_t modifierC = gbm_bo_get_modifier(gbmBoC); + + // Import TexA, TexB on device 3 + wgpu::Texture wrappedTexADevice3 = + WrapVulkanImage(thirdDevice, &defaultDescriptor, fdA, strideA, modifierA, {}); + + wgpu::Texture wrappedTexBDevice3 = + WrapVulkanImage(thirdDevice, &defaultDescriptor, fdB, strideB, modifierB, {}); + + // Clear TexA + ClearImage(thirdDevice, wrappedTexADevice3, + {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + // Copy A->B + SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3, + wrappedTexBDevice3); + + int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + thirdDevice.Get(), wrappedTexBDevice3.Get()); + IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3); + + // Import TexB, TexC on device 2 + fdB = gbm_bo_get_fd(gbmBoB); + wgpu::Texture wrappedTexBDevice2 = WrapVulkanImage( + secondDevice, &defaultDescriptor, fdB, strideB, modifierB, {signalFdTexBDevice3}); + + wgpu::Texture wrappedTexCDevice2 = + WrapVulkanImage(secondDevice, &defaultDescriptor, fdC, strideC, modifierC, {}); + + // Copy B->C on device 2 + SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2, + wrappedTexCDevice2); + + int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + secondDevice.Get(), wrappedTexCDevice2.Get()); + IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2); + + // Import TexC on device 1 + fdC = gbm_bo_get_fd(gbmBoC); + wgpu::Texture wrappedTexCDevice1 = WrapVulkanImage(device, &defaultDescriptor, fdC, strideC, + modifierC, {signalFdTexCDevice2}); + + // Create TexD on device 1 + wgpu::Texture texD = device.CreateTexture(&defaultDescriptor); + + // Copy C->D on device 1 + SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD); + + // Verify D matches clear color + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0); + + IgnoreSignalSemaphore(device, wrappedTexCDevice1); + } + + // Tests a larger image is preserved when importing + TEST_P(VulkanImageWrappingUsageTests, LargerImage) { + close(defaultFd); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = 640; + descriptor.size.height = 480; + descriptor.size.depth = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::BGRA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + + // Fill memory with textures + std::vector textures; + for (int i = 0; i < 20; i++) { + textures.push_back(device.CreateTexture(&descriptor)); + } + + wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); + + // Make an image on |secondDevice| + gbm_bo* gbmBo = CreateGbmBo(640, 480, false /* linear */); + uint32_t fd = gbm_bo_get_fd(gbmBo); + uint32_t stride = gbm_bo_get_stride_for_plane(gbmBo, 0); + uint64_t modifier = gbm_bo_get_modifier(gbmBo); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &descriptor, fd, stride, modifier, {}); + + // Draw a non-trivial picture + uint32_t width = 640, height = 480, pixelSize = 4; + uint32_t bytesPerRow = Align(width * pixelSize, kTextureBytesPerRowAlignment); + std::vector data(bytesPerRow * (height - 1) + width * pixelSize); + + for (uint32_t row = 0; row < height; row++) { + for (uint32_t col = 0; col < width; col++) { + float normRow = static_cast(row) / height; + float normCol = static_cast(col) / width; + float dist = sqrt(normRow * normRow + normCol * normCol) * 3; + dist = dist - static_cast(dist); + data[4 * (row * width + col)] = static_cast(dist * 255); + data[4 * (row * width + col) + 1] = static_cast(dist * 255); + data[4 * (row * width + col) + 2] = static_cast(dist * 255); + data[4 * (row * width + col) + 3] = 255; + } + } + + // Write the picture + { + wgpu::Buffer copySrcBuffer = utils::CreateBufferFromData( + secondDevice, data.data(), data.size(), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView copySrc = + utils::CreateBufferCopyView(copySrcBuffer, 0, bytesPerRow, 0); + wgpu::TextureCopyView copyDst = + utils::CreateTextureCopyView(wrappedTexture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {width, height, 1}; + + wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); + encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + secondDeviceQueue.Submit(1, &commands); + } + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + int nextFd = gbm_bo_get_fd(gbmBo); + + // Import the image on |device| + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &descriptor, nextFd, stride, modifier, {signalFd}); + + // Copy the image into a buffer for comparison + wgpu::BufferDescriptor copyDesc; + copyDesc.size = data.size(); + copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer copyDstBuffer = device.CreateBuffer(©Desc); + { + wgpu::TextureCopyView copySrc = + utils::CreateTextureCopyView(nextWrappedTexture, 0, {0, 0, 0}); + wgpu::BufferCopyView copyDst = + utils::CreateBufferCopyView(copyDstBuffer, 0, bytesPerRow, 0); + + wgpu::Extent3D copySize = {width, height, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + // Check the image is not corrupted on |device| + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(data.data()), copyDstBuffer, 0, + data.size() / 4); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend()); + DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend()); + +}} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/tests/white_box/VulkanImageWrappingTestsOpaqueFD.cpp b/third_party/dawn/src/tests/white_box/VulkanImageWrappingTestsOpaqueFD.cpp new file mode 100644 index 00000000000..431bd03c938 --- /dev/null +++ b/third_party/dawn/src/tests/white_box/VulkanImageWrappingTestsOpaqueFD.cpp @@ -0,0 +1,1021 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/Math.h" +#include "tests/DawnTest.h" + +#include "common/vulkan_platform.h" +#include "dawn_native/VulkanBackend.h" +#include "dawn_native/vulkan/AdapterVk.h" +#include "dawn_native/vulkan/DeviceVk.h" +#include "dawn_native/vulkan/FencedDeleter.h" +#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" +#include "dawn_native/vulkan/TextureVk.h" +#include "utils/SystemUtils.h" +#include "utils/WGPUHelpers.h" + +namespace dawn_native { namespace vulkan { + + namespace { + + class VulkanImageWrappingTestBase : public DawnTest { + public: + void SetUp() override { + DawnTest::SetUp(); + DAWN_SKIP_TEST_IF(UsesWire()); + + deviceVk = reinterpret_cast(device.Get()); + } + + // Creates a VkImage with external memory + ::VkResult CreateImage(dawn_native::vulkan::Device* deviceVk, + uint32_t width, + uint32_t height, + VkFormat format, + VkImage* image) { + VkExternalMemoryImageCreateInfoKHR externalInfo; + externalInfo.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR; + externalInfo.pNext = nullptr; + externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + auto usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | + VK_IMAGE_USAGE_TRANSFER_DST_BIT; + + VkImageCreateInfo createInfo; + createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + createInfo.pNext = &externalInfo; + createInfo.flags = VK_IMAGE_CREATE_ALIAS_BIT_KHR; + createInfo.imageType = VK_IMAGE_TYPE_2D; + createInfo.format = format; + createInfo.extent = {width, height, 1}; + createInfo.mipLevels = 1; + createInfo.arrayLayers = 1; + createInfo.samples = VK_SAMPLE_COUNT_1_BIT; + createInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + createInfo.usage = usage; + createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + createInfo.queueFamilyIndexCount = 0; + createInfo.pQueueFamilyIndices = nullptr; + createInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + + return deviceVk->fn.CreateImage(deviceVk->GetVkDevice(), &createInfo, nullptr, + &**image); + } + + // Allocates memory for an image + ::VkResult AllocateMemory(dawn_native::vulkan::Device* deviceVk, + VkImage handle, + VkDeviceMemory* allocation, + VkDeviceSize* allocationSize, + uint32_t* memoryTypeIndex) { + // Create the image memory and associate it with the container + VkMemoryRequirements requirements; + deviceVk->fn.GetImageMemoryRequirements(deviceVk->GetVkDevice(), handle, + &requirements); + + // Import memory from file descriptor + VkExportMemoryAllocateInfoKHR externalInfo; + externalInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; + externalInfo.pNext = nullptr; + externalInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + int bestType = deviceVk->GetResourceMemoryAllocatorForTesting()->FindBestTypeIndex( + requirements, false); + VkMemoryAllocateInfo allocateInfo; + allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + allocateInfo.pNext = &externalInfo; + allocateInfo.allocationSize = requirements.size; + allocateInfo.memoryTypeIndex = static_cast(bestType); + + *allocationSize = allocateInfo.allocationSize; + *memoryTypeIndex = allocateInfo.memoryTypeIndex; + + return deviceVk->fn.AllocateMemory(deviceVk->GetVkDevice(), &allocateInfo, nullptr, + &**allocation); + } + + // Binds memory to an image + ::VkResult BindMemory(dawn_native::vulkan::Device* deviceVk, + VkImage handle, + VkDeviceMemory* memory) { + return deviceVk->fn.BindImageMemory(deviceVk->GetVkDevice(), handle, *memory, 0); + } + + // Extracts a file descriptor representing memory on a device + int GetMemoryFd(dawn_native::vulkan::Device* deviceVk, VkDeviceMemory memory) { + VkMemoryGetFdInfoKHR getFdInfo; + getFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; + getFdInfo.pNext = nullptr; + getFdInfo.memory = memory; + getFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; + + int memoryFd = -1; + deviceVk->fn.GetMemoryFdKHR(deviceVk->GetVkDevice(), &getFdInfo, &memoryFd); + + EXPECT_GE(memoryFd, 0) << "Failed to get file descriptor for external memory"; + return memoryFd; + } + + // Prepares and exports memory for an image on a given device + void CreateBindExportImage(dawn_native::vulkan::Device* deviceVk, + uint32_t width, + uint32_t height, + VkFormat format, + VkImage* handle, + VkDeviceMemory* allocation, + VkDeviceSize* allocationSize, + uint32_t* memoryTypeIndex, + int* memoryFd) { + ::VkResult result = CreateImage(deviceVk, width, height, format, handle); + EXPECT_EQ(result, VK_SUCCESS) << "Failed to create external image"; + + ::VkResult resultBool = + AllocateMemory(deviceVk, *handle, allocation, allocationSize, memoryTypeIndex); + EXPECT_EQ(resultBool, VK_SUCCESS) << "Failed to allocate external memory"; + + result = BindMemory(deviceVk, *handle, allocation); + EXPECT_EQ(result, VK_SUCCESS) << "Failed to bind image memory"; + + *memoryFd = GetMemoryFd(deviceVk, *allocation); + } + + // Wraps a vulkan image from external memory + wgpu::Texture WrapVulkanImage(wgpu::Device dawnDevice, + const wgpu::TextureDescriptor* textureDescriptor, + int memoryFd, + VkDeviceSize allocationSize, + uint32_t memoryTypeIndex, + std::vector waitFDs, + bool isCleared = true, + bool expectValid = true) { + dawn_native::vulkan::ExternalImageDescriptorOpaqueFD descriptor; + descriptor.cTextureDescriptor = + reinterpret_cast(textureDescriptor); + descriptor.isCleared = isCleared; + descriptor.allocationSize = allocationSize; + descriptor.memoryTypeIndex = memoryTypeIndex; + descriptor.memoryFD = memoryFd; + descriptor.waitFDs = waitFDs; + + WGPUTexture texture = + dawn_native::vulkan::WrapVulkanImage(dawnDevice.Get(), &descriptor); + + if (expectValid) { + EXPECT_NE(texture, nullptr) << "Failed to wrap image, are external memory / " + "semaphore extensions supported?"; + } else { + EXPECT_EQ(texture, nullptr); + } + + return wgpu::Texture::Acquire(texture); + } + + // Exports the signal from a wrapped texture and ignores it + // We have to export the signal before destroying the wrapped texture else it's an + // assertion failure + void IgnoreSignalSemaphore(wgpu::Device dawnDevice, wgpu::Texture wrappedTexture) { + int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(dawnDevice.Get(), + wrappedTexture.Get()); + ASSERT_NE(fd, -1); + close(fd); + } + + protected: + dawn_native::vulkan::Device* deviceVk; + }; + + } // anonymous namespace + + class VulkanImageWrappingValidationTests : public VulkanImageWrappingTestBase { + public: + void SetUp() override { + VulkanImageWrappingTestBase::SetUp(); + if (UsesWire()) { + return; + } + + CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage, + &defaultAllocation, &defaultAllocationSize, + &defaultMemoryTypeIndex, &defaultFd); + defaultDescriptor.dimension = wgpu::TextureDimension::e2D; + defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + defaultDescriptor.size = {1, 1, 1}; + defaultDescriptor.sampleCount = 1; + defaultDescriptor.mipLevelCount = 1; + defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + } + + void TearDown() override { + if (UsesWire()) { + VulkanImageWrappingTestBase::TearDown(); + return; + } + + deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation); + VulkanImageWrappingTestBase::TearDown(); + } + + protected: + wgpu::TextureDescriptor defaultDescriptor; + VkImage defaultImage; + VkDeviceMemory defaultAllocation; + VkDeviceSize defaultAllocationSize; + uint32_t defaultMemoryTypeIndex; + int defaultFd; + }; + + // Test no error occurs if the import is valid + TEST_P(VulkanImageWrappingValidationTests, SuccessfulImport) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, true); + EXPECT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(device, texture); + } + + // Test an error occurs if the texture descriptor is missing + TEST_P(VulkanImageWrappingValidationTests, MissingTextureDescriptor) { + DAWN_SKIP_TEST_IF(UsesWire()); + ASSERT_DEVICE_ERROR(wgpu::Texture texture = + WrapVulkanImage(device, nullptr, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the texture descriptor is invalid + TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDescriptor) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::ChainedStruct chainedDescriptor; + defaultDescriptor.nextInChain = &chainedDescriptor; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the descriptor dimension isn't 2D + TEST_P(VulkanImageWrappingValidationTests, InvalidTextureDimension) { + DAWN_SKIP_TEST_IF(UsesWire()); + defaultDescriptor.dimension = wgpu::TextureDimension::e1D; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the descriptor mip level count isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidMipLevelCount) { + DAWN_SKIP_TEST_IF(UsesWire()); + defaultDescriptor.mipLevelCount = 2; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the descriptor depth isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidDepth) { + DAWN_SKIP_TEST_IF(UsesWire()); + defaultDescriptor.size.depth = 2; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if the descriptor sample count isn't 1 + TEST_P(VulkanImageWrappingValidationTests, InvalidSampleCount) { + DAWN_SKIP_TEST_IF(UsesWire()); + defaultDescriptor.sampleCount = 4; + + ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapVulkanImage( + device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, false)); + EXPECT_EQ(texture.Get(), nullptr); + } + + // Test an error occurs if we try to export the signal semaphore twice + TEST_P(VulkanImageWrappingValidationTests, DoubleSignalSemaphoreExport) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::Texture texture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}, true, true); + ASSERT_NE(texture.Get(), nullptr); + IgnoreSignalSemaphore(device, texture); + ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); + } + + // Test an error occurs if we try to export the signal semaphore from a normal texture + TEST_P(VulkanImageWrappingValidationTests, NormalTextureSignalSemaphoreExport) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); + ASSERT_NE(texture.Get(), nullptr); + ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); + } + + // Test an error occurs if we try to export the signal semaphore from a destroyed texture + TEST_P(VulkanImageWrappingValidationTests, DestroyedTextureSignalSemaphoreExport) { + DAWN_SKIP_TEST_IF(UsesWire()); + wgpu::Texture texture = device.CreateTexture(&defaultDescriptor); + ASSERT_NE(texture.Get(), nullptr); + texture.Destroy(); + ASSERT_DEVICE_ERROR(int fd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + device.Get(), texture.Get())); + ASSERT_EQ(fd, -1); + } + + // Fixture to test using external memory textures through different usages. + // These tests are skipped if the harness is using the wire. + class VulkanImageWrappingUsageTests : public VulkanImageWrappingTestBase { + public: + void SetUp() override { + VulkanImageWrappingTestBase::SetUp(); + if (UsesWire()) { + return; + } + + // Create another device based on the original + backendAdapter = + reinterpret_cast(deviceVk->GetAdapter()); + deviceDescriptor.forceEnabledToggles = GetParam().forceEnabledWorkarounds; + deviceDescriptor.forceDisabledToggles = GetParam().forceDisabledWorkarounds; + + secondDeviceVk = reinterpret_cast( + backendAdapter->CreateDevice(&deviceDescriptor)); + secondDevice = wgpu::Device::Acquire(reinterpret_cast(secondDeviceVk)); + + CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &defaultImage, + &defaultAllocation, &defaultAllocationSize, + &defaultMemoryTypeIndex, &defaultFd); + defaultDescriptor.dimension = wgpu::TextureDimension::e2D; + defaultDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; + defaultDescriptor.size = {1, 1, 1}; + defaultDescriptor.sampleCount = 1; + defaultDescriptor.arrayLayerCount = 1; + defaultDescriptor.mipLevelCount = 1; + defaultDescriptor.usage = wgpu::TextureUsage::OutputAttachment | + wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst; + } + + void TearDown() override { + if (UsesWire()) { + VulkanImageWrappingTestBase::TearDown(); + return; + } + + deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultImage); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(defaultAllocation); + VulkanImageWrappingTestBase::TearDown(); + } + + protected: + wgpu::Device secondDevice; + dawn_native::vulkan::Device* secondDeviceVk; + + dawn_native::vulkan::Adapter* backendAdapter; + dawn_native::DeviceDescriptor deviceDescriptor; + + wgpu::TextureDescriptor defaultDescriptor; + VkImage defaultImage; + VkDeviceMemory defaultAllocation; + VkDeviceSize defaultAllocationSize; + uint32_t defaultMemoryTypeIndex; + int defaultFd; + + // Clear a texture on a given device + void ClearImage(wgpu::Device dawnDevice, + wgpu::Texture wrappedTexture, + wgpu::Color clearColor) { + wgpu::TextureView wrappedView = wrappedTexture.CreateView(); + + // Submit a clear operation + utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {}); + renderPassDescriptor.cColorAttachments[0].clearColor = clearColor; + + wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder(); + wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor); + pass.EndPass(); + + wgpu::CommandBuffer commands = encoder.Finish(); + + wgpu::Queue queue = dawnDevice.GetDefaultQueue(); + queue.Submit(1, &commands); + } + + // Submits a 1x1x1 copy from source to destination + void SimpleCopyTextureToTexture(wgpu::Device dawnDevice, + wgpu::Queue dawnQueue, + wgpu::Texture source, + wgpu::Texture destination) { + wgpu::TextureCopyView copySrc; + copySrc.texture = source; + copySrc.mipLevel = 0; + copySrc.arrayLayer = 0; + copySrc.origin = {0, 0, 0}; + + wgpu::TextureCopyView copyDst; + copyDst.texture = destination; + copyDst.mipLevel = 0; + copyDst.arrayLayer = 0; + copyDst.origin = {0, 0, 0}; + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = dawnDevice.CreateCommandEncoder(); + encoder.CopyTextureToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + + dawnQueue.Submit(1, &commands); + } + }; + + // Clear an image in |secondDevice| + // Verify clear color is visible in |device| + TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevices) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on signalFd + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Verify |device| sees the changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + // Import texture to |device| and |secondDevice| + // Clear image in |secondDevice| + // Verify clear color is visible in |device| + // Verify the very first import into |device| also sees the change, since it should + // alias the same memory + TEST_P(VulkanImageWrappingUsageTests, ClearImageAcrossDevicesAliased) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // WrapVulkanImage consumes the file descriptor so we can't import defaultFd twice. + // Duplicate the file descriptor so we can import it twice. + int defaultFdCopy = dup(defaultFd); + ASSERT(defaultFdCopy != -1); + + // Import the image on |device + wgpu::Texture wrappedTextureAlias = + WrapVulkanImage(device, &defaultDescriptor, defaultFdCopy, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on signalFd + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Verify |device| sees the changes from |secondDevice| (waits) + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + // Verify aliased texture sees changes from |secondDevice| (without waiting!) + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), wrappedTextureAlias, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + IgnoreSignalSemaphore(device, wrappedTextureAlias); + } + + // Clear an image in |secondDevice| + // Verify clear color is not visible in |device| if we import the texture as not cleared + TEST_P(VulkanImageWrappingUsageTests, UnclearedTextureIsCleared) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on signalFd + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}, false); + + // Verify |device| doesn't see the changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 0, 0, 0), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + // Import a texture into |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstTexture| + // Verify the clear color from |secondDevice| is visible in |copyDstTexture| + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureSrcSync) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on |signalFd| + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture deviceWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Create a second texture on |device| + wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Copy |deviceWrappedTexture| into |copyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); + + // Verify |copyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); + } + + // Import a texture into |device| + // Copy color A into texture on |device| + // Import same texture into |secondDevice|, waiting on the copy signal + // Copy color B using Texture to Texture copy on |secondDevice| + // Import texture back into |device|, waiting on color B signal + // Verify texture contains color B + // If texture destination isn't synchronized, |secondDevice| could copy color B + // into the texture first, then |device| writes color A + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToTextureDstSync) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |device| + wgpu::Texture wrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |device| + ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); + + int signalFd = + dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); + + // Import the image to |secondDevice|, making sure we wait on |signalFd| + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture secondDeviceWrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Create a texture with color B on |secondDevice| + wgpu::Texture copySrcTexture = secondDevice.CreateTexture(&defaultDescriptor); + ClearImage(secondDevice, copySrcTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + // Copy color B on |secondDevice| + wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); + SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, copySrcTexture, + secondDeviceWrappedTexture); + + // Re-import back into |device|, waiting on |secondDevice|'s signal + signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + secondDevice.Get(), secondDeviceWrappedTexture.Get()); + memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Verify |nextWrappedTexture| contains the color from our copy + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + // Import a texture from |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstBuffer| + // Verify the clear color from |secondDevice| is visible in |copyDstBuffer| + TEST_P(VulkanImageWrappingUsageTests, CopyTextureToBufferSrcSync) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on |signalFd| + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture deviceWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Create a destination buffer on |device| + wgpu::BufferDescriptor bufferDesc; + bufferDesc.size = 4; + bufferDesc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::CopySrc; + wgpu::Buffer copyDstBuffer = device.CreateBuffer(&bufferDesc); + + // Copy |deviceWrappedTexture| into |copyDstBuffer| + wgpu::TextureCopyView copySrc = + utils::CreateTextureCopyView(deviceWrappedTexture, 0, {0, 0, 0}); + wgpu::BufferCopyView copyDst = utils::CreateBufferCopyView(copyDstBuffer, 0, 256, 0); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + + // Verify |copyDstBuffer| sees changes from |secondDevice| + uint32_t expected = 0x04030201; + EXPECT_BUFFER_U32_EQ(expected, copyDstBuffer, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); + } + + // Import a texture into |device| + // Copy color A into texture on |device| + // Import same texture into |secondDevice|, waiting on the copy signal + // Copy color B using Buffer to Texture copy on |secondDevice| + // Import texture back into |device|, waiting on color B signal + // Verify texture contains color B + // If texture destination isn't synchronized, |secondDevice| could copy color B + // into the texture first, then |device| writes color A + TEST_P(VulkanImageWrappingUsageTests, CopyBufferToTextureDstSync) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |device| + wgpu::Texture wrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |device| + ClearImage(device, wrappedTexture, {5 / 255.0f, 6 / 255.0f, 7 / 255.0f, 8 / 255.0f}); + + int signalFd = + dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(device.Get(), wrappedTexture.Get()); + + // Import the image to |secondDevice|, making sure we wait on |signalFd| + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture secondDeviceWrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Copy color B on |secondDevice| + wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); + + // Create a buffer on |secondDevice| + wgpu::Buffer copySrcBuffer = + utils::CreateBufferFromData(secondDevice, wgpu::BufferUsage::CopySrc, {0x04030201}); + + // Copy |copySrcBuffer| into |secondDeviceWrappedTexture| + wgpu::BufferCopyView copySrc = utils::CreateBufferCopyView(copySrcBuffer, 0, 256, 0); + wgpu::TextureCopyView copyDst = + utils::CreateTextureCopyView(secondDeviceWrappedTexture, 0, {0, 0, 0}); + + wgpu::Extent3D copySize = {1, 1, 1}; + + wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); + encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + secondDeviceQueue.Submit(1, &commands); + + // Re-import back into |device|, waiting on |secondDevice|'s signal + signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + secondDevice.Get(), secondDeviceWrappedTexture.Get()); + memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + + wgpu::Texture nextWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Verify |nextWrappedTexture| contains the color from our copy + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), nextWrappedTexture, 0, 0); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + } + + // Import a texture from |secondDevice| + // Issue a copy of the imported texture inside |device| to |copyDstTexture| + // Issue second copy to |secondCopyDstTexture| + // Verify the clear color from |secondDevice| is visible in both copies + TEST_P(VulkanImageWrappingUsageTests, DoubleTextureUsage) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = + WrapVulkanImage(secondDevice, &defaultDescriptor, defaultFd, defaultAllocationSize, + defaultMemoryTypeIndex, {}); + + // Clear |wrappedTexture| on |secondDevice| + ClearImage(secondDevice, wrappedTexture, {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + + // Import the image to |device|, making sure we wait on |signalFd| + int memoryFd = GetMemoryFd(deviceVk, defaultAllocation); + wgpu::Texture deviceWrappedTexture = + WrapVulkanImage(device, &defaultDescriptor, memoryFd, defaultAllocationSize, + defaultMemoryTypeIndex, {signalFd}); + + // Create a second texture on |device| + wgpu::Texture copyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Create a third texture on |device| + wgpu::Texture secondCopyDstTexture = device.CreateTexture(&defaultDescriptor); + + // Copy |deviceWrappedTexture| into |copyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, copyDstTexture); + + // Copy |deviceWrappedTexture| into |secondCopyDstTexture| + SimpleCopyTextureToTexture(device, queue, deviceWrappedTexture, secondCopyDstTexture); + + // Verify |copyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), copyDstTexture, 0, 0); + + // Verify |secondCopyDstTexture| sees changes from |secondDevice| + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), secondCopyDstTexture, 0, 0); + + IgnoreSignalSemaphore(device, deviceWrappedTexture); + } + + // Tex A on device 3 (external export) + // Tex B on device 2 (external export) + // Tex C on device 1 (external export) + // Clear color for A on device 3 + // Copy A->B on device 3 + // Copy B->C on device 2 (wait on B from previous op) + // Copy C->D on device 1 (wait on C from previous op) + // Verify D has same color as A + TEST_P(VulkanImageWrappingUsageTests, ChainTextureCopy) { + DAWN_SKIP_TEST_IF(UsesWire()); + + // Close |defaultFd| since this test doesn't import it anywhere + close(defaultFd); + + // device 1 = |device| + // device 2 = |secondDevice| + // Create device 3 + dawn_native::vulkan::Device* thirdDeviceVk = reinterpret_cast( + backendAdapter->CreateDevice(&deviceDescriptor)); + wgpu::Device thirdDevice = + wgpu::Device::Acquire(reinterpret_cast(thirdDeviceVk)); + + // Make queue for device 2 and 3 + wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); + wgpu::Queue thirdDeviceQueue = thirdDevice.GetDefaultQueue(); + + // Allocate memory for A, B, C + VkImage imageA; + VkDeviceMemory allocationA; + int memoryFdA; + VkDeviceSize allocationSizeA; + uint32_t memoryTypeIndexA; + CreateBindExportImage(thirdDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageA, &allocationA, + &allocationSizeA, &memoryTypeIndexA, &memoryFdA); + + VkImage imageB; + VkDeviceMemory allocationB; + int memoryFdB; + VkDeviceSize allocationSizeB; + uint32_t memoryTypeIndexB; + CreateBindExportImage(secondDeviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageB, &allocationB, + &allocationSizeB, &memoryTypeIndexB, &memoryFdB); + + VkImage imageC; + VkDeviceMemory allocationC; + int memoryFdC; + VkDeviceSize allocationSizeC; + uint32_t memoryTypeIndexC; + CreateBindExportImage(deviceVk, 1, 1, VK_FORMAT_R8G8B8A8_UNORM, &imageC, &allocationC, + &allocationSizeC, &memoryTypeIndexC, &memoryFdC); + + // Import TexA, TexB on device 3 + wgpu::Texture wrappedTexADevice3 = WrapVulkanImage( + thirdDevice, &defaultDescriptor, memoryFdA, allocationSizeA, memoryTypeIndexA, {}); + + wgpu::Texture wrappedTexBDevice3 = WrapVulkanImage( + thirdDevice, &defaultDescriptor, memoryFdB, allocationSizeB, memoryTypeIndexB, {}); + + // Clear TexA + ClearImage(thirdDevice, wrappedTexADevice3, + {1 / 255.0f, 2 / 255.0f, 3 / 255.0f, 4 / 255.0f}); + + // Copy A->B + SimpleCopyTextureToTexture(thirdDevice, thirdDeviceQueue, wrappedTexADevice3, + wrappedTexBDevice3); + + int signalFdTexBDevice3 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + thirdDevice.Get(), wrappedTexBDevice3.Get()); + IgnoreSignalSemaphore(thirdDevice, wrappedTexADevice3); + + // Import TexB, TexC on device 2 + memoryFdB = GetMemoryFd(secondDeviceVk, allocationB); + wgpu::Texture wrappedTexBDevice2 = + WrapVulkanImage(secondDevice, &defaultDescriptor, memoryFdB, allocationSizeB, + memoryTypeIndexB, {signalFdTexBDevice3}); + + wgpu::Texture wrappedTexCDevice2 = WrapVulkanImage( + secondDevice, &defaultDescriptor, memoryFdC, allocationSizeC, memoryTypeIndexC, {}); + + // Copy B->C on device 2 + SimpleCopyTextureToTexture(secondDevice, secondDeviceQueue, wrappedTexBDevice2, + wrappedTexCDevice2); + + int signalFdTexCDevice2 = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD( + secondDevice.Get(), wrappedTexCDevice2.Get()); + IgnoreSignalSemaphore(secondDevice, wrappedTexBDevice2); + + // Import TexC on device 1 + memoryFdC = GetMemoryFd(deviceVk, allocationC); + wgpu::Texture wrappedTexCDevice1 = + WrapVulkanImage(device, &defaultDescriptor, memoryFdC, allocationSizeC, + memoryTypeIndexC, {signalFdTexCDevice2}); + + // Create TexD on device 1 + wgpu::Texture texD = device.CreateTexture(&defaultDescriptor); + + // Copy C->D on device 1 + SimpleCopyTextureToTexture(device, queue, wrappedTexCDevice1, texD); + + // Verify D matches clear color + EXPECT_PIXEL_RGBA8_EQ(RGBA8(1, 2, 3, 4), texD, 0, 0); + + thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); + thirdDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); + secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageB); + secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationB); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(imageC); + deviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationC); + + IgnoreSignalSemaphore(device, wrappedTexCDevice1); + } + + // Tests a larger image is preserved when importing + // TODO(http://crbug.com/dawn/206): This fails on AMD + TEST_P(VulkanImageWrappingUsageTests, LargerImage) { + DAWN_SKIP_TEST_IF(UsesWire() || IsAMD()); + + close(defaultFd); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = 640; + descriptor.size.height = 480; + descriptor.size.depth = 1; + descriptor.arrayLayerCount = 1; + descriptor.sampleCount = 1; + descriptor.format = wgpu::TextureFormat::BGRA8Unorm; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::CopyDst | wgpu::TextureUsage::CopySrc; + + // Fill memory with textures to trigger layout issues on AMD + std::vector textures; + for (int i = 0; i < 20; i++) { + textures.push_back(device.CreateTexture(&descriptor)); + } + + wgpu::Queue secondDeviceQueue = secondDevice.GetDefaultQueue(); + + // Make an image on |secondDevice| + VkImage imageA; + VkDeviceMemory allocationA; + int memoryFdA; + VkDeviceSize allocationSizeA; + uint32_t memoryTypeIndexA; + CreateBindExportImage(secondDeviceVk, 640, 480, VK_FORMAT_R8G8B8A8_UNORM, &imageA, + &allocationA, &allocationSizeA, &memoryTypeIndexA, &memoryFdA); + + // Import the image on |secondDevice| + wgpu::Texture wrappedTexture = WrapVulkanImage(secondDevice, &descriptor, memoryFdA, + allocationSizeA, memoryTypeIndexA, {}); + + // Draw a non-trivial picture + uint32_t width = 640, height = 480, pixelSize = 4; + uint32_t bytesPerRow = Align(width * pixelSize, kTextureBytesPerRowAlignment); + std::vector data(bytesPerRow * (height - 1) + width * pixelSize); + + for (uint32_t row = 0; row < height; row++) { + for (uint32_t col = 0; col < width; col++) { + float normRow = static_cast(row) / height; + float normCol = static_cast(col) / width; + float dist = sqrt(normRow * normRow + normCol * normCol) * 3; + dist = dist - static_cast(dist); + data[4 * (row * width + col)] = static_cast(dist * 255); + data[4 * (row * width + col) + 1] = static_cast(dist * 255); + data[4 * (row * width + col) + 2] = static_cast(dist * 255); + data[4 * (row * width + col) + 3] = 255; + } + } + + // Write the picture + { + wgpu::Buffer copySrcBuffer = utils::CreateBufferFromData( + secondDevice, data.data(), data.size(), wgpu::BufferUsage::CopySrc); + wgpu::BufferCopyView copySrc = + utils::CreateBufferCopyView(copySrcBuffer, 0, bytesPerRow, 0); + wgpu::TextureCopyView copyDst = + utils::CreateTextureCopyView(wrappedTexture, 0, {0, 0, 0}); + wgpu::Extent3D copySize = {width, height, 1}; + + wgpu::CommandEncoder encoder = secondDevice.CreateCommandEncoder(); + encoder.CopyBufferToTexture(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + secondDeviceQueue.Submit(1, &commands); + } + + int signalFd = dawn_native::vulkan::ExportSignalSemaphoreOpaqueFD(secondDevice.Get(), + wrappedTexture.Get()); + int memoryFd = GetMemoryFd(secondDeviceVk, allocationA); + + // Import the image on |device| + wgpu::Texture nextWrappedTexture = WrapVulkanImage( + device, &descriptor, memoryFd, allocationSizeA, memoryTypeIndexA, {signalFd}); + + // Copy the image into a buffer for comparison + wgpu::BufferDescriptor copyDesc; + copyDesc.size = data.size(); + copyDesc.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst; + wgpu::Buffer copyDstBuffer = device.CreateBuffer(©Desc); + { + wgpu::TextureCopyView copySrc = + utils::CreateTextureCopyView(nextWrappedTexture, 0, {0, 0, 0}); + wgpu::BufferCopyView copyDst = + utils::CreateBufferCopyView(copyDstBuffer, 0, bytesPerRow, 0); + + wgpu::Extent3D copySize = {width, height, 1}; + + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + encoder.CopyTextureToBuffer(©Src, ©Dst, ©Size); + wgpu::CommandBuffer commands = encoder.Finish(); + queue.Submit(1, &commands); + } + + // Check the image is not corrupted on |device| + EXPECT_BUFFER_U32_RANGE_EQ(reinterpret_cast(data.data()), copyDstBuffer, 0, + data.size() / 4); + + IgnoreSignalSemaphore(device, nextWrappedTexture); + secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(imageA); + secondDeviceVk->GetFencedDeleter()->DeleteWhenUnused(allocationA); + } + + DAWN_INSTANTIATE_TEST(VulkanImageWrappingValidationTests, VulkanBackend()); + DAWN_INSTANTIATE_TEST(VulkanImageWrappingUsageTests, VulkanBackend()); + +}} // namespace dawn_native::vulkan diff --git a/third_party/dawn/src/utils/BUILD.gn b/third_party/dawn/src/utils/BUILD.gn new file mode 100644 index 00000000000..a2c57c3e6e3 --- /dev/null +++ b/third_party/dawn/src/utils/BUILD.gn @@ -0,0 +1,174 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../scripts/dawn_overrides_with_defaults.gni") + +import("${dawn_root}/scripts/dawn_features.gni") + +############################################################################### +# GLFW wrapping target +############################################################################### + +# GLFW does not support ChromeOS, Android or Fuchsia, so provide a small mock +# library that can be linked into the Dawn tests on these platforms. Otherwise, +# use the real library from third_party/. +if (dawn_supports_glfw_for_windowing) { + group("dawn_glfw") { + public_deps = [ "${dawn_root}/third_party/gn/glfw" ] + } +} else if (is_fuchsia) { + # The mock implementation of GLFW on Fuchsia + config("dawn_glfw_public_config") { + # Allow inclusion of + include_dirs = [ "${dawn_glfw_dir}/include" ] + + # The GLFW/glfw3.h header includes by default, but the latter + # does not exist on Fuchsia. Defining GLFW_INCLUDE_NONE helps work around + # the issue, but it needs to be defined for any file that includes the + # header. + defines = [ + "GLFW_INCLUDE_NONE", + "GLFW_INCLUDE_VULKAN", + ] + } + + static_library("dawn_glfw") { + sources = [ + # NOTE: The header below is required to pass "gn check". + "${dawn_glfw_dir}/include/GLFW/glfw3.h", + "Glfw3Fuchsia.cpp", + ] + public_configs = [ ":dawn_glfw_public_config" ] + deps = [ "${dawn_root}/src/common" ] + } +} else { + # Just skip GLFW on other systems + group("dawn_glfw") { + } +} + +############################################################################### +# Utils for tests and samples +############################################################################### + +static_library("dawn_utils") { + configs += [ "${dawn_root}/src/common:dawn_internal" ] + + sources = [ + "ComboRenderBundleEncoderDescriptor.cpp", + "ComboRenderBundleEncoderDescriptor.h", + "ComboRenderPipelineDescriptor.cpp", + "ComboRenderPipelineDescriptor.h", + "SystemUtils.cpp", + "SystemUtils.h", + "TerribleCommandBuffer.cpp", + "TerribleCommandBuffer.h", + "TextureFormatUtils.cpp", + "TextureFormatUtils.h", + "Timer.h", + "WGPUHelpers.cpp", + "WGPUHelpers.h", + ] + deps = [ + "${dawn_root}/src/common", + "${dawn_root}/src/dawn_native", + "${dawn_root}/src/dawn_wire", + "${dawn_shaderc_dir}:libshaderc", + ] + libs = [] + frameworks = [] + + if (is_win) { + sources += [ "WindowsTimer.cpp" ] + } else if (is_mac) { + sources += [ + "OSXTimer.cpp", + "ObjCUtils.h", + "ObjCUtils.mm", + ] + frameworks += [ "QuartzCore.framework" ] + } else { + sources += [ "PosixTimer.cpp" ] + } + + if (dawn_supports_glfw_for_windowing) { + sources += [ + "GLFWUtils.cpp", + "GLFWUtils.h", + ] + deps += [ ":dawn_glfw" ] + + if (dawn_enable_metal) { + sources += [ "GLFWUtils_metal.mm" ] + frameworks += [ "Metal.framework" ] + } + } + + public_deps = [ "${dawn_root}/src/dawn:dawncpp_headers" ] +} + +############################################################################### +# Dawn samples, only in standalone builds +############################################################################### + +if (dawn_standalone) { + # Library to handle the interaction of Dawn with GLFW windows in samples + static_library("dawn_bindings") { + configs += [ "${dawn_root}/src/common:dawn_internal" ] + + sources = [ + "BackendBinding.cpp", + "BackendBinding.h", + ] + + public_deps = [ "${dawn_root}/src/dawn:dawn_headers" ] + + deps = [ + ":dawn_glfw", + "${dawn_root}/src/common", + "${dawn_root}/src/dawn_native", + ] + libs = [] + frameworks = [] + + if (dawn_enable_d3d12) { + sources += [ "D3D12Binding.cpp" ] + } + + if (dawn_enable_metal) { + sources += [ "MetalBinding.mm" ] + frameworks += [ + "Metal.framework", + "QuartzCore.framework", + ] + + # Suppress warnings that Metal isn't in the deployment target of Chrome + if (is_mac) { + cflags_objcc = [ "-Wno-unguarded-availability" ] + } + } + + if (dawn_enable_null) { + sources += [ "NullBinding.cpp" ] + } + + if (dawn_enable_opengl) { + sources += [ "OpenGLBinding.cpp" ] + } + + if (dawn_enable_vulkan) { + sources += [ "VulkanBinding.cpp" ] + } + } +} diff --git a/third_party/dawn/src/utils/BackendBinding.cpp b/third_party/dawn/src/utils/BackendBinding.cpp index 39ec8cfb4fe..4fe17b0f835 100644 --- a/third_party/dawn/src/utils/BackendBinding.cpp +++ b/third_party/dawn/src/utils/BackendBinding.cpp @@ -25,43 +25,32 @@ namespace utils { #if defined(DAWN_ENABLE_BACKEND_D3D12) - BackendBinding* CreateD3D12Binding(GLFWwindow* window, DawnDevice device); + BackendBinding* CreateD3D12Binding(GLFWwindow* window, WGPUDevice device); #endif #if defined(DAWN_ENABLE_BACKEND_METAL) - BackendBinding* CreateMetalBinding(GLFWwindow* window, DawnDevice device); + BackendBinding* CreateMetalBinding(GLFWwindow* window, WGPUDevice device); #endif #if defined(DAWN_ENABLE_BACKEND_NULL) - BackendBinding* CreateNullBinding(GLFWwindow* window, DawnDevice device); + BackendBinding* CreateNullBinding(GLFWwindow* window, WGPUDevice device); #endif #if defined(DAWN_ENABLE_BACKEND_OPENGL) - BackendBinding* CreateOpenGLBinding(GLFWwindow* window, DawnDevice device); + BackendBinding* CreateOpenGLBinding(GLFWwindow* window, WGPUDevice device); #endif #if defined(DAWN_ENABLE_BACKEND_VULKAN) - BackendBinding* CreateVulkanBinding(GLFWwindow* window, DawnDevice device); + BackendBinding* CreateVulkanBinding(GLFWwindow* window, WGPUDevice device); #endif - BackendBinding::BackendBinding(GLFWwindow* window, DawnDevice device) + BackendBinding::BackendBinding(GLFWwindow* window, WGPUDevice device) : mWindow(window), mDevice(device) { } - void SetupGLFWWindowHintsForBackend(dawn_native::BackendType type) { - if (type == dawn_native::BackendType::OpenGL) { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - } else { - glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); - } - } - void DiscoverAdapter(dawn_native::Instance* instance, GLFWwindow* window, - dawn_native::BackendType type) { + wgpu::BackendType type) { DAWN_UNUSED(type); DAWN_UNUSED(window); - if (type == dawn_native::BackendType::OpenGL) { + if (type == wgpu::BackendType::OpenGL) { #if defined(DAWN_ENABLE_BACKEND_OPENGL) glfwMakeContextCurrent(window); dawn_native::opengl::AdapterDiscoveryOptions adapterOptions; @@ -73,32 +62,30 @@ namespace utils { } } - BackendBinding* CreateBinding(dawn_native::BackendType type, - GLFWwindow* window, - DawnDevice device) { + BackendBinding* CreateBinding(wgpu::BackendType type, GLFWwindow* window, WGPUDevice device) { switch (type) { #if defined(DAWN_ENABLE_BACKEND_D3D12) - case dawn_native::BackendType::D3D12: + case wgpu::BackendType::D3D12: return CreateD3D12Binding(window, device); #endif #if defined(DAWN_ENABLE_BACKEND_METAL) - case dawn_native::BackendType::Metal: + case wgpu::BackendType::Metal: return CreateMetalBinding(window, device); #endif #if defined(DAWN_ENABLE_BACKEND_NULL) - case dawn_native::BackendType::Null: + case wgpu::BackendType::Null: return CreateNullBinding(window, device); #endif #if defined(DAWN_ENABLE_BACKEND_OPENGL) - case dawn_native::BackendType::OpenGL: + case wgpu::BackendType::OpenGL: return CreateOpenGLBinding(window, device); #endif #if defined(DAWN_ENABLE_BACKEND_VULKAN) - case dawn_native::BackendType::Vulkan: + case wgpu::BackendType::Vulkan: return CreateVulkanBinding(window, device); #endif diff --git a/third_party/dawn/src/utils/BackendBinding.h b/third_party/dawn/src/utils/BackendBinding.h index b3529a2117e..ca1c91ffb9d 100644 --- a/third_party/dawn/src/utils/BackendBinding.h +++ b/third_party/dawn/src/utils/BackendBinding.h @@ -15,7 +15,7 @@ #ifndef UTILS_BACKENDBINDING_H_ #define UTILS_BACKENDBINDING_H_ -#include "dawn/dawncpp.h" +#include "dawn/webgpu_cpp.h" #include "dawn_native/DawnNative.h" struct GLFWwindow; @@ -27,22 +27,19 @@ namespace utils { virtual ~BackendBinding() = default; virtual uint64_t GetSwapChainImplementation() = 0; - virtual DawnTextureFormat GetPreferredSwapChainTextureFormat() = 0; + virtual WGPUTextureFormat GetPreferredSwapChainTextureFormat() = 0; protected: - BackendBinding(GLFWwindow* window, DawnDevice device); + BackendBinding(GLFWwindow* window, WGPUDevice device); GLFWwindow* mWindow = nullptr; - DawnDevice mDevice = nullptr; + WGPUDevice mDevice = nullptr; }; - void SetupGLFWWindowHintsForBackend(dawn_native::BackendType type); void DiscoverAdapter(dawn_native::Instance* instance, GLFWwindow* window, - dawn_native::BackendType type); - BackendBinding* CreateBinding(dawn_native::BackendType type, - GLFWwindow* window, - DawnDevice device); + wgpu::BackendType type); + BackendBinding* CreateBinding(wgpu::BackendType type, GLFWwindow* window, WGPUDevice device); } // namespace utils diff --git a/third_party/dawn/src/utils/CMakeLists.txt b/third_party/dawn/src/utils/CMakeLists.txt new file mode 100644 index 00000000000..3a959401a2b --- /dev/null +++ b/third_party/dawn/src/utils/CMakeLists.txt @@ -0,0 +1,80 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +add_library(dawn_utils STATIC ${DAWN_DUMMY_FILE}) +target_sources(dawn_utils PRIVATE + "BackendBinding.cpp" + "BackendBinding.h" + "ComboRenderBundleEncoderDescriptor.cpp" + "ComboRenderBundleEncoderDescriptor.h" + "ComboRenderPipelineDescriptor.cpp" + "ComboRenderPipelineDescriptor.h" + "GLFWUtils.cpp" + "GLFWUtils.h" + "SystemUtils.cpp" + "SystemUtils.h" + "TerribleCommandBuffer.cpp" + "TerribleCommandBuffer.h" + "TextureFormatUtils.cpp" + "TextureFormatUtils.h" + "Timer.h" + "WGPUHelpers.cpp" + "WGPUHelpers.h" +) +target_link_libraries(dawn_utils + PUBLIC dawncpp_headers + PRIVATE dawn_internal_config + dawn_common + dawn_native + dawn_wire + shaderc + glfw +) + +if(WIN32) + target_sources(dawn_utils PRIVATE "WindowsTimer.cpp") +elseif(APPLE) + target_sources(dawn_utils PRIVATE + "OSXTimer.cpp" + "ObjCUtils.h" + "ObjCUtils.mm" + ) + target_link_libraries(dawn_utils PRIVATE "-framework QuartzCore") +elseif(UNIX) + target_sources(dawn_utils PRIVATE "PosixTimer.cpp") +endif() + +if (DAWN_ENABLE_D3D12) + target_sources(dawn_utils PRIVATE "D3D12Binding.cpp") +endif() + +if (DAWN_ENABLE_METAL) + target_sources(dawn_utils PRIVATE + "GLFWUtils_metal.mm" + "MetalBinding.mm" + ) + target_link_libraries(dawn_utils PRIVATE "-framework Metal") +endif() + +if (DAWN_ENABLE_NULL) + target_sources(dawn_utils PRIVATE "NullBinding.cpp") +endif() + +if (DAWN_ENABLE_OPENGL) + target_sources(dawn_utils PRIVATE "OpenGLBinding.cpp") +endif() + +if (DAWN_ENABLE_VULKAN) + target_sources(dawn_utils PRIVATE "VulkanBinding.cpp") +endif() diff --git a/third_party/dawn/src/utils/ComboRenderBundleEncoderDescriptor.cpp b/third_party/dawn/src/utils/ComboRenderBundleEncoderDescriptor.cpp new file mode 100644 index 00000000000..8b076e11d9c --- /dev/null +++ b/third_party/dawn/src/utils/ComboRenderBundleEncoderDescriptor.cpp @@ -0,0 +1,28 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/ComboRenderBundleEncoderDescriptor.h" + +#include "utils/WGPUHelpers.h" + +namespace utils { + + ComboRenderBundleEncoderDescriptor::ComboRenderBundleEncoderDescriptor() { + wgpu::RenderBundleEncoderDescriptor* descriptor = this; + + descriptor->colorFormatsCount = 0; + descriptor->colorFormats = &cColorFormats[0]; + } + +} // namespace utils diff --git a/third_party/dawn/src/utils/ComboRenderBundleEncoderDescriptor.h b/third_party/dawn/src/utils/ComboRenderBundleEncoderDescriptor.h new file mode 100644 index 00000000000..cd6044b59e9 --- /dev/null +++ b/third_party/dawn/src/utils/ComboRenderBundleEncoderDescriptor.h @@ -0,0 +1,35 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_COMBORENDERBUNDLEENCODERDESCRIPTOR_H_ +#define UTILS_COMBORENDERBUNDLEENCODERDESCRIPTOR_H_ + +#include + +#include "common/Constants.h" + +#include + +namespace utils { + + class ComboRenderBundleEncoderDescriptor : public wgpu::RenderBundleEncoderDescriptor { + public: + ComboRenderBundleEncoderDescriptor(); + + std::array cColorFormats; + }; + +} // namespace utils + +#endif // UTILS_COMBORENDERBUNDLEENCODERDESCRIPTOR_H_ diff --git a/third_party/dawn/src/utils/ComboRenderPipelineDescriptor.cpp b/third_party/dawn/src/utils/ComboRenderPipelineDescriptor.cpp index 59bc5091c33..5fa40c42663 100644 --- a/third_party/dawn/src/utils/ComboRenderPipelineDescriptor.cpp +++ b/third_party/dawn/src/utils/ComboRenderPipelineDescriptor.cpp @@ -14,49 +14,47 @@ #include "utils/ComboRenderPipelineDescriptor.h" -#include "utils/DawnHelpers.h" +#include "utils/WGPUHelpers.h" namespace utils { - ComboVertexInputDescriptor::ComboVertexInputDescriptor() { - dawn::VertexInputDescriptor* descriptor = this; + ComboVertexStateDescriptor::ComboVertexStateDescriptor() { + wgpu::VertexStateDescriptor* descriptor = this; - descriptor->indexFormat = dawn::IndexFormat::Uint32; - descriptor->bufferCount = 0; + descriptor->indexFormat = wgpu::IndexFormat::Uint32; + descriptor->vertexBufferCount = 0; // Fill the default values for vertexBuffers and vertexAttributes in buffers. - dawn::VertexAttributeDescriptor vertexAttribute; + wgpu::VertexAttributeDescriptor vertexAttribute; vertexAttribute.shaderLocation = 0; vertexAttribute.offset = 0; - vertexAttribute.format = dawn::VertexFormat::Float; + vertexAttribute.format = wgpu::VertexFormat::Float; for (uint32_t i = 0; i < kMaxVertexAttributes; ++i) { cAttributes[i] = vertexAttribute; } for (uint32_t i = 0; i < kMaxVertexBuffers; ++i) { - cBuffers[i].stride = 0; - cBuffers[i].stepMode = dawn::InputStepMode::Vertex; - cBuffers[i].attributeCount = 0; - cBuffers[i].attributes = nullptr; + cVertexBuffers[i].arrayStride = 0; + cVertexBuffers[i].stepMode = wgpu::InputStepMode::Vertex; + cVertexBuffers[i].attributeCount = 0; + cVertexBuffers[i].attributes = nullptr; } - // cBuffers[i].attributes points to somewhere in cAttributes. cBuffers[0].attributes - // points to &cAttributes[0] by default. Assuming cBuffers[0] has two attributes, then - // cBuffers[1].attributes should point to &cAttributes[2]. Likewise, if cBuffers[1] - // has 3 attributes, then cBuffers[2].attributes should point to &cAttributes[5]. - cBuffers[0].attributes = &cAttributes[0]; - descriptor->buffers = &cBuffers[0]; + // cVertexBuffers[i].attributes points to somewhere in cAttributes. + // cVertexBuffers[0].attributes points to &cAttributes[0] by default. Assuming + // cVertexBuffers[0] has two attributes, then cVertexBuffers[1].attributes should point to + // &cAttributes[2]. Likewise, if cVertexBuffers[1] has 3 attributes, then + // cVertexBuffers[2].attributes should point to &cAttributes[5]. + cVertexBuffers[0].attributes = &cAttributes[0]; + descriptor->vertexBuffers = &cVertexBuffers[0]; } - ComboRenderPipelineDescriptor::ComboRenderPipelineDescriptor(const dawn::Device& device) { - dawn::RenderPipelineDescriptor* descriptor = this; + ComboRenderPipelineDescriptor::ComboRenderPipelineDescriptor(const wgpu::Device& device) { + wgpu::RenderPipelineDescriptor* descriptor = this; - descriptor->primitiveTopology = dawn::PrimitiveTopology::TriangleList; + descriptor->primitiveTopology = wgpu::PrimitiveTopology::TriangleList; descriptor->sampleCount = 1; // Set defaults for the vertex stage descriptor. - { - descriptor->vertexStage = &cVertexStage; - cVertexStage.entryPoint = "main"; - } + { vertexStage.entryPoint = "main"; } // Set defaults for the fragment stage desriptor. { @@ -65,12 +63,12 @@ namespace utils { } // Set defaults for the input state descriptors. - descriptor->vertexInput = &cVertexInput; + descriptor->vertexState = &cVertexState; // Set defaults for the rasterization state descriptor. { - cRasterizationState.frontFace = dawn::FrontFace::CCW; - cRasterizationState.cullMode = dawn::CullMode::None; + cRasterizationState.frontFace = wgpu::FrontFace::CCW; + cRasterizationState.cullMode = wgpu::CullMode::None; cRasterizationState.depthBias = 0; cRasterizationState.depthBiasSlopeScale = 0.0; @@ -81,42 +79,39 @@ namespace utils { // Set defaults for the color state descriptors. { descriptor->colorStateCount = 1; - descriptor->colorStates = &cColorStates[0]; - - dawn::BlendDescriptor blend; - blend.operation = dawn::BlendOperation::Add; - blend.srcFactor = dawn::BlendFactor::One; - blend.dstFactor = dawn::BlendFactor::Zero; - dawn::ColorStateDescriptor colorStateDescriptor; - colorStateDescriptor.format = dawn::TextureFormat::R8G8B8A8Unorm; + descriptor->colorStates = cColorStates.data(); + + wgpu::BlendDescriptor blend; + blend.operation = wgpu::BlendOperation::Add; + blend.srcFactor = wgpu::BlendFactor::One; + blend.dstFactor = wgpu::BlendFactor::Zero; + wgpu::ColorStateDescriptor colorStateDescriptor; + colorStateDescriptor.format = wgpu::TextureFormat::RGBA8Unorm; colorStateDescriptor.alphaBlend = blend; colorStateDescriptor.colorBlend = blend; - colorStateDescriptor.writeMask = dawn::ColorWriteMask::All; + colorStateDescriptor.writeMask = wgpu::ColorWriteMask::All; for (uint32_t i = 0; i < kMaxColorAttachments; ++i) { - mColorStates[i] = colorStateDescriptor; - cColorStates[i] = &mColorStates[i]; + cColorStates[i] = colorStateDescriptor; } } // Set defaults for the depth stencil state descriptors. { - dawn::StencilStateFaceDescriptor stencilFace; - stencilFace.compare = dawn::CompareFunction::Always; - stencilFace.failOp = dawn::StencilOperation::Keep; - stencilFace.depthFailOp = dawn::StencilOperation::Keep; - stencilFace.passOp = dawn::StencilOperation::Keep; + wgpu::StencilStateFaceDescriptor stencilFace; + stencilFace.compare = wgpu::CompareFunction::Always; + stencilFace.failOp = wgpu::StencilOperation::Keep; + stencilFace.depthFailOp = wgpu::StencilOperation::Keep; + stencilFace.passOp = wgpu::StencilOperation::Keep; - cDepthStencilState.format = dawn::TextureFormat::D32FloatS8Uint; + cDepthStencilState.format = wgpu::TextureFormat::Depth24PlusStencil8; cDepthStencilState.depthWriteEnabled = false; - cDepthStencilState.depthCompare = dawn::CompareFunction::Always; + cDepthStencilState.depthCompare = wgpu::CompareFunction::Always; cDepthStencilState.stencilBack = stencilFace; cDepthStencilState.stencilFront = stencilFace; cDepthStencilState.stencilReadMask = 0xff; cDepthStencilState.stencilWriteMask = 0xff; descriptor->depthStencilState = nullptr; } - - descriptor->layout = utils::MakeBasicPipelineLayout(device, nullptr); } } // namespace utils diff --git a/third_party/dawn/src/utils/ComboRenderPipelineDescriptor.h b/third_party/dawn/src/utils/ComboRenderPipelineDescriptor.h index 8be63d23e04..067b79ef92c 100644 --- a/third_party/dawn/src/utils/ComboRenderPipelineDescriptor.h +++ b/third_party/dawn/src/utils/ComboRenderPipelineDescriptor.h @@ -1,52 +1,53 @@ -// Copyright 2018 The Dawn Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef UTILS_COMBORENDERPIPELINEDESCRIPTOR_H_ -#define UTILS_COMBORENDERPIPELINEDESCRIPTOR_H_ - -#include - -#include "common/Constants.h" - -#include - -namespace utils { - - class ComboVertexInputDescriptor : public dawn::VertexInputDescriptor { - public: - ComboVertexInputDescriptor(); - - std::array cBuffers; - std::array cAttributes; - }; - - class ComboRenderPipelineDescriptor : public dawn::RenderPipelineDescriptor { - public: - ComboRenderPipelineDescriptor(const dawn::Device& device); - - dawn::PipelineStageDescriptor cVertexStage; - dawn::PipelineStageDescriptor cFragmentStage; - - ComboVertexInputDescriptor cVertexInput; - dawn::RasterizationStateDescriptor cRasterizationState; - std::array cColorStates; - dawn::DepthStencilStateDescriptor cDepthStencilState; - - private: - dawn::ColorStateDescriptor mColorStates[kMaxColorAttachments]; - }; - -} // namespace utils - -#endif // UTILS_COMBORENDERPIPELINEDESCRIPTOR_H_ +// Copyright 2018 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_COMBORENDERPIPELINEDESCRIPTOR_H_ +#define UTILS_COMBORENDERPIPELINEDESCRIPTOR_H_ + +#include + +#include "common/Constants.h" + +#include + +namespace utils { + + class ComboVertexStateDescriptor : public wgpu::VertexStateDescriptor { + public: + ComboVertexStateDescriptor(); + + std::array cVertexBuffers; + std::array cAttributes; + }; + + class ComboRenderPipelineDescriptor : public wgpu::RenderPipelineDescriptor { + public: + ComboRenderPipelineDescriptor(const wgpu::Device& device); + + ComboRenderPipelineDescriptor(const ComboRenderPipelineDescriptor&) = delete; + ComboRenderPipelineDescriptor& operator=(const ComboRenderPipelineDescriptor&) = delete; + ComboRenderPipelineDescriptor(ComboRenderPipelineDescriptor&&) = delete; + ComboRenderPipelineDescriptor& operator=(ComboRenderPipelineDescriptor&&) = delete; + + wgpu::ProgrammableStageDescriptor cFragmentStage; + + ComboVertexStateDescriptor cVertexState; + wgpu::RasterizationStateDescriptor cRasterizationState; + std::array cColorStates; + wgpu::DepthStencilStateDescriptor cDepthStencilState; + }; + +} // namespace utils + +#endif // UTILS_COMBORENDERPIPELINEDESCRIPTOR_H_ diff --git a/third_party/dawn/src/utils/D3D12Binding.cpp b/third_party/dawn/src/utils/D3D12Binding.cpp index b2f48809b56..1708b147824 100644 --- a/third_party/dawn/src/utils/D3D12Binding.cpp +++ b/third_party/dawn/src/utils/D3D12Binding.cpp @@ -27,7 +27,7 @@ namespace utils { class D3D12Binding : public BackendBinding { public: - D3D12Binding(GLFWwindow* window, DawnDevice device) : BackendBinding(window, device) { + D3D12Binding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { } uint64_t GetSwapChainImplementation() override { @@ -39,7 +39,7 @@ namespace utils { return reinterpret_cast(&mSwapchainImpl); } - DawnTextureFormat GetPreferredSwapChainTextureFormat() override { + WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { ASSERT(mSwapchainImpl.userData != nullptr); return dawn_native::d3d12::GetNativeSwapChainPreferredFormat(&mSwapchainImpl); } @@ -48,7 +48,7 @@ namespace utils { DawnSwapChainImplementation mSwapchainImpl = {}; }; - BackendBinding* CreateD3D12Binding(GLFWwindow* window, DawnDevice device) { + BackendBinding* CreateD3D12Binding(GLFWwindow* window, WGPUDevice device) { return new D3D12Binding(window, device); } diff --git a/third_party/dawn/src/utils/GLFWUtils.cpp b/third_party/dawn/src/utils/GLFWUtils.cpp new file mode 100644 index 00000000000..55737ddf474 --- /dev/null +++ b/third_party/dawn/src/utils/GLFWUtils.cpp @@ -0,0 +1,83 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/GLFWUtils.h" + +#include "GLFW/glfw3.h" +#include "common/Platform.h" + +#include + +#if defined(DAWN_PLATFORM_WINDOWS) +# define GLFW_EXPOSE_NATIVE_WIN32 +#elif defined(DAWN_USE_X11) +# define GLFW_EXPOSE_NATIVE_X11 +#endif +#include "GLFW/glfw3native.h" + +namespace utils { + + void SetupGLFWWindowHintsForBackend(wgpu::BackendType type) { + if (type == wgpu::BackendType::OpenGL) { + // Ask for OpenGL 4.4 which is what the GL backend requires for compute shaders and + // texture views. + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + } else { + // Without this GLFW will initialize a GL context on the window, which prevents using + // the window with other APIs (by crashing in weird ways). + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + } + } + + wgpu::Surface CreateSurfaceForWindow(wgpu::Instance instance, GLFWwindow* window) { + std::unique_ptr chainedDescriptor = + SetupWindowAndGetSurfaceDescriptorForTesting(window); + + wgpu::SurfaceDescriptor descriptor; + descriptor.nextInChain = chainedDescriptor.get(); + wgpu::Surface surface = instance.CreateSurface(&descriptor); + + return surface; + } + +#if defined(DAWN_PLATFORM_WINDOWS) + std::unique_ptr SetupWindowAndGetSurfaceDescriptorForTesting( + GLFWwindow* window) { + std::unique_ptr desc = + std::make_unique(); + desc->hwnd = glfwGetWin32Window(window); + desc->hinstance = GetModuleHandle(nullptr); + return std::move(desc); + } +#elif defined(DAWN_USE_X11) + std::unique_ptr SetupWindowAndGetSurfaceDescriptorForTesting( + GLFWwindow* window) { + std::unique_ptr desc = + std::make_unique(); + desc->display = glfwGetX11Display(); + desc->window = glfwGetX11Window(window); + return std::move(desc); + } +#elif defined(DAWN_ENABLE_BACKEND_METAL) + // SetupWindowAndGetSurfaceDescriptorForTesting defined in GLFWUtils_metal.mm +#else + std::unique_ptr SetupWindowAndGetSurfaceDescriptorForTesting(GLFWwindow*) { + return nullptr; + } +#endif + +} // namespace utils diff --git a/third_party/dawn/src/utils/GLFWUtils.h b/third_party/dawn/src/utils/GLFWUtils.h new file mode 100644 index 00000000000..f2299cba9d2 --- /dev/null +++ b/third_party/dawn/src/utils/GLFWUtils.h @@ -0,0 +1,42 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_GLFWUTILS_H_ +#define UTILS_GLFWUTILS_H_ + +#include "dawn/webgpu_cpp.h" + +#include + +struct GLFWwindow; + +namespace utils { + + // Adds all the necessary glfwWindowHint calls for the next GLFWwindow created to be used with + // the specified backend. + void SetupGLFWWindowHintsForBackend(wgpu::BackendType type); + + // Does the necessary setup on the GLFWwindow to allow creating a wgpu::Surface with it and + // calls `instance.CreateSurface` with the correct descriptor for this window. + // Returns a null wgpu::Surface on failure. + wgpu::Surface CreateSurfaceForWindow(wgpu::Instance instance, GLFWwindow* window); + + // Use for testing only. Does everything that CreateSurfaceForWindow does except the call to + // CreateSurface so the descriptor can be modified for testing. + std::unique_ptr SetupWindowAndGetSurfaceDescriptorForTesting( + GLFWwindow* window); + +} // namespace utils + +#endif // UTILS_GLFWUTILS_H_ diff --git a/third_party/dawn/src/utils/GLFWUtils_metal.mm b/third_party/dawn/src/utils/GLFWUtils_metal.mm new file mode 100644 index 00000000000..a920ec07d32 --- /dev/null +++ b/third_party/dawn/src/utils/GLFWUtils_metal.mm @@ -0,0 +1,54 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#if !defined(DAWN_ENABLE_BACKEND_METAL) +# error "GLFWUtils_metal.mm requires the Metal backend to be enabled." +#endif // !defined(DAWN_ENABLE_BACKEND_METAL) + +#include "utils/GLFWUtils.h" + +#import +#include "GLFW/glfw3.h" + +#include + +#define GLFW_EXPOSE_NATIVE_COCOA +#include "GLFW/glfw3native.h" + +namespace utils { + + std::unique_ptr SetupWindowAndGetSurfaceDescriptorForTesting( + GLFWwindow* window) { + if (@available(macOS 10.11, *)) { + NSWindow* nsWindow = glfwGetCocoaWindow(window); + NSView* view = [nsWindow contentView]; + + // Create a CAMetalLayer that covers the whole window that will be passed to + // CreateSurface. + [view setWantsLayer:YES]; + [view setLayer:[CAMetalLayer layer]]; + + // Use retina if the window was created with retina support. + [[view layer] setContentsScale:[nsWindow backingScaleFactor]]; + + std::unique_ptr desc = + std::make_unique(); + desc->layer = [view layer]; + return std::move(desc); + } + + return nullptr; + } + +} // namespace utils diff --git a/third_party/dawn/src/utils/Glfw3Fuchsia.cpp b/third_party/dawn/src/utils/Glfw3Fuchsia.cpp new file mode 100644 index 00000000000..cc8ed3ba03c --- /dev/null +++ b/third_party/dawn/src/utils/Glfw3Fuchsia.cpp @@ -0,0 +1,100 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// A mock GLFW implementation that supports Fuchsia, but only implements +// the functions called from Dawn. + +// NOTE: This must be included before GLFW/glfw3.h because the latter will +// include and "common/vulkan_platform.h" wants to be +// the first header to do so for sanity reasons (e.g. undefining weird +// macros on Windows and Linux). +// clang-format off +#include "common/vulkan_platform.h" +#include "common/Assert.h" +#include +// clang-format on + +#include + +int glfwInit(void) { + return GLFW_TRUE; +} + +void glfwDefaultWindowHints(void) { +} + +void glfwWindowHint(int hint, int value) { + DAWN_UNUSED(hint); + DAWN_UNUSED(value); +} + +struct GLFWwindow { + PFN_vkGetInstanceProcAddr GetInstanceProcAddress = nullptr; + void* vulkan_loader = nullptr; + + GLFWwindow() { + vulkan_loader = ::dlopen("libvulkan.so", RTLD_NOW); + ASSERT(vulkan_loader != nullptr); + GetInstanceProcAddress = reinterpret_cast( + dlsym(vulkan_loader, "vkGetInstanceProcAddr")); + ASSERT(GetInstanceProcAddress != nullptr); + } + + ~GLFWwindow() { + if (vulkan_loader) { + ::dlclose(vulkan_loader); + } + vulkan_loader = nullptr; + } +}; + +GLFWwindow* glfwCreateWindow(int width, + int height, + const char* title, + GLFWmonitor* monitor, + GLFWwindow* share) { + ASSERT(monitor == nullptr); + ASSERT(share == nullptr); + DAWN_UNUSED(width); + DAWN_UNUSED(height); + DAWN_UNUSED(title); + return new GLFWwindow(); +} + +VkResult glfwCreateWindowSurface(VkInstance instance, + GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) { + // IMPORTANT: This assumes that the VkInstance was created with a Fuchsia + // swapchain layer enabled, as well as the corresponding extension that + // is queried here to perform the surface creation. Dawn should do all + // required steps in VulkanInfo.cpp, VulkanFunctions.cpp and BackendVk.cpp. + + auto vkCreateImagePipeSurfaceFUCHSIA = reinterpret_cast( + window->GetInstanceProcAddress(instance, "vkCreateImagePipeSurfaceFUCHSIA")); + ASSERT(vkCreateImagePipeSurfaceFUCHSIA != nullptr); + if (!vkCreateImagePipeSurfaceFUCHSIA) { + *surface = VK_NULL_HANDLE; + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + const struct VkImagePipeSurfaceCreateInfoFUCHSIA create_info = { + VK_STRUCTURE_TYPE_IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA, + nullptr, // pNext + 0, // flags, ignored for now + ZX_HANDLE_INVALID, // imagePipeHandle, a null handle matches the framebuffer. + }; + + return vkCreateImagePipeSurfaceFUCHSIA(instance, &create_info, nullptr, surface); +} diff --git a/third_party/dawn/src/utils/MetalBinding.mm b/third_party/dawn/src/utils/MetalBinding.mm index 7bc7f417d86..d8875d54e2f 100644 --- a/third_party/dawn/src/utils/MetalBinding.mm +++ b/third_party/dawn/src/utils/MetalBinding.mm @@ -39,14 +39,14 @@ void Init(DawnWSIContextMetal* ctx) { mMtlDevice = ctx->device; - mCommandQueue = [mMtlDevice newCommandQueue]; + mCommandQueue = ctx->queue; } - DawnSwapChainError Configure(DawnTextureFormat format, - DawnTextureUsageBit usage, + DawnSwapChainError Configure(WGPUTextureFormat format, + WGPUTextureUsage usage, uint32_t width, uint32_t height) { - if (format != DAWN_TEXTURE_FORMAT_B8_G8_R8_A8_UNORM) { + if (format != WGPUTextureFormat_BGRA8Unorm) { return "unsupported format"; } ASSERT(width > 0); @@ -65,7 +65,7 @@ DawnSwapChainError Configure(DawnTextureFormat format, [mLayer setDrawableSize:size]; constexpr uint32_t kFramebufferOnlyTextureUsages = - DAWN_TEXTURE_USAGE_BIT_OUTPUT_ATTACHMENT | DAWN_TEXTURE_USAGE_BIT_PRESENT; + WGPUTextureUsage_OutputAttachment | WGPUTextureUsage_Present; bool hasOnlyFramebufferUsages = !(usage & (~kFramebufferOnlyTextureUsages)); if (hasOnlyFramebufferUsages) { [mLayer setFramebufferOnly:YES]; @@ -110,7 +110,7 @@ DawnSwapChainError Present() { class MetalBinding : public BackendBinding { public: - MetalBinding(GLFWwindow* window, DawnDevice device) : BackendBinding(window, device) { + MetalBinding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { } uint64_t GetSwapChainImplementation() override { @@ -121,15 +121,15 @@ uint64_t GetSwapChainImplementation() override { return reinterpret_cast(&mSwapchainImpl); } - DawnTextureFormat GetPreferredSwapChainTextureFormat() override { - return DAWN_TEXTURE_FORMAT_B8_G8_R8_A8_UNORM; + WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { + return WGPUTextureFormat_BGRA8Unorm; } private: DawnSwapChainImplementation mSwapchainImpl = {}; }; - BackendBinding* CreateMetalBinding(GLFWwindow* window, DawnDevice device) { + BackendBinding* CreateMetalBinding(GLFWwindow* window, WGPUDevice device) { return new MetalBinding(window, device); } } diff --git a/third_party/dawn/src/utils/NullBinding.cpp b/third_party/dawn/src/utils/NullBinding.cpp index 3fe4fabda3b..f47b81c6745 100644 --- a/third_party/dawn/src/utils/NullBinding.cpp +++ b/third_party/dawn/src/utils/NullBinding.cpp @@ -23,7 +23,7 @@ namespace utils { class NullBinding : public BackendBinding { public: - NullBinding(GLFWwindow* window, DawnDevice device) : BackendBinding(window, device) { + NullBinding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { } uint64_t GetSwapChainImplementation() override { @@ -32,15 +32,15 @@ namespace utils { } return reinterpret_cast(&mSwapchainImpl); } - DawnTextureFormat GetPreferredSwapChainTextureFormat() override { - return DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM; + WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { + return WGPUTextureFormat_RGBA8Unorm; } private: DawnSwapChainImplementation mSwapchainImpl = {}; }; - BackendBinding* CreateNullBinding(GLFWwindow* window, DawnDevice device) { + BackendBinding* CreateNullBinding(GLFWwindow* window, WGPUDevice device) { return new NullBinding(window, device); } diff --git a/third_party/dawn/src/utils/OSXTimer.cpp b/third_party/dawn/src/utils/OSXTimer.cpp new file mode 100644 index 00000000000..da413759d28 --- /dev/null +++ b/third_party/dawn/src/utils/OSXTimer.cpp @@ -0,0 +1,77 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/Timer.h" + +#include +#include +#include + +namespace utils { + + class OSXTimer : public Timer { + public: + OSXTimer() : Timer(), mRunning(false), mSecondCoeff(0) { + } + + ~OSXTimer() override = default; + + void Start() override { + mStartTime = mach_absolute_time(); + // Cache secondCoeff + GetSecondCoeff(); + mRunning = true; + } + + void Stop() override { + mStopTime = mach_absolute_time(); + mRunning = false; + } + + double GetElapsedTime() const override { + if (mRunning) { + return mSecondCoeff * (mach_absolute_time() - mStartTime); + } else { + return mSecondCoeff * (mStopTime - mStartTime); + } + } + + double GetAbsoluteTime() override { + return GetSecondCoeff() * mach_absolute_time(); + } + + private: + double GetSecondCoeff() { + // If this is the first time we've run, get the timebase. + if (mSecondCoeff == 0.0) { + mach_timebase_info_data_t timebaseInfo; + mach_timebase_info(&timebaseInfo); + + mSecondCoeff = timebaseInfo.numer * (1.0 / 1000000000) / timebaseInfo.denom; + } + + return mSecondCoeff; + } + + bool mRunning; + uint64_t mStartTime; + uint64_t mStopTime; + double mSecondCoeff; + }; + + Timer* CreateTimer() { + return new OSXTimer(); + } + +} // namespace utils diff --git a/third_party/dawn/src/utils/ObjCUtils.h b/third_party/dawn/src/utils/ObjCUtils.h new file mode 100644 index 00000000000..17b3956a165 --- /dev/null +++ b/third_party/dawn/src/utils/ObjCUtils.h @@ -0,0 +1,29 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_OBJCUTILS_H_ +#define UTILS_OBJCUTILS_H_ + +// Contains helper function to manipulate ObjC objects. This helps having C++ files do a little bit +// of ObjectiveC calls, when they cannot be converted to ObjectiveC++ because they are used on +// multiple platforms. + +namespace utils { + + // The returned CALayer is autoreleased. + void* CreateDummyCALayer(); + +} // namespace utils + +#endif // UTILS_OBJCUTILS_H_ diff --git a/third_party/dawn/src/utils/ObjCUtils.mm b/third_party/dawn/src/utils/ObjCUtils.mm new file mode 100644 index 00000000000..5eba147cb60 --- /dev/null +++ b/third_party/dawn/src/utils/ObjCUtils.mm @@ -0,0 +1,25 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/ObjCUtils.h" + +#include + +namespace utils { + + void* CreateDummyCALayer() { + return [CALayer layer]; + } + +} // namespace utils diff --git a/third_party/dawn/src/utils/OpenGLBinding.cpp b/third_party/dawn/src/utils/OpenGLBinding.cpp index d5737f6c6ef..f48f426c7cd 100644 --- a/third_party/dawn/src/utils/OpenGLBinding.cpp +++ b/third_party/dawn/src/utils/OpenGLBinding.cpp @@ -20,102 +20,35 @@ #include "dawn/dawn_wsi.h" #include "dawn_native/OpenGLBackend.h" -// Glad needs to be included before GLFW otherwise it complain that GL.h was already included -#include "glad/glad.h" - #include #include "GLFW/glfw3.h" namespace utils { - class SwapChainImplGL { - public: - using WSIContext = DawnWSIContextGL; - - SwapChainImplGL(GLFWwindow* window) : mWindow(window) { - } - - ~SwapChainImplGL() { - glDeleteTextures(1, &mBackTexture); - glDeleteFramebuffers(1, &mBackFBO); - } - - void Init(DawnWSIContextGL*) { - glGenTextures(1, &mBackTexture); - glBindTexture(GL_TEXTURE_2D, mBackTexture); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); - - glGenFramebuffers(1, &mBackFBO); - glBindFramebuffer(GL_READ_FRAMEBUFFER, mBackFBO); - glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - mBackTexture, 0); - } - - DawnSwapChainError Configure(DawnTextureFormat format, - DawnTextureUsageBit, - uint32_t width, - uint32_t height) { - if (format != DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM) { - return "unsupported format"; - } - ASSERT(width > 0); - ASSERT(height > 0); - mWidth = width; - mHeight = height; - - glBindTexture(GL_TEXTURE_2D, mBackTexture); - // Reallocate the texture - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, - nullptr); - - return DAWN_SWAP_CHAIN_NO_ERROR; - } - - DawnSwapChainError GetNextTexture(DawnSwapChainNextTexture* nextTexture) { - nextTexture->texture.u32 = mBackTexture; - return DAWN_SWAP_CHAIN_NO_ERROR; - } - - DawnSwapChainError Present() { - glBindFramebuffer(GL_READ_FRAMEBUFFER, mBackFBO); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBlitFramebuffer(0, 0, mWidth, mHeight, 0, mHeight, mWidth, 0, GL_COLOR_BUFFER_BIT, - GL_NEAREST); - glfwSwapBuffers(mWindow); - - return DAWN_SWAP_CHAIN_NO_ERROR; - } - - private: - GLFWwindow* mWindow = nullptr; - uint32_t mWidth = 0; - uint32_t mHeight = 0; - GLuint mBackFBO = 0; - GLuint mBackTexture = 0; - }; class OpenGLBinding : public BackendBinding { public: - OpenGLBinding(GLFWwindow* window, DawnDevice device) : BackendBinding(window, device) { - // Load the GL entry points in our copy of the glad static library - gladLoadGLLoader(reinterpret_cast(glfwGetProcAddress)); + OpenGLBinding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { } uint64_t GetSwapChainImplementation() override { if (mSwapchainImpl.userData == nullptr) { - mSwapchainImpl = CreateSwapChainImplementation(new SwapChainImplGL(mWindow)); + mSwapchainImpl = dawn_native::opengl::CreateNativeSwapChainImpl( + mDevice, + [](void* userdata) { glfwSwapBuffers(static_cast(userdata)); }, + mWindow); } return reinterpret_cast(&mSwapchainImpl); } - DawnTextureFormat GetPreferredSwapChainTextureFormat() override { - return DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM; + WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { + return dawn_native::opengl::GetNativeSwapChainPreferredFormat(&mSwapchainImpl); } private: DawnSwapChainImplementation mSwapchainImpl = {}; }; - BackendBinding* CreateOpenGLBinding(GLFWwindow* window, DawnDevice device) { + BackendBinding* CreateOpenGLBinding(GLFWwindow* window, WGPUDevice device) { return new OpenGLBinding(window, device); } diff --git a/third_party/dawn/src/utils/PosixTimer.cpp b/third_party/dawn/src/utils/PosixTimer.cpp new file mode 100644 index 00000000000..a79e7b15211 --- /dev/null +++ b/third_party/dawn/src/utils/PosixTimer.cpp @@ -0,0 +1,74 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/Timer.h" + +#include +#include + +namespace utils { + + namespace { + + uint64_t GetCurrentTimeNs() { + struct timespec currentTime; + clock_gettime(CLOCK_MONOTONIC, ¤tTime); + return currentTime.tv_sec * 1'000'000'000llu + currentTime.tv_nsec; + } + + } // anonymous namespace + + class PosixTimer : public Timer { + public: + PosixTimer() : Timer(), mRunning(false) { + } + + ~PosixTimer() override = default; + + void Start() override { + mStartTimeNs = GetCurrentTimeNs(); + mRunning = true; + } + + void Stop() override { + mStopTimeNs = GetCurrentTimeNs(); + mRunning = false; + } + + double GetElapsedTime() const override { + uint64_t endTimeNs; + if (mRunning) { + endTimeNs = GetCurrentTimeNs(); + } else { + endTimeNs = mStopTimeNs; + } + + return (endTimeNs - mStartTimeNs) * 1e-9; + } + + double GetAbsoluteTime() override { + return GetCurrentTimeNs() * 1e-9; + } + + private: + bool mRunning; + uint64_t mStartTimeNs; + uint64_t mStopTimeNs; + }; + + Timer* CreateTimer() { + return new PosixTimer(); + } + +} // namespace utils diff --git a/third_party/dawn/src/utils/SystemUtils.h b/third_party/dawn/src/utils/SystemUtils.h index 828eb58aabf..1f42cc539a6 100644 --- a/third_party/dawn/src/utils/SystemUtils.h +++ b/third_party/dawn/src/utils/SystemUtils.h @@ -20,4 +20,4 @@ namespace utils { void USleep(unsigned int usecs); } -#endif // UTILS_SYSTEMUTILS_H_ \ No newline at end of file +#endif // UTILS_SYSTEMUTILS_H_ diff --git a/third_party/dawn/src/utils/TerribleCommandBuffer.cpp b/third_party/dawn/src/utils/TerribleCommandBuffer.cpp index 77f86ec4b29..aa0bc8ca284 100644 --- a/third_party/dawn/src/utils/TerribleCommandBuffer.cpp +++ b/third_party/dawn/src/utils/TerribleCommandBuffer.cpp @@ -34,26 +34,71 @@ namespace utils { // (Here and/or in the caller?) It might be good to make the wire receiver get a nullptr // instead of pointer to zero-sized allocation in mBuffer. + // Cannot have commands in mBuffer and mLargeBuffer at same time. + ASSERT(mOffset == 0 || mLargeBufferCmdSize == 0); + if (size > sizeof(mBuffer)) { - return nullptr; + // Flush current cmds in mBuffer to keep order. + if (mOffset > 0) { + if (!Flush()) { + return nullptr; + } + return GetCmdSpace(size); + } + + // Resize large buffer to the size that can + // contain incoming command if needed. + if (mLargeBuffer.size() < size) { + mLargeBuffer.resize(size); + } + + // Record whole cmd space. + mLargeBufferCmdSize = size; + + return mLargeBuffer.data(); + } + + // Trigger flush if large buffer contain cmds. + if (mLargeBufferCmdSize > 0) { + if (!Flush()) { + return nullptr; + } + return GetCmdSpace(size); } + // Need to flush large buffer first. + ASSERT(mLargeBufferCmdSize == 0); + char* result = &mBuffer[mOffset]; - mOffset += size; - if (mOffset > sizeof(mBuffer)) { + if (sizeof(mBuffer) - size < mOffset) { if (!Flush()) { return nullptr; } return GetCmdSpace(size); } + mOffset += size; + return result; } bool TerribleCommandBuffer::Flush() { - bool success = mHandler->HandleCommands(mBuffer, mOffset) != nullptr; + // Cannot have commands in mBuffer and mLargeBuffer at same time. + ASSERT(mOffset == 0 || mLargeBufferCmdSize == 0); + + bool success = false; + // Big buffer not empty, flush it! + if (mLargeBufferCmdSize > 0) { + success = mHandler->HandleCommands(mLargeBuffer.data(), mLargeBufferCmdSize) != nullptr; + // Clear big command buffers. + mLargeBufferCmdSize = 0; + return success; + } + + success = mHandler->HandleCommands(mBuffer, mOffset) != nullptr; mOffset = 0; + return success; } diff --git a/third_party/dawn/src/utils/TerribleCommandBuffer.h b/third_party/dawn/src/utils/TerribleCommandBuffer.h index b5affc85530..9a41bb36112 100644 --- a/third_party/dawn/src/utils/TerribleCommandBuffer.h +++ b/third_party/dawn/src/utils/TerribleCommandBuffer.h @@ -34,7 +34,11 @@ namespace utils { private: dawn_wire::CommandHandler* mHandler = nullptr; size_t mOffset = 0; - char mBuffer[10000000]; + // Cannot have commands in mBuffer and mLargeBuffer + // at the same time to ensure commands order. + char mBuffer[1000000]; + std::vector mLargeBuffer; + size_t mLargeBufferCmdSize = 0; }; } // namespace utils diff --git a/third_party/dawn/src/utils/TextureFormatUtils.cpp b/third_party/dawn/src/utils/TextureFormatUtils.cpp new file mode 100644 index 00000000000..9a577798dfc --- /dev/null +++ b/third_party/dawn/src/utils/TextureFormatUtils.cpp @@ -0,0 +1,387 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "TextureFormatUtils.h" + +namespace utils { + const char* GetColorTextureComponentTypePrefix(wgpu::TextureFormat textureFormat) { + switch (textureFormat) { + case wgpu::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Snorm: + case wgpu::TextureFormat::R16Float: + case wgpu::TextureFormat::RG8Unorm: + case wgpu::TextureFormat::RG8Snorm: + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::RG16Float: + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGB10A2Unorm: + case wgpu::TextureFormat::RG11B10Float: + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RGBA16Float: + case wgpu::TextureFormat::RGBA32Float: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + case wgpu::TextureFormat::RGBA8UnormSrgb: + return ""; + + case wgpu::TextureFormat::R8Uint: + case wgpu::TextureFormat::R16Uint: + case wgpu::TextureFormat::RG8Uint: + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::RG16Uint: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA32Uint: + return "u"; + + case wgpu::TextureFormat::R8Sint: + case wgpu::TextureFormat::R16Sint: + case wgpu::TextureFormat::RG8Sint: + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::RG16Sint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA32Sint: + return "i"; + default: + UNREACHABLE(); + return ""; + } + } + + bool TextureFormatSupportsStorageTexture(wgpu::TextureFormat format) { + switch (format) { + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA16Float: + case wgpu::TextureFormat::RGBA32Uint: + case wgpu::TextureFormat::RGBA32Sint: + case wgpu::TextureFormat::RGBA32Float: + return true; + default: + return false; + } + } + + uint32_t GetTexelBlockSizeInBytes(wgpu::TextureFormat textureFormat) { + switch (textureFormat) { + case wgpu::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Snorm: + case wgpu::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Sint: + return 1u; + + case wgpu::TextureFormat::R16Uint: + case wgpu::TextureFormat::R16Sint: + case wgpu::TextureFormat::R16Float: + case wgpu::TextureFormat::RG8Unorm: + case wgpu::TextureFormat::RG8Snorm: + case wgpu::TextureFormat::RG8Uint: + case wgpu::TextureFormat::RG8Sint: + return 2u; + + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::RG16Uint: + case wgpu::TextureFormat::RG16Sint: + case wgpu::TextureFormat::RG16Float: + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8UnormSrgb: + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + case wgpu::TextureFormat::RGB10A2Unorm: + case wgpu::TextureFormat::RG11B10Float: + case wgpu::TextureFormat::Depth32Float: + return 4u; + + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA16Float: + return 8u; + + case wgpu::TextureFormat::RGBA32Float: + case wgpu::TextureFormat::RGBA32Uint: + case wgpu::TextureFormat::RGBA32Sint: + return 16u; + + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + case wgpu::TextureFormat::BC4RUnorm: + case wgpu::TextureFormat::BC4RSnorm: + return 8u; + + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + case wgpu::TextureFormat::BC3RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + case wgpu::TextureFormat::BC5RGUnorm: + case wgpu::TextureFormat::BC5RGSnorm: + case wgpu::TextureFormat::BC6HRGBUfloat: + case wgpu::TextureFormat::BC6HRGBSfloat: + case wgpu::TextureFormat::BC7RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return 16u; + + case wgpu::TextureFormat::Depth24Plus: + case wgpu::TextureFormat::Depth24PlusStencil8: + case wgpu::TextureFormat::Undefined: + default: + UNREACHABLE(); + return 0u; + } + } + + uint32_t GetTextureFormatBlockWidth(wgpu::TextureFormat textureFormat) { + switch (textureFormat) { + case wgpu::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Snorm: + case wgpu::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Sint: + case wgpu::TextureFormat::R16Uint: + case wgpu::TextureFormat::R16Sint: + case wgpu::TextureFormat::R16Float: + case wgpu::TextureFormat::RG8Unorm: + case wgpu::TextureFormat::RG8Snorm: + case wgpu::TextureFormat::RG8Uint: + case wgpu::TextureFormat::RG8Sint: + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::RG16Uint: + case wgpu::TextureFormat::RG16Sint: + case wgpu::TextureFormat::RG16Float: + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8UnormSrgb: + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + case wgpu::TextureFormat::RGB10A2Unorm: + case wgpu::TextureFormat::RG11B10Float: + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA16Float: + case wgpu::TextureFormat::RGBA32Float: + case wgpu::TextureFormat::RGBA32Uint: + case wgpu::TextureFormat::RGBA32Sint: + case wgpu::TextureFormat::Depth32Float: + case wgpu::TextureFormat::Depth24Plus: + case wgpu::TextureFormat::Depth24PlusStencil8: + return 1u; + + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + case wgpu::TextureFormat::BC4RUnorm: + case wgpu::TextureFormat::BC4RSnorm: + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + case wgpu::TextureFormat::BC3RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + case wgpu::TextureFormat::BC5RGUnorm: + case wgpu::TextureFormat::BC5RGSnorm: + case wgpu::TextureFormat::BC6HRGBUfloat: + case wgpu::TextureFormat::BC6HRGBSfloat: + case wgpu::TextureFormat::BC7RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return 4u; + + case wgpu::TextureFormat::Undefined: + default: + UNREACHABLE(); + return 0u; + } + } + + uint32_t GetTextureFormatBlockHeight(wgpu::TextureFormat textureFormat) { + switch (textureFormat) { + case wgpu::TextureFormat::R8Unorm: + case wgpu::TextureFormat::R8Snorm: + case wgpu::TextureFormat::R8Uint: + case wgpu::TextureFormat::R8Sint: + case wgpu::TextureFormat::R16Uint: + case wgpu::TextureFormat::R16Sint: + case wgpu::TextureFormat::R16Float: + case wgpu::TextureFormat::RG8Unorm: + case wgpu::TextureFormat::RG8Snorm: + case wgpu::TextureFormat::RG8Uint: + case wgpu::TextureFormat::RG8Sint: + case wgpu::TextureFormat::R32Float: + case wgpu::TextureFormat::R32Uint: + case wgpu::TextureFormat::R32Sint: + case wgpu::TextureFormat::RG16Uint: + case wgpu::TextureFormat::RG16Sint: + case wgpu::TextureFormat::RG16Float: + case wgpu::TextureFormat::RGBA8Unorm: + case wgpu::TextureFormat::RGBA8UnormSrgb: + case wgpu::TextureFormat::RGBA8Snorm: + case wgpu::TextureFormat::RGBA8Uint: + case wgpu::TextureFormat::RGBA8Sint: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + case wgpu::TextureFormat::RGB10A2Unorm: + case wgpu::TextureFormat::RG11B10Float: + case wgpu::TextureFormat::RG32Float: + case wgpu::TextureFormat::RG32Uint: + case wgpu::TextureFormat::RG32Sint: + case wgpu::TextureFormat::RGBA16Uint: + case wgpu::TextureFormat::RGBA16Sint: + case wgpu::TextureFormat::RGBA16Float: + case wgpu::TextureFormat::RGBA32Float: + case wgpu::TextureFormat::RGBA32Uint: + case wgpu::TextureFormat::RGBA32Sint: + case wgpu::TextureFormat::Depth32Float: + case wgpu::TextureFormat::Depth24Plus: + case wgpu::TextureFormat::Depth24PlusStencil8: + return 1u; + + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + case wgpu::TextureFormat::BC4RUnorm: + case wgpu::TextureFormat::BC4RSnorm: + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + case wgpu::TextureFormat::BC3RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + case wgpu::TextureFormat::BC5RGUnorm: + case wgpu::TextureFormat::BC5RGSnorm: + case wgpu::TextureFormat::BC6HRGBUfloat: + case wgpu::TextureFormat::BC6HRGBSfloat: + case wgpu::TextureFormat::BC7RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return 4u; + + case wgpu::TextureFormat::Undefined: + default: + UNREACHABLE(); + return 0u; + } + } + + const char* GetGLSLImageFormatQualifier(wgpu::TextureFormat textureFormat) { + switch (textureFormat) { + case wgpu::TextureFormat::R8Unorm: + return "r8"; + case wgpu::TextureFormat::R8Snorm: + return "r8_snorm"; + case wgpu::TextureFormat::R8Uint: + return "r8ui"; + case wgpu::TextureFormat::R8Sint: + return "r8i"; + case wgpu::TextureFormat::R16Uint: + return "r16ui"; + case wgpu::TextureFormat::R16Sint: + return "r16i"; + case wgpu::TextureFormat::R16Float: + return "r16f"; + case wgpu::TextureFormat::RG8Unorm: + return "rg8"; + case wgpu::TextureFormat::RG8Snorm: + return "rg8_snorm"; + case wgpu::TextureFormat::RG8Uint: + return "rg8ui"; + case wgpu::TextureFormat::RG8Sint: + return "rg8i"; + case wgpu::TextureFormat::R32Float: + return "r32f"; + case wgpu::TextureFormat::R32Uint: + return "r32ui"; + case wgpu::TextureFormat::R32Sint: + return "r32i"; + case wgpu::TextureFormat::RG16Uint: + return "rg16ui"; + case wgpu::TextureFormat::RG16Sint: + return "rg16i"; + case wgpu::TextureFormat::RG16Float: + return "rg16f"; + case wgpu::TextureFormat::RGBA8Unorm: + return "rgba8"; + case wgpu::TextureFormat::RGBA8Snorm: + return "rgba8_snorm"; + case wgpu::TextureFormat::RGBA8Uint: + return "rgba8ui"; + case wgpu::TextureFormat::RGBA8Sint: + return "rgba8i"; + case wgpu::TextureFormat::RGB10A2Unorm: + return "rgb10_a2"; + case wgpu::TextureFormat::RG11B10Float: + return "r11f_g11f_b10f"; + case wgpu::TextureFormat::RG32Float: + return "rg32f"; + case wgpu::TextureFormat::RG32Uint: + return "rg32ui"; + case wgpu::TextureFormat::RG32Sint: + return "rg32i"; + case wgpu::TextureFormat::RGBA16Uint: + return "rgba16ui"; + case wgpu::TextureFormat::RGBA16Sint: + return "rgba16i"; + case wgpu::TextureFormat::RGBA16Float: + return "rgba16f"; + case wgpu::TextureFormat::RGBA32Float: + return "rgba32f"; + case wgpu::TextureFormat::RGBA32Uint: + return "rgba32ui"; + case wgpu::TextureFormat::RGBA32Sint: + return "rgba32i"; + + case wgpu::TextureFormat::RGBA8UnormSrgb: + case wgpu::TextureFormat::BGRA8Unorm: + case wgpu::TextureFormat::BGRA8UnormSrgb: + case wgpu::TextureFormat::BC1RGBAUnorm: + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + case wgpu::TextureFormat::BC4RUnorm: + case wgpu::TextureFormat::BC4RSnorm: + case wgpu::TextureFormat::BC2RGBAUnorm: + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + case wgpu::TextureFormat::BC3RGBAUnorm: + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + case wgpu::TextureFormat::BC5RGUnorm: + case wgpu::TextureFormat::BC5RGSnorm: + case wgpu::TextureFormat::BC6HRGBUfloat: + case wgpu::TextureFormat::BC6HRGBSfloat: + case wgpu::TextureFormat::BC7RGBAUnorm: + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + case wgpu::TextureFormat::Depth32Float: + case wgpu::TextureFormat::Depth24Plus: + case wgpu::TextureFormat::Depth24PlusStencil8: + case wgpu::TextureFormat::Undefined: + UNREACHABLE(); + return ""; + } + } +} // namespace utils diff --git a/third_party/dawn/src/utils/TextureFormatUtils.h b/third_party/dawn/src/utils/TextureFormatUtils.h new file mode 100644 index 00000000000..cdd8942583b --- /dev/null +++ b/third_party/dawn/src/utils/TextureFormatUtils.h @@ -0,0 +1,63 @@ +// Copyright 2020 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_TEXTURE_FORMAT_UTILS_H_ +#define UTILS_TEXTURE_FORMAT_UTILS_H_ + +#include + +#include + +#include "common/Assert.h" + +namespace utils { + static constexpr std::array kAllTextureFormats = { + wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Snorm, + wgpu::TextureFormat::R8Uint, wgpu::TextureFormat::R8Sint, + wgpu::TextureFormat::R16Uint, wgpu::TextureFormat::R16Sint, + wgpu::TextureFormat::R16Float, wgpu::TextureFormat::RG8Unorm, + wgpu::TextureFormat::RG8Snorm, wgpu::TextureFormat::RG8Uint, + wgpu::TextureFormat::RG8Sint, wgpu::TextureFormat::R32Float, + wgpu::TextureFormat::R32Uint, wgpu::TextureFormat::R32Sint, + wgpu::TextureFormat::RG16Uint, wgpu::TextureFormat::RG16Sint, + wgpu::TextureFormat::RG16Float, wgpu::TextureFormat::RGBA8Unorm, + wgpu::TextureFormat::RGBA8UnormSrgb, wgpu::TextureFormat::RGBA8Snorm, + wgpu::TextureFormat::RGBA8Uint, wgpu::TextureFormat::RGBA8Sint, + wgpu::TextureFormat::BGRA8Unorm, wgpu::TextureFormat::BGRA8UnormSrgb, + wgpu::TextureFormat::RGB10A2Unorm, wgpu::TextureFormat::RG11B10Float, + wgpu::TextureFormat::RG32Float, wgpu::TextureFormat::RG32Uint, + wgpu::TextureFormat::RG32Sint, wgpu::TextureFormat::RGBA16Uint, + wgpu::TextureFormat::RGBA16Sint, wgpu::TextureFormat::RGBA16Float, + wgpu::TextureFormat::RGBA32Float, wgpu::TextureFormat::RGBA32Uint, + wgpu::TextureFormat::RGBA32Sint, wgpu::TextureFormat::Depth32Float, + wgpu::TextureFormat::Depth24Plus, wgpu::TextureFormat::Depth24PlusStencil8, + wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb, + wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb, + wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb, + wgpu::TextureFormat::BC4RUnorm, wgpu::TextureFormat::BC4RSnorm, + wgpu::TextureFormat::BC5RGUnorm, wgpu::TextureFormat::BC5RGSnorm, + wgpu::TextureFormat::BC6HRGBUfloat, wgpu::TextureFormat::BC6HRGBSfloat, + wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb, + }; + + const char* GetColorTextureComponentTypePrefix(wgpu::TextureFormat textureFormat); + bool TextureFormatSupportsStorageTexture(wgpu::TextureFormat format); + + uint32_t GetTexelBlockSizeInBytes(wgpu::TextureFormat textureFormat); + uint32_t GetTextureFormatBlockWidth(wgpu::TextureFormat textureFormat); + uint32_t GetTextureFormatBlockHeight(wgpu::TextureFormat textureFormat); + const char* GetGLSLImageFormatQualifier(wgpu::TextureFormat textureFormat); +} // namespace utils + +#endif diff --git a/third_party/dawn/src/utils/Timer.h b/third_party/dawn/src/utils/Timer.h new file mode 100644 index 00000000000..86587ddba06 --- /dev/null +++ b/third_party/dawn/src/utils/Timer.h @@ -0,0 +1,41 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_TIMER_H_ +#define UTILS_TIMER_H_ + +namespace utils { + + class Timer { + public: + virtual ~Timer() { + } + + // Timer functionality: Use start() and stop() to record the duration and use + // getElapsedTime() to query that duration. If getElapsedTime() is called in between, it + // will report the elapsed time since start(). + virtual void Start() = 0; + virtual void Stop() = 0; + virtual double GetElapsedTime() const = 0; + + // Timestamp functionality: Use getAbsoluteTime() to get an absolute time with an unknown + // origin. This time moves forward regardless of start()/stop(). + virtual double GetAbsoluteTime() = 0; + }; + + Timer* CreateTimer(); + +} // namespace utils + +#endif // UTILS_TIMER_H_ diff --git a/third_party/dawn/src/utils/VulkanBinding.cpp b/third_party/dawn/src/utils/VulkanBinding.cpp index 61386a4eb12..577c3bce6b4 100644 --- a/third_party/dawn/src/utils/VulkanBinding.cpp +++ b/third_party/dawn/src/utils/VulkanBinding.cpp @@ -26,7 +26,7 @@ namespace utils { class VulkanBinding : public BackendBinding { public: - VulkanBinding(GLFWwindow* window, DawnDevice device) : BackendBinding(window, device) { + VulkanBinding(GLFWwindow* window, WGPUDevice device) : BackendBinding(window, device) { } uint64_t GetSwapChainImplementation() override { @@ -41,7 +41,7 @@ namespace utils { } return reinterpret_cast(&mSwapchainImpl); } - DawnTextureFormat GetPreferredSwapChainTextureFormat() override { + WGPUTextureFormat GetPreferredSwapChainTextureFormat() override { ASSERT(mSwapchainImpl.userData != nullptr); return dawn_native::vulkan::GetNativeSwapChainPreferredFormat(&mSwapchainImpl); } @@ -50,7 +50,7 @@ namespace utils { DawnSwapChainImplementation mSwapchainImpl = {}; }; - BackendBinding* CreateVulkanBinding(GLFWwindow* window, DawnDevice device) { + BackendBinding* CreateVulkanBinding(GLFWwindow* window, WGPUDevice device) { return new VulkanBinding(window, device); } diff --git a/third_party/dawn/src/utils/WGPUHelpers.cpp b/third_party/dawn/src/utils/WGPUHelpers.cpp new file mode 100644 index 00000000000..0a47b8d709d --- /dev/null +++ b/third_party/dawn/src/utils/WGPUHelpers.cpp @@ -0,0 +1,458 @@ +// Copyright 2017 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/WGPUHelpers.h" + +#include "common/Assert.h" +#include "common/Constants.h" +#include "common/Log.h" +#include "common/Math.h" +#include "utils/TextureFormatUtils.h" + +#include + +#include +#include +#include +#include + +namespace utils { + + namespace { + + shaderc_shader_kind ShadercShaderKind(SingleShaderStage stage) { + switch (stage) { + case SingleShaderStage::Vertex: + return shaderc_glsl_vertex_shader; + case SingleShaderStage::Fragment: + return shaderc_glsl_fragment_shader; + case SingleShaderStage::Compute: + return shaderc_glsl_compute_shader; + default: + UNREACHABLE(); + } + } + + wgpu::ShaderModule CreateShaderModuleFromResult( + const wgpu::Device& device, + const shaderc::SpvCompilationResult& result) { + // result.cend and result.cbegin return pointers to uint32_t. + const uint32_t* resultBegin = result.cbegin(); + const uint32_t* resultEnd = result.cend(); + // So this size is in units of sizeof(uint32_t). + ptrdiff_t resultSize = resultEnd - resultBegin; + // SetSource takes data as uint32_t*. + + wgpu::ShaderModuleSPIRVDescriptor spirvDesc; + spirvDesc.codeSize = static_cast(resultSize); + spirvDesc.code = result.cbegin(); + + wgpu::ShaderModuleDescriptor descriptor; + descriptor.nextInChain = &spirvDesc; + + return device.CreateShaderModule(&descriptor); + } + + class CompilerSingleton { + public: + static shaderc::Compiler* Get() { + std::call_once(mInitFlag, &CompilerSingleton::Initialize); + return mCompiler; + } + + private: + CompilerSingleton() = default; + ~CompilerSingleton() = default; + CompilerSingleton(const CompilerSingleton&) = delete; + CompilerSingleton& operator=(const CompilerSingleton&) = delete; + + static shaderc::Compiler* mCompiler; + static std::once_flag mInitFlag; + + static void Initialize() { + mCompiler = new shaderc::Compiler(); + } + }; + + shaderc::Compiler* CompilerSingleton::mCompiler = nullptr; + std::once_flag CompilerSingleton::mInitFlag; + + } // anonymous namespace + + wgpu::ShaderModule CreateShaderModule(const wgpu::Device& device, + SingleShaderStage stage, + const char* source) { + shaderc_shader_kind kind = ShadercShaderKind(stage); + + shaderc::Compiler* compiler = CompilerSingleton::Get(); + auto result = compiler->CompileGlslToSpv(source, strlen(source), kind, "myshader?"); + if (result.GetCompilationStatus() != shaderc_compilation_status_success) { + dawn::ErrorLog() << result.GetErrorMessage(); + return {}; + } +#ifdef DUMP_SPIRV_ASSEMBLY + { + shaderc::CompileOptions options; + auto resultAsm = compiler->CompileGlslToSpvAssembly(source, strlen(source), kind, + "myshader?", options); + size_t sizeAsm = (resultAsm.cend() - resultAsm.cbegin()); + + char* buffer = reinterpret_cast(malloc(sizeAsm + 1)); + memcpy(buffer, resultAsm.cbegin(), sizeAsm); + buffer[sizeAsm] = '\0'; + printf("SPIRV ASSEMBLY DUMP START\n%s\nSPIRV ASSEMBLY DUMP END\n", buffer); + free(buffer); + } +#endif + +#ifdef DUMP_SPIRV_JS_ARRAY + printf("SPIRV JS ARRAY DUMP START\n"); + for (size_t i = 0; i < size; i++) { + printf("%#010x", result.cbegin()[i]); + if ((i + 1) % 4 == 0) { + printf(",\n"); + } else { + printf(", "); + } + } + printf("\n"); + printf("SPIRV JS ARRAY DUMP END\n"); +#endif + + return CreateShaderModuleFromResult(device, result); + } + + wgpu::ShaderModule CreateShaderModuleFromASM(const wgpu::Device& device, const char* source) { + shaderc::Compiler* compiler = CompilerSingleton::Get(); + shaderc::SpvCompilationResult result = compiler->AssembleToSpv(source, strlen(source)); + if (result.GetCompilationStatus() != shaderc_compilation_status_success) { + dawn::ErrorLog() << result.GetErrorMessage(); + return {}; + } + + return CreateShaderModuleFromResult(device, result); + } + + std::vector CompileGLSLToSpirv(SingleShaderStage stage, const char* source) { + shaderc_shader_kind kind = ShadercShaderKind(stage); + + shaderc::Compiler* compiler = CompilerSingleton::Get(); + auto result = compiler->CompileGlslToSpv(source, strlen(source), kind, "myshader?"); + if (result.GetCompilationStatus() != shaderc_compilation_status_success) { + dawn::ErrorLog() << result.GetErrorMessage(); + return {}; + } + return {result.cbegin(), result.cend()}; + } + + wgpu::Buffer CreateBufferFromData(const wgpu::Device& device, + const void* data, + uint64_t size, + wgpu::BufferUsage usage) { + wgpu::BufferDescriptor descriptor; + descriptor.size = size; + descriptor.usage = usage | wgpu::BufferUsage::CopyDst; + wgpu::Buffer buffer = device.CreateBuffer(&descriptor); + + device.GetDefaultQueue().WriteBuffer(buffer, 0, data, size); + return buffer; + } + + ComboRenderPassDescriptor::ComboRenderPassDescriptor( + std::initializer_list colorAttachmentInfo, + wgpu::TextureView depthStencil) { + for (uint32_t i = 0; i < kMaxColorAttachments; ++i) { + cColorAttachments[i].loadOp = wgpu::LoadOp::Clear; + cColorAttachments[i].storeOp = wgpu::StoreOp::Store; + cColorAttachments[i].clearColor = {0.0f, 0.0f, 0.0f, 0.0f}; + } + + cDepthStencilAttachmentInfo.clearDepth = 1.0f; + cDepthStencilAttachmentInfo.clearStencil = 0; + cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear; + cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store; + cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear; + cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store; + + colorAttachmentCount = static_cast(colorAttachmentInfo.size()); + uint32_t colorAttachmentIndex = 0; + for (const wgpu::TextureView& colorAttachment : colorAttachmentInfo) { + if (colorAttachment.Get() != nullptr) { + cColorAttachments[colorAttachmentIndex].attachment = colorAttachment; + } + ++colorAttachmentIndex; + } + colorAttachments = cColorAttachments.data(); + + if (depthStencil.Get() != nullptr) { + cDepthStencilAttachmentInfo.attachment = depthStencil; + depthStencilAttachment = &cDepthStencilAttachmentInfo; + } else { + depthStencilAttachment = nullptr; + } + } + + ComboRenderPassDescriptor::ComboRenderPassDescriptor(const ComboRenderPassDescriptor& other) { + *this = other; + } + + const ComboRenderPassDescriptor& ComboRenderPassDescriptor::operator=( + const ComboRenderPassDescriptor& otherRenderPass) { + cDepthStencilAttachmentInfo = otherRenderPass.cDepthStencilAttachmentInfo; + cColorAttachments = otherRenderPass.cColorAttachments; + colorAttachmentCount = otherRenderPass.colorAttachmentCount; + + colorAttachments = cColorAttachments.data(); + + if (otherRenderPass.depthStencilAttachment != nullptr) { + // Assign desc.depthStencilAttachment to this->depthStencilAttachmentInfo; + depthStencilAttachment = &cDepthStencilAttachmentInfo; + } else { + depthStencilAttachment = nullptr; + } + + return *this; + } + + BasicRenderPass::BasicRenderPass() + : width(0), + height(0), + color(nullptr), + colorFormat(wgpu::TextureFormat::RGBA8Unorm), + renderPassInfo({}) { + } + + BasicRenderPass::BasicRenderPass(uint32_t texWidth, + uint32_t texHeight, + wgpu::Texture colorAttachment, + wgpu::TextureFormat textureFormat) + : width(texWidth), + height(texHeight), + color(colorAttachment), + colorFormat(textureFormat), + renderPassInfo({colorAttachment.CreateView()}) { + } + + BasicRenderPass CreateBasicRenderPass(const wgpu::Device& device, + uint32_t width, + uint32_t height) { + DAWN_ASSERT(width > 0 && height > 0); + + wgpu::TextureDescriptor descriptor; + descriptor.dimension = wgpu::TextureDimension::e2D; + descriptor.size.width = width; + descriptor.size.height = height; + descriptor.size.depth = 1; + descriptor.sampleCount = 1; + descriptor.format = BasicRenderPass::kDefaultColorFormat; + descriptor.mipLevelCount = 1; + descriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + wgpu::Texture color = device.CreateTexture(&descriptor); + + return BasicRenderPass(width, height, color); + } + + wgpu::BufferCopyView CreateBufferCopyView(wgpu::Buffer buffer, + uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage) { + wgpu::BufferCopyView bufferCopyView = {}; + bufferCopyView.buffer = buffer; + bufferCopyView.layout = CreateTextureDataLayout(offset, bytesPerRow, rowsPerImage); + + return bufferCopyView; + } + + wgpu::TextureCopyView CreateTextureCopyView(wgpu::Texture texture, + uint32_t mipLevel, + wgpu::Origin3D origin) { + wgpu::TextureCopyView textureCopyView; + textureCopyView.texture = texture; + textureCopyView.mipLevel = mipLevel; + textureCopyView.origin = origin; + + return textureCopyView; + } + + wgpu::TextureDataLayout CreateTextureDataLayout(uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage) { + wgpu::TextureDataLayout textureDataLayout; + textureDataLayout.offset = offset; + textureDataLayout.bytesPerRow = bytesPerRow; + textureDataLayout.rowsPerImage = rowsPerImage; + + return textureDataLayout; + } + + wgpu::SamplerDescriptor GetDefaultSamplerDescriptor() { + wgpu::SamplerDescriptor desc = {}; + + desc.minFilter = wgpu::FilterMode::Linear; + desc.magFilter = wgpu::FilterMode::Linear; + desc.mipmapFilter = wgpu::FilterMode::Linear; + desc.addressModeU = wgpu::AddressMode::Repeat; + desc.addressModeV = wgpu::AddressMode::Repeat; + desc.addressModeW = wgpu::AddressMode::Repeat; + + return desc; + } + + wgpu::PipelineLayout MakeBasicPipelineLayout(const wgpu::Device& device, + const wgpu::BindGroupLayout* bindGroupLayout) { + wgpu::PipelineLayoutDescriptor descriptor; + if (bindGroupLayout != nullptr) { + descriptor.bindGroupLayoutCount = 1; + descriptor.bindGroupLayouts = bindGroupLayout; + } else { + descriptor.bindGroupLayoutCount = 0; + descriptor.bindGroupLayouts = nullptr; + } + return device.CreatePipelineLayout(&descriptor); + } + + wgpu::BindGroupLayout MakeBindGroupLayout( + const wgpu::Device& device, + std::initializer_list entriesInitializer) { + std::vector entries; + for (const wgpu::BindGroupLayoutEntry& entry : entriesInitializer) { + entries.push_back(entry); + } + + wgpu::BindGroupLayoutDescriptor descriptor; + descriptor.entryCount = static_cast(entries.size()); + descriptor.entries = entries.data(); + return device.CreateBindGroupLayout(&descriptor); + } + + BindingInitializationHelper::BindingInitializationHelper(uint32_t binding, + const wgpu::Sampler& sampler) + : binding(binding), sampler(sampler) { + } + + BindingInitializationHelper::BindingInitializationHelper(uint32_t binding, + const wgpu::TextureView& textureView) + : binding(binding), textureView(textureView) { + } + + BindingInitializationHelper::BindingInitializationHelper(uint32_t binding, + const wgpu::Buffer& buffer, + uint64_t offset, + uint64_t size) + : binding(binding), buffer(buffer), offset(offset), size(size) { + } + + wgpu::BindGroupEntry BindingInitializationHelper::GetAsBinding() const { + wgpu::BindGroupEntry result; + + result.binding = binding; + result.sampler = sampler; + result.textureView = textureView; + result.buffer = buffer; + result.offset = offset; + result.size = size; + + return result; + } + + wgpu::BindGroup MakeBindGroup( + const wgpu::Device& device, + const wgpu::BindGroupLayout& layout, + std::initializer_list entriesInitializer) { + std::vector entries; + for (const BindingInitializationHelper& helper : entriesInitializer) { + entries.push_back(helper.GetAsBinding()); + } + + wgpu::BindGroupDescriptor descriptor; + descriptor.layout = layout; + descriptor.entryCount = entries.size(); + descriptor.entries = entries.data(); + + return device.CreateBindGroup(&descriptor); + } + + uint32_t GetMinimumBytesPerRow(wgpu::TextureFormat format, uint32_t width) { + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); + return Align(bytesPerTexel * width, kTextureBytesPerRowAlignment); + } + + uint32_t GetBytesInBufferTextureCopy(wgpu::TextureFormat format, + uint32_t width, + uint32_t bytesPerRow, + uint32_t rowsPerImage, + uint32_t copyArrayLayerCount) { + ASSERT(rowsPerImage > 0); + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); + const uint32_t bytesAtLastImage = bytesPerRow * (rowsPerImage - 1) + bytesPerTexel * width; + return bytesPerRow * rowsPerImage * (copyArrayLayerCount - 1) + bytesAtLastImage; + } + + // TODO(jiawei.shao@intel.com): support compressed texture formats + TextureDataCopyLayout GetTextureDataCopyLayoutForTexture2DAtLevel( + wgpu::TextureFormat format, + wgpu::Extent3D textureSizeAtLevel0, + uint32_t mipmapLevel, + uint32_t rowsPerImage) { + TextureDataCopyLayout layout; + + layout.mipSize = {textureSizeAtLevel0.width >> mipmapLevel, + textureSizeAtLevel0.height >> mipmapLevel, textureSizeAtLevel0.depth}; + + layout.bytesPerRow = GetMinimumBytesPerRow(format, layout.mipSize.width); + + uint32_t appliedRowsPerImage = rowsPerImage > 0 ? rowsPerImage : layout.mipSize.height; + layout.bytesPerImage = layout.bytesPerRow * appliedRowsPerImage; + + layout.byteLength = + GetBytesInBufferTextureCopy(format, layout.mipSize.width, layout.bytesPerRow, + appliedRowsPerImage, textureSizeAtLevel0.depth); + + const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format); + layout.texelBlocksPerRow = layout.bytesPerRow / bytesPerTexel; + layout.texelBlocksPerImage = layout.bytesPerImage / bytesPerTexel; + layout.texelBlockCount = layout.byteLength / bytesPerTexel; + + return layout; + } + + const std::array kBCFormats = { + wgpu::TextureFormat::BC1RGBAUnorm, wgpu::TextureFormat::BC1RGBAUnormSrgb, + wgpu::TextureFormat::BC2RGBAUnorm, wgpu::TextureFormat::BC2RGBAUnormSrgb, + wgpu::TextureFormat::BC3RGBAUnorm, wgpu::TextureFormat::BC3RGBAUnormSrgb, + wgpu::TextureFormat::BC4RUnorm, wgpu::TextureFormat::BC4RSnorm, + wgpu::TextureFormat::BC5RGUnorm, wgpu::TextureFormat::BC5RGSnorm, + wgpu::TextureFormat::BC6HRGBUfloat, wgpu::TextureFormat::BC6HRGBSfloat, + wgpu::TextureFormat::BC7RGBAUnorm, wgpu::TextureFormat::BC7RGBAUnormSrgb}; + + uint64_t RequiredBytesInCopy(uint64_t bytesPerRow, + uint64_t rowsPerImage, + wgpu::Extent3D copyExtent, + wgpu::TextureFormat textureFormat) { + if (copyExtent.width == 0 || copyExtent.height == 0 || copyExtent.depth == 0) { + return 0; + } else { + uint32_t blockSize = utils::GetTexelBlockSizeInBytes(textureFormat); + uint32_t blockWidth = utils::GetTextureFormatBlockWidth(textureFormat); + uint32_t blockHeight = utils::GetTextureFormatBlockHeight(textureFormat); + + uint64_t texelBlockRowsPerImage = rowsPerImage / blockHeight; + uint64_t bytesPerImage = bytesPerRow * texelBlockRowsPerImage; + uint64_t bytesInLastSlice = bytesPerRow * (copyExtent.height / blockHeight - 1) + + (copyExtent.width / blockWidth * blockSize); + return bytesPerImage * (copyExtent.depth - 1) + bytesInLastSlice; + } + } + +} // namespace utils diff --git a/third_party/dawn/src/utils/WGPUHelpers.h b/third_party/dawn/src/utils/WGPUHelpers.h new file mode 100644 index 00000000000..0c63c4de979 --- /dev/null +++ b/third_party/dawn/src/utils/WGPUHelpers.h @@ -0,0 +1,167 @@ +// Copyright 2017 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef UTILS_DAWNHELPERS_H_ +#define UTILS_DAWNHELPERS_H_ + +#include + +#include +#include +#include + +#include "common/Constants.h" +#include "utils/TextureFormatUtils.h" + +namespace utils { + + enum Expectation { Success, Failure }; + + enum class SingleShaderStage { Vertex, Fragment, Compute }; + + wgpu::ShaderModule CreateShaderModule(const wgpu::Device& device, + SingleShaderStage stage, + const char* source); + wgpu::ShaderModule CreateShaderModuleFromASM(const wgpu::Device& device, const char* source); + std::vector CompileGLSLToSpirv(SingleShaderStage stage, const char* source); + + wgpu::Buffer CreateBufferFromData(const wgpu::Device& device, + const void* data, + uint64_t size, + wgpu::BufferUsage usage); + + template + wgpu::Buffer CreateBufferFromData(const wgpu::Device& device, + wgpu::BufferUsage usage, + std::initializer_list data) { + return CreateBufferFromData(device, data.begin(), uint32_t(sizeof(T) * data.size()), usage); + } + + wgpu::BufferCopyView CreateBufferCopyView(wgpu::Buffer buffer, + uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage); + wgpu::TextureCopyView CreateTextureCopyView(wgpu::Texture texture, + uint32_t level, + wgpu::Origin3D origin); + wgpu::TextureDataLayout CreateTextureDataLayout(uint64_t offset, + uint32_t bytesPerRow, + uint32_t rowsPerImage); + + struct ComboRenderPassDescriptor : public wgpu::RenderPassDescriptor { + public: + ComboRenderPassDescriptor(std::initializer_list colorAttachmentInfo, + wgpu::TextureView depthStencil = wgpu::TextureView()); + + ComboRenderPassDescriptor(const ComboRenderPassDescriptor& otherRenderPass); + const ComboRenderPassDescriptor& operator=( + const ComboRenderPassDescriptor& otherRenderPass); + + std::array + cColorAttachments; + wgpu::RenderPassDepthStencilAttachmentDescriptor cDepthStencilAttachmentInfo = {}; + }; + + struct BasicRenderPass { + public: + BasicRenderPass(); + BasicRenderPass(uint32_t width, + uint32_t height, + wgpu::Texture color, + wgpu::TextureFormat texture = kDefaultColorFormat); + + static constexpr wgpu::TextureFormat kDefaultColorFormat = wgpu::TextureFormat::RGBA8Unorm; + + uint32_t width; + uint32_t height; + wgpu::Texture color; + wgpu::TextureFormat colorFormat; + utils::ComboRenderPassDescriptor renderPassInfo; + }; + BasicRenderPass CreateBasicRenderPass(const wgpu::Device& device, + uint32_t width, + uint32_t height); + + wgpu::SamplerDescriptor GetDefaultSamplerDescriptor(); + wgpu::PipelineLayout MakeBasicPipelineLayout(const wgpu::Device& device, + const wgpu::BindGroupLayout* bindGroupLayout); + wgpu::BindGroupLayout MakeBindGroupLayout( + const wgpu::Device& device, + std::initializer_list entriesInitializer); + + // Helpers to make creating bind groups look nicer: + // + // utils::MakeBindGroup(device, layout, { + // {0, mySampler}, + // {1, myBuffer, offset, size}, + // {3, myTextureView} + // }); + + // Structure with one constructor per-type of bindings, so that the initializer_list accepts + // bindings with the right type and no extra information. + struct BindingInitializationHelper { + BindingInitializationHelper(uint32_t binding, const wgpu::Sampler& sampler); + BindingInitializationHelper(uint32_t binding, const wgpu::TextureView& textureView); + BindingInitializationHelper(uint32_t binding, + const wgpu::Buffer& buffer, + uint64_t offset = 0, + uint64_t size = wgpu::kWholeSize); + + wgpu::BindGroupEntry GetAsBinding() const; + + uint32_t binding; + wgpu::Sampler sampler; + wgpu::TextureView textureView; + wgpu::Buffer buffer; + uint64_t offset = 0; + uint64_t size = 0; + }; + + wgpu::BindGroup MakeBindGroup( + const wgpu::Device& device, + const wgpu::BindGroupLayout& layout, + std::initializer_list entriesInitializer); + + struct TextureDataCopyLayout { + uint64_t byteLength; + uint64_t texelBlockCount; + uint32_t bytesPerRow; + uint32_t texelBlocksPerRow; + uint32_t bytesPerImage; + uint32_t texelBlocksPerImage; + wgpu::Extent3D mipSize; + }; + + uint32_t GetMinimumBytesPerRow(wgpu::TextureFormat format, uint32_t width); + uint32_t GetBytesInBufferTextureCopy(wgpu::TextureFormat format, + uint32_t width, + uint32_t bytesPerRow, + uint32_t rowsPerImage, + uint32_t copyArrayLayerCount); + TextureDataCopyLayout GetTextureDataCopyLayoutForTexture2DAtLevel( + wgpu::TextureFormat format, + wgpu::Extent3D textureSizeAtLevel0, + uint32_t mipmapLevel, + uint32_t rowsPerImage); + + extern const std::array kBCFormats; + + uint64_t RequiredBytesInCopy(uint64_t bytesPerRow, + uint64_t rowsPerImage, + wgpu::Extent3D copyExtent, + wgpu::TextureFormat textureFormat); + +} // namespace utils + +#endif // UTILS_DAWNHELPERS_H_ diff --git a/third_party/dawn/src/utils/WindowsTimer.cpp b/third_party/dawn/src/utils/WindowsTimer.cpp new file mode 100644 index 00000000000..95996a11067 --- /dev/null +++ b/third_party/dawn/src/utils/WindowsTimer.cpp @@ -0,0 +1,89 @@ +// Copyright 2019 The Dawn Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "utils/Timer.h" + +#include + +namespace utils { + + class WindowsTimer : public Timer { + public: + WindowsTimer() : Timer(), mRunning(false), mFrequency(0) { + } + + ~WindowsTimer() override = default; + + void Start() override { + LARGE_INTEGER curTime; + QueryPerformanceCounter(&curTime); + mStartTime = curTime.QuadPart; + + // Cache the frequency + GetFrequency(); + + mRunning = true; + } + + void Stop() override { + LARGE_INTEGER curTime; + QueryPerformanceCounter(&curTime); + mStopTime = curTime.QuadPart; + + mRunning = false; + } + + double GetElapsedTime() const override { + LONGLONG endTime; + if (mRunning) { + LARGE_INTEGER curTime; + QueryPerformanceCounter(&curTime); + endTime = curTime.QuadPart; + } else { + endTime = mStopTime; + } + + return static_cast(endTime - mStartTime) / mFrequency; + } + + double GetAbsoluteTime() override { + LARGE_INTEGER curTime; + QueryPerformanceCounter(&curTime); + + return static_cast(curTime.QuadPart) / GetFrequency(); + } + + private: + LONGLONG GetFrequency() { + if (mFrequency == 0) { + LARGE_INTEGER frequency = {}; + QueryPerformanceFrequency(&frequency); + + mFrequency = frequency.QuadPart; + } + + return mFrequency; + } + + bool mRunning; + LONGLONG mStartTime; + LONGLONG mStopTime; + LONGLONG mFrequency; + }; + + Timer* CreateTimer() { + return new WindowsTimer(); + } + +} // namespace utils diff --git a/third_party/dawn/third_party/.clang-format b/third_party/dawn/third_party/.clang-format new file mode 100644 index 00000000000..9d159247d51 --- /dev/null +++ b/third_party/dawn/third_party/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false diff --git a/third_party/dawn/third_party/CMakeLists.txt b/third_party/dawn/third_party/CMakeLists.txt index 61e614c0c65..dbceb4db29d 100644 --- a/third_party/dawn/third_party/CMakeLists.txt +++ b/third_party/dawn/third_party/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2017 The Dawn Authors +# Copyright 2020 The Dawn Authors # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,140 +12,86 @@ # See the License for the specific language governing permissions and # limitations under the License. -# GLFW, only build the library -set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) -set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) -set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) -set(GLFW_INSTALL OFF CACHE BOOL "" FORCE) -add_subdirectory(glfw) -DawnExternalTarget("third_party" glfw) +if (NOT TARGET SPIRV-Headers) + set(SPIRV_HEADERS_SKIP_EXAMPLES ON CACHE BOOL "" FORCE) + set(SPIRV_HEADERS_SKIP_INSTALL ON CACHE BOOL "" FORCE) -# GoogleTest -set(GTEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googletest) -set(GMOCK_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest/googlemock) -add_library(gtest STATIC ${GTEST_DIR}/src/gtest-all.cc ${GMOCK_DIR}/src/gmock-all.cc) -target_include_directories(gtest SYSTEM PUBLIC ${GTEST_DIR}/include ${GMOCK_DIR}/include) -target_include_directories(gtest SYSTEM PRIVATE ${GTEST_DIR} ${GMOCK_DIR}) -find_package(Threads) -target_link_libraries(gtest ${CMAKE_THREAD_LIBS_INIT}) -DawnExternalTarget("third_party" gtest) - -# Glad -add_library(glad STATIC - ${CMAKE_CURRENT_SOURCE_DIR}/glad/src/glad.c - ${CMAKE_CURRENT_SOURCE_DIR}/glad/include/glad/glad.h - ${CMAKE_CURRENT_SOURCE_DIR}/glad/include/KHR/khrplatform.h -) -set(GLAD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/glad/include) -set(GLAD_INCLUDE_DIR ${GLAD_INCLUDE_DIR} PARENT_SCOPE) -target_include_directories(glad SYSTEM PUBLIC ${GLAD_INCLUDE_DIR}) -DawnExternalTarget("third_party" glad) + message(STATUS "Dawn: using SPIRV-Headers at ${DAWN_SPIRV_HEADERS_DIR}") + add_subdirectory(${DAWN_SPIRV_HEADERS_DIR}) +endif() -# SPIRV-Tools -set(SPIRV_TOOLS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spirv-tools/include PARENT_SCOPE) +if (NOT TARGET SPIRV-Tools) + set(SPIRV_SKIP_TESTS ON CACHE BOOL "" FORCE) + set(SPIRV_SKIP_EXECUTABLES ON CACHE BOOL "" FORCE) + set(SKIP_SPIRV_TOOLS_INSTALL ON CACHE BOOL "" FORCE) -# ShaderC -# Prevent SPIRV-Tools from using Werror as it has a warning on MSVC -set(SPIRV_WERROR OFF CACHE BOOL "" FORCE) -# Don't add unnecessary shaderc targets -set(SHADERC_SKIP_TESTS ON) -set(SHADERC_SKIP_INSTALL ON) -# Remove unused glslang and spirv-tools parts -# set(ENABLE_HLSL OFF CACHE BOOL "") -set(ENABLE_OPT OFF CACHE BOOL "") -set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "") -set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "") -set(SKIP_SPIRV_TOOLS_INSTALL ON CACHE BOOL "") -set(SPIRV_SKIP_EXECUTABLES ON CACHE BOOL "") -# Help shaderc find the non-standard paths for its dependencies -set(SHADERC_GOOGLE_TEST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/googletest CACHE STRING "Location of googletest source") -set(SHADERC_GLSLANG_DIR "${CMAKE_CURRENT_SOURCE_DIR}/glslang" CACHE STRING "Location of glslang source") -set(SHADERC_SPIRV_TOOLS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spirv-tools CACHE STRING "Location of spirv-tools source") -# Help shaderc find the python executable when run inside VS. -find_package(PythonInterp REQUIRED) -set(PYTHON_EXE ${PYTHON_EXECUTABLE}) -# Need to include this for spirv-tools to find it -add_subdirectory(spirv-headers) -add_subdirectory(shaderc) -# Namespace the shaderc targets in a folder to avoid cluttering the -# Visual Studio solution explorer -set_target_properties( - add-copyright - build-version - check-copyright - glslc - glslc_exe - install-headers - shaderc - shaderc_shared - shaderc_util - shaderc_combined_genfile - shaderc-online-compile testdata - SPIRV-Headers-example - SPIRV-Headers-example-1.1 - PROPERTIES FOLDER "third_party/shaderc" -) -# Remove a bunch of targets we don't need that are pulled by shaderc and glslang -set_target_properties( - SPIRV-Headers-example-1.1 - SPIRV-Headers-example - glslc_exe - SPIRV-Tools-link - SPVRemapper - shaderc - shaderc-online-compile - shaderc_combined_genfile - PROPERTIES EXCLUDE_FROM_ALL true -) + message(STATUS "Dawn: using SPIRV-Tools at ${DAWN_SPIRV_TOOLS_DIR}") + add_subdirectory(${DAWN_SPIRV_TOOLS_DIR}) +endif() -# SPIRV-Cross -set(SPIRV_CROSS_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/GLSL.std.450.h - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_common.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_cfg.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_cfg.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_cross.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_cross.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv.hpp -) +if (NOT TARGET glslang) + set(SKIP_GLSLANG_INSTALL ON CACHE BOOL "" FORCE) + set(ENABLE_SPVREMAPPER OFF CACHE BOOL "" FORCE) + set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "" FORCE) + set(ENABLE_CTEST OFF CACHE BOOL "" FORCE) -set(NEED_SPIRV_CROSS_GLSL OFF) -if (DAWN_ENABLE_D3D12) - list(APPEND SPIRV_CROSS_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_hlsl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_hlsl.hpp - ) - set(NEED_SPIRV_CROSS_GLSL ON) + message(STATUS "Dawn: using GLSLang at ${DAWN_GLSLANG_DIR}") + add_subdirectory(${DAWN_GLSLANG_DIR}) endif() -if (DAWN_ENABLE_METAL) - list(APPEND SPIRV_CROSS_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_msl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_msl.hpp - ) - set(NEED_SPIRV_CROSS_GLSL ON) -endif() +if (TARGET shaderc) + if (NOT TARGET shaderc_spvc) + message(FATAL_ERROR "Dawn: If shaderc is configured before Dawn, it must include SPVC") + endif() +else() + set(SHADERC_SKIP_TESTS ON CACHE BOOL "" FORCE) + set(SHADERC_SKIP_INSTALL ON CACHE BOOL "" FORCE) + set(SHADERC_ENABLE_SPVC ON CACHE BOOL "" FORCE) + + # Change the default value of SHADERC_ENABLE_SHARED_CRT to ON as that's what matches the + # CMake defaults better. + if(MSVC) + option(SHADERC_ENABLE_SHARED_CRT "Use the shared CRT instead of the static CRT" ON CACHE BOOL "" FORCE) + endif() -if (DAWN_ENABLE_OPENGL OR NEED_SPIRV_CROSS_GLSL) - list(APPEND SPIRV_CROSS_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_glsl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/spirv-cross/spirv_glsl.hpp - ) + # Let SPVC's CMakeLists.txt deal with configuring SPIRV-Cross + set(SPIRV_CROSS_ENABLE_TESTS OFF CACHE BOOL "" FORCE) + set(SHADERC_SPIRV_CROSS_DIR "${DAWN_SPIRV_CROSS_DIR}" CACHE BOOL "" FORCE) + + message(STATUS "Dawn: using shaderc[_spvc] at ${DAWN_SHADERC_DIR}") + message(STATUS "Dawn: - with SPIRV-Cross at ${DAWN_SPIRV_CROSS_DIR}") + add_subdirectory(${DAWN_SHADERC_DIR}) endif() -add_library(spirv_cross STATIC ${SPIRV_CROSS_SOURCES}) -target_compile_definitions(spirv_cross PUBLIC SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS) -set(SPIRV_CROSS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) -DawnExternalTarget("third_party" spirv_cross) +if (DAWN_BUILD_EXAMPLES) + if (NOT TARGET glfw) + set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) -# STB, used for stb_image -set(STB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/stb PARENT_SCOPE) + message(STATUS "Dawn: using GLFW at ${DAWN_GLFW_DIR}") + add_subdirectory(${DAWN_GLFW_DIR}) + endif() -# glm matrix math library -set(GLM_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/glm PARENT_SCOPE) + if (NOT TARGET glm) + message(STATUS "Dawn: using GLM at ${DAWN_GLM_DIR}") + add_subdirectory(${DAWN_GLM_DIR}) + endif() +endif() -# Tiny glTF loader library -set(TINYGLTFLOADER_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) +# Header-only library for khrplatform.h +add_library(dawn_khronos_platform INTERFACE) +target_sources(dawn_khronos_platform INTERFACE "${DAWN_THIRD_PARTY_DIR}/khronos/KHR/khrplatform.h") +target_include_directories(dawn_khronos_platform INTERFACE "${DAWN_THIRD_PARTY_DIR}/khronos") -# Vulkan headers -set(VULKAN_HEADERS_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) +# Header-only library for Vulkan headers +add_library(dawn_vulkan_headers INTERFACE) +target_sources(dawn_vulkan_headers INTERFACE + "${DAWN_THIRD_PARTY_DIR}/khronos/vulkan/vk_icd.h" + "${DAWN_THIRD_PARTY_DIR}/khronos/vulkan/vk_layer.h" + "${DAWN_THIRD_PARTY_DIR}/khronos/vulkan/vk_platform.h" + "${DAWN_THIRD_PARTY_DIR}/khronos/vulkan/vk_sdk_platform.h" + "${DAWN_THIRD_PARTY_DIR}/khronos/vulkan/vulkan.h" + "${DAWN_THIRD_PARTY_DIR}/khronos/vulkan/vulkan_core.h" +) +target_include_directories(dawn_vulkan_headers INTERFACE "${DAWN_THIRD_PARTY_DIR}/khronos") diff --git a/third_party/dawn/third_party/gn/glfw/BUILD.gn b/third_party/dawn/third_party/gn/glfw/BUILD.gn new file mode 100644 index 00000000000..ba2336fbd6a --- /dev/null +++ b/third_party/dawn/third_party/gn/glfw/BUILD.gn @@ -0,0 +1,149 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../../scripts/dawn_overrides_with_defaults.gni") + +import("${dawn_root}/scripts/dawn_features.gni") + +# Only expose GLFW targets on platforms where GLFW is supported: otherwise they +# might get discovered by GN when another target in this file is referenced, +# and GLFW will be built as part of "all" builds, causing compilation failures. + +assert(dawn_supports_glfw_for_windowing) +glfw_dir = dawn_glfw_dir + +config("glfw_public") { + include_dirs = [ "${glfw_dir}/include" ] + + if (is_win) { + defines = [ "_GLFW_WIN32" ] + } + + if (is_mac) { + defines = [ "_GLFW_COCOA" ] + } + + if (is_linux) { + defines = [ "_GLFW_X11" ] + } +} + +static_library("glfw") { + public_configs = [ ":glfw_public" ] + + if (dawn_has_build) { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + } + + if (is_win && !is_clang) { + # nonstandard extension, function/data pointer conversion in expression + cflags_c = [ "/wd4152" ] + } else { + cflags_c = [ + "-Wno-sign-compare", + "-Wno-missing-field-initializers", + ] + } + + sources = [ + "${glfw_dir}/include/GLFW/glfw3.h", + "${glfw_dir}/include/GLFW/glfw3native.h", + "${glfw_dir}/src/context.c", + "${glfw_dir}/src/egl_context.c", + "${glfw_dir}/src/egl_context.h", + "${glfw_dir}/src/init.c", + "${glfw_dir}/src/input.c", + "${glfw_dir}/src/internal.h", + "${glfw_dir}/src/monitor.c", + "${glfw_dir}/src/osmesa_context.c", + "${glfw_dir}/src/osmesa_context.h", + "${glfw_dir}/src/vulkan.c", + "${glfw_dir}/src/window.c", + ] + libs = [] + + if (is_win) { + sources += [ + "${glfw_dir}/src/wgl_context.c", + "${glfw_dir}/src/wgl_context.h", + "${glfw_dir}/src/win32_init.c", + "${glfw_dir}/src/win32_joystick.c", + "${glfw_dir}/src/win32_joystick.h", + "${glfw_dir}/src/win32_monitor.c", + "${glfw_dir}/src/win32_platform.h", + "${glfw_dir}/src/win32_thread.c", + "${glfw_dir}/src/win32_time.c", + "${glfw_dir}/src/win32_window.c", + ] + } + + if (is_linux || is_mac) { + sources += [ + "${glfw_dir}/src/posix_thread.c", + "${glfw_dir}/src/posix_thread.h", + ] + } + + if (is_linux) { + sources += [ + "${glfw_dir}/src/glx_context.c", + "${glfw_dir}/src/glx_context.h", + "${glfw_dir}/src/linux_joystick.c", + "${glfw_dir}/src/linux_joystick.h", + "${glfw_dir}/src/posix_time.c", + "${glfw_dir}/src/posix_time.h", + "${glfw_dir}/src/x11_init.c", + "${glfw_dir}/src/x11_monitor.c", + "${glfw_dir}/src/x11_platform.h", + "${glfw_dir}/src/x11_window.c", + "${glfw_dir}/src/xkb_unicode.c", + "${glfw_dir}/src/xkb_unicode.h", + ] + + libs += [ + "rt", + "dl", + "X11", + "Xcursor", + "Xinerama", + "Xrandr", + ] + } + + if (is_mac) { + sources += [ + "${glfw_dir}/src/cocoa_init.m", + "${glfw_dir}/src/cocoa_joystick.h", + "${glfw_dir}/src/cocoa_joystick.m", + "${glfw_dir}/src/cocoa_monitor.m", + "${glfw_dir}/src/cocoa_platform.h", + "${glfw_dir}/src/cocoa_time.c", + "${glfw_dir}/src/cocoa_window.m", + "${glfw_dir}/src/nsgl_context.h", + "${glfw_dir}/src/nsgl_context.m", + ] + frameworks = [ + "Cocoa.framework", + "IOKit.framework", + "CoreFoundation.framework", + "CoreVideo.framework", + ] + cflags_objc = [ + "-Wno-sign-compare", + "-Wno-unguarded-availability", + "-Wno-objc-multiple-method-names", + ] + } +} diff --git a/third_party/dawn/third_party/gn/glm/BUILD.gn b/third_party/dawn/third_party/gn/glm/BUILD.gn new file mode 100644 index 00000000000..4bbbf9dc778 --- /dev/null +++ b/third_party/dawn/third_party/gn/glm/BUILD.gn @@ -0,0 +1,38 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../../scripts/dawn_overrides_with_defaults.gni") + +assert(dawn_standalone) + +config("glm_public_config") { + include_dirs = [ "${dawn_glm_dir}" ] + + # GLM tries to suppress the warning for clang but gets confused by clang-cl + # and thinks it is MSVC. + if (is_win && is_clang) { + cflags = [ + "-Wno-gnu-anonymous-struct", + "-Wno-nested-anon-types", + ] + } +} +source_set("glm") { + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + public_configs = [ ":glm_public_config" ] + + # GLM is header only but has too many files to list them for "gn check" +} diff --git a/third_party/dawn/third_party/khronos/BUILD.gn b/third_party/dawn/third_party/khronos/BUILD.gn new file mode 100644 index 00000000000..f9e6df25096 --- /dev/null +++ b/third_party/dawn/third_party/khronos/BUILD.gn @@ -0,0 +1,47 @@ +# Copyright 2020 The Dawn Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Empty targets to add the include dirs and list the sources of Khronos +# headers for header inclusion check. + +config("vulkan_headers_config") { + include_dirs = [ "." ] +} + +source_set("vulkan_headers") { + sources = [ + "vulkan/vk_icd.h", + "vulkan/vk_layer.h", + "vulkan/vk_platform.h", + "vulkan/vk_sdk_platform.h", + "vulkan/vulkan.h", + "vulkan/vulkan_core.h", + ] + + if (is_fuchsia) { + sources += [ "vulkan/vulkan_fuchsia_extras.h" ] + } + + public_configs = [ ":vulkan_headers_config" ] +} + +config("khronos_headers_public") { + include_dirs = [ "." ] +} + +source_set("khronos_platform") { + sources = [ "KHR/khrplatform.h" ] + + public_configs = [ ":khronos_headers_public" ] +} diff --git a/third_party/dawn/third_party/khronos/KHR/khrplatform.h b/third_party/dawn/third_party/khronos/KHR/khrplatform.h new file mode 100644 index 00000000000..5b55ea2b981 --- /dev/null +++ b/third_party/dawn/third_party/khronos/KHR/khrplatform.h @@ -0,0 +1,290 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC) + /* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef _WIN64 +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ diff --git a/third_party/dawn/third_party/khronos/gl.xml b/third_party/dawn/third_party/khronos/gl.xml new file mode 100644 index 00000000000..ce4ba106c07 --- /dev/null +++ b/third_party/dawn/third_party/khronos/gl.xml @@ -0,0 +1,50579 @@ + + + +Copyright (c) 2013-2018 The Khronos Group Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +------------------------------------------------------------------------ + +This file, gl.xml, is the OpenGL and OpenGL API Registry. The canonical +version of the registry, together with documentation, schema, and Python +generator scripts used to generate C header files for OpenGL and OpenGL ES, +can always be found in the Khronos Registry at + https://github.com/KhronosGroup/OpenGL-Registry + + + + + + #include <KHR/khrplatform.h> + + typedef unsigned int GLenum; + typedef unsigned char GLboolean; + typedef unsigned int GLbitfield; + typedef void GLvoid; + typedef khronos_int8_t GLbyte; + typedef khronos_uint8_t GLubyte; + typedef khronos_int16_t GLshort; + typedef khronos_uint16_t GLushort; + typedef int GLint; + typedef unsigned int GLuint; + typedef khronos_int32_t GLclampx; + typedef int GLsizei; + typedef khronos_float_t GLfloat; + typedef khronos_float_t GLclampf; + typedef double GLdouble; + typedef double GLclampd; + typedef void *GLeglClientBufferEXT; + typedef void *GLeglImageOES; + typedef char GLchar; + typedef char GLcharARB; + #ifdef __APPLE__ +typedef void *GLhandleARB; +#else +typedef unsigned int GLhandleARB; +#endif + typedef khronos_uint16_t GLhalf; + typedef khronos_uint16_t GLhalfARB; + typedef khronos_int32_t GLfixed; + typedef khronos_intptr_t GLintptr; + typedef khronos_intptr_t GLintptrARB; + typedef khronos_ssize_t GLsizeiptr; + typedef khronos_ssize_t GLsizeiptrARB; + typedef khronos_int64_t GLint64; + typedef khronos_int64_t GLint64EXT; + typedef khronos_uint64_t GLuint64; + typedef khronos_uint64_t GLuint64EXT; + typedef struct __GLsync *GLsync; + struct _cl_context; + struct _cl_event; + typedef void ( *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + typedef void ( *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + typedef void ( *GLDEBUGPROCKHR)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,const void *userParam); + + + typedef void ( *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,void *userParam); + typedef unsigned short GLhalfNV; + typedef GLintptr GLvdpauSurfaceNV; + typedef void ( *GLVULKANPROCNV)(void); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + void glAccum + GLenum op + GLfloat value + + + + void glAccumxOES + GLenum op + GLfixed value + + + void glActiveProgramEXT + GLuint program + + + void glActiveShaderProgram + GLuint pipeline + GLuint program + + + void glActiveShaderProgramEXT + GLuint pipeline + GLuint program + + + void glActiveStencilFaceEXT + GLenum face + + + + void glActiveTexture + GLenum texture + + + + void glActiveTextureARB + GLenum texture + + + + + void glActiveVaryingNV + GLuint program + const GLchar *name + + + void glAlphaFragmentOp1ATI + GLenum op + GLuint dst + GLuint dstMod + GLuint arg1 + GLuint arg1Rep + GLuint arg1Mod + + + void glAlphaFragmentOp2ATI + GLenum op + GLuint dst + GLuint dstMod + GLuint arg1 + GLuint arg1Rep + GLuint arg1Mod + GLuint arg2 + GLuint arg2Rep + GLuint arg2Mod + + + void glAlphaFragmentOp3ATI + GLenum op + GLuint dst + GLuint dstMod + GLuint arg1 + GLuint arg1Rep + GLuint arg1Mod + GLuint arg2 + GLuint arg2Rep + GLuint arg2Mod + GLuint arg3 + GLuint arg3Rep + GLuint arg3Mod + + + void glAlphaFunc + GLenum func + GLfloat ref + + + + void glAlphaFuncQCOM + GLenum func + GLclampf ref + + + void glAlphaFuncx + GLenum func + GLfixed ref + + + void glAlphaFuncxOES + GLenum func + GLfixed ref + + + void glAlphaToCoverageDitherControlNV + GLenum mode + + + void glApplyFramebufferAttachmentCMAAINTEL + + + void glApplyTextureEXT + GLenum mode + + + GLboolean glAcquireKeyedMutexWin32EXT + GLuint memory + GLuint64 key + GLuint timeout + + + GLboolean glAreProgramsResidentNV + GLsizei n + const GLuint *programs + GLboolean *residences + + + + GLboolean glAreTexturesResident + GLsizei n + const GLuint *textures + GLboolean *residences + + + + GLboolean glAreTexturesResidentEXT + GLsizei n + const GLuint *textures + GLboolean *residences + + + + void glArrayElement + GLint i + + + void glArrayElementEXT + GLint i + + + + void glArrayObjectATI + GLenum array + GLint size + GLenum type + GLsizei stride + GLuint buffer + GLuint offset + + + void glAsyncMarkerSGIX + GLuint marker + + + void glAttachObjectARB + GLhandleARB containerObj + GLhandleARB obj + + + + void glAttachShader + GLuint program + GLuint shader + + + void glBegin + GLenum mode + + + + void glBeginConditionalRender + GLuint id + GLenum mode + + + void glBeginConditionalRenderNV + GLuint id + GLenum mode + + + + + void glBeginConditionalRenderNVX + GLuint id + + + void glBeginFragmentShaderATI + + + void glBeginOcclusionQueryNV + GLuint id + + + void glBeginPerfMonitorAMD + GLuint monitor + + + void glBeginPerfQueryINTEL + GLuint queryHandle + + + void glBeginQuery + GLenum target + GLuint id + + + + void glBeginQueryARB + GLenum target + GLuint id + + + + void glBeginQueryEXT + GLenum target + GLuint id + + + void glBeginQueryIndexed + GLenum target + GLuint index + GLuint id + + + void glBeginTransformFeedback + GLenum primitiveMode + + + + void glBeginTransformFeedbackEXT + GLenum primitiveMode + + + + void glBeginTransformFeedbackNV + GLenum primitiveMode + + + + void glBeginVertexShaderEXT + + + void glBeginVideoCaptureNV + GLuint video_capture_slot + + + void glBindAttribLocation + GLuint program + GLuint index + const GLchar *name + + + void glBindAttribLocationARB + GLhandleARB programObj + GLuint index + const GLcharARB *name + + + + void glBindBuffer + GLenum target + GLuint buffer + + + void glBindBufferARB + GLenum target + GLuint buffer + + + + void glBindBufferBase + GLenum target + GLuint index + GLuint buffer + + + + void glBindBufferBaseEXT + GLenum target + GLuint index + GLuint buffer + + + + void glBindBufferBaseNV + GLenum target + GLuint index + GLuint buffer + + + + void glBindBufferOffsetEXT + GLenum target + GLuint index + GLuint buffer + GLintptr offset + + + void glBindBufferOffsetNV + GLenum target + GLuint index + GLuint buffer + GLintptr offset + + + + void glBindBufferRange + GLenum target + GLuint index + GLuint buffer + GLintptr offset + GLsizeiptr size + + + + void glBindBufferRangeEXT + GLenum target + GLuint index + GLuint buffer + GLintptr offset + GLsizeiptr size + + + + void glBindBufferRangeNV + GLenum target + GLuint index + GLuint buffer + GLintptr offset + GLsizeiptr size + + + + void glBindBuffersBase + GLenum target + GLuint first + GLsizei count + const GLuint *buffers + + + void glBindBuffersRange + GLenum target + GLuint first + GLsizei count + const GLuint *buffers + const GLintptr *offsets + const GLsizeiptr *sizes + + + void glBindFragDataLocation + GLuint program + GLuint color + const GLchar *name + + + void glBindFragDataLocationEXT + GLuint program + GLuint color + const GLchar *name + + + + void glBindFragDataLocationIndexed + GLuint program + GLuint colorNumber + GLuint index + const GLchar *name + + + void glBindFragDataLocationIndexedEXT + GLuint program + GLuint colorNumber + GLuint index + const GLchar *name + + + + void glBindFragmentShaderATI + GLuint id + + + void glBindFramebuffer + GLenum target + GLuint framebuffer + + + + void glBindFramebufferEXT + GLenum target + GLuint framebuffer + + + + void glBindFramebufferOES + GLenum target + GLuint framebuffer + + + void glBindImageTexture + GLuint unit + GLuint texture + GLint level + GLboolean layered + GLint layer + GLenum access + GLenum format + + + void glBindImageTextureEXT + GLuint index + GLuint texture + GLint level + GLboolean layered + GLint layer + GLenum access + GLint format + + + void glBindImageTextures + GLuint first + GLsizei count + const GLuint *textures + + + GLuint glBindLightParameterEXT + GLenum light + GLenum value + + + GLuint glBindMaterialParameterEXT + GLenum face + GLenum value + + + void glBindMultiTextureEXT + GLenum texunit + GLenum target + GLuint texture + + + GLuint glBindParameterEXT + GLenum value + + + void glBindProgramARB + GLenum target + GLuint program + + + + void glBindProgramNV + GLenum target + GLuint id + + + + + void glBindProgramPipeline + GLuint pipeline + + + void glBindProgramPipelineEXT + GLuint pipeline + + + void glBindRenderbuffer + GLenum target + GLuint renderbuffer + + + + void glBindRenderbufferEXT + GLenum target + GLuint renderbuffer + + + + void glBindRenderbufferOES + GLenum target + GLuint renderbuffer + + + void glBindSampler + GLuint unit + GLuint sampler + + + void glBindSamplers + GLuint first + GLsizei count + const GLuint *samplers + + + void glBindShadingRateImageNV + GLuint texture + + + GLuint glBindTexGenParameterEXT + GLenum unit + GLenum coord + GLenum value + + + void glBindTexture + GLenum target + GLuint texture + + + + void glBindTextureEXT + GLenum target + GLuint texture + + + + + void glBindTextureUnit + GLuint unit + GLuint texture + + + GLuint glBindTextureUnitParameterEXT + GLenum unit + GLenum value + + + void glBindTextures + GLuint first + GLsizei count + const GLuint *textures + + + void glBindTransformFeedback + GLenum target + GLuint id + + + void glBindTransformFeedbackNV + GLenum target + GLuint id + + + void glBindVertexArray + GLuint array + + + + void glBindVertexArrayAPPLE + GLuint array + + + void glBindVertexArrayOES + GLuint array + + + + void glBindVertexBuffer + GLuint bindingindex + GLuint buffer + GLintptr offset + GLsizei stride + + + void glBindVertexBuffers + GLuint first + GLsizei count + const GLuint *buffers + const GLintptr *offsets + const GLsizei *strides + + + void glBindVertexShaderEXT + GLuint id + + + void glBindVideoCaptureStreamBufferNV + GLuint video_capture_slot + GLuint stream + GLenum frame_region + GLintptrARB offset + + + void glBindVideoCaptureStreamTextureNV + GLuint video_capture_slot + GLuint stream + GLenum frame_region + GLenum target + GLuint texture + + + void glBinormal3bEXT + GLbyte bx + GLbyte by + GLbyte bz + + + + void glBinormal3bvEXT + const GLbyte *v + + + void glBinormal3dEXT + GLdouble bx + GLdouble by + GLdouble bz + + + + void glBinormal3dvEXT + const GLdouble *v + + + void glBinormal3fEXT + GLfloat bx + GLfloat by + GLfloat bz + + + + void glBinormal3fvEXT + const GLfloat *v + + + void glBinormal3iEXT + GLint bx + GLint by + GLint bz + + + + void glBinormal3ivEXT + const GLint *v + + + void glBinormal3sEXT + GLshort bx + GLshort by + GLshort bz + + + + void glBinormal3svEXT + const GLshort *v + + + void glBinormalPointerEXT + GLenum type + GLsizei stride + const void *pointer + + + void glBitmap + GLsizei width + GLsizei height + GLfloat xorig + GLfloat yorig + GLfloat xmove + GLfloat ymove + const GLubyte *bitmap + + + + + void glBitmapxOES + GLsizei width + GLsizei height + GLfixed xorig + GLfixed yorig + GLfixed xmove + GLfixed ymove + const GLubyte *bitmap + + + void glBlendBarrier + + + void glBlendBarrierKHR + + + + void glBlendBarrierNV + + + + void glBlendColor + GLfloat red + GLfloat green + GLfloat blue + GLfloat alpha + + + + void glBlendColorEXT + GLfloat red + GLfloat green + GLfloat blue + GLfloat alpha + + + + + void glBlendColorxOES + GLfixed red + GLfixed green + GLfixed blue + GLfixed alpha + + + void glBlendEquation + GLenum mode + + + + void glBlendEquationEXT + GLenum mode + + + + + void glBlendEquationIndexedAMD + GLuint buf + GLenum mode + + + + void glBlendEquationOES + GLenum mode + + + void glBlendEquationSeparate + GLenum modeRGB + GLenum modeAlpha + + + + void glBlendEquationSeparateEXT + GLenum modeRGB + GLenum modeAlpha + + + + + void glBlendEquationSeparateIndexedAMD + GLuint buf + GLenum modeRGB + GLenum modeAlpha + + + + void glBlendEquationSeparateOES + GLenum modeRGB + GLenum modeAlpha + + + void glBlendEquationSeparatei + GLuint buf + GLenum modeRGB + GLenum modeAlpha + + + void glBlendEquationSeparateiARB + GLuint buf + GLenum modeRGB + GLenum modeAlpha + + + + void glBlendEquationSeparateiEXT + GLuint buf + GLenum modeRGB + GLenum modeAlpha + + + + void glBlendEquationSeparateiOES + GLuint buf + GLenum modeRGB + GLenum modeAlpha + + + + void glBlendEquationi + GLuint buf + GLenum mode + + + void glBlendEquationiARB + GLuint buf + GLenum mode + + + + void glBlendEquationiEXT + GLuint buf + GLenum mode + + + + void glBlendEquationiOES + GLuint buf + GLenum mode + + + + void glBlendFunc + GLenum sfactor + GLenum dfactor + + + + void glBlendFuncIndexedAMD + GLuint buf + GLenum src + GLenum dst + + + + void glBlendFuncSeparate + GLenum sfactorRGB + GLenum dfactorRGB + GLenum sfactorAlpha + GLenum dfactorAlpha + + + + void glBlendFuncSeparateEXT + GLenum sfactorRGB + GLenum dfactorRGB + GLenum sfactorAlpha + GLenum dfactorAlpha + + + + + void glBlendFuncSeparateINGR + GLenum sfactorRGB + GLenum dfactorRGB + GLenum sfactorAlpha + GLenum dfactorAlpha + + + + + void glBlendFuncSeparateIndexedAMD + GLuint buf + GLenum srcRGB + GLenum dstRGB + GLenum srcAlpha + GLenum dstAlpha + + + + void glBlendFuncSeparateOES + GLenum srcRGB + GLenum dstRGB + GLenum srcAlpha + GLenum dstAlpha + + + void glBlendFuncSeparatei + GLuint buf + GLenum srcRGB + GLenum dstRGB + GLenum srcAlpha + GLenum dstAlpha + + + void glBlendFuncSeparateiARB + GLuint buf + GLenum srcRGB + GLenum dstRGB + GLenum srcAlpha + GLenum dstAlpha + + + + void glBlendFuncSeparateiEXT + GLuint buf + GLenum srcRGB + GLenum dstRGB + GLenum srcAlpha + GLenum dstAlpha + + + + void glBlendFuncSeparateiOES + GLuint buf + GLenum srcRGB + GLenum dstRGB + GLenum srcAlpha + GLenum dstAlpha + + + + void glBlendFunci + GLuint buf + GLenum src + GLenum dst + + + void glBlendFunciARB + GLuint buf + GLenum src + GLenum dst + + + + void glBlendFunciEXT + GLuint buf + GLenum src + GLenum dst + + + + void glBlendFunciOES + GLuint buf + GLenum src + GLenum dst + + + + void glBlendParameteriNV + GLenum pname + GLint value + + + void glBlitFramebuffer + GLint srcX0 + GLint srcY0 + GLint srcX1 + GLint srcY1 + GLint dstX0 + GLint dstY0 + GLint dstX1 + GLint dstY1 + GLbitfield mask + GLenum filter + + + + void glBlitFramebufferANGLE + GLint srcX0 + GLint srcY0 + GLint srcX1 + GLint srcY1 + GLint dstX0 + GLint dstY0 + GLint dstX1 + GLint dstY1 + GLbitfield mask + GLenum filter + + + void glBlitFramebufferEXT + GLint srcX0 + GLint srcY0 + GLint srcX1 + GLint srcY1 + GLint dstX0 + GLint dstY0 + GLint dstX1 + GLint dstY1 + GLbitfield mask + GLenum filter + + + + + void glBlitFramebufferNV + GLint srcX0 + GLint srcY0 + GLint srcX1 + GLint srcY1 + GLint dstX0 + GLint dstY0 + GLint dstX1 + GLint dstY1 + GLbitfield mask + GLenum filter + + + + void glBlitNamedFramebuffer + GLuint readFramebuffer + GLuint drawFramebuffer + GLint srcX0 + GLint srcY0 + GLint srcX1 + GLint srcY1 + GLint dstX0 + GLint dstY0 + GLint dstX1 + GLint dstY1 + GLbitfield mask + GLenum filter + + + void glBufferAddressRangeNV + GLenum pname + GLuint index + GLuint64EXT address + GLsizeiptr length + + + void glBufferAttachMemoryNV + GLenum target + GLuint memory + GLuint64 offset + + + void glBufferData + GLenum target + GLsizeiptr size + const void *data + GLenum usage + + + void glBufferDataARB + GLenum target + GLsizeiptrARB size + const void *data + GLenum usage + + + + void glBufferPageCommitmentARB + GLenum target + GLintptr offset + GLsizeiptr size + GLboolean commit + + + void glBufferParameteriAPPLE + GLenum target + GLenum pname + GLint param + + + void glBufferStorage + GLenum target + GLsizeiptr size + const void *data + GLbitfield flags + + + void glBufferStorageEXT + GLenum target + GLsizeiptr size + const void *data + GLbitfield flags + + + + void glBufferStorageExternalEXT + GLenum target + GLintptr offset + GLsizeiptr size + GLeglClientBufferEXT clientBuffer + GLbitfield flags + + + void glBufferStorageMemEXT + GLenum target + GLsizeiptr size + GLuint memory + GLuint64 offset + + + void glBufferSubData + GLenum target + GLintptr offset + GLsizeiptr size + const void *data + + + void glBufferSubDataARB + GLenum target + GLintptrARB offset + GLsizeiptrARB size + const void *data + + + + void glCallCommandListNV + GLuint list + + + void glCallList + GLuint list + + + + void glCallLists + GLsizei n + GLenum type + const void *lists + + + + GLenum glCheckFramebufferStatus + GLenum target + + + + GLenum glCheckFramebufferStatusEXT + GLenum target + + + + + GLenum glCheckFramebufferStatusOES + GLenum target + + + GLenum glCheckNamedFramebufferStatus + GLuint framebuffer + GLenum target + + + GLenum glCheckNamedFramebufferStatusEXT + GLuint framebuffer + GLenum target + + + void glClampColor + GLenum target + GLenum clamp + + + + void glClampColorARB + GLenum target + GLenum clamp + + + + + void glClear + GLbitfield mask + + + + void glClearAccum + GLfloat red + GLfloat green + GLfloat blue + GLfloat alpha + + + + void glClearAccumxOES + GLfixed red + GLfixed green + GLfixed blue + GLfixed alpha + + + void glClearBufferData + GLenum target + GLenum internalformat + GLenum format + GLenum type + const void *data + + + void glClearBufferSubData + GLenum target + GLenum internalformat + GLintptr offset + GLsizeiptr size + GLenum format + GLenum type + const void *data + + + void glClearBufferfi + GLenum buffer + GLint drawbuffer + GLfloat depth + GLint stencil + + + + void glClearBufferfv + GLenum buffer + GLint drawbuffer + const GLfloat *value + + + + void glClearBufferiv + GLenum buffer + GLint drawbuffer + const GLint *value + + + + void glClearBufferuiv + GLenum buffer + GLint drawbuffer + const GLuint *value + + + + void glClearColor + GLfloat red + GLfloat green + GLfloat blue + GLfloat alpha + + + + void glClearColorIiEXT + GLint red + GLint green + GLint blue + GLint alpha + + + + void glClearColorIuiEXT + GLuint red + GLuint green + GLuint blue + GLuint alpha + + + + void glClearColorx + GLfixed red + GLfixed green + GLfixed blue + GLfixed alpha + + + void glClearColorxOES + GLfixed red + GLfixed green + GLfixed blue + GLfixed alpha + + + void glClearDepth + GLdouble depth + + + + void glClearDepthdNV + GLdouble depth + + + + void glClearDepthf + GLfloat d + + + void glClearDepthfOES + GLclampf depth + + + + + void glClearDepthx + GLfixed depth + + + void glClearDepthxOES + GLfixed depth + + + void glClearIndex + GLfloat c + + + + void glClearNamedBufferData + GLuint buffer + GLenum internalformat + GLenum format + GLenum type + const void *data + + + void glClearNamedBufferDataEXT + GLuint buffer + GLenum internalformat + GLenum format + GLenum type + const void *data + + + void glClearNamedBufferSubData + GLuint buffer + GLenum internalformat + GLintptr offset + GLsizeiptr size + GLenum format + GLenum type + const void *data + + + void glClearNamedBufferSubDataEXT + GLuint buffer + GLenum internalformat + GLsizeiptr offset + GLsizeiptr size + GLenum format + GLenum type + const void *data + + + void glClearNamedFramebufferfi + GLuint framebuffer + GLenum buffer + GLint drawbuffer + GLfloat depth + GLint stencil + + + void glClearNamedFramebufferfv + GLuint framebuffer + GLenum buffer + GLint drawbuffer + const GLfloat *value + + + void glClearNamedFramebufferiv + GLuint framebuffer + GLenum buffer + GLint drawbuffer + const GLint *value + + + void glClearNamedFramebufferuiv + GLuint framebuffer + GLenum buffer + GLint drawbuffer + const GLuint *value + + + void glClearPixelLocalStorageuiEXT + GLsizei offset + GLsizei n + const GLuint *values + + + void glClearStencil + GLint s + + + + void glClearTexImage + GLuint texture + GLint level + GLenum format + GLenum type + const void *data + + + void glClearTexImageEXT + GLuint texture + GLint level + GLenum format + GLenum type + const void *data + + + + void glClearTexSubImage + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + const void *data + + + void glClearTexSubImageEXT + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + const void *data + + + + void glClientActiveTexture + GLenum texture + + + void glClientActiveTextureARB + GLenum texture + + + + void glClientActiveVertexStreamATI + GLenum stream + + + void glClientAttribDefaultEXT + GLbitfield mask + + + GLenum glClientWaitSync + GLsync sync + GLbitfield flags + GLuint64 timeout + + + GLenum glClientWaitSyncAPPLE + GLsync sync + GLbitfield flags + GLuint64 timeout + + + + void glClipControl + GLenum origin + GLenum depth + + + void glClipControlEXT + GLenum origin + GLenum depth + + + + void glClipPlane + GLenum plane + const GLdouble *equation + + + + void glClipPlanef + GLenum p + const GLfloat *eqn + + + void glClipPlanefIMG + GLenum p + const GLfloat *eqn + + + void glClipPlanefOES + GLenum plane + const GLfloat *equation + + + + void glClipPlanex + GLenum plane + const GLfixed *equation + + + void glClipPlanexIMG + GLenum p + const GLfixed *eqn + + + void glClipPlanexOES + GLenum plane + const GLfixed *equation + + + void glColor3b + GLbyte red + GLbyte green + GLbyte blue + + + + void glColor3bv + const GLbyte *v + + + + void glColor3d + GLdouble red + GLdouble green + GLdouble blue + + + + void glColor3dv + const GLdouble *v + + + + void glColor3f + GLfloat red + GLfloat green + GLfloat blue + + + + void glColor3fVertex3fSUN + GLfloat r + GLfloat g + GLfloat b + GLfloat x + GLfloat y + GLfloat z + + + void glColor3fVertex3fvSUN + const GLfloat *c + const GLfloat *v + + + void glColor3fv + const GLfloat *v + + + + void glColor3hNV + GLhalfNV red + GLhalfNV green + GLhalfNV blue + + + + void glColor3hvNV + const GLhalfNV *v + + + + void glColor3i + GLint red + GLint green + GLint blue + + + + void glColor3iv + const GLint *v + + + + void glColor3s + GLshort red + GLshort green + GLshort blue + + + + void glColor3sv + const GLshort *v + + + + void glColor3ub + GLubyte red + GLubyte green + GLubyte blue + + + + void glColor3ubv + const GLubyte *v + + + + void glColor3ui + GLuint red + GLuint green + GLuint blue + + + + void glColor3uiv + const GLuint *v + + + + void glColor3us + GLushort red + GLushort green + GLushort blue + + + + void glColor3usv + const GLushort *v + + + + void glColor3xOES + GLfixed red + GLfixed green + GLfixed blue + + + void glColor3xvOES + const GLfixed *components + + + void glColor4b + GLbyte red + GLbyte green + GLbyte blue + GLbyte alpha + + + + void glColor4bv + const GLbyte *v + + + + void glColor4d + GLdouble red + GLdouble green + GLdouble blue + GLdouble alpha + + + + void glColor4dv + const GLdouble *v + + + + void glColor4f + GLfloat red + GLfloat green + GLfloat blue + GLfloat alpha + + + + void glColor4fNormal3fVertex3fSUN + GLfloat r + GLfloat g + GLfloat b + GLfloat a + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + + + void glColor4fNormal3fVertex3fvSUN + const GLfloat *c + const GLfloat *n + const GLfloat *v + + + void glColor4fv + const GLfloat *v + + + + void glColor4hNV + GLhalfNV red + GLhalfNV green + GLhalfNV blue + GLhalfNV alpha + + + + void glColor4hvNV + const GLhalfNV *v + + + + void glColor4i + GLint red + GLint green + GLint blue + GLint alpha + + + + void glColor4iv + const GLint *v + + + + void glColor4s + GLshort red + GLshort green + GLshort blue + GLshort alpha + + + + void glColor4sv + const GLshort *v + + + + void glColor4ub + GLubyte red + GLubyte green + GLubyte blue + GLubyte alpha + + + + void glColor4ubVertex2fSUN + GLubyte r + GLubyte g + GLubyte b + GLubyte a + GLfloat x + GLfloat y + + + void glColor4ubVertex2fvSUN + const GLubyte *c + const GLfloat *v + + + void glColor4ubVertex3fSUN + GLubyte r + GLubyte g + GLubyte b + GLubyte a + GLfloat x + GLfloat y + GLfloat z + + + void glColor4ubVertex3fvSUN + const GLubyte *c + const GLfloat *v + + + void glColor4ubv + const GLubyte *v + + + + void glColor4ui + GLuint red + GLuint green + GLuint blue + GLuint alpha + + + + void glColor4uiv + const GLuint *v + + + + void glColor4us + GLushort red + GLushort green + GLushort blue + GLushort alpha + + + + void glColor4usv + const GLushort *v + + + + void glColor4x + GLfixed red + GLfixed green + GLfixed blue + GLfixed alpha + + + void glColor4xOES + GLfixed red + GLfixed green + GLfixed blue + GLfixed alpha + + + void glColor4xvOES + const GLfixed *components + + + void glColorFormatNV + GLint size + GLenum type + GLsizei stride + + + void glColorFragmentOp1ATI + GLenum op + GLuint dst + GLuint dstMask + GLuint dstMod + GLuint arg1 + GLuint arg1Rep + GLuint arg1Mod + + + void glColorFragmentOp2ATI + GLenum op + GLuint dst + GLuint dstMask + GLuint dstMod + GLuint arg1 + GLuint arg1Rep + GLuint arg1Mod + GLuint arg2 + GLuint arg2Rep + GLuint arg2Mod + + + void glColorFragmentOp3ATI + GLenum op + GLuint dst + GLuint dstMask + GLuint dstMod + GLuint arg1 + GLuint arg1Rep + GLuint arg1Mod + GLuint arg2 + GLuint arg2Rep + GLuint arg2Mod + GLuint arg3 + GLuint arg3Rep + GLuint arg3Mod + + + void glColorMask + GLboolean red + GLboolean green + GLboolean blue + GLboolean alpha + + + + void glColorMaskIndexedEXT + GLuint index + GLboolean r + GLboolean g + GLboolean b + GLboolean a + + + + + void glColorMaski + GLuint index + GLboolean r + GLboolean g + GLboolean b + GLboolean a + + + void glColorMaskiEXT + GLuint index + GLboolean r + GLboolean g + GLboolean b + GLboolean a + + + + void glColorMaskiOES + GLuint index + GLboolean r + GLboolean g + GLboolean b + GLboolean a + + + + void glColorMaterial + GLenum face + GLenum mode + + + + void glColorP3ui + GLenum type + GLuint color + + + void glColorP3uiv + GLenum type + const GLuint *color + + + void glColorP4ui + GLenum type + GLuint color + + + void glColorP4uiv + GLenum type + const GLuint *color + + + void glColorPointer + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glColorPointerEXT + GLint size + GLenum type + GLsizei stride + GLsizei count + const void *pointer + + + void glColorPointerListIBM + GLint size + GLenum type + GLint stride + const void **pointer + GLint ptrstride + + + void glColorPointervINTEL + GLint size + GLenum type + const void **pointer + + + void glColorSubTable + GLenum target + GLsizei start + GLsizei count + GLenum format + GLenum type + const void *data + + + + + void glColorSubTableEXT + GLenum target + GLsizei start + GLsizei count + GLenum format + GLenum type + const void *data + + + + void glColorTable + GLenum target + GLenum internalformat + GLsizei width + GLenum format + GLenum type + const void *table + + + + + void glColorTableEXT + GLenum target + GLenum internalFormat + GLsizei width + GLenum format + GLenum type + const void *table + + + + void glColorTableParameterfv + GLenum target + GLenum pname + const GLfloat *params + + + + void glColorTableParameterfvSGI + GLenum target + GLenum pname + const GLfloat *params + + + + + void glColorTableParameteriv + GLenum target + GLenum pname + const GLint *params + + + + void glColorTableParameterivSGI + GLenum target + GLenum pname + const GLint *params + + + + + void glColorTableSGI + GLenum target + GLenum internalformat + GLsizei width + GLenum format + GLenum type + const void *table + + + + + void glCombinerInputNV + GLenum stage + GLenum portion + GLenum variable + GLenum input + GLenum mapping + GLenum componentUsage + + + + void glCombinerOutputNV + GLenum stage + GLenum portion + GLenum abOutput + GLenum cdOutput + GLenum sumOutput + GLenum scale + GLenum bias + GLboolean abDotProduct + GLboolean cdDotProduct + GLboolean muxSum + + + + void glCombinerParameterfNV + GLenum pname + GLfloat param + + + + void glCombinerParameterfvNV + GLenum pname + const GLfloat *params + + + + void glCombinerParameteriNV + GLenum pname + GLint param + + + + void glCombinerParameterivNV + GLenum pname + const GLint *params + + + + void glCombinerStageParameterfvNV + GLenum stage + GLenum pname + const GLfloat *params + + + void glCommandListSegmentsNV + GLuint list + GLuint segments + + + void glCompileCommandListNV + GLuint list + + + void glCompileShader + GLuint shader + + + void glCompileShaderARB + GLhandleARB shaderObj + + + + void glCompileShaderIncludeARB + GLuint shader + GLsizei count + const GLchar *const*path + const GLint *length + + + void glCompressedMultiTexImage1DEXT + GLenum texunit + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLint border + GLsizei imageSize + const void *bits + + + void glCompressedMultiTexImage2DEXT + GLenum texunit + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLint border + GLsizei imageSize + const void *bits + + + void glCompressedMultiTexImage3DEXT + GLenum texunit + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLsizei imageSize + const void *bits + + + void glCompressedMultiTexSubImage1DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLsizei width + GLenum format + GLsizei imageSize + const void *bits + + + void glCompressedMultiTexSubImage2DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLsizei imageSize + const void *bits + + + void glCompressedMultiTexSubImage3DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLsizei imageSize + const void *bits + + + void glCompressedTexImage1D + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLint border + GLsizei imageSize + const void *data + + + + + void glCompressedTexImage1DARB + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLint border + GLsizei imageSize + const void *data + + + + + void glCompressedTexImage2D + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLint border + GLsizei imageSize + const void *data + + + + + void glCompressedTexImage2DARB + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLint border + GLsizei imageSize + const void *data + + + + + void glCompressedTexImage3D + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLsizei imageSize + const void *data + + + + + void glCompressedTexImage3DARB + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLsizei imageSize + const void *data + + + + + void glCompressedTexImage3DOES + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLsizei imageSize + const void *data + + + void glCompressedTexSubImage1D + GLenum target + GLint level + GLint xoffset + GLsizei width + GLenum format + GLsizei imageSize + const void *data + + + + + void glCompressedTexSubImage1DARB + GLenum target + GLint level + GLint xoffset + GLsizei width + GLenum format + GLsizei imageSize + const void *data + + + + + void glCompressedTexSubImage2D + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLsizei imageSize + const void *data + + + + + void glCompressedTexSubImage2DARB + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLsizei imageSize + const void *data + + + + + void glCompressedTexSubImage3D + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLsizei imageSize + const void *data + + + + + void glCompressedTexSubImage3DARB + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLsizei imageSize + const void *data + + + + + void glCompressedTexSubImage3DOES + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLsizei imageSize + const void *data + + + void glCompressedTextureImage1DEXT + GLuint texture + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLint border + GLsizei imageSize + const void *bits + + + void glCompressedTextureImage2DEXT + GLuint texture + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLint border + GLsizei imageSize + const void *bits + + + void glCompressedTextureImage3DEXT + GLuint texture + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLsizei imageSize + const void *bits + + + void glCompressedTextureSubImage1D + GLuint texture + GLint level + GLint xoffset + GLsizei width + GLenum format + GLsizei imageSize + const void *data + + + void glCompressedTextureSubImage1DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLsizei width + GLenum format + GLsizei imageSize + const void *bits + + + void glCompressedTextureSubImage2D + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLsizei imageSize + const void *data + + + void glCompressedTextureSubImage2DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLsizei imageSize + const void *bits + + + void glCompressedTextureSubImage3D + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLsizei imageSize + const void *data + + + void glCompressedTextureSubImage3DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLsizei imageSize + const void *bits + + + void glConservativeRasterParameterfNV + GLenum pname + GLfloat value + + + void glConservativeRasterParameteriNV + GLenum pname + GLint param + + + void glConvolutionFilter1D + GLenum target + GLenum internalformat + GLsizei width + GLenum format + GLenum type + const void *image + + + + + void glConvolutionFilter1DEXT + GLenum target + GLenum internalformat + GLsizei width + GLenum format + GLenum type + const void *image + + + + + void glConvolutionFilter2D + GLenum target + GLenum internalformat + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *image + + + + + void glConvolutionFilter2DEXT + GLenum target + GLenum internalformat + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *image + + + + + void glConvolutionParameterf + GLenum target + GLenum pname + GLfloat params + + + + void glConvolutionParameterfEXT + GLenum target + GLenum pname + GLfloat params + + + + + void glConvolutionParameterfv + GLenum target + GLenum pname + const GLfloat *params + + + + void glConvolutionParameterfvEXT + GLenum target + GLenum pname + const GLfloat *params + + + + + void glConvolutionParameteri + GLenum target + GLenum pname + GLint params + + + + void glConvolutionParameteriEXT + GLenum target + GLenum pname + GLint params + + + + + void glConvolutionParameteriv + GLenum target + GLenum pname + const GLint *params + + + + void glConvolutionParameterivEXT + GLenum target + GLenum pname + const GLint *params + + + + + void glConvolutionParameterxOES + GLenum target + GLenum pname + GLfixed param + + + void glConvolutionParameterxvOES + GLenum target + GLenum pname + const GLfixed *params + + + void glCopyBufferSubData + GLenum readTarget + GLenum writeTarget + GLintptr readOffset + GLintptr writeOffset + GLsizeiptr size + + + + void glCopyBufferSubDataNV + GLenum readTarget + GLenum writeTarget + GLintptr readOffset + GLintptr writeOffset + GLsizeiptr size + + + + void glCopyColorSubTable + GLenum target + GLsizei start + GLint x + GLint y + GLsizei width + + + + void glCopyColorSubTableEXT + GLenum target + GLsizei start + GLint x + GLint y + GLsizei width + + + + void glCopyColorTable + GLenum target + GLenum internalformat + GLint x + GLint y + GLsizei width + + + + void glCopyColorTableSGI + GLenum target + GLenum internalformat + GLint x + GLint y + GLsizei width + + + + + void glCopyConvolutionFilter1D + GLenum target + GLenum internalformat + GLint x + GLint y + GLsizei width + + + + void glCopyConvolutionFilter1DEXT + GLenum target + GLenum internalformat + GLint x + GLint y + GLsizei width + + + + + void glCopyConvolutionFilter2D + GLenum target + GLenum internalformat + GLint x + GLint y + GLsizei width + GLsizei height + + + + void glCopyConvolutionFilter2DEXT + GLenum target + GLenum internalformat + GLint x + GLint y + GLsizei width + GLsizei height + + + + + void glCopyImageSubData + GLuint srcName + GLenum srcTarget + GLint srcLevel + GLint srcX + GLint srcY + GLint srcZ + GLuint dstName + GLenum dstTarget + GLint dstLevel + GLint dstX + GLint dstY + GLint dstZ + GLsizei srcWidth + GLsizei srcHeight + GLsizei srcDepth + + + void glCopyImageSubDataEXT + GLuint srcName + GLenum srcTarget + GLint srcLevel + GLint srcX + GLint srcY + GLint srcZ + GLuint dstName + GLenum dstTarget + GLint dstLevel + GLint dstX + GLint dstY + GLint dstZ + GLsizei srcWidth + GLsizei srcHeight + GLsizei srcDepth + + + + void glCopyImageSubDataNV + GLuint srcName + GLenum srcTarget + GLint srcLevel + GLint srcX + GLint srcY + GLint srcZ + GLuint dstName + GLenum dstTarget + GLint dstLevel + GLint dstX + GLint dstY + GLint dstZ + GLsizei width + GLsizei height + GLsizei depth + + + + void glCopyImageSubDataOES + GLuint srcName + GLenum srcTarget + GLint srcLevel + GLint srcX + GLint srcY + GLint srcZ + GLuint dstName + GLenum dstTarget + GLint dstLevel + GLint dstX + GLint dstY + GLint dstZ + GLsizei srcWidth + GLsizei srcHeight + GLsizei srcDepth + + + + void glCopyMultiTexImage1DEXT + GLenum texunit + GLenum target + GLint level + GLenum internalformat + GLint x + GLint y + GLsizei width + GLint border + + + void glCopyMultiTexImage2DEXT + GLenum texunit + GLenum target + GLint level + GLenum internalformat + GLint x + GLint y + GLsizei width + GLsizei height + GLint border + + + void glCopyMultiTexSubImage1DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLint x + GLint y + GLsizei width + + + void glCopyMultiTexSubImage2DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + void glCopyMultiTexSubImage3DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + void glCopyNamedBufferSubData + GLuint readBuffer + GLuint writeBuffer + GLintptr readOffset + GLintptr writeOffset + GLsizeiptr size + + + void glCopyPathNV + GLuint resultPath + GLuint srcPath + + + void glCopyPixels + GLint x + GLint y + GLsizei width + GLsizei height + GLenum type + + + + void glCopyTexImage1D + GLenum target + GLint level + GLenum internalformat + GLint x + GLint y + GLsizei width + GLint border + + + + void glCopyTexImage1DEXT + GLenum target + GLint level + GLenum internalformat + GLint x + GLint y + GLsizei width + GLint border + + + + + void glCopyTexImage2D + GLenum target + GLint level + GLenum internalformat + GLint x + GLint y + GLsizei width + GLsizei height + GLint border + + + + void glCopyTexImage2DEXT + GLenum target + GLint level + GLenum internalformat + GLint x + GLint y + GLsizei width + GLsizei height + GLint border + + + + + void glCopyTexSubImage1D + GLenum target + GLint level + GLint xoffset + GLint x + GLint y + GLsizei width + + + + void glCopyTexSubImage1DEXT + GLenum target + GLint level + GLint xoffset + GLint x + GLint y + GLsizei width + + + + + void glCopyTexSubImage2D + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + + void glCopyTexSubImage2DEXT + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + + + void glCopyTexSubImage3D + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + + void glCopyTexSubImage3DEXT + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + + + void glCopyTexSubImage3DOES + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + void glCopyTextureImage1DEXT + GLuint texture + GLenum target + GLint level + GLenum internalformat + GLint x + GLint y + GLsizei width + GLint border + + + void glCopyTextureImage2DEXT + GLuint texture + GLenum target + GLint level + GLenum internalformat + GLint x + GLint y + GLsizei width + GLsizei height + GLint border + + + void glCopyTextureLevelsAPPLE + GLuint destinationTexture + GLuint sourceTexture + GLint sourceBaseLevel + GLsizei sourceLevelCount + + + void glCopyTextureSubImage1D + GLuint texture + GLint level + GLint xoffset + GLint x + GLint y + GLsizei width + + + void glCopyTextureSubImage1DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLint x + GLint y + GLsizei width + + + void glCopyTextureSubImage2D + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + void glCopyTextureSubImage2DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + void glCopyTextureSubImage3D + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + void glCopyTextureSubImage3DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLint x + GLint y + GLsizei width + GLsizei height + + + void glCoverFillPathInstancedNV + GLsizei numPaths + GLenum pathNameType + const void *paths + GLuint pathBase + GLenum coverMode + GLenum transformType + const GLfloat *transformValues + + + void glCoverFillPathNV + GLuint path + GLenum coverMode + + + void glCoverStrokePathInstancedNV + GLsizei numPaths + GLenum pathNameType + const void *paths + GLuint pathBase + GLenum coverMode + GLenum transformType + const GLfloat *transformValues + + + void glCoverStrokePathNV + GLuint path + GLenum coverMode + + + void glCoverageMaskNV + GLboolean mask + + + void glCoverageModulationNV + GLenum components + + + void glCoverageModulationTableNV + GLsizei n + const GLfloat *v + + + void glCoverageOperationNV + GLenum operation + + + void glCreateBuffers + GLsizei n + GLuint *buffers + + + void glCreateCommandListsNV + GLsizei n + GLuint *lists + + + void glCreateFramebuffers + GLsizei n + GLuint *framebuffers + + + void glCreateMemoryObjectsEXT + GLsizei n + GLuint *memoryObjects + + + void glCreatePerfQueryINTEL + GLuint queryId + GLuint *queryHandle + + + GLuint glCreateProgram + + + GLhandleARB glCreateProgramObjectARB + + + + void glCreateProgramPipelines + GLsizei n + GLuint *pipelines + + + void glCreateQueries + GLenum target + GLsizei n + GLuint *ids + + + void glCreateRenderbuffers + GLsizei n + GLuint *renderbuffers + + + void glCreateSamplers + GLsizei n + GLuint *samplers + + + GLuint glCreateShader + GLenum type + + + GLhandleARB glCreateShaderObjectARB + GLenum shaderType + + + + GLuint glCreateShaderProgramEXT + GLenum type + const GLchar *string + + + GLuint glCreateShaderProgramv + GLenum type + GLsizei count + const GLchar *const*strings + + + GLuint glCreateShaderProgramvEXT + GLenum type + GLsizei count + const GLchar **strings + + + void glCreateStatesNV + GLsizei n + GLuint *states + + + GLsync glCreateSyncFromCLeventARB + struct _cl_context *context + struct _cl_event *event + GLbitfield flags + + + void glCreateTextures + GLenum target + GLsizei n + GLuint *textures + + + void glCreateTransformFeedbacks + GLsizei n + GLuint *ids + + + void glCreateVertexArrays + GLsizei n + GLuint *arrays + + + void glCullFace + GLenum mode + + + + void glCullParameterdvEXT + GLenum pname + GLdouble *params + + + void glCullParameterfvEXT + GLenum pname + GLfloat *params + + + void glCurrentPaletteMatrixARB + GLint index + + + + void glCurrentPaletteMatrixOES + GLuint matrixpaletteindex + + + void glDebugMessageCallback + GLDEBUGPROC callback + const void *userParam + + + void glDebugMessageCallbackAMD + GLDEBUGPROCAMD callback + void *userParam + + + void glDebugMessageCallbackARB + GLDEBUGPROCARB callback + const void *userParam + + + + void glDebugMessageCallbackKHR + GLDEBUGPROCKHR callback + const void *userParam + + + + void glDebugMessageControl + GLenum source + GLenum type + GLenum severity + GLsizei count + const GLuint *ids + GLboolean enabled + + + void glDebugMessageControlARB + GLenum source + GLenum type + GLenum severity + GLsizei count + const GLuint *ids + GLboolean enabled + + + + void glDebugMessageControlKHR + GLenum source + GLenum type + GLenum severity + GLsizei count + const GLuint *ids + GLboolean enabled + + + + void glDebugMessageEnableAMD + GLenum category + GLenum severity + GLsizei count + const GLuint *ids + GLboolean enabled + + + void glDebugMessageInsert + GLenum source + GLenum type + GLuint id + GLenum severity + GLsizei length + const GLchar *buf + + + void glDebugMessageInsertAMD + GLenum category + GLenum severity + GLuint id + GLsizei length + const GLchar *buf + + + void glDebugMessageInsertARB + GLenum source + GLenum type + GLuint id + GLenum severity + GLsizei length + const GLchar *buf + + + + void glDebugMessageInsertKHR + GLenum source + GLenum type + GLuint id + GLenum severity + GLsizei length + const GLchar *buf + + + + void glDeformSGIX + GLbitfield mask + + + + void glDeformationMap3dSGIX + GLenum target + GLdouble u1 + GLdouble u2 + GLint ustride + GLint uorder + GLdouble v1 + GLdouble v2 + GLint vstride + GLint vorder + GLdouble w1 + GLdouble w2 + GLint wstride + GLint worder + const GLdouble *points + + + + void glDeformationMap3fSGIX + GLenum target + GLfloat u1 + GLfloat u2 + GLint ustride + GLint uorder + GLfloat v1 + GLfloat v2 + GLint vstride + GLint vorder + GLfloat w1 + GLfloat w2 + GLint wstride + GLint worder + const GLfloat *points + + + + void glDeleteAsyncMarkersSGIX + GLuint marker + GLsizei range + + + void glDeleteBuffers + GLsizei n + const GLuint *buffers + + + void glDeleteBuffersARB + GLsizei n + const GLuint *buffers + + + + void glDeleteCommandListsNV + GLsizei n + const GLuint *lists + + + void glDeleteFencesAPPLE + GLsizei n + const GLuint *fences + + + void glDeleteFencesNV + GLsizei n + const GLuint *fences + + + + void glDeleteFragmentShaderATI + GLuint id + + + void glDeleteFramebuffers + GLsizei n + const GLuint *framebuffers + + + + void glDeleteFramebuffersEXT + GLsizei n + const GLuint *framebuffers + + + + + void glDeleteFramebuffersOES + GLsizei n + const GLuint *framebuffers + + + void glDeleteLists + GLuint list + GLsizei range + + + + void glDeleteMemoryObjectsEXT + GLsizei n + const GLuint *memoryObjects + + + void glDeleteNamedStringARB + GLint namelen + const GLchar *name + + + void glDeleteNamesAMD + GLenum identifier + GLuint num + const GLuint *names + + + void glDeleteObjectARB + GLhandleARB obj + + + void glDeleteOcclusionQueriesNV + GLsizei n + const GLuint *ids + + + void glDeletePathsNV + GLuint path + GLsizei range + + + void glDeletePerfMonitorsAMD + GLsizei n + GLuint *monitors + + + void glDeletePerfQueryINTEL + GLuint queryHandle + + + void glDeleteProgram + GLuint program + + + + void glDeleteProgramPipelines + GLsizei n + const GLuint *pipelines + + + void glDeleteProgramPipelinesEXT + GLsizei n + const GLuint *pipelines + + + void glDeleteProgramsARB + GLsizei n + const GLuint *programs + + + + void glDeleteProgramsNV + GLsizei n + const GLuint *programs + + + + + void glDeleteQueries + GLsizei n + const GLuint *ids + + + + void glDeleteQueriesARB + GLsizei n + const GLuint *ids + + + + void glDeleteQueriesEXT + GLsizei n + const GLuint *ids + + + void glDeleteQueryResourceTagNV + GLsizei n + const GLint *tagIds + + + void glDeleteRenderbuffers + GLsizei n + const GLuint *renderbuffers + + + + void glDeleteRenderbuffersEXT + GLsizei n + const GLuint *renderbuffers + + + + + void glDeleteRenderbuffersOES + GLsizei n + const GLuint *renderbuffers + + + void glDeleteSamplers + GLsizei count + const GLuint *samplers + + + void glDeleteSemaphoresEXT + GLsizei n + const GLuint *semaphores + + + void glDeleteShader + GLuint shader + + + + void glDeleteStatesNV + GLsizei n + const GLuint *states + + + void glDeleteSync + GLsync sync + + + void glDeleteSyncAPPLE + GLsync sync + + + + void glDeleteTextures + GLsizei n + const GLuint *textures + + + + void glDeleteTexturesEXT + GLsizei n + const GLuint *textures + + + + void glDeleteTransformFeedbacks + GLsizei n + const GLuint *ids + + + void glDeleteTransformFeedbacksNV + GLsizei n + const GLuint *ids + + + + void glDeleteVertexArrays + GLsizei n + const GLuint *arrays + + + + void glDeleteVertexArraysAPPLE + GLsizei n + const GLuint *arrays + + + + void glDeleteVertexArraysOES + GLsizei n + const GLuint *arrays + + + + void glDeleteVertexShaderEXT + GLuint id + + + void glDepthBoundsEXT + GLclampd zmin + GLclampd zmax + + + + void glDepthBoundsdNV + GLdouble zmin + GLdouble zmax + + + + void glDepthFunc + GLenum func + + + + void glDepthMask + GLboolean flag + + + + void glDepthRange + GLdouble n + GLdouble f + + + + void glDepthRangeArrayfvNV + GLuint first + GLsizei count + const GLfloat *v + + + void glDepthRangeArrayfvOES + GLuint first + GLsizei count + const GLfloat *v + + + void glDepthRangeArrayv + GLuint first + GLsizei count + const GLdouble *v + + + void glDepthRangeIndexed + GLuint index + GLdouble n + GLdouble f + + + void glDepthRangeIndexedfNV + GLuint index + GLfloat n + GLfloat f + + + void glDepthRangeIndexedfOES + GLuint index + GLfloat n + GLfloat f + + + void glDepthRangedNV + GLdouble zNear + GLdouble zFar + + + + void glDepthRangef + GLfloat n + GLfloat f + + + void glDepthRangefOES + GLclampf n + GLclampf f + + + + + void glDepthRangex + GLfixed n + GLfixed f + + + void glDepthRangexOES + GLfixed n + GLfixed f + + + void glDetachObjectARB + GLhandleARB containerObj + GLhandleARB attachedObj + + + + void glDetachShader + GLuint program + GLuint shader + + + void glDetailTexFuncSGIS + GLenum target + GLsizei n + const GLfloat *points + + + + void glDisable + GLenum cap + + + + void glDisableClientState + GLenum array + + + void glDisableClientStateIndexedEXT + GLenum array + GLuint index + + + void glDisableClientStateiEXT + GLenum array + GLuint index + + + void glDisableDriverControlQCOM + GLuint driverControl + + + void glDisableIndexedEXT + GLenum target + GLuint index + + + + + void glDisableVariantClientStateEXT + GLuint id + + + void glDisableVertexArrayAttrib + GLuint vaobj + GLuint index + + + void glDisableVertexArrayAttribEXT + GLuint vaobj + GLuint index + + + void glDisableVertexArrayEXT + GLuint vaobj + GLenum array + + + void glDisableVertexAttribAPPLE + GLuint index + GLenum pname + + + void glDisableVertexAttribArray + GLuint index + + + void glDisableVertexAttribArrayARB + GLuint index + + + + void glDisablei + GLenum target + GLuint index + + + void glDisableiEXT + GLenum target + GLuint index + + + + void glDisableiNV + GLenum target + GLuint index + + + + void glDisableiOES + GLenum target + GLuint index + + + + void glDiscardFramebufferEXT + GLenum target + GLsizei numAttachments + const GLenum *attachments + + + void glDispatchCompute + GLuint num_groups_x + GLuint num_groups_y + GLuint num_groups_z + + + void glDispatchComputeGroupSizeARB + GLuint num_groups_x + GLuint num_groups_y + GLuint num_groups_z + GLuint group_size_x + GLuint group_size_y + GLuint group_size_z + + + void glDispatchComputeIndirect + GLintptr indirect + + + void glDrawArrays + GLenum mode + GLint first + GLsizei count + + + + void glDrawArraysEXT + GLenum mode + GLint first + GLsizei count + + + + + void glDrawArraysIndirect + GLenum mode + const void *indirect + + + void glDrawArraysInstanced + GLenum mode + GLint first + GLsizei count + GLsizei instancecount + + + void glDrawArraysInstancedANGLE + GLenum mode + GLint first + GLsizei count + GLsizei primcount + + + + void glDrawArraysInstancedARB + GLenum mode + GLint first + GLsizei count + GLsizei primcount + + + + void glDrawArraysInstancedBaseInstance + GLenum mode + GLint first + GLsizei count + GLsizei instancecount + GLuint baseinstance + + + void glDrawArraysInstancedBaseInstanceEXT + GLenum mode + GLint first + GLsizei count + GLsizei instancecount + GLuint baseinstance + + + + void glDrawArraysInstancedEXT + GLenum mode + GLint start + GLsizei count + GLsizei primcount + + + + void glDrawArraysInstancedNV + GLenum mode + GLint first + GLsizei count + GLsizei primcount + + + + void glDrawBuffer + GLenum buf + + + + void glDrawBuffers + GLsizei n + const GLenum *bufs + + + + void glDrawBuffersARB + GLsizei n + const GLenum *bufs + + + + void glDrawBuffersATI + GLsizei n + const GLenum *bufs + + + + + void glDrawBuffersEXT + GLsizei n + const GLenum *bufs + + + + void glDrawBuffersIndexedEXT + GLint n + const GLenum *location + const GLint *indices + + + void glDrawBuffersNV + GLsizei n + const GLenum *bufs + + + void glDrawCommandsAddressNV + GLenum primitiveMode + const GLuint64 *indirects + const GLsizei *sizes + GLuint count + + + void glDrawCommandsNV + GLenum primitiveMode + GLuint buffer + const GLintptr *indirects + const GLsizei *sizes + GLuint count + + + void glDrawCommandsStatesAddressNV + const GLuint64 *indirects + const GLsizei *sizes + const GLuint *states + const GLuint *fbos + GLuint count + + + void glDrawCommandsStatesNV + GLuint buffer + const GLintptr *indirects + const GLsizei *sizes + const GLuint *states + const GLuint *fbos + GLuint count + + + void glDrawElementArrayAPPLE + GLenum mode + GLint first + GLsizei count + + + void glDrawElementArrayATI + GLenum mode + GLsizei count + + + void glDrawElements + GLenum mode + GLsizei count + GLenum type + const void *indices + + + void glDrawElementsBaseVertex + GLenum mode + GLsizei count + GLenum type + const void *indices + GLint basevertex + + + void glDrawElementsBaseVertexEXT + GLenum mode + GLsizei count + GLenum type + const void *indices + GLint basevertex + + + + void glDrawElementsBaseVertexOES + GLenum mode + GLsizei count + GLenum type + const void *indices + GLint basevertex + + + + void glDrawElementsIndirect + GLenum mode + GLenum type + const void *indirect + + + void glDrawElementsInstanced + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei instancecount + + + void glDrawElementsInstancedANGLE + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei primcount + + + + void glDrawElementsInstancedARB + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei primcount + + + + void glDrawElementsInstancedBaseInstance + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei instancecount + GLuint baseinstance + + + void glDrawElementsInstancedBaseInstanceEXT + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei instancecount + GLuint baseinstance + + + + void glDrawElementsInstancedBaseVertex + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei instancecount + GLint basevertex + + + void glDrawElementsInstancedBaseVertexBaseInstance + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei instancecount + GLint basevertex + GLuint baseinstance + + + void glDrawElementsInstancedBaseVertexBaseInstanceEXT + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei instancecount + GLint basevertex + GLuint baseinstance + + + + void glDrawElementsInstancedBaseVertexEXT + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei instancecount + GLint basevertex + + + + void glDrawElementsInstancedBaseVertexOES + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei instancecount + GLint basevertex + + + + void glDrawElementsInstancedEXT + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei primcount + + + + void glDrawElementsInstancedNV + GLenum mode + GLsizei count + GLenum type + const void *indices + GLsizei primcount + + + + void glDrawMeshArraysSUN + GLenum mode + GLint first + GLsizei count + GLsizei width + + + void glDrawMeshTasksNV + GLuint first + GLuint count + + + void glDrawMeshTasksIndirectNV + GLintptr indirect + + + void glDrawPixels + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *pixels + + + + + void glDrawRangeElementArrayAPPLE + GLenum mode + GLuint start + GLuint end + GLint first + GLsizei count + + + void glDrawRangeElementArrayATI + GLenum mode + GLuint start + GLuint end + GLsizei count + + + void glDrawRangeElements + GLenum mode + GLuint start + GLuint end + GLsizei count + GLenum type + const void *indices + + + void glDrawRangeElementsBaseVertex + GLenum mode + GLuint start + GLuint end + GLsizei count + GLenum type + const void *indices + GLint basevertex + + + void glDrawRangeElementsBaseVertexEXT + GLenum mode + GLuint start + GLuint end + GLsizei count + GLenum type + const void *indices + GLint basevertex + + + + void glDrawRangeElementsBaseVertexOES + GLenum mode + GLuint start + GLuint end + GLsizei count + GLenum type + const void *indices + GLint basevertex + + + + void glDrawRangeElementsEXT + GLenum mode + GLuint start + GLuint end + GLsizei count + GLenum type + const void *indices + + + + void glDrawTexfOES + GLfloat x + GLfloat y + GLfloat z + GLfloat width + GLfloat height + + + + void glDrawTexfvOES + const GLfloat *coords + + + void glDrawTexiOES + GLint x + GLint y + GLint z + GLint width + GLint height + + + + void glDrawTexivOES + const GLint *coords + + + void glDrawTexsOES + GLshort x + GLshort y + GLshort z + GLshort width + GLshort height + + + + void glDrawTexsvOES + const GLshort *coords + + + void glDrawTextureNV + GLuint texture + GLuint sampler + GLfloat x0 + GLfloat y0 + GLfloat x1 + GLfloat y1 + GLfloat z + GLfloat s0 + GLfloat t0 + GLfloat s1 + GLfloat t1 + + + void glDrawTexxOES + GLfixed x + GLfixed y + GLfixed z + GLfixed width + GLfixed height + + + + void glDrawTexxvOES + const GLfixed *coords + + + void glDrawTransformFeedback + GLenum mode + GLuint id + + + void glDrawTransformFeedbackEXT + GLenum mode + GLuint id + + + + void glDrawTransformFeedbackInstanced + GLenum mode + GLuint id + GLsizei instancecount + + + void glDrawTransformFeedbackInstancedEXT + GLenum mode + GLuint id + GLsizei instancecount + + + + void glDrawTransformFeedbackNV + GLenum mode + GLuint id + + + + void glDrawTransformFeedbackStream + GLenum mode + GLuint id + GLuint stream + + + void glDrawTransformFeedbackStreamInstanced + GLenum mode + GLuint id + GLuint stream + GLsizei instancecount + + + void glEGLImageTargetRenderbufferStorageOES + GLenum target + GLeglImageOES image + + + void glEGLImageTargetTexStorageEXT + GLenum target + GLeglImageOES image + const GLint* attrib_list + + + void glEGLImageTargetTexture2DOES + GLenum target + GLeglImageOES image + + + void glEGLImageTargetTextureStorageEXT + GLuint texture + GLeglImageOES image + const GLint* attrib_list + + + void glEdgeFlag + GLboolean flag + + + + void glEdgeFlagFormatNV + GLsizei stride + + + void glEdgeFlagPointer + GLsizei stride + const void *pointer + + + void glEdgeFlagPointerEXT + GLsizei stride + GLsizei count + const GLboolean *pointer + + + void glEdgeFlagPointerListIBM + GLint stride + const GLboolean **pointer + GLint ptrstride + + + void glEdgeFlagv + const GLboolean *flag + + + + void glElementPointerAPPLE + GLenum type + const void *pointer + + + void glElementPointerATI + GLenum type + const void *pointer + + + void glEnable + GLenum cap + + + + void glEnableClientState + GLenum array + + + void glEnableClientStateIndexedEXT + GLenum array + GLuint index + + + void glEnableClientStateiEXT + GLenum array + GLuint index + + + void glEnableDriverControlQCOM + GLuint driverControl + + + void glEnableIndexedEXT + GLenum target + GLuint index + + + + + void glEnableVariantClientStateEXT + GLuint id + + + void glEnableVertexArrayAttrib + GLuint vaobj + GLuint index + + + void glEnableVertexArrayAttribEXT + GLuint vaobj + GLuint index + + + void glEnableVertexArrayEXT + GLuint vaobj + GLenum array + + + void glEnableVertexAttribAPPLE + GLuint index + GLenum pname + + + void glEnableVertexAttribArray + GLuint index + + + void glEnableVertexAttribArrayARB + GLuint index + + + + void glEnablei + GLenum target + GLuint index + + + void glEnableiEXT + GLenum target + GLuint index + + + + void glEnableiNV + GLenum target + GLuint index + + + + void glEnableiOES + GLenum target + GLuint index + + + + void glEnd + + + + void glEndConditionalRender + + + + void glEndConditionalRenderNV + + + + void glEndConditionalRenderNVX + + + + void glEndFragmentShaderATI + + + void glEndList + + + + void glEndOcclusionQueryNV + + + void glEndPerfMonitorAMD + GLuint monitor + + + void glEndPerfQueryINTEL + GLuint queryHandle + + + void glEndQuery + GLenum target + + + + void glEndQueryARB + GLenum target + + + + void glEndQueryEXT + GLenum target + + + void glEndQueryIndexed + GLenum target + GLuint index + + + void glEndTilingQCOM + GLbitfield preserveMask + + + void glEndTransformFeedback + + + + void glEndTransformFeedbackEXT + + + + void glEndTransformFeedbackNV + + + + void glEndVertexShaderEXT + + + void glEndVideoCaptureNV + GLuint video_capture_slot + + + void glEvalCoord1d + GLdouble u + + + + void glEvalCoord1dv + const GLdouble *u + + + + void glEvalCoord1f + GLfloat u + + + + void glEvalCoord1fv + const GLfloat *u + + + + void glEvalCoord1xOES + GLfixed u + + + void glEvalCoord1xvOES + const GLfixed *coords + + + void glEvalCoord2d + GLdouble u + GLdouble v + + + + void glEvalCoord2dv + const GLdouble *u + + + + void glEvalCoord2f + GLfloat u + GLfloat v + + + + void glEvalCoord2fv + const GLfloat *u + + + + void glEvalCoord2xOES + GLfixed u + GLfixed v + + + void glEvalCoord2xvOES + const GLfixed *coords + + + void glEvalMapsNV + GLenum target + GLenum mode + + + void glEvalMesh1 + GLenum mode + GLint i1 + GLint i2 + + + + void glEvalMesh2 + GLenum mode + GLint i1 + GLint i2 + GLint j1 + GLint j2 + + + + void glEvalPoint1 + GLint i + + + + void glEvalPoint2 + GLint i + GLint j + + + + void glEvaluateDepthValuesARB + + + void glExecuteProgramNV + GLenum target + GLuint id + const GLfloat *params + + + + void glExtGetBufferPointervQCOM + GLenum target + void **params + + + void glExtGetBuffersQCOM + GLuint *buffers + GLint maxBuffers + GLint *numBuffers + + + void glExtGetFramebuffersQCOM + GLuint *framebuffers + GLint maxFramebuffers + GLint *numFramebuffers + + + void glExtGetProgramBinarySourceQCOM + GLuint program + GLenum shadertype + GLchar *source + GLint *length + + + void glExtGetProgramsQCOM + GLuint *programs + GLint maxPrograms + GLint *numPrograms + + + void glExtGetRenderbuffersQCOM + GLuint *renderbuffers + GLint maxRenderbuffers + GLint *numRenderbuffers + + + void glExtGetShadersQCOM + GLuint *shaders + GLint maxShaders + GLint *numShaders + + + void glExtGetTexLevelParameterivQCOM + GLuint texture + GLenum face + GLint level + GLenum pname + GLint *params + + + void glExtGetTexSubImageQCOM + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + void *texels + + + void glExtGetTexturesQCOM + GLuint *textures + GLint maxTextures + GLint *numTextures + + + GLboolean glExtIsProgramBinaryQCOM + GLuint program + + + void glExtTexObjectStateOverrideiQCOM + GLenum target + GLenum pname + GLint param + + + void glExtractComponentEXT + GLuint res + GLuint src + GLuint num + + + void glFeedbackBuffer + GLsizei size + GLenum type + GLfloat *buffer + + + + void glFeedbackBufferxOES + GLsizei n + GLenum type + const GLfixed *buffer + + + GLsync glFenceSync + GLenum condition + GLbitfield flags + + + GLsync glFenceSyncAPPLE + GLenum condition + GLbitfield flags + + + + void glFinalCombinerInputNV + GLenum variable + GLenum input + GLenum mapping + GLenum componentUsage + + + + void glFinish + + + + GLint glFinishAsyncSGIX + GLuint *markerp + + + void glFinishFenceAPPLE + GLuint fence + + + void glFinishFenceNV + GLuint fence + + + + void glFinishObjectAPPLE + GLenum object + GLint name + + + void glFinishTextureSUNX + + + void glFlush + + + + void glFlushMappedBufferRange + GLenum target + GLintptr offset + GLsizeiptr length + + + void glFlushMappedBufferRangeAPPLE + GLenum target + GLintptr offset + GLsizeiptr size + + + + void glFlushMappedBufferRangeEXT + GLenum target + GLintptr offset + GLsizeiptr length + + + + void glFlushMappedNamedBufferRange + GLuint buffer + GLintptr offset + GLsizeiptr length + + + void glFlushMappedNamedBufferRangeEXT + GLuint buffer + GLintptr offset + GLsizeiptr length + + + void glFlushPixelDataRangeNV + GLenum target + + + void glFlushRasterSGIX + + + + void glFlushStaticDataIBM + GLenum target + + + void glFlushVertexArrayRangeAPPLE + GLsizei length + void *pointer + + + void glFlushVertexArrayRangeNV + + + void glFogCoordFormatNV + GLenum type + GLsizei stride + + + void glFogCoordPointer + GLenum type + GLsizei stride + const void *pointer + + + void glFogCoordPointerEXT + GLenum type + GLsizei stride + const void *pointer + + + + void glFogCoordPointerListIBM + GLenum type + GLint stride + const void **pointer + GLint ptrstride + + + void glFogCoordd + GLdouble coord + + + + void glFogCoorddEXT + GLdouble coord + + + + + void glFogCoorddv + const GLdouble *coord + + + + void glFogCoorddvEXT + const GLdouble *coord + + + + + void glFogCoordf + GLfloat coord + + + + void glFogCoordfEXT + GLfloat coord + + + + + void glFogCoordfv + const GLfloat *coord + + + + void glFogCoordfvEXT + const GLfloat *coord + + + + + void glFogCoordhNV + GLhalfNV fog + + + + void glFogCoordhvNV + const GLhalfNV *fog + + + + void glFogFuncSGIS + GLsizei n + const GLfloat *points + + + + void glFogf + GLenum pname + GLfloat param + + + + void glFogfv + GLenum pname + const GLfloat *params + + + + void glFogi + GLenum pname + GLint param + + + + void glFogiv + GLenum pname + const GLint *params + + + + void glFogx + GLenum pname + GLfixed param + + + void glFogxOES + GLenum pname + GLfixed param + + + void glFogxv + GLenum pname + const GLfixed *param + + + void glFogxvOES + GLenum pname + const GLfixed *param + + + void glFragmentColorMaterialSGIX + GLenum face + GLenum mode + + + void glFragmentCoverageColorNV + GLuint color + + + void glFragmentLightModelfSGIX + GLenum pname + GLfloat param + + + void glFragmentLightModelfvSGIX + GLenum pname + const GLfloat *params + + + void glFragmentLightModeliSGIX + GLenum pname + GLint param + + + void glFragmentLightModelivSGIX + GLenum pname + const GLint *params + + + void glFragmentLightfSGIX + GLenum light + GLenum pname + GLfloat param + + + void glFragmentLightfvSGIX + GLenum light + GLenum pname + const GLfloat *params + + + void glFragmentLightiSGIX + GLenum light + GLenum pname + GLint param + + + void glFragmentLightivSGIX + GLenum light + GLenum pname + const GLint *params + + + void glFragmentMaterialfSGIX + GLenum face + GLenum pname + GLfloat param + + + void glFragmentMaterialfvSGIX + GLenum face + GLenum pname + const GLfloat *params + + + void glFragmentMaterialiSGIX + GLenum face + GLenum pname + GLint param + + + void glFragmentMaterialivSGIX + GLenum face + GLenum pname + const GLint *params + + + void glFrameTerminatorGREMEDY + + + void glFrameZoomSGIX + GLint factor + + + + void glFramebufferDrawBufferEXT + GLuint framebuffer + GLenum mode + + + void glFramebufferDrawBuffersEXT + GLuint framebuffer + GLsizei n + const GLenum *bufs + + + void glFramebufferFetchBarrierEXT + + + void glFramebufferFetchBarrierQCOM + + + void glFramebufferFoveationConfigQCOM + GLuint framebuffer + GLuint numLayers + GLuint focalPointsPerLayer + GLuint requestedFeatures + GLuint *providedFeatures + + + void glFramebufferFoveationParametersQCOM + GLuint framebuffer + GLuint layer + GLuint focalPoint + GLfloat focalX + GLfloat focalY + GLfloat gainX + GLfloat gainY + GLfloat foveaArea + + + void glFramebufferParameteri + GLenum target + GLenum pname + GLint param + + + void glFramebufferPixelLocalStorageSizeEXT + GLuint target + GLsizei size + + + void glFramebufferReadBufferEXT + GLuint framebuffer + GLenum mode + + + void glFramebufferRenderbuffer + GLenum target + GLenum attachment + GLenum renderbuffertarget + GLuint renderbuffer + + + + void glFramebufferRenderbufferEXT + GLenum target + GLenum attachment + GLenum renderbuffertarget + GLuint renderbuffer + + + + + void glFramebufferRenderbufferOES + GLenum target + GLenum attachment + GLenum renderbuffertarget + GLuint renderbuffer + + + void glFramebufferSampleLocationsfvARB + GLenum target + GLuint start + GLsizei count + const GLfloat *v + + + void glFramebufferSampleLocationsfvNV + GLenum target + GLuint start + GLsizei count + const GLfloat *v + + + void glFramebufferSamplePositionsfvAMD + GLenum target + GLuint numsamples + GLuint pixelindex + const GLfloat *values + + + void glFramebufferTexture + GLenum target + GLenum attachment + GLuint texture + GLint level + + + void glFramebufferTexture1D + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + + + + void glFramebufferTexture1DEXT + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + + + + + void glFramebufferTexture2D + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + + + + void glFramebufferTexture2DEXT + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + + + + + void glFramebufferTexture2DDownsampleIMG + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + GLint xscale + GLint yscale + + + void glFramebufferTexture2DMultisampleEXT + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + GLsizei samples + + + void glFramebufferTexture2DMultisampleIMG + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + GLsizei samples + + + void glFramebufferTexture2DOES + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + + + void glFramebufferTexture3D + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + GLint zoffset + + + + void glFramebufferTexture3DEXT + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + GLint zoffset + + + + + void glFramebufferTexture3DOES + GLenum target + GLenum attachment + GLenum textarget + GLuint texture + GLint level + GLint zoffset + + + void glFramebufferTextureARB + GLenum target + GLenum attachment + GLuint texture + GLint level + + + + void glFramebufferTextureEXT + GLenum target + GLenum attachment + GLuint texture + GLint level + + + + void glFramebufferTextureFaceARB + GLenum target + GLenum attachment + GLuint texture + GLint level + GLenum face + + + void glFramebufferTextureFaceEXT + GLenum target + GLenum attachment + GLuint texture + GLint level + GLenum face + + + + void glFramebufferTextureLayer + GLenum target + GLenum attachment + GLuint texture + GLint level + GLint layer + + + + void glFramebufferTextureLayerARB + GLenum target + GLenum attachment + GLuint texture + GLint level + GLint layer + + + + void glFramebufferTextureLayerEXT + GLenum target + GLenum attachment + GLuint texture + GLint level + GLint layer + + + + void glFramebufferTextureLayerDownsampleIMG + GLenum target + GLenum attachment + GLuint texture + GLint level + GLint layer + GLint xscale + GLint yscale + + + void glFramebufferTextureMultisampleMultiviewOVR + GLenum target + GLenum attachment + GLuint texture + GLint level + GLsizei samples + GLint baseViewIndex + GLsizei numViews + + + void glFramebufferTextureMultiviewOVR + GLenum target + GLenum attachment + GLuint texture + GLint level + GLint baseViewIndex + GLsizei numViews + + + void glFramebufferTextureOES + GLenum target + GLenum attachment + GLuint texture + GLint level + + + + void glFreeObjectBufferATI + GLuint buffer + + + void glFrontFace + GLenum mode + + + + void glFrustum + GLdouble left + GLdouble right + GLdouble bottom + GLdouble top + GLdouble zNear + GLdouble zFar + + + + void glFrustumf + GLfloat l + GLfloat r + GLfloat b + GLfloat t + GLfloat n + GLfloat f + + + void glFrustumfOES + GLfloat l + GLfloat r + GLfloat b + GLfloat t + GLfloat n + GLfloat f + + + + void glFrustumx + GLfixed l + GLfixed r + GLfixed b + GLfixed t + GLfixed n + GLfixed f + + + void glFrustumxOES + GLfixed l + GLfixed r + GLfixed b + GLfixed t + GLfixed n + GLfixed f + + + GLuint glGenAsyncMarkersSGIX + GLsizei range + + + void glGenBuffers + GLsizei n + GLuint *buffers + + + void glGenBuffersARB + GLsizei n + GLuint *buffers + + + + void glGenFencesAPPLE + GLsizei n + GLuint *fences + + + void glGenFencesNV + GLsizei n + GLuint *fences + + + + GLuint glGenFragmentShadersATI + GLuint range + + + void glGenFramebuffers + GLsizei n + GLuint *framebuffers + + + + void glGenFramebuffersEXT + GLsizei n + GLuint *framebuffers + + + + + void glGenFramebuffersOES + GLsizei n + GLuint *framebuffers + + + GLuint glGenLists + GLsizei range + + + + void glGenNamesAMD + GLenum identifier + GLuint num + GLuint *names + + + void glGenOcclusionQueriesNV + GLsizei n + GLuint *ids + + + GLuint glGenPathsNV + GLsizei range + + + void glGenPerfMonitorsAMD + GLsizei n + GLuint *monitors + + + void glGenProgramPipelines + GLsizei n + GLuint *pipelines + + + void glGenProgramPipelinesEXT + GLsizei n + GLuint *pipelines + + + void glGenProgramsARB + GLsizei n + GLuint *programs + + + + void glGenProgramsNV + GLsizei n + GLuint *programs + + + + + void glGenQueries + GLsizei n + GLuint *ids + + + + void glGenQueriesARB + GLsizei n + GLuint *ids + + + + void glGenQueriesEXT + GLsizei n + GLuint *ids + + + void glGenQueryResourceTagNV + GLsizei n + GLint *tagIds + + + void glGenRenderbuffers + GLsizei n + GLuint *renderbuffers + + + + void glGenRenderbuffersEXT + GLsizei n + GLuint *renderbuffers + + + + + void glGenRenderbuffersOES + GLsizei n + GLuint *renderbuffers + + + void glGenSamplers + GLsizei count + GLuint *samplers + + + void glGenSemaphoresEXT + GLsizei n + GLuint *semaphores + + + GLuint glGenSymbolsEXT + GLenum datatype + GLenum storagetype + GLenum range + GLuint components + + + void glGenTextures + GLsizei n + GLuint *textures + + + + void glGenTexturesEXT + GLsizei n + GLuint *textures + + + + void glGenTransformFeedbacks + GLsizei n + GLuint *ids + + + void glGenTransformFeedbacksNV + GLsizei n + GLuint *ids + + + + void glGenVertexArrays + GLsizei n + GLuint *arrays + + + + void glGenVertexArraysAPPLE + GLsizei n + GLuint *arrays + + + + void glGenVertexArraysOES + GLsizei n + GLuint *arrays + + + + GLuint glGenVertexShadersEXT + GLuint range + + + void glGenerateMipmap + GLenum target + + + + void glGenerateMipmapEXT + GLenum target + + + + + void glGenerateMipmapOES + GLenum target + + + void glGenerateMultiTexMipmapEXT + GLenum texunit + GLenum target + + + void glGenerateTextureMipmap + GLuint texture + + + void glGenerateTextureMipmapEXT + GLuint texture + GLenum target + + + void glGetActiveAtomicCounterBufferiv + GLuint program + GLuint bufferIndex + GLenum pname + GLint *params + + + void glGetActiveAttrib + GLuint program + GLuint index + GLsizei bufSize + GLsizei *length + GLint *size + GLenum *type + GLchar *name + + + void glGetActiveAttribARB + GLhandleARB programObj + GLuint index + GLsizei maxLength + GLsizei *length + GLint *size + GLenum *type + GLcharARB *name + + + + void glGetActiveSubroutineName + GLuint program + GLenum shadertype + GLuint index + GLsizei bufsize + GLsizei *length + GLchar *name + + + void glGetActiveSubroutineUniformName + GLuint program + GLenum shadertype + GLuint index + GLsizei bufsize + GLsizei *length + GLchar *name + + + void glGetActiveSubroutineUniformiv + GLuint program + GLenum shadertype + GLuint index + GLenum pname + GLint *values + + + void glGetActiveUniform + GLuint program + GLuint index + GLsizei bufSize + GLsizei *length + GLint *size + GLenum *type + GLchar *name + + + void glGetActiveUniformARB + GLhandleARB programObj + GLuint index + GLsizei maxLength + GLsizei *length + GLint *size + GLenum *type + GLcharARB *name + + + + void glGetActiveUniformBlockName + GLuint program + GLuint uniformBlockIndex + GLsizei bufSize + GLsizei *length + GLchar *uniformBlockName + + + + void glGetActiveUniformBlockiv + GLuint program + GLuint uniformBlockIndex + GLenum pname + GLint *params + + + + void glGetActiveUniformName + GLuint program + GLuint uniformIndex + GLsizei bufSize + GLsizei *length + GLchar *uniformName + + + + void glGetActiveUniformsiv + GLuint program + GLsizei uniformCount + const GLuint *uniformIndices + GLenum pname + GLint *params + + + + void glGetActiveVaryingNV + GLuint program + GLuint index + GLsizei bufSize + GLsizei *length + GLsizei *size + GLenum *type + GLchar *name + + + void glGetArrayObjectfvATI + GLenum array + GLenum pname + GLfloat *params + + + void glGetArrayObjectivATI + GLenum array + GLenum pname + GLint *params + + + void glGetAttachedObjectsARB + GLhandleARB containerObj + GLsizei maxCount + GLsizei *count + GLhandleARB *obj + + + void glGetAttachedShaders + GLuint program + GLsizei maxCount + GLsizei *count + GLuint *shaders + + + GLint glGetAttribLocation + GLuint program + const GLchar *name + + + GLint glGetAttribLocationARB + GLhandleARB programObj + const GLcharARB *name + + + + void glGetBooleanIndexedvEXT + GLenum target + GLuint index + GLboolean *data + + + + + void glGetBooleani_v + GLenum target + GLuint index + GLboolean *data + + + void glGetBooleanv + GLenum pname + GLboolean *data + + + + void glGetBufferParameteri64v + GLenum target + GLenum pname + GLint64 *params + + + void glGetBufferParameteriv + GLenum target + GLenum pname + GLint *params + + + void glGetBufferParameterivARB + GLenum target + GLenum pname + GLint *params + + + + void glGetBufferParameterui64vNV + GLenum target + GLenum pname + GLuint64EXT *params + + + void glGetBufferPointerv + GLenum target + GLenum pname + void **params + + + void glGetBufferPointervARB + GLenum target + GLenum pname + void **params + + + + void glGetBufferPointervOES + GLenum target + GLenum pname + void **params + + + + void glGetBufferSubData + GLenum target + GLintptr offset + GLsizeiptr size + void *data + + + void glGetBufferSubDataARB + GLenum target + GLintptrARB offset + GLsizeiptrARB size + void *data + + + + void glGetClipPlane + GLenum plane + GLdouble *equation + + + + void glGetClipPlanef + GLenum plane + GLfloat *equation + + + void glGetClipPlanefOES + GLenum plane + GLfloat *equation + + + + void glGetClipPlanex + GLenum plane + GLfixed *equation + + + void glGetClipPlanexOES + GLenum plane + GLfixed *equation + + + void glGetColorTable + GLenum target + GLenum format + GLenum type + void *table + + + + + void glGetColorTableEXT + GLenum target + GLenum format + GLenum type + void *data + + + + void glGetColorTableParameterfv + GLenum target + GLenum pname + GLfloat *params + + + + void glGetColorTableParameterfvEXT + GLenum target + GLenum pname + GLfloat *params + + + + void glGetColorTableParameterfvSGI + GLenum target + GLenum pname + GLfloat *params + + + + void glGetColorTableParameteriv + GLenum target + GLenum pname + GLint *params + + + + void glGetColorTableParameterivEXT + GLenum target + GLenum pname + GLint *params + + + + void glGetColorTableParameterivSGI + GLenum target + GLenum pname + GLint *params + + + + void glGetColorTableSGI + GLenum target + GLenum format + GLenum type + void *table + + + + void glGetCombinerInputParameterfvNV + GLenum stage + GLenum portion + GLenum variable + GLenum pname + GLfloat *params + + + + void glGetCombinerInputParameterivNV + GLenum stage + GLenum portion + GLenum variable + GLenum pname + GLint *params + + + + void glGetCombinerOutputParameterfvNV + GLenum stage + GLenum portion + GLenum pname + GLfloat *params + + + + void glGetCombinerOutputParameterivNV + GLenum stage + GLenum portion + GLenum pname + GLint *params + + + + void glGetCombinerStageParameterfvNV + GLenum stage + GLenum pname + GLfloat *params + + + GLuint glGetCommandHeaderNV + GLenum tokenID + GLuint size + + + void glGetCompressedMultiTexImageEXT + GLenum texunit + GLenum target + GLint lod + void *img + + + void glGetCompressedTexImage + GLenum target + GLint level + void *img + + + + + void glGetCompressedTexImageARB + GLenum target + GLint level + void *img + + + + + void glGetCompressedTextureImage + GLuint texture + GLint level + GLsizei bufSize + void *pixels + + + void glGetCompressedTextureImageEXT + GLuint texture + GLenum target + GLint lod + void *img + + + void glGetCompressedTextureSubImage + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLsizei bufSize + void *pixels + + + void glGetConvolutionFilter + GLenum target + GLenum format + GLenum type + void *image + + + + + void glGetConvolutionFilterEXT + GLenum target + GLenum format + GLenum type + void *image + + + + void glGetConvolutionParameterfv + GLenum target + GLenum pname + GLfloat *params + + + + void glGetConvolutionParameterfvEXT + GLenum target + GLenum pname + GLfloat *params + + + + void glGetConvolutionParameteriv + GLenum target + GLenum pname + GLint *params + + + + void glGetConvolutionParameterivEXT + GLenum target + GLenum pname + GLint *params + + + + void glGetConvolutionParameterxvOES + GLenum target + GLenum pname + GLfixed *params + + + void glGetCoverageModulationTableNV + GLsizei bufsize + GLfloat *v + + + GLuint glGetDebugMessageLog + GLuint count + GLsizei bufSize + GLenum *sources + GLenum *types + GLuint *ids + GLenum *severities + GLsizei *lengths + GLchar *messageLog + + + GLuint glGetDebugMessageLogAMD + GLuint count + GLsizei bufsize + GLenum *categories + GLuint *severities + GLuint *ids + GLsizei *lengths + GLchar *message + + + GLuint glGetDebugMessageLogARB + GLuint count + GLsizei bufSize + GLenum *sources + GLenum *types + GLuint *ids + GLenum *severities + GLsizei *lengths + GLchar *messageLog + + + + GLuint glGetDebugMessageLogKHR + GLuint count + GLsizei bufSize + GLenum *sources + GLenum *types + GLuint *ids + GLenum *severities + GLsizei *lengths + GLchar *messageLog + + + + void glGetDetailTexFuncSGIS + GLenum target + GLfloat *points + + + + void glGetDoubleIndexedvEXT + GLenum target + GLuint index + GLdouble *data + + + + void glGetDoublei_v + GLenum target + GLuint index + GLdouble *data + + + void glGetDoublei_vEXT + GLenum pname + GLuint index + GLdouble *params + + + + void glGetDoublev + GLenum pname + GLdouble *data + + + + void glGetDriverControlStringQCOM + GLuint driverControl + GLsizei bufSize + GLsizei *length + GLchar *driverControlString + + + void glGetDriverControlsQCOM + GLint *num + GLsizei size + GLuint *driverControls + + + GLenum glGetError + + + + void glGetFenceivNV + GLuint fence + GLenum pname + GLint *params + + + + void glGetFinalCombinerInputParameterfvNV + GLenum variable + GLenum pname + GLfloat *params + + + + void glGetFinalCombinerInputParameterivNV + GLenum variable + GLenum pname + GLint *params + + + + void glGetFirstPerfQueryIdINTEL + GLuint *queryId + + + void glGetFixedv + GLenum pname + GLfixed *params + + + void glGetFixedvOES + GLenum pname + GLfixed *params + + + void glGetFloatIndexedvEXT + GLenum target + GLuint index + GLfloat *data + + + + void glGetFloati_v + GLenum target + GLuint index + GLfloat *data + + + void glGetFloati_vEXT + GLenum pname + GLuint index + GLfloat *params + + + + void glGetFloati_vNV + GLenum target + GLuint index + GLfloat *data + + + + void glGetFloati_vOES + GLenum target + GLuint index + GLfloat *data + + + + void glGetFloatv + GLenum pname + GLfloat *data + + + + void glGetFogFuncSGIS + GLfloat *points + + + GLint glGetFragDataIndex + GLuint program + const GLchar *name + + + GLint glGetFragDataIndexEXT + GLuint program + const GLchar *name + + + + GLint glGetFragDataLocation + GLuint program + const GLchar *name + + + GLint glGetFragDataLocationEXT + GLuint program + const GLchar *name + + + + void glGetFragmentLightfvSGIX + GLenum light + GLenum pname + GLfloat *params + + + void glGetFragmentLightivSGIX + GLenum light + GLenum pname + GLint *params + + + void glGetFragmentMaterialfvSGIX + GLenum face + GLenum pname + GLfloat *params + + + void glGetFragmentMaterialivSGIX + GLenum face + GLenum pname + GLint *params + + + void glGetFramebufferAttachmentParameteriv + GLenum target + GLenum attachment + GLenum pname + GLint *params + + + + void glGetFramebufferAttachmentParameterivEXT + GLenum target + GLenum attachment + GLenum pname + GLint *params + + + + + void glGetFramebufferAttachmentParameterivOES + GLenum target + GLenum attachment + GLenum pname + GLint *params + + + void glGetFramebufferParameterfvAMD + GLenum target + GLenum pname + GLuint numsamples + GLuint pixelindex + GLsizei size + GLfloat *values + + + void glGetFramebufferParameteriv + GLenum target + GLenum pname + GLint *params + + + void glGetFramebufferParameterivEXT + GLuint framebuffer + GLenum pname + GLint *params + + + GLsizei glGetFramebufferPixelLocalStorageSizeEXT + GLuint target + + + GLenum glGetGraphicsResetStatus + + + GLenum glGetGraphicsResetStatusARB + + + GLenum glGetGraphicsResetStatusEXT + + + + GLenum glGetGraphicsResetStatusKHR + + + + GLhandleARB glGetHandleARB + GLenum pname + + + void glGetHistogram + GLenum target + GLboolean reset + GLenum format + GLenum type + void *values + + + + + void glGetHistogramEXT + GLenum target + GLboolean reset + GLenum format + GLenum type + void *values + + + + void glGetHistogramParameterfv + GLenum target + GLenum pname + GLfloat *params + + + + void glGetHistogramParameterfvEXT + GLenum target + GLenum pname + GLfloat *params + + + + void glGetHistogramParameteriv + GLenum target + GLenum pname + GLint *params + + + + void glGetHistogramParameterivEXT + GLenum target + GLenum pname + GLint *params + + + + void glGetHistogramParameterxvOES + GLenum target + GLenum pname + GLfixed *params + + + GLuint64 glGetImageHandleARB + GLuint texture + GLint level + GLboolean layered + GLint layer + GLenum format + + + GLuint64 glGetImageHandleNV + GLuint texture + GLint level + GLboolean layered + GLint layer + GLenum format + + + void glGetImageTransformParameterfvHP + GLenum target + GLenum pname + GLfloat *params + + + void glGetImageTransformParameterivHP + GLenum target + GLenum pname + GLint *params + + + void glGetInfoLogARB + GLhandleARB obj + GLsizei maxLength + GLsizei *length + GLcharARB *infoLog + + + GLint glGetInstrumentsSGIX + + + + void glGetInteger64i_v + GLenum target + GLuint index + GLint64 *data + + + void glGetInteger64v + GLenum pname + GLint64 *data + + + void glGetInteger64vAPPLE + GLenum pname + GLint64 *params + + + + void glGetIntegerIndexedvEXT + GLenum target + GLuint index + GLint *data + + + + + void glGetIntegeri_v + GLenum target + GLuint index + GLint *data + + + void glGetIntegeri_vEXT + GLenum target + GLuint index + GLint *data + + + void glGetIntegerui64i_vNV + GLenum value + GLuint index + GLuint64EXT *result + + + void glGetIntegerui64vNV + GLenum value + GLuint64EXT *result + + + void glGetIntegerv + GLenum pname + GLint *data + + + + void glGetInternalformatSampleivNV + GLenum target + GLenum internalformat + GLsizei samples + GLenum pname + GLsizei bufSize + GLint *params + + + void glGetInternalformati64v + GLenum target + GLenum internalformat + GLenum pname + GLsizei bufSize + GLint64 *params + + + void glGetInternalformativ + GLenum target + GLenum internalformat + GLenum pname + GLsizei bufSize + GLint *params + + + void glGetInvariantBooleanvEXT + GLuint id + GLenum value + GLboolean *data + + + void glGetInvariantFloatvEXT + GLuint id + GLenum value + GLfloat *data + + + void glGetInvariantIntegervEXT + GLuint id + GLenum value + GLint *data + + + void glGetLightfv + GLenum light + GLenum pname + GLfloat *params + + + + void glGetLightiv + GLenum light + GLenum pname + GLint *params + + + + void glGetLightxOES + GLenum light + GLenum pname + GLfixed *params + + + void glGetLightxv + GLenum light + GLenum pname + GLfixed *params + + + void glGetLightxvOES + GLenum light + GLenum pname + GLfixed *params + + + void glGetListParameterfvSGIX + GLuint list + GLenum pname + GLfloat *params + + + void glGetListParameterivSGIX + GLuint list + GLenum pname + GLint *params + + + void glGetLocalConstantBooleanvEXT + GLuint id + GLenum value + GLboolean *data + + + void glGetLocalConstantFloatvEXT + GLuint id + GLenum value + GLfloat *data + + + void glGetLocalConstantIntegervEXT + GLuint id + GLenum value + GLint *data + + + void glGetMapAttribParameterfvNV + GLenum target + GLuint index + GLenum pname + GLfloat *params + + + void glGetMapAttribParameterivNV + GLenum target + GLuint index + GLenum pname + GLint *params + + + void glGetMapControlPointsNV + GLenum target + GLuint index + GLenum type + GLsizei ustride + GLsizei vstride + GLboolean packed + void *points + + + void glGetMapParameterfvNV + GLenum target + GLenum pname + GLfloat *params + + + void glGetMapParameterivNV + GLenum target + GLenum pname + GLint *params + + + void glGetMapdv + GLenum target + GLenum query + GLdouble *v + + + + void glGetMapfv + GLenum target + GLenum query + GLfloat *v + + + + void glGetMapiv + GLenum target + GLenum query + GLint *v + + + + void glGetMapxvOES + GLenum target + GLenum query + GLfixed *v + + + void glGetMaterialfv + GLenum face + GLenum pname + GLfloat *params + + + + void glGetMaterialiv + GLenum face + GLenum pname + GLint *params + + + + void glGetMaterialxOES + GLenum face + GLenum pname + GLfixed param + + + void glGetMaterialxv + GLenum face + GLenum pname + GLfixed *params + + + void glGetMaterialxvOES + GLenum face + GLenum pname + GLfixed *params + + + void glGetMemoryObjectDetachedResourcesuivNV + GLuint memory + GLenum pname + GLint first + GLsizei count + GLuint *params + + + void glGetMemoryObjectParameterivEXT + GLuint memoryObject + GLenum pname + GLint *params + + + void glGetMinmax + GLenum target + GLboolean reset + GLenum format + GLenum type + void *values + + + + + void glGetMinmaxEXT + GLenum target + GLboolean reset + GLenum format + GLenum type + void *values + + + + void glGetMinmaxParameterfv + GLenum target + GLenum pname + GLfloat *params + + + + void glGetMinmaxParameterfvEXT + GLenum target + GLenum pname + GLfloat *params + + + + void glGetMinmaxParameteriv + GLenum target + GLenum pname + GLint *params + + + + void glGetMinmaxParameterivEXT + GLenum target + GLenum pname + GLint *params + + + + void glGetMultiTexEnvfvEXT + GLenum texunit + GLenum target + GLenum pname + GLfloat *params + + + void glGetMultiTexEnvivEXT + GLenum texunit + GLenum target + GLenum pname + GLint *params + + + void glGetMultiTexGendvEXT + GLenum texunit + GLenum coord + GLenum pname + GLdouble *params + + + void glGetMultiTexGenfvEXT + GLenum texunit + GLenum coord + GLenum pname + GLfloat *params + + + void glGetMultiTexGenivEXT + GLenum texunit + GLenum coord + GLenum pname + GLint *params + + + void glGetMultiTexImageEXT + GLenum texunit + GLenum target + GLint level + GLenum format + GLenum type + void *pixels + + + void glGetMultiTexLevelParameterfvEXT + GLenum texunit + GLenum target + GLint level + GLenum pname + GLfloat *params + + + void glGetMultiTexLevelParameterivEXT + GLenum texunit + GLenum target + GLint level + GLenum pname + GLint *params + + + void glGetMultiTexParameterIivEXT + GLenum texunit + GLenum target + GLenum pname + GLint *params + + + void glGetMultiTexParameterIuivEXT + GLenum texunit + GLenum target + GLenum pname + GLuint *params + + + void glGetMultiTexParameterfvEXT + GLenum texunit + GLenum target + GLenum pname + GLfloat *params + + + void glGetMultiTexParameterivEXT + GLenum texunit + GLenum target + GLenum pname + GLint *params + + + void glGetMultisamplefv + GLenum pname + GLuint index + GLfloat *val + + + void glGetMultisamplefvNV + GLenum pname + GLuint index + GLfloat *val + + + + void glGetNamedBufferParameteri64v + GLuint buffer + GLenum pname + GLint64 *params + + + void glGetNamedBufferParameteriv + GLuint buffer + GLenum pname + GLint *params + + + void glGetNamedBufferParameterivEXT + GLuint buffer + GLenum pname + GLint *params + + + void glGetNamedBufferParameterui64vNV + GLuint buffer + GLenum pname + GLuint64EXT *params + + + void glGetNamedBufferPointerv + GLuint buffer + GLenum pname + void **params + + + void glGetNamedBufferPointervEXT + GLuint buffer + GLenum pname + void **params + + + void glGetNamedBufferSubData + GLuint buffer + GLintptr offset + GLsizeiptr size + void *data + + + void glGetNamedBufferSubDataEXT + GLuint buffer + GLintptr offset + GLsizeiptr size + void *data + + + void glGetNamedFramebufferParameterfvAMD + GLuint framebuffer + GLenum pname + GLuint numsamples + GLuint pixelindex + GLsizei size + GLfloat *values + + + void glGetNamedFramebufferAttachmentParameteriv + GLuint framebuffer + GLenum attachment + GLenum pname + GLint *params + + + void glGetNamedFramebufferAttachmentParameterivEXT + GLuint framebuffer + GLenum attachment + GLenum pname + GLint *params + + + void glGetNamedFramebufferParameteriv + GLuint framebuffer + GLenum pname + GLint *param + + + void glGetNamedFramebufferParameterivEXT + GLuint framebuffer + GLenum pname + GLint *params + + + void glGetNamedProgramLocalParameterIivEXT + GLuint program + GLenum target + GLuint index + GLint *params + + + void glGetNamedProgramLocalParameterIuivEXT + GLuint program + GLenum target + GLuint index + GLuint *params + + + void glGetNamedProgramLocalParameterdvEXT + GLuint program + GLenum target + GLuint index + GLdouble *params + + + void glGetNamedProgramLocalParameterfvEXT + GLuint program + GLenum target + GLuint index + GLfloat *params + + + void glGetNamedProgramStringEXT + GLuint program + GLenum target + GLenum pname + void *string + + + void glGetNamedProgramivEXT + GLuint program + GLenum target + GLenum pname + GLint *params + + + void glGetNamedRenderbufferParameteriv + GLuint renderbuffer + GLenum pname + GLint *params + + + void glGetNamedRenderbufferParameterivEXT + GLuint renderbuffer + GLenum pname + GLint *params + + + void glGetNamedStringARB + GLint namelen + const GLchar *name + GLsizei bufSize + GLint *stringlen + GLchar *string + + + void glGetNamedStringivARB + GLint namelen + const GLchar *name + GLenum pname + GLint *params + + + void glGetNextPerfQueryIdINTEL + GLuint queryId + GLuint *nextQueryId + + + void glGetObjectBufferfvATI + GLuint buffer + GLenum pname + GLfloat *params + + + void glGetObjectBufferivATI + GLuint buffer + GLenum pname + GLint *params + + + void glGetObjectLabel + GLenum identifier + GLuint name + GLsizei bufSize + GLsizei *length + GLchar *label + + + void glGetObjectLabelEXT + GLenum type + GLuint object + GLsizei bufSize + GLsizei *length + GLchar *label + + + void glGetObjectLabelKHR + GLenum identifier + GLuint name + GLsizei bufSize + GLsizei *length + GLchar *label + + + + void glGetObjectParameterfvARB + GLhandleARB obj + GLenum pname + GLfloat *params + + + void glGetObjectParameterivAPPLE + GLenum objectType + GLuint name + GLenum pname + GLint *params + + + void glGetObjectParameterivARB + GLhandleARB obj + GLenum pname + GLint *params + + + void glGetObjectPtrLabel + const void *ptr + GLsizei bufSize + GLsizei *length + GLchar *label + + + void glGetObjectPtrLabelKHR + const void *ptr + GLsizei bufSize + GLsizei *length + GLchar *label + + + + void glGetOcclusionQueryivNV + GLuint id + GLenum pname + GLint *params + + + void glGetOcclusionQueryuivNV + GLuint id + GLenum pname + GLuint *params + + + void glGetPathColorGenfvNV + GLenum color + GLenum pname + GLfloat *value + + + void glGetPathColorGenivNV + GLenum color + GLenum pname + GLint *value + + + void glGetPathCommandsNV + GLuint path + GLubyte *commands + + + void glGetPathCoordsNV + GLuint path + GLfloat *coords + + + void glGetPathDashArrayNV + GLuint path + GLfloat *dashArray + + + GLfloat glGetPathLengthNV + GLuint path + GLsizei startSegment + GLsizei numSegments + + + void glGetPathMetricRangeNV + GLbitfield metricQueryMask + GLuint firstPathName + GLsizei numPaths + GLsizei stride + GLfloat *metrics + + + void glGetPathMetricsNV + GLbitfield metricQueryMask + GLsizei numPaths + GLenum pathNameType + const void *paths + GLuint pathBase + GLsizei stride + GLfloat *metrics + + + void glGetPathParameterfvNV + GLuint path + GLenum pname + GLfloat *value + + + void glGetPathParameterivNV + GLuint path + GLenum pname + GLint *value + + + void glGetPathSpacingNV + GLenum pathListMode + GLsizei numPaths + GLenum pathNameType + const void *paths + GLuint pathBase + GLfloat advanceScale + GLfloat kerningScale + GLenum transformType + GLfloat *returnedSpacing + + + void glGetPathTexGenfvNV + GLenum texCoordSet + GLenum pname + GLfloat *value + + + void glGetPathTexGenivNV + GLenum texCoordSet + GLenum pname + GLint *value + + + void glGetPerfCounterInfoINTEL + GLuint queryId + GLuint counterId + GLuint counterNameLength + GLchar *counterName + GLuint counterDescLength + GLchar *counterDesc + GLuint *counterOffset + GLuint *counterDataSize + GLuint *counterTypeEnum + GLuint *counterDataTypeEnum + GLuint64 *rawCounterMaxValue + + + void glGetPerfMonitorCounterDataAMD + GLuint monitor + GLenum pname + GLsizei dataSize + GLuint *data + GLint *bytesWritten + + + void glGetPerfMonitorCounterInfoAMD + GLuint group + GLuint counter + GLenum pname + void *data + + + void glGetPerfMonitorCounterStringAMD + GLuint group + GLuint counter + GLsizei bufSize + GLsizei *length + GLchar *counterString + + + void glGetPerfMonitorCountersAMD + GLuint group + GLint *numCounters + GLint *maxActiveCounters + GLsizei counterSize + GLuint *counters + + + void glGetPerfMonitorGroupStringAMD + GLuint group + GLsizei bufSize + GLsizei *length + GLchar *groupString + + + void glGetPerfMonitorGroupsAMD + GLint *numGroups + GLsizei groupsSize + GLuint *groups + + + void glGetPerfQueryDataINTEL + GLuint queryHandle + GLuint flags + GLsizei dataSize + void *data + GLuint *bytesWritten + + + void glGetPerfQueryIdByNameINTEL + GLchar *queryName + GLuint *queryId + + + void glGetPerfQueryInfoINTEL + GLuint queryId + GLuint queryNameLength + GLchar *queryName + GLuint *dataSize + GLuint *noCounters + GLuint *noInstances + GLuint *capsMask + + + void glGetPixelMapfv + GLenum map + GLfloat *values + + + + + void glGetPixelMapuiv + GLenum map + GLuint *values + + + + + void glGetPixelMapusv + GLenum map + GLushort *values + + + + + void glGetPixelMapxv + GLenum map + GLint size + GLfixed *values + + + void glGetPixelTexGenParameterfvSGIS + GLenum pname + GLfloat *params + + + void glGetPixelTexGenParameterivSGIS + GLenum pname + GLint *params + + + void glGetPixelTransformParameterfvEXT + GLenum target + GLenum pname + GLfloat *params + + + + void glGetPixelTransformParameterivEXT + GLenum target + GLenum pname + GLint *params + + + + void glGetPointerIndexedvEXT + GLenum target + GLuint index + void **data + + + void glGetPointeri_vEXT + GLenum pname + GLuint index + void **params + + + void glGetPointerv + GLenum pname + void **params + + + + void glGetPointervEXT + GLenum pname + void **params + + + + void glGetPointervKHR + GLenum pname + void **params + + + + void glGetPolygonStipple + GLubyte *mask + + + + + void glGetProgramBinary + GLuint program + GLsizei bufSize + GLsizei *length + GLenum *binaryFormat + void *binary + + + void glGetProgramBinaryOES + GLuint program + GLsizei bufSize + GLsizei *length + GLenum *binaryFormat + void *binary + + + + void glGetProgramEnvParameterIivNV + GLenum target + GLuint index + GLint *params + + + void glGetProgramEnvParameterIuivNV + GLenum target + GLuint index + GLuint *params + + + void glGetProgramEnvParameterdvARB + GLenum target + GLuint index + GLdouble *params + + + void glGetProgramEnvParameterfvARB + GLenum target + GLuint index + GLfloat *params + + + void glGetProgramInfoLog + GLuint program + GLsizei bufSize + GLsizei *length + GLchar *infoLog + + + + void glGetProgramInterfaceiv + GLuint program + GLenum programInterface + GLenum pname + GLint *params + + + void glGetProgramLocalParameterIivNV + GLenum target + GLuint index + GLint *params + + + void glGetProgramLocalParameterIuivNV + GLenum target + GLuint index + GLuint *params + + + void glGetProgramLocalParameterdvARB + GLenum target + GLuint index + GLdouble *params + + + void glGetProgramLocalParameterfvARB + GLenum target + GLuint index + GLfloat *params + + + void glGetProgramNamedParameterdvNV + GLuint id + GLsizei len + const GLubyte *name + GLdouble *params + + + + void glGetProgramNamedParameterfvNV + GLuint id + GLsizei len + const GLubyte *name + GLfloat *params + + + + void glGetProgramParameterdvNV + GLenum target + GLuint index + GLenum pname + GLdouble *params + + + + void glGetProgramParameterfvNV + GLenum target + GLuint index + GLenum pname + GLfloat *params + + + + void glGetProgramPipelineInfoLog + GLuint pipeline + GLsizei bufSize + GLsizei *length + GLchar *infoLog + + + void glGetProgramPipelineInfoLogEXT + GLuint pipeline + GLsizei bufSize + GLsizei *length + GLchar *infoLog + + + void glGetProgramPipelineiv + GLuint pipeline + GLenum pname + GLint *params + + + void glGetProgramPipelineivEXT + GLuint pipeline + GLenum pname + GLint *params + + + GLuint glGetProgramResourceIndex + GLuint program + GLenum programInterface + const GLchar *name + + + GLint glGetProgramResourceLocation + GLuint program + GLenum programInterface + const GLchar *name + + + GLint glGetProgramResourceLocationIndex + GLuint program + GLenum programInterface + const GLchar *name + + + GLint glGetProgramResourceLocationIndexEXT + GLuint program + GLenum programInterface + const GLchar *name + + + void glGetProgramResourceName + GLuint program + GLenum programInterface + GLuint index + GLsizei bufSize + GLsizei *length + GLchar *name + + + void glGetProgramResourcefvNV + GLuint program + GLenum programInterface + GLuint index + GLsizei propCount + const GLenum *props + GLsizei bufSize + GLsizei *length + GLfloat *params + + + void glGetProgramResourceiv + GLuint program + GLenum programInterface + GLuint index + GLsizei propCount + const GLenum *props + GLsizei bufSize + GLsizei *length + GLint *params + + + void glGetProgramStageiv + GLuint program + GLenum shadertype + GLenum pname + GLint *values + + + void glGetProgramStringARB + GLenum target + GLenum pname + void *string + + + void glGetProgramStringNV + GLuint id + GLenum pname + GLubyte *program + + + + void glGetProgramSubroutineParameteruivNV + GLenum target + GLuint index + GLuint *param + + + void glGetProgramiv + GLuint program + GLenum pname + GLint *params + + + + void glGetProgramivARB + GLenum target + GLenum pname + GLint *params + + + void glGetProgramivNV + GLuint id + GLenum pname + GLint *params + + + + void glGetQueryBufferObjecti64v + GLuint id + GLuint buffer + GLenum pname + GLintptr offset + + + void glGetQueryBufferObjectiv + GLuint id + GLuint buffer + GLenum pname + GLintptr offset + + + void glGetQueryBufferObjectui64v + GLuint id + GLuint buffer + GLenum pname + GLintptr offset + + + void glGetQueryBufferObjectuiv + GLuint id + GLuint buffer + GLenum pname + GLintptr offset + + + void glGetQueryIndexediv + GLenum target + GLuint index + GLenum pname + GLint *params + + + void glGetQueryObjecti64v + GLuint id + GLenum pname + GLint64 *params + + + void glGetQueryObjecti64vEXT + GLuint id + GLenum pname + GLint64 *params + + + + + void glGetQueryObjectiv + GLuint id + GLenum pname + GLint *params + + + + void glGetQueryObjectivARB + GLuint id + GLenum pname + GLint *params + + + + void glGetQueryObjectivEXT + GLuint id + GLenum pname + GLint *params + + + + void glGetQueryObjectui64v + GLuint id + GLenum pname + GLuint64 *params + + + void glGetQueryObjectui64vEXT + GLuint id + GLenum pname + GLuint64 *params + + + + + void glGetQueryObjectuiv + GLuint id + GLenum pname + GLuint *params + + + + void glGetQueryObjectuivARB + GLuint id + GLenum pname + GLuint *params + + + + void glGetQueryObjectuivEXT + GLuint id + GLenum pname + GLuint *params + + + void glGetQueryiv + GLenum target + GLenum pname + GLint *params + + + + void glGetQueryivARB + GLenum target + GLenum pname + GLint *params + + + + void glGetQueryivEXT + GLenum target + GLenum pname + GLint *params + + + void glGetRenderbufferParameteriv + GLenum target + GLenum pname + GLint *params + + + + void glGetRenderbufferParameterivEXT + GLenum target + GLenum pname + GLint *params + + + + + void glGetRenderbufferParameterivOES + GLenum target + GLenum pname + GLint *params + + + void glGetSamplerParameterIiv + GLuint sampler + GLenum pname + GLint *params + + + void glGetSamplerParameterIivEXT + GLuint sampler + GLenum pname + GLint *params + + + + void glGetSamplerParameterIivOES + GLuint sampler + GLenum pname + GLint *params + + + + void glGetSamplerParameterIuiv + GLuint sampler + GLenum pname + GLuint *params + + + void glGetSamplerParameterIuivEXT + GLuint sampler + GLenum pname + GLuint *params + + + + void glGetSamplerParameterIuivOES + GLuint sampler + GLenum pname + GLuint *params + + + + void glGetSamplerParameterfv + GLuint sampler + GLenum pname + GLfloat *params + + + void glGetSamplerParameteriv + GLuint sampler + GLenum pname + GLint *params + + + void glGetSemaphoreParameterui64vEXT + GLuint semaphore + GLenum pname + GLuint64 *params + + + void glGetSeparableFilter + GLenum target + GLenum format + GLenum type + void *row + void *column + void *span + + + + + void glGetSeparableFilterEXT + GLenum target + GLenum format + GLenum type + void *row + void *column + void *span + + + + void glGetShaderInfoLog + GLuint shader + GLsizei bufSize + GLsizei *length + GLchar *infoLog + + + + void glGetShaderPrecisionFormat + GLenum shadertype + GLenum precisiontype + GLint *range + GLint *precision + + + void glGetShaderSource + GLuint shader + GLsizei bufSize + GLsizei *length + GLchar *source + + + void glGetShaderSourceARB + GLhandleARB obj + GLsizei maxLength + GLsizei *length + GLcharARB *source + + + + void glGetShaderiv + GLuint shader + GLenum pname + GLint *params + + + + void glGetShadingRateImagePaletteNV + GLuint viewport + GLuint entry + GLenum *rate + + + void glGetShadingRateSampleLocationivNV + GLenum rate + GLuint samples + GLuint index + GLint *location + + + void glGetSharpenTexFuncSGIS + GLenum target + GLfloat *points + + + + GLushort glGetStageIndexNV + GLenum shadertype + + + const GLubyte *glGetString + GLenum name + + + + const GLubyte *glGetStringi + GLenum name + GLuint index + + + + GLuint glGetSubroutineIndex + GLuint program + GLenum shadertype + const GLchar *name + + + GLint glGetSubroutineUniformLocation + GLuint program + GLenum shadertype + const GLchar *name + + + void glGetSynciv + GLsync sync + GLenum pname + GLsizei bufSize + GLsizei *length + GLint *values + + + void glGetSyncivAPPLE + GLsync sync + GLenum pname + GLsizei bufSize + GLsizei *length + GLint *values + + + + void glGetTexBumpParameterfvATI + GLenum pname + GLfloat *param + + + void glGetTexBumpParameterivATI + GLenum pname + GLint *param + + + void glGetTexEnvfv + GLenum target + GLenum pname + GLfloat *params + + + + void glGetTexEnviv + GLenum target + GLenum pname + GLint *params + + + + void glGetTexEnvxv + GLenum target + GLenum pname + GLfixed *params + + + void glGetTexEnvxvOES + GLenum target + GLenum pname + GLfixed *params + + + void glGetTexFilterFuncSGIS + GLenum target + GLenum filter + GLfloat *weights + + + + void glGetTexGendv + GLenum coord + GLenum pname + GLdouble *params + + + + void glGetTexGenfv + GLenum coord + GLenum pname + GLfloat *params + + + + void glGetTexGenfvOES + GLenum coord + GLenum pname + GLfloat *params + + + void glGetTexGeniv + GLenum coord + GLenum pname + GLint *params + + + + void glGetTexGenivOES + GLenum coord + GLenum pname + GLint *params + + + void glGetTexGenxvOES + GLenum coord + GLenum pname + GLfixed *params + + + void glGetTexImage + GLenum target + GLint level + GLenum format + GLenum type + void *pixels + + + + + void glGetTexLevelParameterfv + GLenum target + GLint level + GLenum pname + GLfloat *params + + + + void glGetTexLevelParameteriv + GLenum target + GLint level + GLenum pname + GLint *params + + + + void glGetTexLevelParameterxvOES + GLenum target + GLint level + GLenum pname + GLfixed *params + + + void glGetTexParameterIiv + GLenum target + GLenum pname + GLint *params + + + + void glGetTexParameterIivEXT + GLenum target + GLenum pname + GLint *params + + + + void glGetTexParameterIivOES + GLenum target + GLenum pname + GLint *params + + + + void glGetTexParameterIuiv + GLenum target + GLenum pname + GLuint *params + + + + void glGetTexParameterIuivEXT + GLenum target + GLenum pname + GLuint *params + + + + void glGetTexParameterIuivOES + GLenum target + GLenum pname + GLuint *params + + + + void glGetTexParameterPointervAPPLE + GLenum target + GLenum pname + void **params + + + void glGetTexParameterfv + GLenum target + GLenum pname + GLfloat *params + + + + void glGetTexParameteriv + GLenum target + GLenum pname + GLint *params + + + + void glGetTexParameterxv + GLenum target + GLenum pname + GLfixed *params + + + void glGetTexParameterxvOES + GLenum target + GLenum pname + GLfixed *params + + + GLuint64 glGetTextureHandleARB + GLuint texture + + + GLuint64 glGetTextureHandleIMG + GLuint texture + + + + GLuint64 glGetTextureHandleNV + GLuint texture + + + void glGetTextureImage + GLuint texture + GLint level + GLenum format + GLenum type + GLsizei bufSize + void *pixels + + + void glGetTextureImageEXT + GLuint texture + GLenum target + GLint level + GLenum format + GLenum type + void *pixels + + + void glGetTextureLevelParameterfv + GLuint texture + GLint level + GLenum pname + GLfloat *params + + + void glGetTextureLevelParameterfvEXT + GLuint texture + GLenum target + GLint level + GLenum pname + GLfloat *params + + + void glGetTextureLevelParameteriv + GLuint texture + GLint level + GLenum pname + GLint *params + + + void glGetTextureLevelParameterivEXT + GLuint texture + GLenum target + GLint level + GLenum pname + GLint *params + + + void glGetTextureParameterIiv + GLuint texture + GLenum pname + GLint *params + + + void glGetTextureParameterIivEXT + GLuint texture + GLenum target + GLenum pname + GLint *params + + + void glGetTextureParameterIuiv + GLuint texture + GLenum pname + GLuint *params + + + void glGetTextureParameterIuivEXT + GLuint texture + GLenum target + GLenum pname + GLuint *params + + + void glGetTextureParameterfv + GLuint texture + GLenum pname + GLfloat *params + + + void glGetTextureParameterfvEXT + GLuint texture + GLenum target + GLenum pname + GLfloat *params + + + void glGetTextureParameteriv + GLuint texture + GLenum pname + GLint *params + + + void glGetTextureParameterivEXT + GLuint texture + GLenum target + GLenum pname + GLint *params + + + GLuint64 glGetTextureSamplerHandleARB + GLuint texture + GLuint sampler + + + GLuint64 glGetTextureSamplerHandleIMG + GLuint texture + GLuint sampler + + + + GLuint64 glGetTextureSamplerHandleNV + GLuint texture + GLuint sampler + + + void glGetTextureSubImage + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + GLsizei bufSize + void *pixels + + + void glGetTrackMatrixivNV + GLenum target + GLuint address + GLenum pname + GLint *params + + + + void glGetTransformFeedbackVarying + GLuint program + GLuint index + GLsizei bufSize + GLsizei *length + GLsizei *size + GLenum *type + GLchar *name + + + + void glGetTransformFeedbackVaryingEXT + GLuint program + GLuint index + GLsizei bufSize + GLsizei *length + GLsizei *size + GLenum *type + GLchar *name + + + + void glGetTransformFeedbackVaryingNV + GLuint program + GLuint index + GLint *location + + + void glGetTransformFeedbacki64_v + GLuint xfb + GLenum pname + GLuint index + GLint64 *param + + + void glGetTransformFeedbacki_v + GLuint xfb + GLenum pname + GLuint index + GLint *param + + + void glGetTransformFeedbackiv + GLuint xfb + GLenum pname + GLint *param + + + void glGetTranslatedShaderSourceANGLE + GLuint shader + GLsizei bufsize + GLsizei *length + GLchar *source + + + GLuint glGetUniformBlockIndex + GLuint program + const GLchar *uniformBlockName + + + + GLint glGetUniformBufferSizeEXT + GLuint program + GLint location + + + void glGetUniformIndices + GLuint program + GLsizei uniformCount + const GLchar *const*uniformNames + GLuint *uniformIndices + + + + GLint glGetUniformLocation + GLuint program + const GLchar *name + + + GLint glGetUniformLocationARB + GLhandleARB programObj + const GLcharARB *name + + + + GLintptr glGetUniformOffsetEXT + GLuint program + GLint location + + + void glGetUniformSubroutineuiv + GLenum shadertype + GLint location + GLuint *params + + + void glGetUniformdv + GLuint program + GLint location + GLdouble *params + + + void glGetUniformfv + GLuint program + GLint location + GLfloat *params + + + void glGetUniformfvARB + GLhandleARB programObj + GLint location + GLfloat *params + + + + void glGetUniformi64vARB + GLuint program + GLint location + GLint64 *params + + + void glGetUniformi64vNV + GLuint program + GLint location + GLint64EXT *params + + + void glGetUniformiv + GLuint program + GLint location + GLint *params + + + void glGetUniformivARB + GLhandleARB programObj + GLint location + GLint *params + + + + void glGetUniformui64vARB + GLuint program + GLint location + GLuint64 *params + + + void glGetUniformui64vNV + GLuint program + GLint location + GLuint64EXT *params + + + void glGetUniformuiv + GLuint program + GLint location + GLuint *params + + + void glGetUniformuivEXT + GLuint program + GLint location + GLuint *params + + + + void glGetUnsignedBytevEXT + GLenum pname + GLubyte *data + + + void glGetUnsignedBytei_vEXT + GLenum target + GLuint index + GLubyte *data + + + void glGetVariantArrayObjectfvATI + GLuint id + GLenum pname + GLfloat *params + + + void glGetVariantArrayObjectivATI + GLuint id + GLenum pname + GLint *params + + + void glGetVariantBooleanvEXT + GLuint id + GLenum value + GLboolean *data + + + void glGetVariantFloatvEXT + GLuint id + GLenum value + GLfloat *data + + + void glGetVariantIntegervEXT + GLuint id + GLenum value + GLint *data + + + void glGetVariantPointervEXT + GLuint id + GLenum value + void **data + + + GLint glGetVaryingLocationNV + GLuint program + const GLchar *name + + + void glGetVertexArrayIndexed64iv + GLuint vaobj + GLuint index + GLenum pname + GLint64 *param + + + void glGetVertexArrayIndexediv + GLuint vaobj + GLuint index + GLenum pname + GLint *param + + + void glGetVertexArrayIntegeri_vEXT + GLuint vaobj + GLuint index + GLenum pname + GLint *param + + + void glGetVertexArrayIntegervEXT + GLuint vaobj + GLenum pname + GLint *param + + + void glGetVertexArrayPointeri_vEXT + GLuint vaobj + GLuint index + GLenum pname + void **param + + + void glGetVertexArrayPointervEXT + GLuint vaobj + GLenum pname + void **param + + + void glGetVertexArrayiv + GLuint vaobj + GLenum pname + GLint *param + + + void glGetVertexAttribArrayObjectfvATI + GLuint index + GLenum pname + GLfloat *params + + + void glGetVertexAttribArrayObjectivATI + GLuint index + GLenum pname + GLint *params + + + void glGetVertexAttribIiv + GLuint index + GLenum pname + GLint *params + + + void glGetVertexAttribIivEXT + GLuint index + GLenum pname + GLint *params + + + + void glGetVertexAttribIuiv + GLuint index + GLenum pname + GLuint *params + + + void glGetVertexAttribIuivEXT + GLuint index + GLenum pname + GLuint *params + + + + void glGetVertexAttribLdv + GLuint index + GLenum pname + GLdouble *params + + + void glGetVertexAttribLdvEXT + GLuint index + GLenum pname + GLdouble *params + + + + void glGetVertexAttribLi64vNV + GLuint index + GLenum pname + GLint64EXT *params + + + void glGetVertexAttribLui64vARB + GLuint index + GLenum pname + GLuint64EXT *params + + + void glGetVertexAttribLui64vNV + GLuint index + GLenum pname + GLuint64EXT *params + + + void glGetVertexAttribPointerv + GLuint index + GLenum pname + void **pointer + + + + void glGetVertexAttribPointervARB + GLuint index + GLenum pname + void **pointer + + + + void glGetVertexAttribPointervNV + GLuint index + GLenum pname + void **pointer + + + + void glGetVertexAttribdv + GLuint index + GLenum pname + GLdouble *params + + + + void glGetVertexAttribdvARB + GLuint index + GLenum pname + GLdouble *params + + + + + void glGetVertexAttribdvNV + GLuint index + GLenum pname + GLdouble *params + + + + + void glGetVertexAttribfv + GLuint index + GLenum pname + GLfloat *params + + + + void glGetVertexAttribfvARB + GLuint index + GLenum pname + GLfloat *params + + + + + void glGetVertexAttribfvNV + GLuint index + GLenum pname + GLfloat *params + + + + + void glGetVertexAttribiv + GLuint index + GLenum pname + GLint *params + + + + void glGetVertexAttribivARB + GLuint index + GLenum pname + GLint *params + + + + + void glGetVertexAttribivNV + GLuint index + GLenum pname + GLint *params + + + + + void glGetVideoCaptureStreamdvNV + GLuint video_capture_slot + GLuint stream + GLenum pname + GLdouble *params + + + void glGetVideoCaptureStreamfvNV + GLuint video_capture_slot + GLuint stream + GLenum pname + GLfloat *params + + + void glGetVideoCaptureStreamivNV + GLuint video_capture_slot + GLuint stream + GLenum pname + GLint *params + + + void glGetVideoCaptureivNV + GLuint video_capture_slot + GLenum pname + GLint *params + + + void glGetVideoi64vNV + GLuint video_slot + GLenum pname + GLint64EXT *params + + + void glGetVideoivNV + GLuint video_slot + GLenum pname + GLint *params + + + void glGetVideoui64vNV + GLuint video_slot + GLenum pname + GLuint64EXT *params + + + void glGetVideouivNV + GLuint video_slot + GLenum pname + GLuint *params + + + void glGetnColorTable + GLenum target + GLenum format + GLenum type + GLsizei bufSize + void *table + + + void glGetnColorTableARB + GLenum target + GLenum format + GLenum type + GLsizei bufSize + void *table + + + void glGetnCompressedTexImage + GLenum target + GLint lod + GLsizei bufSize + void *pixels + + + void glGetnCompressedTexImageARB + GLenum target + GLint lod + GLsizei bufSize + void *img + + + void glGetnConvolutionFilter + GLenum target + GLenum format + GLenum type + GLsizei bufSize + void *image + + + void glGetnConvolutionFilterARB + GLenum target + GLenum format + GLenum type + GLsizei bufSize + void *image + + + void glGetnHistogram + GLenum target + GLboolean reset + GLenum format + GLenum type + GLsizei bufSize + void *values + + + void glGetnHistogramARB + GLenum target + GLboolean reset + GLenum format + GLenum type + GLsizei bufSize + void *values + + + void glGetnMapdv + GLenum target + GLenum query + GLsizei bufSize + GLdouble *v + + + void glGetnMapdvARB + GLenum target + GLenum query + GLsizei bufSize + GLdouble *v + + + void glGetnMapfv + GLenum target + GLenum query + GLsizei bufSize + GLfloat *v + + + void glGetnMapfvARB + GLenum target + GLenum query + GLsizei bufSize + GLfloat *v + + + void glGetnMapiv + GLenum target + GLenum query + GLsizei bufSize + GLint *v + + + void glGetnMapivARB + GLenum target + GLenum query + GLsizei bufSize + GLint *v + + + void glGetnMinmax + GLenum target + GLboolean reset + GLenum format + GLenum type + GLsizei bufSize + void *values + + + void glGetnMinmaxARB + GLenum target + GLboolean reset + GLenum format + GLenum type + GLsizei bufSize + void *values + + + void glGetnPixelMapfv + GLenum map + GLsizei bufSize + GLfloat *values + + + void glGetnPixelMapfvARB + GLenum map + GLsizei bufSize + GLfloat *values + + + void glGetnPixelMapuiv + GLenum map + GLsizei bufSize + GLuint *values + + + void glGetnPixelMapuivARB + GLenum map + GLsizei bufSize + GLuint *values + + + void glGetnPixelMapusv + GLenum map + GLsizei bufSize + GLushort *values + + + void glGetnPixelMapusvARB + GLenum map + GLsizei bufSize + GLushort *values + + + void glGetnPolygonStipple + GLsizei bufSize + GLubyte *pattern + + + void glGetnPolygonStippleARB + GLsizei bufSize + GLubyte *pattern + + + void glGetnSeparableFilter + GLenum target + GLenum format + GLenum type + GLsizei rowBufSize + void *row + GLsizei columnBufSize + void *column + void *span + + + void glGetnSeparableFilterARB + GLenum target + GLenum format + GLenum type + GLsizei rowBufSize + void *row + GLsizei columnBufSize + void *column + void *span + + + void glGetnTexImage + GLenum target + GLint level + GLenum format + GLenum type + GLsizei bufSize + void *pixels + + + void glGetnTexImageARB + GLenum target + GLint level + GLenum format + GLenum type + GLsizei bufSize + void *img + + + void glGetnUniformdv + GLuint program + GLint location + GLsizei bufSize + GLdouble *params + + + void glGetnUniformdvARB + GLuint program + GLint location + GLsizei bufSize + GLdouble *params + + + void glGetnUniformfv + GLuint program + GLint location + GLsizei bufSize + GLfloat *params + + + void glGetnUniformfvARB + GLuint program + GLint location + GLsizei bufSize + GLfloat *params + + + void glGetnUniformfvEXT + GLuint program + GLint location + GLsizei bufSize + GLfloat *params + + + + void glGetnUniformfvKHR + GLuint program + GLint location + GLsizei bufSize + GLfloat *params + + + + void glGetnUniformi64vARB + GLuint program + GLint location + GLsizei bufSize + GLint64 *params + + + void glGetnUniformiv + GLuint program + GLint location + GLsizei bufSize + GLint *params + + + void glGetnUniformivARB + GLuint program + GLint location + GLsizei bufSize + GLint *params + + + void glGetnUniformivEXT + GLuint program + GLint location + GLsizei bufSize + GLint *params + + + + void glGetnUniformivKHR + GLuint program + GLint location + GLsizei bufSize + GLint *params + + + + void glGetnUniformui64vARB + GLuint program + GLint location + GLsizei bufSize + GLuint64 *params + + + void glGetnUniformuiv + GLuint program + GLint location + GLsizei bufSize + GLuint *params + + + void glGetnUniformuivARB + GLuint program + GLint location + GLsizei bufSize + GLuint *params + + + void glGetnUniformuivKHR + GLuint program + GLint location + GLsizei bufSize + GLuint *params + + + + void glGlobalAlphaFactorbSUN + GLbyte factor + + + void glGlobalAlphaFactordSUN + GLdouble factor + + + void glGlobalAlphaFactorfSUN + GLfloat factor + + + void glGlobalAlphaFactoriSUN + GLint factor + + + void glGlobalAlphaFactorsSUN + GLshort factor + + + void glGlobalAlphaFactorubSUN + GLubyte factor + + + void glGlobalAlphaFactoruiSUN + GLuint factor + + + void glGlobalAlphaFactorusSUN + GLushort factor + + + void glHint + GLenum target + GLenum mode + + + + void glHintPGI + GLenum target + GLint mode + + + void glHistogram + GLenum target + GLsizei width + GLenum internalformat + GLboolean sink + + + + void glHistogramEXT + GLenum target + GLsizei width + GLenum internalformat + GLboolean sink + + + + + void glIglooInterfaceSGIX + GLenum pname + const void *params + + + + void glImageTransformParameterfHP + GLenum target + GLenum pname + GLfloat param + + + void glImageTransformParameterfvHP + GLenum target + GLenum pname + const GLfloat *params + + + void glImageTransformParameteriHP + GLenum target + GLenum pname + GLint param + + + void glImageTransformParameterivHP + GLenum target + GLenum pname + const GLint *params + + + void glImportMemoryFdEXT + GLuint memory + GLuint64 size + GLenum handleType + GLint fd + + + void glImportMemoryWin32HandleEXT + GLuint memory + GLuint64 size + GLenum handleType + void *handle + + + void glImportMemoryWin32NameEXT + GLuint memory + GLuint64 size + GLenum handleType + const void *name + + + void glImportSemaphoreFdEXT + GLuint semaphore + GLenum handleType + GLint fd + + + void glImportSemaphoreWin32HandleEXT + GLuint semaphore + GLenum handleType + void *handle + + + void glImportSemaphoreWin32NameEXT + GLuint semaphore + GLenum handleType + const void *name + + + GLsync glImportSyncEXT + GLenum external_sync_type + GLintptr external_sync + GLbitfield flags + + + void glIndexFormatNV + GLenum type + GLsizei stride + + + void glIndexFuncEXT + GLenum func + GLclampf ref + + + void glIndexMask + GLuint mask + + + + void glIndexMaterialEXT + GLenum face + GLenum mode + + + void glIndexPointer + GLenum type + GLsizei stride + const void *pointer + + + void glIndexPointerEXT + GLenum type + GLsizei stride + GLsizei count + const void *pointer + + + void glIndexPointerListIBM + GLenum type + GLint stride + const void **pointer + GLint ptrstride + + + void glIndexd + GLdouble c + + + + void glIndexdv + const GLdouble *c + + + + void glIndexf + GLfloat c + + + + void glIndexfv + const GLfloat *c + + + + void glIndexi + GLint c + + + + void glIndexiv + const GLint *c + + + + void glIndexs + GLshort c + + + + void glIndexsv + const GLshort *c + + + + void glIndexub + GLubyte c + + + + void glIndexubv + const GLubyte *c + + + + void glIndexxOES + GLfixed component + + + void glIndexxvOES + const GLfixed *component + + + void glInitNames + + + + void glInsertComponentEXT + GLuint res + GLuint src + GLuint num + + + void glInsertEventMarkerEXT + GLsizei length + const GLchar *marker + + + void glInstrumentsBufferSGIX + GLsizei size + GLint *buffer + + + + void glInterleavedArrays + GLenum format + GLsizei stride + const void *pointer + + + void glInterpolatePathsNV + GLuint resultPath + GLuint pathA + GLuint pathB + GLfloat weight + + + void glInvalidateBufferData + GLuint buffer + + + void glInvalidateBufferSubData + GLuint buffer + GLintptr offset + GLsizeiptr length + + + void glInvalidateFramebuffer + GLenum target + GLsizei numAttachments + const GLenum *attachments + + + void glInvalidateNamedFramebufferData + GLuint framebuffer + GLsizei numAttachments + const GLenum *attachments + + + void glInvalidateNamedFramebufferSubData + GLuint framebuffer + GLsizei numAttachments + const GLenum *attachments + GLint x + GLint y + GLsizei width + GLsizei height + + + void glInvalidateSubFramebuffer + GLenum target + GLsizei numAttachments + const GLenum *attachments + GLint x + GLint y + GLsizei width + GLsizei height + + + void glInvalidateTexImage + GLuint texture + GLint level + + + void glInvalidateTexSubImage + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + + + GLboolean glIsAsyncMarkerSGIX + GLuint marker + + + GLboolean glIsBuffer + GLuint buffer + + + GLboolean glIsBufferARB + GLuint buffer + + + + GLboolean glIsBufferResidentNV + GLenum target + + + GLboolean glIsCommandListNV + GLuint list + + + GLboolean glIsEnabled + GLenum cap + + + + GLboolean glIsEnabledIndexedEXT + GLenum target + GLuint index + + + + + GLboolean glIsEnabledi + GLenum target + GLuint index + + + GLboolean glIsEnablediEXT + GLenum target + GLuint index + + + + GLboolean glIsEnablediNV + GLenum target + GLuint index + + + + GLboolean glIsEnablediOES + GLenum target + GLuint index + + + + GLboolean glIsFenceAPPLE + GLuint fence + + + GLboolean glIsFenceNV + GLuint fence + + + + GLboolean glIsFramebuffer + GLuint framebuffer + + + + GLboolean glIsFramebufferEXT + GLuint framebuffer + + + + + GLboolean glIsFramebufferOES + GLuint framebuffer + + + GLboolean glIsImageHandleResidentARB + GLuint64 handle + + + GLboolean glIsImageHandleResidentNV + GLuint64 handle + + + GLboolean glIsList + GLuint list + + + + GLboolean glIsMemoryObjectEXT + GLuint memoryObject + + + GLboolean glIsNameAMD + GLenum identifier + GLuint name + + + GLboolean glIsNamedBufferResidentNV + GLuint buffer + + + GLboolean glIsNamedStringARB + GLint namelen + const GLchar *name + + + GLboolean glIsObjectBufferATI + GLuint buffer + + + GLboolean glIsOcclusionQueryNV + GLuint id + + + GLboolean glIsPathNV + GLuint path + + + GLboolean glIsPointInFillPathNV + GLuint path + GLuint mask + GLfloat x + GLfloat y + + + GLboolean glIsPointInStrokePathNV + GLuint path + GLfloat x + GLfloat y + + + GLboolean glIsProgram + GLuint program + + + + GLboolean glIsProgramARB + GLuint program + + + + GLboolean glIsProgramNV + GLuint id + + + + + GLboolean glIsProgramPipeline + GLuint pipeline + + + GLboolean glIsProgramPipelineEXT + GLuint pipeline + + + GLboolean glIsQuery + GLuint id + + + + GLboolean glIsQueryARB + GLuint id + + + + GLboolean glIsQueryEXT + GLuint id + + + GLboolean glIsRenderbuffer + GLuint renderbuffer + + + + GLboolean glIsRenderbufferEXT + GLuint renderbuffer + + + + + GLboolean glIsRenderbufferOES + GLuint renderbuffer + + + GLboolean glIsSemaphoreEXT + GLuint semaphore + + + GLboolean glIsSampler + GLuint sampler + + + GLboolean glIsShader + GLuint shader + + + + GLboolean glIsStateNV + GLuint state + + + GLboolean glIsSync + GLsync sync + + + GLboolean glIsSyncAPPLE + GLsync sync + + + + GLboolean glIsTexture + GLuint texture + + + + GLboolean glIsTextureEXT + GLuint texture + + + + GLboolean glIsTextureHandleResidentARB + GLuint64 handle + + + GLboolean glIsTextureHandleResidentNV + GLuint64 handle + + + GLboolean glIsTransformFeedback + GLuint id + + + GLboolean glIsTransformFeedbackNV + GLuint id + + + + GLboolean glIsVariantEnabledEXT + GLuint id + GLenum cap + + + GLboolean glIsVertexArray + GLuint array + + + + GLboolean glIsVertexArrayAPPLE + GLuint array + + + + GLboolean glIsVertexArrayOES + GLuint array + + + + GLboolean glIsVertexAttribEnabledAPPLE + GLuint index + GLenum pname + + + void glLGPUCopyImageSubDataNVX + GLuint sourceGpu + GLbitfield destinationGpuMask + GLuint srcName + GLenum srcTarget + GLint srcLevel + GLint srcX + GLint srxY + GLint srcZ + GLuint dstName + GLenum dstTarget + GLint dstLevel + GLint dstX + GLint dstY + GLint dstZ + GLsizei width + GLsizei height + GLsizei depth + + + void glLGPUInterlockNVX + + + void glLGPUNamedBufferSubDataNVX + GLbitfield gpuMask + GLuint buffer + GLintptr offset + GLsizeiptr size + const void *data + + + void glLabelObjectEXT + GLenum type + GLuint object + GLsizei length + const GLchar *label + + + void glLightEnviSGIX + GLenum pname + GLint param + + + void glLightModelf + GLenum pname + GLfloat param + + + + void glLightModelfv + GLenum pname + const GLfloat *params + + + + void glLightModeli + GLenum pname + GLint param + + + + void glLightModeliv + GLenum pname + const GLint *params + + + + void glLightModelx + GLenum pname + GLfixed param + + + void glLightModelxOES + GLenum pname + GLfixed param + + + void glLightModelxv + GLenum pname + const GLfixed *param + + + void glLightModelxvOES + GLenum pname + const GLfixed *param + + + void glLightf + GLenum light + GLenum pname + GLfloat param + + + + void glLightfv + GLenum light + GLenum pname + const GLfloat *params + + + + void glLighti + GLenum light + GLenum pname + GLint param + + + + void glLightiv + GLenum light + GLenum pname + const GLint *params + + + + void glLightx + GLenum light + GLenum pname + GLfixed param + + + void glLightxOES + GLenum light + GLenum pname + GLfixed param + + + void glLightxv + GLenum light + GLenum pname + const GLfixed *params + + + void glLightxvOES + GLenum light + GLenum pname + const GLfixed *params + + + void glLineStipple + GLint factor + GLushort pattern + + + + void glLineWidth + GLfloat width + + + + void glLineWidthx + GLfixed width + + + void glLineWidthxOES + GLfixed width + + + void glLinkProgram + GLuint program + + + void glLinkProgramARB + GLhandleARB programObj + + + + void glListBase + GLuint base + + + + void glListDrawCommandsStatesClientNV + GLuint list + GLuint segment + const void **indirects + const GLsizei *sizes + const GLuint *states + const GLuint *fbos + GLuint count + + + void glListParameterfSGIX + GLuint list + GLenum pname + GLfloat param + + + + void glListParameterfvSGIX + GLuint list + GLenum pname + const GLfloat *params + + + + void glListParameteriSGIX + GLuint list + GLenum pname + GLint param + + + + void glListParameterivSGIX + GLuint list + GLenum pname + const GLint *params + + + + void glLoadIdentity + + + + void glLoadIdentityDeformationMapSGIX + GLbitfield mask + + + + void glLoadMatrixd + const GLdouble *m + + + + void glLoadMatrixf + const GLfloat *m + + + + void glLoadMatrixx + const GLfixed *m + + + void glLoadMatrixxOES + const GLfixed *m + + + void glLoadName + GLuint name + + + + void glLoadPaletteFromModelViewMatrixOES + + + void glLoadProgramNV + GLenum target + GLuint id + GLsizei len + const GLubyte *program + + + + void glLoadTransposeMatrixd + const GLdouble *m + + + void glLoadTransposeMatrixdARB + const GLdouble *m + + + + void glLoadTransposeMatrixf + const GLfloat *m + + + void glLoadTransposeMatrixfARB + const GLfloat *m + + + + void glLoadTransposeMatrixxOES + const GLfixed *m + + + void glLockArraysEXT + GLint first + GLsizei count + + + void glLogicOp + GLenum opcode + + + + void glMakeBufferNonResidentNV + GLenum target + + + void glMakeBufferResidentNV + GLenum target + GLenum access + + + void glMakeImageHandleNonResidentARB + GLuint64 handle + + + void glMakeImageHandleNonResidentNV + GLuint64 handle + + + void glMakeImageHandleResidentARB + GLuint64 handle + GLenum access + + + void glMakeImageHandleResidentNV + GLuint64 handle + GLenum access + + + void glMakeNamedBufferNonResidentNV + GLuint buffer + + + void glMakeNamedBufferResidentNV + GLuint buffer + GLenum access + + + void glMakeTextureHandleNonResidentARB + GLuint64 handle + + + void glMakeTextureHandleNonResidentNV + GLuint64 handle + + + void glMakeTextureHandleResidentARB + GLuint64 handle + + + void glMakeTextureHandleResidentNV + GLuint64 handle + + + void glMap1d + GLenum target + GLdouble u1 + GLdouble u2 + GLint stride + GLint order + const GLdouble *points + + + + void glMap1f + GLenum target + GLfloat u1 + GLfloat u2 + GLint stride + GLint order + const GLfloat *points + + + + void glMap1xOES + GLenum target + GLfixed u1 + GLfixed u2 + GLint stride + GLint order + GLfixed points + + + void glMap2d + GLenum target + GLdouble u1 + GLdouble u2 + GLint ustride + GLint uorder + GLdouble v1 + GLdouble v2 + GLint vstride + GLint vorder + const GLdouble *points + + + + void glMap2f + GLenum target + GLfloat u1 + GLfloat u2 + GLint ustride + GLint uorder + GLfloat v1 + GLfloat v2 + GLint vstride + GLint vorder + const GLfloat *points + + + + void glMap2xOES + GLenum target + GLfixed u1 + GLfixed u2 + GLint ustride + GLint uorder + GLfixed v1 + GLfixed v2 + GLint vstride + GLint vorder + GLfixed points + + + void *glMapBuffer + GLenum target + GLenum access + + + void *glMapBufferARB + GLenum target + GLenum access + + + + void *glMapBufferOES + GLenum target + GLenum access + + + + void *glMapBufferRange + GLenum target + GLintptr offset + GLsizeiptr length + GLbitfield access + + + + void *glMapBufferRangeEXT + GLenum target + GLintptr offset + GLsizeiptr length + GLbitfield access + + + + void glMapControlPointsNV + GLenum target + GLuint index + GLenum type + GLsizei ustride + GLsizei vstride + GLint uorder + GLint vorder + GLboolean packed + const void *points + + + void glMapGrid1d + GLint un + GLdouble u1 + GLdouble u2 + + + + void glMapGrid1f + GLint un + GLfloat u1 + GLfloat u2 + + + + void glMapGrid1xOES + GLint n + GLfixed u1 + GLfixed u2 + + + void glMapGrid2d + GLint un + GLdouble u1 + GLdouble u2 + GLint vn + GLdouble v1 + GLdouble v2 + + + + void glMapGrid2f + GLint un + GLfloat u1 + GLfloat u2 + GLint vn + GLfloat v1 + GLfloat v2 + + + + void glMapGrid2xOES + GLint n + GLfixed u1 + GLfixed u2 + GLfixed v1 + GLfixed v2 + + + void *glMapNamedBuffer + GLuint buffer + GLenum access + + + void *glMapNamedBufferEXT + GLuint buffer + GLenum access + + + void *glMapNamedBufferRange + GLuint buffer + GLintptr offset + GLsizeiptr length + GLbitfield access + + + void *glMapNamedBufferRangeEXT + GLuint buffer + GLintptr offset + GLsizeiptr length + GLbitfield access + + + void *glMapObjectBufferATI + GLuint buffer + + + void glMapParameterfvNV + GLenum target + GLenum pname + const GLfloat *params + + + void glMapParameterivNV + GLenum target + GLenum pname + const GLint *params + + + void *glMapTexture2DINTEL + GLuint texture + GLint level + GLbitfield access + GLint *stride + GLenum *layout + + + void glMapVertexAttrib1dAPPLE + GLuint index + GLuint size + GLdouble u1 + GLdouble u2 + GLint stride + GLint order + const GLdouble *points + + + void glMapVertexAttrib1fAPPLE + GLuint index + GLuint size + GLfloat u1 + GLfloat u2 + GLint stride + GLint order + const GLfloat *points + + + void glMapVertexAttrib2dAPPLE + GLuint index + GLuint size + GLdouble u1 + GLdouble u2 + GLint ustride + GLint uorder + GLdouble v1 + GLdouble v2 + GLint vstride + GLint vorder + const GLdouble *points + + + void glMapVertexAttrib2fAPPLE + GLuint index + GLuint size + GLfloat u1 + GLfloat u2 + GLint ustride + GLint uorder + GLfloat v1 + GLfloat v2 + GLint vstride + GLint vorder + const GLfloat *points + + + void glMaterialf + GLenum face + GLenum pname + GLfloat param + + + + void glMaterialfv + GLenum face + GLenum pname + const GLfloat *params + + + + void glMateriali + GLenum face + GLenum pname + GLint param + + + + void glMaterialiv + GLenum face + GLenum pname + const GLint *params + + + + void glMaterialx + GLenum face + GLenum pname + GLfixed param + + + void glMaterialxOES + GLenum face + GLenum pname + GLfixed param + + + void glMaterialxv + GLenum face + GLenum pname + const GLfixed *param + + + void glMaterialxvOES + GLenum face + GLenum pname + const GLfixed *param + + + void glMatrixFrustumEXT + GLenum mode + GLdouble left + GLdouble right + GLdouble bottom + GLdouble top + GLdouble zNear + GLdouble zFar + + + void glMatrixIndexPointerARB + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glMatrixIndexPointerOES + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glMatrixIndexubvARB + GLint size + const GLubyte *indices + + + + void glMatrixIndexuivARB + GLint size + const GLuint *indices + + + + void glMatrixIndexusvARB + GLint size + const GLushort *indices + + + + void glMatrixLoad3x2fNV + GLenum matrixMode + const GLfloat *m + + + void glMatrixLoad3x3fNV + GLenum matrixMode + const GLfloat *m + + + void glMatrixLoadIdentityEXT + GLenum mode + + + void glMatrixLoadTranspose3x3fNV + GLenum matrixMode + const GLfloat *m + + + void glMatrixLoadTransposedEXT + GLenum mode + const GLdouble *m + + + void glMatrixLoadTransposefEXT + GLenum mode + const GLfloat *m + + + void glMatrixLoaddEXT + GLenum mode + const GLdouble *m + + + void glMatrixLoadfEXT + GLenum mode + const GLfloat *m + + + void glMatrixMode + GLenum mode + + + + void glMatrixMult3x2fNV + GLenum matrixMode + const GLfloat *m + + + void glMatrixMult3x3fNV + GLenum matrixMode + const GLfloat *m + + + void glMatrixMultTranspose3x3fNV + GLenum matrixMode + const GLfloat *m + + + void glMatrixMultTransposedEXT + GLenum mode + const GLdouble *m + + + void glMatrixMultTransposefEXT + GLenum mode + const GLfloat *m + + + void glMatrixMultdEXT + GLenum mode + const GLdouble *m + + + void glMatrixMultfEXT + GLenum mode + const GLfloat *m + + + void glMatrixOrthoEXT + GLenum mode + GLdouble left + GLdouble right + GLdouble bottom + GLdouble top + GLdouble zNear + GLdouble zFar + + + void glMatrixPopEXT + GLenum mode + + + void glMatrixPushEXT + GLenum mode + + + void glMatrixRotatedEXT + GLenum mode + GLdouble angle + GLdouble x + GLdouble y + GLdouble z + + + void glMatrixRotatefEXT + GLenum mode + GLfloat angle + GLfloat x + GLfloat y + GLfloat z + + + void glMatrixScaledEXT + GLenum mode + GLdouble x + GLdouble y + GLdouble z + + + void glMatrixScalefEXT + GLenum mode + GLfloat x + GLfloat y + GLfloat z + + + void glMatrixTranslatedEXT + GLenum mode + GLdouble x + GLdouble y + GLdouble z + + + void glMatrixTranslatefEXT + GLenum mode + GLfloat x + GLfloat y + GLfloat z + + + void glMaxShaderCompilerThreadsKHR + GLuint count + + + void glMaxShaderCompilerThreadsARB + GLuint count + + + + void glMemoryBarrier + GLbitfield barriers + + + void glMemoryBarrierByRegion + GLbitfield barriers + + + void glMemoryBarrierEXT + GLbitfield barriers + + + + void glMemoryObjectParameterivEXT + GLuint memoryObject + GLenum pname + const GLint *params + + + void glMinSampleShading + GLfloat value + + + void glMinSampleShadingARB + GLfloat value + + + + void glMinSampleShadingOES + GLfloat value + + + + void glMinmax + GLenum target + GLenum internalformat + GLboolean sink + + + + void glMinmaxEXT + GLenum target + GLenum internalformat + GLboolean sink + + + + + void glMultMatrixd + const GLdouble *m + + + + void glMultMatrixf + const GLfloat *m + + + + void glMultMatrixx + const GLfixed *m + + + void glMultMatrixxOES + const GLfixed *m + + + void glMultTransposeMatrixd + const GLdouble *m + + + void glMultTransposeMatrixdARB + const GLdouble *m + + + + void glMultTransposeMatrixf + const GLfloat *m + + + void glMultTransposeMatrixfARB + const GLfloat *m + + + + void glMultTransposeMatrixxOES + const GLfixed *m + + + void glMultiDrawArrays + GLenum mode + const GLint *first + const GLsizei *count + GLsizei drawcount + + + void glMultiDrawArraysEXT + GLenum mode + const GLint *first + const GLsizei *count + GLsizei primcount + + + + void glMultiDrawArraysIndirect + GLenum mode + const void *indirect + GLsizei drawcount + GLsizei stride + + + void glMultiDrawArraysIndirectAMD + GLenum mode + const void *indirect + GLsizei primcount + GLsizei stride + + + + void glMultiDrawArraysIndirectBindlessCountNV + GLenum mode + const void *indirect + GLsizei drawCount + GLsizei maxDrawCount + GLsizei stride + GLint vertexBufferCount + + + void glMultiDrawArraysIndirectBindlessNV + GLenum mode + const void *indirect + GLsizei drawCount + GLsizei stride + GLint vertexBufferCount + + + void glMultiDrawArraysIndirectCount + GLenum mode + const void *indirect + GLintptr drawcount + GLsizei maxdrawcount + GLsizei stride + + + void glMultiDrawArraysIndirectCountARB + GLenum mode + const void *indirect + GLintptr drawcount + GLsizei maxdrawcount + GLsizei stride + + + + void glMultiDrawArraysIndirectEXT + GLenum mode + const void *indirect + GLsizei drawcount + GLsizei stride + + + + void glMultiDrawElementArrayAPPLE + GLenum mode + const GLint *first + const GLsizei *count + GLsizei primcount + + + void glMultiDrawElements + GLenum mode + const GLsizei *count + GLenum type + const void *const*indices + GLsizei drawcount + + + void glMultiDrawElementsBaseVertex + GLenum mode + const GLsizei *count + GLenum type + const void *const*indices + GLsizei drawcount + const GLint *basevertex + + + void glMultiDrawElementsBaseVertexEXT + GLenum mode + const GLsizei *count + GLenum type + const void *const*indices + GLsizei primcount + const GLint *basevertex + + + + void glMultiDrawElementsEXT + GLenum mode + const GLsizei *count + GLenum type + const void *const*indices + GLsizei primcount + + + + void glMultiDrawElementsIndirect + GLenum mode + GLenum type + const void *indirect + GLsizei drawcount + GLsizei stride + + + void glMultiDrawElementsIndirectAMD + GLenum mode + GLenum type + const void *indirect + GLsizei primcount + GLsizei stride + + + + void glMultiDrawElementsIndirectBindlessCountNV + GLenum mode + GLenum type + const void *indirect + GLsizei drawCount + GLsizei maxDrawCount + GLsizei stride + GLint vertexBufferCount + + + void glMultiDrawElementsIndirectBindlessNV + GLenum mode + GLenum type + const void *indirect + GLsizei drawCount + GLsizei stride + GLint vertexBufferCount + + + void glMultiDrawElementsIndirectCount + GLenum mode + GLenum type + const void *indirect + GLintptr drawcount + GLsizei maxdrawcount + GLsizei stride + + + void glMultiDrawElementsIndirectCountARB + GLenum mode + GLenum type + const void *indirect + GLintptr drawcount + GLsizei maxdrawcount + GLsizei stride + + + + void glMultiDrawElementsIndirectEXT + GLenum mode + GLenum type + const void *indirect + GLsizei drawcount + GLsizei stride + + + + void glMultiDrawMeshTasksIndirectNV + GLintptr indirect + GLsizei drawcount + GLsizei stride + + + void glMultiDrawMeshTasksIndirectCountNV + GLintptr indirect + GLintptr drawcount + GLsizei maxdrawcount + GLsizei stride + + + void glMultiDrawRangeElementArrayAPPLE + GLenum mode + GLuint start + GLuint end + const GLint *first + const GLsizei *count + GLsizei primcount + + + void glMultiModeDrawArraysIBM + const GLenum *mode + const GLint *first + const GLsizei *count + GLsizei primcount + GLint modestride + + + void glMultiModeDrawElementsIBM + const GLenum *mode + const GLsizei *count + GLenum type + const void *const*indices + GLsizei primcount + GLint modestride + + + void glMultiTexBufferEXT + GLenum texunit + GLenum target + GLenum internalformat + GLuint buffer + + + void glMultiTexCoord1bOES + GLenum texture + GLbyte s + + + void glMultiTexCoord1bvOES + GLenum texture + const GLbyte *coords + + + void glMultiTexCoord1d + GLenum target + GLdouble s + + + + void glMultiTexCoord1dARB + GLenum target + GLdouble s + + + + + void glMultiTexCoord1dv + GLenum target + const GLdouble *v + + + + void glMultiTexCoord1dvARB + GLenum target + const GLdouble *v + + + + + void glMultiTexCoord1f + GLenum target + GLfloat s + + + + void glMultiTexCoord1fARB + GLenum target + GLfloat s + + + + + void glMultiTexCoord1fv + GLenum target + const GLfloat *v + + + + void glMultiTexCoord1fvARB + GLenum target + const GLfloat *v + + + + + void glMultiTexCoord1hNV + GLenum target + GLhalfNV s + + + + void glMultiTexCoord1hvNV + GLenum target + const GLhalfNV *v + + + + void glMultiTexCoord1i + GLenum target + GLint s + + + + void glMultiTexCoord1iARB + GLenum target + GLint s + + + + + void glMultiTexCoord1iv + GLenum target + const GLint *v + + + + void glMultiTexCoord1ivARB + GLenum target + const GLint *v + + + + + void glMultiTexCoord1s + GLenum target + GLshort s + + + + void glMultiTexCoord1sARB + GLenum target + GLshort s + + + + + void glMultiTexCoord1sv + GLenum target + const GLshort *v + + + + void glMultiTexCoord1svARB + GLenum target + const GLshort *v + + + + + void glMultiTexCoord1xOES + GLenum texture + GLfixed s + + + void glMultiTexCoord1xvOES + GLenum texture + const GLfixed *coords + + + void glMultiTexCoord2bOES + GLenum texture + GLbyte s + GLbyte t + + + void glMultiTexCoord2bvOES + GLenum texture + const GLbyte *coords + + + void glMultiTexCoord2d + GLenum target + GLdouble s + GLdouble t + + + + void glMultiTexCoord2dARB + GLenum target + GLdouble s + GLdouble t + + + + + void glMultiTexCoord2dv + GLenum target + const GLdouble *v + + + + void glMultiTexCoord2dvARB + GLenum target + const GLdouble *v + + + + + void glMultiTexCoord2f + GLenum target + GLfloat s + GLfloat t + + + + void glMultiTexCoord2fARB + GLenum target + GLfloat s + GLfloat t + + + + + void glMultiTexCoord2fv + GLenum target + const GLfloat *v + + + + void glMultiTexCoord2fvARB + GLenum target + const GLfloat *v + + + + + void glMultiTexCoord2hNV + GLenum target + GLhalfNV s + GLhalfNV t + + + + void glMultiTexCoord2hvNV + GLenum target + const GLhalfNV *v + + + + void glMultiTexCoord2i + GLenum target + GLint s + GLint t + + + + void glMultiTexCoord2iARB + GLenum target + GLint s + GLint t + + + + + void glMultiTexCoord2iv + GLenum target + const GLint *v + + + + void glMultiTexCoord2ivARB + GLenum target + const GLint *v + + + + + void glMultiTexCoord2s + GLenum target + GLshort s + GLshort t + + + + void glMultiTexCoord2sARB + GLenum target + GLshort s + GLshort t + + + + + void glMultiTexCoord2sv + GLenum target + const GLshort *v + + + + void glMultiTexCoord2svARB + GLenum target + const GLshort *v + + + + + void glMultiTexCoord2xOES + GLenum texture + GLfixed s + GLfixed t + + + void glMultiTexCoord2xvOES + GLenum texture + const GLfixed *coords + + + void glMultiTexCoord3bOES + GLenum texture + GLbyte s + GLbyte t + GLbyte r + + + void glMultiTexCoord3bvOES + GLenum texture + const GLbyte *coords + + + void glMultiTexCoord3d + GLenum target + GLdouble s + GLdouble t + GLdouble r + + + + void glMultiTexCoord3dARB + GLenum target + GLdouble s + GLdouble t + GLdouble r + + + + + void glMultiTexCoord3dv + GLenum target + const GLdouble *v + + + + void glMultiTexCoord3dvARB + GLenum target + const GLdouble *v + + + + + void glMultiTexCoord3f + GLenum target + GLfloat s + GLfloat t + GLfloat r + + + + void glMultiTexCoord3fARB + GLenum target + GLfloat s + GLfloat t + GLfloat r + + + + + void glMultiTexCoord3fv + GLenum target + const GLfloat *v + + + + void glMultiTexCoord3fvARB + GLenum target + const GLfloat *v + + + + + void glMultiTexCoord3hNV + GLenum target + GLhalfNV s + GLhalfNV t + GLhalfNV r + + + + void glMultiTexCoord3hvNV + GLenum target + const GLhalfNV *v + + + + void glMultiTexCoord3i + GLenum target + GLint s + GLint t + GLint r + + + + void glMultiTexCoord3iARB + GLenum target + GLint s + GLint t + GLint r + + + + + void glMultiTexCoord3iv + GLenum target + const GLint *v + + + + void glMultiTexCoord3ivARB + GLenum target + const GLint *v + + + + + void glMultiTexCoord3s + GLenum target + GLshort s + GLshort t + GLshort r + + + + void glMultiTexCoord3sARB + GLenum target + GLshort s + GLshort t + GLshort r + + + + + void glMultiTexCoord3sv + GLenum target + const GLshort *v + + + + void glMultiTexCoord3svARB + GLenum target + const GLshort *v + + + + + void glMultiTexCoord3xOES + GLenum texture + GLfixed s + GLfixed t + GLfixed r + + + void glMultiTexCoord3xvOES + GLenum texture + const GLfixed *coords + + + void glMultiTexCoord4bOES + GLenum texture + GLbyte s + GLbyte t + GLbyte r + GLbyte q + + + void glMultiTexCoord4bvOES + GLenum texture + const GLbyte *coords + + + void glMultiTexCoord4d + GLenum target + GLdouble s + GLdouble t + GLdouble r + GLdouble q + + + + void glMultiTexCoord4dARB + GLenum target + GLdouble s + GLdouble t + GLdouble r + GLdouble q + + + + + void glMultiTexCoord4dv + GLenum target + const GLdouble *v + + + + void glMultiTexCoord4dvARB + GLenum target + const GLdouble *v + + + + + void glMultiTexCoord4f + GLenum target + GLfloat s + GLfloat t + GLfloat r + GLfloat q + + + + void glMultiTexCoord4fARB + GLenum target + GLfloat s + GLfloat t + GLfloat r + GLfloat q + + + + + void glMultiTexCoord4fv + GLenum target + const GLfloat *v + + + + void glMultiTexCoord4fvARB + GLenum target + const GLfloat *v + + + + + void glMultiTexCoord4hNV + GLenum target + GLhalfNV s + GLhalfNV t + GLhalfNV r + GLhalfNV q + + + + void glMultiTexCoord4hvNV + GLenum target + const GLhalfNV *v + + + + void glMultiTexCoord4i + GLenum target + GLint s + GLint t + GLint r + GLint q + + + + void glMultiTexCoord4iARB + GLenum target + GLint s + GLint t + GLint r + GLint q + + + + + void glMultiTexCoord4iv + GLenum target + const GLint *v + + + + void glMultiTexCoord4ivARB + GLenum target + const GLint *v + + + + + void glMultiTexCoord4s + GLenum target + GLshort s + GLshort t + GLshort r + GLshort q + + + + void glMultiTexCoord4sARB + GLenum target + GLshort s + GLshort t + GLshort r + GLshort q + + + + + void glMultiTexCoord4sv + GLenum target + const GLshort *v + + + + void glMultiTexCoord4svARB + GLenum target + const GLshort *v + + + + + void glMultiTexCoord4x + GLenum texture + GLfixed s + GLfixed t + GLfixed r + GLfixed q + + + void glMultiTexCoord4xOES + GLenum texture + GLfixed s + GLfixed t + GLfixed r + GLfixed q + + + void glMultiTexCoord4xvOES + GLenum texture + const GLfixed *coords + + + void glMultiTexCoordP1ui + GLenum texture + GLenum type + GLuint coords + + + void glMultiTexCoordP1uiv + GLenum texture + GLenum type + const GLuint *coords + + + void glMultiTexCoordP2ui + GLenum texture + GLenum type + GLuint coords + + + void glMultiTexCoordP2uiv + GLenum texture + GLenum type + const GLuint *coords + + + void glMultiTexCoordP3ui + GLenum texture + GLenum type + GLuint coords + + + void glMultiTexCoordP3uiv + GLenum texture + GLenum type + const GLuint *coords + + + void glMultiTexCoordP4ui + GLenum texture + GLenum type + GLuint coords + + + void glMultiTexCoordP4uiv + GLenum texture + GLenum type + const GLuint *coords + + + void glMultiTexCoordPointerEXT + GLenum texunit + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glMultiTexEnvfEXT + GLenum texunit + GLenum target + GLenum pname + GLfloat param + + + + void glMultiTexEnvfvEXT + GLenum texunit + GLenum target + GLenum pname + const GLfloat *params + + + void glMultiTexEnviEXT + GLenum texunit + GLenum target + GLenum pname + GLint param + + + + void glMultiTexEnvivEXT + GLenum texunit + GLenum target + GLenum pname + const GLint *params + + + void glMultiTexGendEXT + GLenum texunit + GLenum coord + GLenum pname + GLdouble param + + + + void glMultiTexGendvEXT + GLenum texunit + GLenum coord + GLenum pname + const GLdouble *params + + + void glMultiTexGenfEXT + GLenum texunit + GLenum coord + GLenum pname + GLfloat param + + + + void glMultiTexGenfvEXT + GLenum texunit + GLenum coord + GLenum pname + const GLfloat *params + + + void glMultiTexGeniEXT + GLenum texunit + GLenum coord + GLenum pname + GLint param + + + + void glMultiTexGenivEXT + GLenum texunit + GLenum coord + GLenum pname + const GLint *params + + + void glMultiTexImage1DEXT + GLenum texunit + GLenum target + GLint level + GLint internalformat + GLsizei width + GLint border + GLenum format + GLenum type + const void *pixels + + + void glMultiTexImage2DEXT + GLenum texunit + GLenum target + GLint level + GLint internalformat + GLsizei width + GLsizei height + GLint border + GLenum format + GLenum type + const void *pixels + + + void glMultiTexImage3DEXT + GLenum texunit + GLenum target + GLint level + GLint internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLenum format + GLenum type + const void *pixels + + + void glMultiTexParameterIivEXT + GLenum texunit + GLenum target + GLenum pname + const GLint *params + + + void glMultiTexParameterIuivEXT + GLenum texunit + GLenum target + GLenum pname + const GLuint *params + + + void glMultiTexParameterfEXT + GLenum texunit + GLenum target + GLenum pname + GLfloat param + + + + void glMultiTexParameterfvEXT + GLenum texunit + GLenum target + GLenum pname + const GLfloat *params + + + void glMultiTexParameteriEXT + GLenum texunit + GLenum target + GLenum pname + GLint param + + + + void glMultiTexParameterivEXT + GLenum texunit + GLenum target + GLenum pname + const GLint *params + + + void glMultiTexRenderbufferEXT + GLenum texunit + GLenum target + GLuint renderbuffer + + + void glMultiTexSubImage1DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLsizei width + GLenum format + GLenum type + const void *pixels + + + void glMultiTexSubImage2DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *pixels + + + void glMultiTexSubImage3DEXT + GLenum texunit + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + const void *pixels + + + void glMulticastBarrierNV + + + void glMulticastBlitFramebufferNV + GLuint srcGpu + GLuint dstGpu + GLint srcX0 + GLint srcY0 + GLint srcX1 + GLint srcY1 + GLint dstX0 + GLint dstY0 + GLint dstX1 + GLint dstY1 + GLbitfield mask + GLenum filter + + + void glMulticastBufferSubDataNV + GLbitfield gpuMask + GLuint buffer + GLintptr offset + GLsizeiptr size + const void *data + + + void glMulticastCopyBufferSubDataNV + GLuint readGpu + GLbitfield writeGpuMask + GLuint readBuffer + GLuint writeBuffer + GLintptr readOffset + GLintptr writeOffset + GLsizeiptr size + + + void glMulticastCopyImageSubDataNV + GLuint srcGpu + GLbitfield dstGpuMask + GLuint srcName + GLenum srcTarget + GLint srcLevel + GLint srcX + GLint srcY + GLint srcZ + GLuint dstName + GLenum dstTarget + GLint dstLevel + GLint dstX + GLint dstY + GLint dstZ + GLsizei srcWidth + GLsizei srcHeight + GLsizei srcDepth + + + void glMulticastFramebufferSampleLocationsfvNV + GLuint gpu + GLuint framebuffer + GLuint start + GLsizei count + const GLfloat *v + + + void glMulticastGetQueryObjecti64vNV + GLuint gpu + GLuint id + GLenum pname + GLint64 *params + + + void glMulticastGetQueryObjectivNV + GLuint gpu + GLuint id + GLenum pname + GLint *params + + + void glMulticastGetQueryObjectui64vNV + GLuint gpu + GLuint id + GLenum pname + GLuint64 *params + + + void glMulticastGetQueryObjectuivNV + GLuint gpu + GLuint id + GLenum pname + GLuint *params + + + void glMulticastWaitSyncNV + GLuint signalGpu + GLbitfield waitGpuMask + + + void glNamedBufferAttachMemoryNV + GLuint buffer + GLuint memory + GLuint64 offset + + + void glNamedBufferData + GLuint buffer + GLsizeiptr size + const void *data + GLenum usage + + + void glNamedBufferDataEXT + GLuint buffer + GLsizeiptr size + const void *data + GLenum usage + + + void glNamedBufferPageCommitmentARB + GLuint buffer + GLintptr offset + GLsizeiptr size + GLboolean commit + + + void glNamedBufferPageCommitmentEXT + GLuint buffer + GLintptr offset + GLsizeiptr size + GLboolean commit + + + void glNamedBufferStorage + GLuint buffer + GLsizeiptr size + const void *data + GLbitfield flags + + + void glNamedBufferStorageExternalEXT + GLuint buffer + GLintptr offset + GLsizeiptr size + GLeglClientBufferEXT clientBuffer + GLbitfield flags + + + void glNamedBufferStorageEXT + GLuint buffer + GLsizeiptr size + const void *data + GLbitfield flags + + + + void glNamedBufferStorageMemEXT + GLuint buffer + GLsizeiptr size + GLuint memory + GLuint64 offset + + + void glNamedBufferSubData + GLuint buffer + GLintptr offset + GLsizeiptr size + const void *data + + + void glNamedBufferSubDataEXT + GLuint buffer + GLintptr offset + GLsizeiptr size + const void *data + + + + void glNamedCopyBufferSubDataEXT + GLuint readBuffer + GLuint writeBuffer + GLintptr readOffset + GLintptr writeOffset + GLsizeiptr size + + + void glNamedFramebufferDrawBuffer + GLuint framebuffer + GLenum buf + + + void glNamedFramebufferDrawBuffers + GLuint framebuffer + GLsizei n + const GLenum *bufs + + + void glNamedFramebufferParameteri + GLuint framebuffer + GLenum pname + GLint param + + + void glNamedFramebufferParameteriEXT + GLuint framebuffer + GLenum pname + GLint param + + + void glNamedFramebufferReadBuffer + GLuint framebuffer + GLenum src + + + void glNamedFramebufferRenderbuffer + GLuint framebuffer + GLenum attachment + GLenum renderbuffertarget + GLuint renderbuffer + + + void glNamedFramebufferRenderbufferEXT + GLuint framebuffer + GLenum attachment + GLenum renderbuffertarget + GLuint renderbuffer + + + void glNamedFramebufferSampleLocationsfvARB + GLuint framebuffer + GLuint start + GLsizei count + const GLfloat *v + + + void glNamedFramebufferSampleLocationsfvNV + GLuint framebuffer + GLuint start + GLsizei count + const GLfloat *v + + + void glNamedFramebufferTexture + GLuint framebuffer + GLenum attachment + GLuint texture + GLint level + + + void glNamedFramebufferSamplePositionsfvAMD + GLuint framebuffer + GLuint numsamples + GLuint pixelindex + const GLfloat *values + + + void glNamedFramebufferTexture1DEXT + GLuint framebuffer + GLenum attachment + GLenum textarget + GLuint texture + GLint level + + + void glNamedFramebufferTexture2DEXT + GLuint framebuffer + GLenum attachment + GLenum textarget + GLuint texture + GLint level + + + void glNamedFramebufferTexture3DEXT + GLuint framebuffer + GLenum attachment + GLenum textarget + GLuint texture + GLint level + GLint zoffset + + + void glNamedFramebufferTextureEXT + GLuint framebuffer + GLenum attachment + GLuint texture + GLint level + + + void glNamedFramebufferTextureFaceEXT + GLuint framebuffer + GLenum attachment + GLuint texture + GLint level + GLenum face + + + void glNamedFramebufferTextureLayer + GLuint framebuffer + GLenum attachment + GLuint texture + GLint level + GLint layer + + + void glNamedFramebufferTextureLayerEXT + GLuint framebuffer + GLenum attachment + GLuint texture + GLint level + GLint layer + + + void glNamedProgramLocalParameter4dEXT + GLuint program + GLenum target + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glNamedProgramLocalParameter4dvEXT + GLuint program + GLenum target + GLuint index + const GLdouble *params + + + void glNamedProgramLocalParameter4fEXT + GLuint program + GLenum target + GLuint index + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glNamedProgramLocalParameter4fvEXT + GLuint program + GLenum target + GLuint index + const GLfloat *params + + + void glNamedProgramLocalParameterI4iEXT + GLuint program + GLenum target + GLuint index + GLint x + GLint y + GLint z + GLint w + + + + void glNamedProgramLocalParameterI4ivEXT + GLuint program + GLenum target + GLuint index + const GLint *params + + + void glNamedProgramLocalParameterI4uiEXT + GLuint program + GLenum target + GLuint index + GLuint x + GLuint y + GLuint z + GLuint w + + + + void glNamedProgramLocalParameterI4uivEXT + GLuint program + GLenum target + GLuint index + const GLuint *params + + + void glNamedProgramLocalParameters4fvEXT + GLuint program + GLenum target + GLuint index + GLsizei count + const GLfloat *params + + + void glNamedProgramLocalParametersI4ivEXT + GLuint program + GLenum target + GLuint index + GLsizei count + const GLint *params + + + void glNamedProgramLocalParametersI4uivEXT + GLuint program + GLenum target + GLuint index + GLsizei count + const GLuint *params + + + void glNamedProgramStringEXT + GLuint program + GLenum target + GLenum format + GLsizei len + const void *string + + + void glNamedRenderbufferStorage + GLuint renderbuffer + GLenum internalformat + GLsizei width + GLsizei height + + + void glNamedRenderbufferStorageEXT + GLuint renderbuffer + GLenum internalformat + GLsizei width + GLsizei height + + + void glNamedRenderbufferStorageMultisample + GLuint renderbuffer + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + + + void glNamedRenderbufferStorageMultisampleAdvancedAMD + GLuint renderbuffer + GLsizei samples + GLsizei storageSamples + GLenum internalformat + GLsizei width + GLsizei height + + + void glNamedRenderbufferStorageMultisampleCoverageEXT + GLuint renderbuffer + GLsizei coverageSamples + GLsizei colorSamples + GLenum internalformat + GLsizei width + GLsizei height + + + void glNamedRenderbufferStorageMultisampleEXT + GLuint renderbuffer + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + + + void glNamedStringARB + GLenum type + GLint namelen + const GLchar *name + GLint stringlen + const GLchar *string + + + void glNewList + GLuint list + GLenum mode + + + + GLuint glNewObjectBufferATI + GLsizei size + const void *pointer + GLenum usage + + + void glNormal3b + GLbyte nx + GLbyte ny + GLbyte nz + + + + void glNormal3bv + const GLbyte *v + + + + void glNormal3d + GLdouble nx + GLdouble ny + GLdouble nz + + + + void glNormal3dv + const GLdouble *v + + + + void glNormal3f + GLfloat nx + GLfloat ny + GLfloat nz + + + + void glNormal3fVertex3fSUN + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + + + void glNormal3fVertex3fvSUN + const GLfloat *n + const GLfloat *v + + + void glNormal3fv + const GLfloat *v + + + + void glNormal3hNV + GLhalfNV nx + GLhalfNV ny + GLhalfNV nz + + + + void glNormal3hvNV + const GLhalfNV *v + + + + void glNormal3i + GLint nx + GLint ny + GLint nz + + + + void glNormal3iv + const GLint *v + + + + void glNormal3s + GLshort nx + GLshort ny + GLshort nz + + + + void glNormal3sv + const GLshort *v + + + + void glNormal3x + GLfixed nx + GLfixed ny + GLfixed nz + + + void glNormal3xOES + GLfixed nx + GLfixed ny + GLfixed nz + + + void glNormal3xvOES + const GLfixed *coords + + + void glNormalFormatNV + GLenum type + GLsizei stride + + + void glNormalP3ui + GLenum type + GLuint coords + + + void glNormalP3uiv + GLenum type + const GLuint *coords + + + void glNormalPointer + GLenum type + GLsizei stride + const void *pointer + + + void glNormalPointerEXT + GLenum type + GLsizei stride + GLsizei count + const void *pointer + + + void glNormalPointerListIBM + GLenum type + GLint stride + const void **pointer + GLint ptrstride + + + void glNormalPointervINTEL + GLenum type + const void **pointer + + + void glNormalStream3bATI + GLenum stream + GLbyte nx + GLbyte ny + GLbyte nz + + + void glNormalStream3bvATI + GLenum stream + const GLbyte *coords + + + void glNormalStream3dATI + GLenum stream + GLdouble nx + GLdouble ny + GLdouble nz + + + void glNormalStream3dvATI + GLenum stream + const GLdouble *coords + + + void glNormalStream3fATI + GLenum stream + GLfloat nx + GLfloat ny + GLfloat nz + + + void glNormalStream3fvATI + GLenum stream + const GLfloat *coords + + + void glNormalStream3iATI + GLenum stream + GLint nx + GLint ny + GLint nz + + + void glNormalStream3ivATI + GLenum stream + const GLint *coords + + + void glNormalStream3sATI + GLenum stream + GLshort nx + GLshort ny + GLshort nz + + + void glNormalStream3svATI + GLenum stream + const GLshort *coords + + + void glObjectLabel + GLenum identifier + GLuint name + GLsizei length + const GLchar *label + + + void glObjectLabelKHR + GLenum identifier + GLuint name + GLsizei length + const GLchar *label + + + + void glObjectPtrLabel + const void *ptr + GLsizei length + const GLchar *label + + + void glObjectPtrLabelKHR + const void *ptr + GLsizei length + const GLchar *label + + + + GLenum glObjectPurgeableAPPLE + GLenum objectType + GLuint name + GLenum option + + + GLenum glObjectUnpurgeableAPPLE + GLenum objectType + GLuint name + GLenum option + + + void glOrtho + GLdouble left + GLdouble right + GLdouble bottom + GLdouble top + GLdouble zNear + GLdouble zFar + + + + void glOrthof + GLfloat l + GLfloat r + GLfloat b + GLfloat t + GLfloat n + GLfloat f + + + void glOrthofOES + GLfloat l + GLfloat r + GLfloat b + GLfloat t + GLfloat n + GLfloat f + + + + void glOrthox + GLfixed l + GLfixed r + GLfixed b + GLfixed t + GLfixed n + GLfixed f + + + void glOrthoxOES + GLfixed l + GLfixed r + GLfixed b + GLfixed t + GLfixed n + GLfixed f + + + void glPNTrianglesfATI + GLenum pname + GLfloat param + + + void glPNTrianglesiATI + GLenum pname + GLint param + + + void glPassTexCoordATI + GLuint dst + GLuint coord + GLenum swizzle + + + void glPassThrough + GLfloat token + + + + void glPassThroughxOES + GLfixed token + + + void glPatchParameterfv + GLenum pname + const GLfloat *values + + + void glPatchParameteri + GLenum pname + GLint value + + + void glPatchParameteriEXT + GLenum pname + GLint value + + + + void glPatchParameteriOES + GLenum pname + GLint value + + + + void glPathColorGenNV + GLenum color + GLenum genMode + GLenum colorFormat + const GLfloat *coeffs + + + void glPathCommandsNV + GLuint path + GLsizei numCommands + const GLubyte *commands + GLsizei numCoords + GLenum coordType + const void *coords + + + void glPathCoordsNV + GLuint path + GLsizei numCoords + GLenum coordType + const void *coords + + + void glPathCoverDepthFuncNV + GLenum func + + + void glPathDashArrayNV + GLuint path + GLsizei dashCount + const GLfloat *dashArray + + + void glPathFogGenNV + GLenum genMode + + + GLenum glPathGlyphIndexArrayNV + GLuint firstPathName + GLenum fontTarget + const void *fontName + GLbitfield fontStyle + GLuint firstGlyphIndex + GLsizei numGlyphs + GLuint pathParameterTemplate + GLfloat emScale + + + GLenum glPathGlyphIndexRangeNV + GLenum fontTarget + const void *fontName + GLbitfield fontStyle + GLuint pathParameterTemplate + GLfloat emScale + GLuint baseAndCount[2] + + + void glPathGlyphRangeNV + GLuint firstPathName + GLenum fontTarget + const void *fontName + GLbitfield fontStyle + GLuint firstGlyph + GLsizei numGlyphs + GLenum handleMissingGlyphs + GLuint pathParameterTemplate + GLfloat emScale + + + void glPathGlyphsNV + GLuint firstPathName + GLenum fontTarget + const void *fontName + GLbitfield fontStyle + GLsizei numGlyphs + GLenum type + const void *charcodes + GLenum handleMissingGlyphs + GLuint pathParameterTemplate + GLfloat emScale + + + GLenum glPathMemoryGlyphIndexArrayNV + GLuint firstPathName + GLenum fontTarget + GLsizeiptr fontSize + const void *fontData + GLsizei faceIndex + GLuint firstGlyphIndex + GLsizei numGlyphs + GLuint pathParameterTemplate + GLfloat emScale + + + void glPathParameterfNV + GLuint path + GLenum pname + GLfloat value + + + void glPathParameterfvNV + GLuint path + GLenum pname + const GLfloat *value + + + void glPathParameteriNV + GLuint path + GLenum pname + GLint value + + + void glPathParameterivNV + GLuint path + GLenum pname + const GLint *value + + + void glPathStencilDepthOffsetNV + GLfloat factor + GLfloat units + + + void glPathStencilFuncNV + GLenum func + GLint ref + GLuint mask + + + void glPathStringNV + GLuint path + GLenum format + GLsizei length + const void *pathString + + + void glPathSubCommandsNV + GLuint path + GLsizei commandStart + GLsizei commandsToDelete + GLsizei numCommands + const GLubyte *commands + GLsizei numCoords + GLenum coordType + const void *coords + + + void glPathSubCoordsNV + GLuint path + GLsizei coordStart + GLsizei numCoords + GLenum coordType + const void *coords + + + void glPathTexGenNV + GLenum texCoordSet + GLenum genMode + GLint components + const GLfloat *coeffs + + + void glPauseTransformFeedback + + + void glPauseTransformFeedbackNV + + + + void glPixelDataRangeNV + GLenum target + GLsizei length + const void *pointer + + + void glPixelMapfv + GLenum map + GLsizei mapsize + const GLfloat *values + + + + + void glPixelMapuiv + GLenum map + GLsizei mapsize + const GLuint *values + + + + + void glPixelMapusv + GLenum map + GLsizei mapsize + const GLushort *values + + + + + void glPixelMapx + GLenum map + GLint size + const GLfixed *values + + + void glPixelStoref + GLenum pname + GLfloat param + + + + void glPixelStorei + GLenum pname + GLint param + + + + void glPixelStorex + GLenum pname + GLfixed param + + + void glPixelTexGenParameterfSGIS + GLenum pname + GLfloat param + + + void glPixelTexGenParameterfvSGIS + GLenum pname + const GLfloat *params + + + void glPixelTexGenParameteriSGIS + GLenum pname + GLint param + + + void glPixelTexGenParameterivSGIS + GLenum pname + const GLint *params + + + void glPixelTexGenSGIX + GLenum mode + + + + void glPixelTransferf + GLenum pname + GLfloat param + + + + void glPixelTransferi + GLenum pname + GLint param + + + + void glPixelTransferxOES + GLenum pname + GLfixed param + + + void glPixelTransformParameterfEXT + GLenum target + GLenum pname + GLfloat param + + + + void glPixelTransformParameterfvEXT + GLenum target + GLenum pname + const GLfloat *params + + + void glPixelTransformParameteriEXT + GLenum target + GLenum pname + GLint param + + + + void glPixelTransformParameterivEXT + GLenum target + GLenum pname + const GLint *params + + + void glPixelZoom + GLfloat xfactor + GLfloat yfactor + + + + void glPixelZoomxOES + GLfixed xfactor + GLfixed yfactor + + + GLboolean glPointAlongPathNV + GLuint path + GLsizei startSegment + GLsizei numSegments + GLfloat distance + GLfloat *x + GLfloat *y + GLfloat *tangentX + GLfloat *tangentY + + + void glPointParameterf + GLenum pname + GLfloat param + + + + void glPointParameterfARB + GLenum pname + GLfloat param + + + + + void glPointParameterfEXT + GLenum pname + GLfloat param + + + + void glPointParameterfSGIS + GLenum pname + GLfloat param + + + + void glPointParameterfv + GLenum pname + const GLfloat *params + + + + void glPointParameterfvARB + GLenum pname + const GLfloat *params + + + + + void glPointParameterfvEXT + GLenum pname + const GLfloat *params + + + + void glPointParameterfvSGIS + GLenum pname + const GLfloat *params + + + + void glPointParameteri + GLenum pname + GLint param + + + + void glPointParameteriNV + GLenum pname + GLint param + + + + + void glPointParameteriv + GLenum pname + const GLint *params + + + + void glPointParameterivNV + GLenum pname + const GLint *params + + + + + void glPointParameterx + GLenum pname + GLfixed param + + + void glPointParameterxOES + GLenum pname + GLfixed param + + + void glPointParameterxv + GLenum pname + const GLfixed *params + + + void glPointParameterxvOES + GLenum pname + const GLfixed *params + + + void glPointSize + GLfloat size + + + + void glPointSizePointerOES + GLenum type + GLsizei stride + const void *pointer + + + void glPointSizex + GLfixed size + + + void glPointSizexOES + GLfixed size + + + GLint glPollAsyncSGIX + GLuint *markerp + + + GLint glPollInstrumentsSGIX + GLint *marker_p + + + + void glPolygonMode + GLenum face + GLenum mode + + + + void glPolygonModeNV + GLenum face + GLenum mode + + + + void glPolygonOffset + GLfloat factor + GLfloat units + + + + void glPolygonOffsetClamp + GLfloat factor + GLfloat units + GLfloat clamp + + + + void glPolygonOffsetClampEXT + GLfloat factor + GLfloat units + GLfloat clamp + + + + void glPolygonOffsetEXT + GLfloat factor + GLfloat bias + + + + void glPolygonOffsetx + GLfixed factor + GLfixed units + + + void glPolygonOffsetxOES + GLfixed factor + GLfixed units + + + void glPolygonStipple + const GLubyte *mask + + + + + void glPopAttrib + + + + void glPopClientAttrib + + + void glPopDebugGroup + + + void glPopDebugGroupKHR + + + + void glPopGroupMarkerEXT + + + void glPopMatrix + + + + void glPopName + + + + void glPresentFrameDualFillNV + GLuint video_slot + GLuint64EXT minPresentTime + GLuint beginPresentTimeId + GLuint presentDurationId + GLenum type + GLenum target0 + GLuint fill0 + GLenum target1 + GLuint fill1 + GLenum target2 + GLuint fill2 + GLenum target3 + GLuint fill3 + + + void glPresentFrameKeyedNV + GLuint video_slot + GLuint64EXT minPresentTime + GLuint beginPresentTimeId + GLuint presentDurationId + GLenum type + GLenum target0 + GLuint fill0 + GLuint key0 + GLenum target1 + GLuint fill1 + GLuint key1 + + + void glPrimitiveBoundingBox + GLfloat minX + GLfloat minY + GLfloat minZ + GLfloat minW + GLfloat maxX + GLfloat maxY + GLfloat maxZ + GLfloat maxW + + + void glPrimitiveBoundingBoxARB + GLfloat minX + GLfloat minY + GLfloat minZ + GLfloat minW + GLfloat maxX + GLfloat maxY + GLfloat maxZ + GLfloat maxW + + + + void glPrimitiveBoundingBoxEXT + GLfloat minX + GLfloat minY + GLfloat minZ + GLfloat minW + GLfloat maxX + GLfloat maxY + GLfloat maxZ + GLfloat maxW + + + + void glPrimitiveBoundingBoxOES + GLfloat minX + GLfloat minY + GLfloat minZ + GLfloat minW + GLfloat maxX + GLfloat maxY + GLfloat maxZ + GLfloat maxW + + + + void glPrimitiveRestartIndex + GLuint index + + + void glPrimitiveRestartIndexNV + GLuint index + + + + void glPrimitiveRestartNV + + + + void glPrioritizeTextures + GLsizei n + const GLuint *textures + const GLfloat *priorities + + + + void glPrioritizeTexturesEXT + GLsizei n + const GLuint *textures + const GLclampf *priorities + + + + + void glPrioritizeTexturesxOES + GLsizei n + const GLuint *textures + const GLfixed *priorities + + + void glProgramBinary + GLuint program + GLenum binaryFormat + const void *binary + GLsizei length + + + void glProgramBinaryOES + GLuint program + GLenum binaryFormat + const void *binary + GLint length + + + + void glProgramBufferParametersIivNV + GLenum target + GLuint bindingIndex + GLuint wordIndex + GLsizei count + const GLint *params + + + void glProgramBufferParametersIuivNV + GLenum target + GLuint bindingIndex + GLuint wordIndex + GLsizei count + const GLuint *params + + + void glProgramBufferParametersfvNV + GLenum target + GLuint bindingIndex + GLuint wordIndex + GLsizei count + const GLfloat *params + + + void glProgramEnvParameter4dARB + GLenum target + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glProgramEnvParameter4dvARB + GLenum target + GLuint index + const GLdouble *params + + + void glProgramEnvParameter4fARB + GLenum target + GLuint index + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glProgramEnvParameter4fvARB + GLenum target + GLuint index + const GLfloat *params + + + void glProgramEnvParameterI4iNV + GLenum target + GLuint index + GLint x + GLint y + GLint z + GLint w + + + + void glProgramEnvParameterI4ivNV + GLenum target + GLuint index + const GLint *params + + + void glProgramEnvParameterI4uiNV + GLenum target + GLuint index + GLuint x + GLuint y + GLuint z + GLuint w + + + + void glProgramEnvParameterI4uivNV + GLenum target + GLuint index + const GLuint *params + + + void glProgramEnvParameters4fvEXT + GLenum target + GLuint index + GLsizei count + const GLfloat *params + + + + void glProgramEnvParametersI4ivNV + GLenum target + GLuint index + GLsizei count + const GLint *params + + + void glProgramEnvParametersI4uivNV + GLenum target + GLuint index + GLsizei count + const GLuint *params + + + void glProgramLocalParameter4dARB + GLenum target + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glProgramLocalParameter4dvARB + GLenum target + GLuint index + const GLdouble *params + + + void glProgramLocalParameter4fARB + GLenum target + GLuint index + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glProgramLocalParameter4fvARB + GLenum target + GLuint index + const GLfloat *params + + + void glProgramLocalParameterI4iNV + GLenum target + GLuint index + GLint x + GLint y + GLint z + GLint w + + + + void glProgramLocalParameterI4ivNV + GLenum target + GLuint index + const GLint *params + + + void glProgramLocalParameterI4uiNV + GLenum target + GLuint index + GLuint x + GLuint y + GLuint z + GLuint w + + + + void glProgramLocalParameterI4uivNV + GLenum target + GLuint index + const GLuint *params + + + void glProgramLocalParameters4fvEXT + GLenum target + GLuint index + GLsizei count + const GLfloat *params + + + + void glProgramLocalParametersI4ivNV + GLenum target + GLuint index + GLsizei count + const GLint *params + + + void glProgramLocalParametersI4uivNV + GLenum target + GLuint index + GLsizei count + const GLuint *params + + + void glProgramNamedParameter4dNV + GLuint id + GLsizei len + const GLubyte *name + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glProgramNamedParameter4dvNV + GLuint id + GLsizei len + const GLubyte *name + const GLdouble *v + + + + void glProgramNamedParameter4fNV + GLuint id + GLsizei len + const GLubyte *name + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glProgramNamedParameter4fvNV + GLuint id + GLsizei len + const GLubyte *name + const GLfloat *v + + + + void glProgramParameter4dNV + GLenum target + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glProgramParameter4dvNV + GLenum target + GLuint index + const GLdouble *v + + + + void glProgramParameter4fNV + GLenum target + GLuint index + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glProgramParameter4fvNV + GLenum target + GLuint index + const GLfloat *v + + + + void glProgramParameteri + GLuint program + GLenum pname + GLint value + + + void glProgramParameteriARB + GLuint program + GLenum pname + GLint value + + + + void glProgramParameteriEXT + GLuint program + GLenum pname + GLint value + + + + void glProgramParameters4dvNV + GLenum target + GLuint index + GLsizei count + const GLdouble *v + + + + void glProgramParameters4fvNV + GLenum target + GLuint index + GLsizei count + const GLfloat *v + + + + void glProgramPathFragmentInputGenNV + GLuint program + GLint location + GLenum genMode + GLint components + const GLfloat *coeffs + + + void glProgramStringARB + GLenum target + GLenum format + GLsizei len + const void *string + + + void glProgramSubroutineParametersuivNV + GLenum target + GLsizei count + const GLuint *params + + + void glProgramUniform1d + GLuint program + GLint location + GLdouble v0 + + + void glProgramUniform1dEXT + GLuint program + GLint location + GLdouble x + + + void glProgramUniform1dv + GLuint program + GLint location + GLsizei count + const GLdouble *value + + + void glProgramUniform1dvEXT + GLuint program + GLint location + GLsizei count + const GLdouble *value + + + void glProgramUniform1f + GLuint program + GLint location + GLfloat v0 + + + void glProgramUniform1fEXT + GLuint program + GLint location + GLfloat v0 + + + + void glProgramUniform1fv + GLuint program + GLint location + GLsizei count + const GLfloat *value + + + void glProgramUniform1fvEXT + GLuint program + GLint location + GLsizei count + const GLfloat *value + + + + void glProgramUniform1i + GLuint program + GLint location + GLint v0 + + + void glProgramUniform1i64ARB + GLuint program + GLint location + GLint64 x + + + void glProgramUniform1i64NV + GLuint program + GLint location + GLint64EXT x + + + void glProgramUniform1i64vARB + GLuint program + GLint location + GLsizei count + const GLint64 *value + + + void glProgramUniform1i64vNV + GLuint program + GLint location + GLsizei count + const GLint64EXT *value + + + void glProgramUniform1iEXT + GLuint program + GLint location + GLint v0 + + + + void glProgramUniform1iv + GLuint program + GLint location + GLsizei count + const GLint *value + + + void glProgramUniform1ivEXT + GLuint program + GLint location + GLsizei count + const GLint *value + + + + void glProgramUniform1ui + GLuint program + GLint location + GLuint v0 + + + void glProgramUniform1ui64ARB + GLuint program + GLint location + GLuint64 x + + + void glProgramUniform1ui64NV + GLuint program + GLint location + GLuint64EXT x + + + void glProgramUniform1ui64vARB + GLuint program + GLint location + GLsizei count + const GLuint64 *value + + + void glProgramUniform1ui64vNV + GLuint program + GLint location + GLsizei count + const GLuint64EXT *value + + + void glProgramUniform1uiEXT + GLuint program + GLint location + GLuint v0 + + + + void glProgramUniform1uiv + GLuint program + GLint location + GLsizei count + const GLuint *value + + + void glProgramUniform1uivEXT + GLuint program + GLint location + GLsizei count + const GLuint *value + + + + void glProgramUniform2d + GLuint program + GLint location + GLdouble v0 + GLdouble v1 + + + void glProgramUniform2dEXT + GLuint program + GLint location + GLdouble x + GLdouble y + + + void glProgramUniform2dv + GLuint program + GLint location + GLsizei count + const GLdouble *value + + + void glProgramUniform2dvEXT + GLuint program + GLint location + GLsizei count + const GLdouble *value + + + void glProgramUniform2f + GLuint program + GLint location + GLfloat v0 + GLfloat v1 + + + void glProgramUniform2fEXT + GLuint program + GLint location + GLfloat v0 + GLfloat v1 + + + + void glProgramUniform2fv + GLuint program + GLint location + GLsizei count + const GLfloat *value + + + void glProgramUniform2fvEXT + GLuint program + GLint location + GLsizei count + const GLfloat *value + + + + void glProgramUniform2i + GLuint program + GLint location + GLint v0 + GLint v1 + + + void glProgramUniform2i64ARB + GLuint program + GLint location + GLint64 x + GLint64 y + + + void glProgramUniform2i64NV + GLuint program + GLint location + GLint64EXT x + GLint64EXT y + + + void glProgramUniform2i64vARB + GLuint program + GLint location + GLsizei count + const GLint64 *value + + + void glProgramUniform2i64vNV + GLuint program + GLint location + GLsizei count + const GLint64EXT *value + + + void glProgramUniform2iEXT + GLuint program + GLint location + GLint v0 + GLint v1 + + + + void glProgramUniform2iv + GLuint program + GLint location + GLsizei count + const GLint *value + + + void glProgramUniform2ivEXT + GLuint program + GLint location + GLsizei count + const GLint *value + + + + void glProgramUniform2ui + GLuint program + GLint location + GLuint v0 + GLuint v1 + + + void glProgramUniform2ui64ARB + GLuint program + GLint location + GLuint64 x + GLuint64 y + + + void glProgramUniform2ui64NV + GLuint program + GLint location + GLuint64EXT x + GLuint64EXT y + + + void glProgramUniform2ui64vARB + GLuint program + GLint location + GLsizei count + const GLuint64 *value + + + void glProgramUniform2ui64vNV + GLuint program + GLint location + GLsizei count + const GLuint64EXT *value + + + void glProgramUniform2uiEXT + GLuint program + GLint location + GLuint v0 + GLuint v1 + + + + void glProgramUniform2uiv + GLuint program + GLint location + GLsizei count + const GLuint *value + + + void glProgramUniform2uivEXT + GLuint program + GLint location + GLsizei count + const GLuint *value + + + + void glProgramUniform3d + GLuint program + GLint location + GLdouble v0 + GLdouble v1 + GLdouble v2 + + + void glProgramUniform3dEXT + GLuint program + GLint location + GLdouble x + GLdouble y + GLdouble z + + + void glProgramUniform3dv + GLuint program + GLint location + GLsizei count + const GLdouble *value + + + void glProgramUniform3dvEXT + GLuint program + GLint location + GLsizei count + const GLdouble *value + + + void glProgramUniform3f + GLuint program + GLint location + GLfloat v0 + GLfloat v1 + GLfloat v2 + + + void glProgramUniform3fEXT + GLuint program + GLint location + GLfloat v0 + GLfloat v1 + GLfloat v2 + + + + void glProgramUniform3fv + GLuint program + GLint location + GLsizei count + const GLfloat *value + + + void glProgramUniform3fvEXT + GLuint program + GLint location + GLsizei count + const GLfloat *value + + + + void glProgramUniform3i + GLuint program + GLint location + GLint v0 + GLint v1 + GLint v2 + + + void glProgramUniform3i64ARB + GLuint program + GLint location + GLint64 x + GLint64 y + GLint64 z + + + void glProgramUniform3i64NV + GLuint program + GLint location + GLint64EXT x + GLint64EXT y + GLint64EXT z + + + void glProgramUniform3i64vARB + GLuint program + GLint location + GLsizei count + const GLint64 *value + + + void glProgramUniform3i64vNV + GLuint program + GLint location + GLsizei count + const GLint64EXT *value + + + void glProgramUniform3iEXT + GLuint program + GLint location + GLint v0 + GLint v1 + GLint v2 + + + + void glProgramUniform3iv + GLuint program + GLint location + GLsizei count + const GLint *value + + + void glProgramUniform3ivEXT + GLuint program + GLint location + GLsizei count + const GLint *value + + + + void glProgramUniform3ui + GLuint program + GLint location + GLuint v0 + GLuint v1 + GLuint v2 + + + void glProgramUniform3ui64ARB + GLuint program + GLint location + GLuint64 x + GLuint64 y + GLuint64 z + + + void glProgramUniform3ui64NV + GLuint program + GLint location + GLuint64EXT x + GLuint64EXT y + GLuint64EXT z + + + void glProgramUniform3ui64vARB + GLuint program + GLint location + GLsizei count + const GLuint64 *value + + + void glProgramUniform3ui64vNV + GLuint program + GLint location + GLsizei count + const GLuint64EXT *value + + + void glProgramUniform3uiEXT + GLuint program + GLint location + GLuint v0 + GLuint v1 + GLuint v2 + + + + void glProgramUniform3uiv + GLuint program + GLint location + GLsizei count + const GLuint *value + + + void glProgramUniform3uivEXT + GLuint program + GLint location + GLsizei count + const GLuint *value + + + + void glProgramUniform4d + GLuint program + GLint location + GLdouble v0 + GLdouble v1 + GLdouble v2 + GLdouble v3 + + + void glProgramUniform4dEXT + GLuint program + GLint location + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + void glProgramUniform4dv + GLuint program + GLint location + GLsizei count + const GLdouble *value + + + void glProgramUniform4dvEXT + GLuint program + GLint location + GLsizei count + const GLdouble *value + + + void glProgramUniform4f + GLuint program + GLint location + GLfloat v0 + GLfloat v1 + GLfloat v2 + GLfloat v3 + + + void glProgramUniform4fEXT + GLuint program + GLint location + GLfloat v0 + GLfloat v1 + GLfloat v2 + GLfloat v3 + + + + void glProgramUniform4fv + GLuint program + GLint location + GLsizei count + const GLfloat *value + + + void glProgramUniform4fvEXT + GLuint program + GLint location + GLsizei count + const GLfloat *value + + + + void glProgramUniform4i + GLuint program + GLint location + GLint v0 + GLint v1 + GLint v2 + GLint v3 + + + void glProgramUniform4i64ARB + GLuint program + GLint location + GLint64 x + GLint64 y + GLint64 z + GLint64 w + + + void glProgramUniform4i64NV + GLuint program + GLint location + GLint64EXT x + GLint64EXT y + GLint64EXT z + GLint64EXT w + + + void glProgramUniform4i64vARB + GLuint program + GLint location + GLsizei count + const GLint64 *value + + + void glProgramUniform4i64vNV + GLuint program + GLint location + GLsizei count + const GLint64EXT *value + + + void glProgramUniform4iEXT + GLuint program + GLint location + GLint v0 + GLint v1 + GLint v2 + GLint v3 + + + + void glProgramUniform4iv + GLuint program + GLint location + GLsizei count + const GLint *value + + + void glProgramUniform4ivEXT + GLuint program + GLint location + GLsizei count + const GLint *value + + + + void glProgramUniform4ui + GLuint program + GLint location + GLuint v0 + GLuint v1 + GLuint v2 + GLuint v3 + + + void glProgramUniform4ui64ARB + GLuint program + GLint location + GLuint64 x + GLuint64 y + GLuint64 z + GLuint64 w + + + void glProgramUniform4ui64NV + GLuint program + GLint location + GLuint64EXT x + GLuint64EXT y + GLuint64EXT z + GLuint64EXT w + + + void glProgramUniform4ui64vARB + GLuint program + GLint location + GLsizei count + const GLuint64 *value + + + void glProgramUniform4ui64vNV + GLuint program + GLint location + GLsizei count + const GLuint64EXT *value + + + void glProgramUniform4uiEXT + GLuint program + GLint location + GLuint v0 + GLuint v1 + GLuint v2 + GLuint v3 + + + + void glProgramUniform4uiv + GLuint program + GLint location + GLsizei count + const GLuint *value + + + void glProgramUniform4uivEXT + GLuint program + GLint location + GLsizei count + const GLuint *value + + + + void glProgramUniformHandleui64ARB + GLuint program + GLint location + GLuint64 value + + + void glProgramUniformHandleui64IMG + GLuint program + GLint location + GLuint64 value + + + + void glProgramUniformHandleui64NV + GLuint program + GLint location + GLuint64 value + + + void glProgramUniformHandleui64vARB + GLuint program + GLint location + GLsizei count + const GLuint64 *values + + + void glProgramUniformHandleui64vIMG + GLuint program + GLint location + GLsizei count + const GLuint64 *values + + + + void glProgramUniformHandleui64vNV + GLuint program + GLint location + GLsizei count + const GLuint64 *values + + + void glProgramUniformMatrix2dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix2dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix2fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix2fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformMatrix2x3dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix2x3dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix2x3fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix2x3fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformMatrix2x4dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix2x4dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix2x4fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix2x4fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformMatrix3dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix3dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix3fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix3fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformMatrix3x2dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix3x2dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix3x2fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix3x2fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformMatrix3x4dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix3x4dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix3x4fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix3x4fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformMatrix4dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix4dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix4fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix4fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformMatrix4x2dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix4x2dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix4x2fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix4x2fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformMatrix4x3dv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix4x3dvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glProgramUniformMatrix4x3fv + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glProgramUniformMatrix4x3fvEXT + GLuint program + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glProgramUniformui64NV + GLuint program + GLint location + GLuint64EXT value + + + void glProgramUniformui64vNV + GLuint program + GLint location + GLsizei count + const GLuint64EXT *value + + + void glProgramVertexLimitNV + GLenum target + GLint limit + + + void glProvokingVertex + GLenum mode + + + void glProvokingVertexEXT + GLenum mode + + + + void glPushAttrib + GLbitfield mask + + + + void glPushClientAttrib + GLbitfield mask + + + void glPushClientAttribDefaultEXT + GLbitfield mask + + + void glPushDebugGroup + GLenum source + GLuint id + GLsizei length + const GLchar *message + + + void glPushDebugGroupKHR + GLenum source + GLuint id + GLsizei length + const GLchar *message + + + + void glPushGroupMarkerEXT + GLsizei length + const GLchar *marker + + + void glPushMatrix + + + + void glPushName + GLuint name + + + + void glQueryCounter + GLuint id + GLenum target + + + void glQueryCounterEXT + GLuint id + GLenum target + + + + GLbitfield glQueryMatrixxOES + GLfixed *mantissa + GLint *exponent + + + void glQueryObjectParameteruiAMD + GLenum target + GLuint id + GLenum pname + GLuint param + + + GLint glQueryResourceNV + GLenum queryType + GLint tagId + GLuint bufSize + GLint *buffer + + + void glQueryResourceTagNV + GLint tagId + const GLchar *tagString + + + void glRasterPos2d + GLdouble x + GLdouble y + + + + void glRasterPos2dv + const GLdouble *v + + + + void glRasterPos2f + GLfloat x + GLfloat y + + + + void glRasterPos2fv + const GLfloat *v + + + + void glRasterPos2i + GLint x + GLint y + + + + void glRasterPos2iv + const GLint *v + + + + void glRasterPos2s + GLshort x + GLshort y + + + + void glRasterPos2sv + const GLshort *v + + + + void glRasterPos2xOES + GLfixed x + GLfixed y + + + void glRasterPos2xvOES + const GLfixed *coords + + + void glRasterPos3d + GLdouble x + GLdouble y + GLdouble z + + + + void glRasterPos3dv + const GLdouble *v + + + + void glRasterPos3f + GLfloat x + GLfloat y + GLfloat z + + + + void glRasterPos3fv + const GLfloat *v + + + + void glRasterPos3i + GLint x + GLint y + GLint z + + + + void glRasterPos3iv + const GLint *v + + + + void glRasterPos3s + GLshort x + GLshort y + GLshort z + + + + void glRasterPos3sv + const GLshort *v + + + + void glRasterPos3xOES + GLfixed x + GLfixed y + GLfixed z + + + void glRasterPos3xvOES + const GLfixed *coords + + + void glRasterPos4d + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glRasterPos4dv + const GLdouble *v + + + + void glRasterPos4f + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glRasterPos4fv + const GLfloat *v + + + + void glRasterPos4i + GLint x + GLint y + GLint z + GLint w + + + + void glRasterPos4iv + const GLint *v + + + + void glRasterPos4s + GLshort x + GLshort y + GLshort z + GLshort w + + + + void glRasterPos4sv + const GLshort *v + + + + void glRasterPos4xOES + GLfixed x + GLfixed y + GLfixed z + GLfixed w + + + void glRasterPos4xvOES + const GLfixed *coords + + + void glRasterSamplesEXT + GLuint samples + GLboolean fixedsamplelocations + + + void glReadBuffer + GLenum src + + + + void glReadBufferIndexedEXT + GLenum src + GLint index + + + void glReadBufferNV + GLenum mode + + + void glReadInstrumentsSGIX + GLint marker + + + + void glReadPixels + GLint x + GLint y + GLsizei width + GLsizei height + GLenum format + GLenum type + void *pixels + + + + + void glReadnPixels + GLint x + GLint y + GLsizei width + GLsizei height + GLenum format + GLenum type + GLsizei bufSize + void *data + + + void glReadnPixelsARB + GLint x + GLint y + GLsizei width + GLsizei height + GLenum format + GLenum type + GLsizei bufSize + void *data + + + + void glReadnPixelsEXT + GLint x + GLint y + GLsizei width + GLsizei height + GLenum format + GLenum type + GLsizei bufSize + void *data + + + + void glReadnPixelsKHR + GLint x + GLint y + GLsizei width + GLsizei height + GLenum format + GLenum type + GLsizei bufSize + void *data + + + + GLboolean glReleaseKeyedMutexWin32EXT + GLuint memory + GLuint64 key + + + void glRectd + GLdouble x1 + GLdouble y1 + GLdouble x2 + GLdouble y2 + + + + void glRectdv + const GLdouble *v1 + const GLdouble *v2 + + + + void glRectf + GLfloat x1 + GLfloat y1 + GLfloat x2 + GLfloat y2 + + + + void glRectfv + const GLfloat *v1 + const GLfloat *v2 + + + + void glRecti + GLint x1 + GLint y1 + GLint x2 + GLint y2 + + + + void glRectiv + const GLint *v1 + const GLint *v2 + + + + void glRects + GLshort x1 + GLshort y1 + GLshort x2 + GLshort y2 + + + + void glRectsv + const GLshort *v1 + const GLshort *v2 + + + + void glRectxOES + GLfixed x1 + GLfixed y1 + GLfixed x2 + GLfixed y2 + + + void glRectxvOES + const GLfixed *v1 + const GLfixed *v2 + + + void glReferencePlaneSGIX + const GLdouble *equation + + + + void glReleaseShaderCompiler + + + void glRenderGpuMaskNV + GLbitfield mask + + + GLint glRenderMode + GLenum mode + + + + void glRenderbufferStorage + GLenum target + GLenum internalformat + GLsizei width + GLsizei height + + + + void glRenderbufferStorageEXT + GLenum target + GLenum internalformat + GLsizei width + GLsizei height + + + + + void glRenderbufferStorageMultisample + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + + + + void glRenderbufferStorageMultisampleANGLE + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + + + void glRenderbufferStorageMultisampleAPPLE + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + + + void glRenderbufferStorageMultisampleAdvancedAMD + GLenum target + GLsizei samples + GLsizei storageSamples + GLenum internalformat + GLsizei width + GLsizei height + + + void glRenderbufferStorageMultisampleCoverageNV + GLenum target + GLsizei coverageSamples + GLsizei colorSamples + GLenum internalformat + GLsizei width + GLsizei height + + + void glRenderbufferStorageMultisampleEXT + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + + + + + void glRenderbufferStorageMultisampleIMG + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + + + void glRenderbufferStorageMultisampleNV + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + + + + void glRenderbufferStorageOES + GLenum target + GLenum internalformat + GLsizei width + GLsizei height + + + void glReplacementCodePointerSUN + GLenum type + GLsizei stride + const void **pointer + + + void glReplacementCodeubSUN + GLubyte code + + + void glReplacementCodeubvSUN + const GLubyte *code + + + void glReplacementCodeuiColor3fVertex3fSUN + GLuint rc + GLfloat r + GLfloat g + GLfloat b + GLfloat x + GLfloat y + GLfloat z + + + void glReplacementCodeuiColor3fVertex3fvSUN + const GLuint *rc + const GLfloat *c + const GLfloat *v + + + void glReplacementCodeuiColor4fNormal3fVertex3fSUN + GLuint rc + GLfloat r + GLfloat g + GLfloat b + GLfloat a + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + + + void glReplacementCodeuiColor4fNormal3fVertex3fvSUN + const GLuint *rc + const GLfloat *c + const GLfloat *n + const GLfloat *v + + + void glReplacementCodeuiColor4ubVertex3fSUN + GLuint rc + GLubyte r + GLubyte g + GLubyte b + GLubyte a + GLfloat x + GLfloat y + GLfloat z + + + void glReplacementCodeuiColor4ubVertex3fvSUN + const GLuint *rc + const GLubyte *c + const GLfloat *v + + + void glReplacementCodeuiNormal3fVertex3fSUN + GLuint rc + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + + + void glReplacementCodeuiNormal3fVertex3fvSUN + const GLuint *rc + const GLfloat *n + const GLfloat *v + + + void glReplacementCodeuiSUN + GLuint code + + + void glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fSUN + GLuint rc + GLfloat s + GLfloat t + GLfloat r + GLfloat g + GLfloat b + GLfloat a + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + + + void glReplacementCodeuiTexCoord2fColor4fNormal3fVertex3fvSUN + const GLuint *rc + const GLfloat *tc + const GLfloat *c + const GLfloat *n + const GLfloat *v + + + void glReplacementCodeuiTexCoord2fNormal3fVertex3fSUN + GLuint rc + GLfloat s + GLfloat t + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + + + void glReplacementCodeuiTexCoord2fNormal3fVertex3fvSUN + const GLuint *rc + const GLfloat *tc + const GLfloat *n + const GLfloat *v + + + void glReplacementCodeuiTexCoord2fVertex3fSUN + GLuint rc + GLfloat s + GLfloat t + GLfloat x + GLfloat y + GLfloat z + + + void glReplacementCodeuiTexCoord2fVertex3fvSUN + const GLuint *rc + const GLfloat *tc + const GLfloat *v + + + void glReplacementCodeuiVertex3fSUN + GLuint rc + GLfloat x + GLfloat y + GLfloat z + + + void glReplacementCodeuiVertex3fvSUN + const GLuint *rc + const GLfloat *v + + + void glReplacementCodeuivSUN + const GLuint *code + + + void glReplacementCodeusSUN + GLushort code + + + void glReplacementCodeusvSUN + const GLushort *code + + + void glRequestResidentProgramsNV + GLsizei n + const GLuint *programs + + + + void glResetHistogram + GLenum target + + + + void glResetHistogramEXT + GLenum target + + + + + void glResetMemoryObjectParameterNV + GLuint memory + GLenum pname + + + void glResetMinmax + GLenum target + + + + void glResetMinmaxEXT + GLenum target + + + + + void glResizeBuffersMESA + + + void glResolveDepthValuesNV + + + void glResolveMultisampleFramebufferAPPLE + + + void glResumeTransformFeedback + + + void glResumeTransformFeedbackNV + + + + void glRotated + GLdouble angle + GLdouble x + GLdouble y + GLdouble z + + + + void glRotatef + GLfloat angle + GLfloat x + GLfloat y + GLfloat z + + + + void glRotatex + GLfixed angle + GLfixed x + GLfixed y + GLfixed z + + + void glRotatexOES + GLfixed angle + GLfixed x + GLfixed y + GLfixed z + + + void glSampleCoverage + GLfloat value + GLboolean invert + + + + void glSampleCoverageARB + GLfloat value + GLboolean invert + + + + void glSampleCoveragex + GLclampx value + GLboolean invert + + + void glSampleCoveragexOES + GLclampx value + GLboolean invert + + + void glSampleMapATI + GLuint dst + GLuint interp + GLenum swizzle + + + void glSampleMaskEXT + GLclampf value + GLboolean invert + + + void glSampleMaskIndexedNV + GLuint index + GLbitfield mask + + + void glSampleMaskSGIS + GLclampf value + GLboolean invert + + + + + void glSampleMaski + GLuint maskNumber + GLbitfield mask + + + void glSamplePatternEXT + GLenum pattern + + + void glSamplePatternSGIS + GLenum pattern + + + + + void glSamplerParameterIiv + GLuint sampler + GLenum pname + const GLint *param + + + void glSamplerParameterIivEXT + GLuint sampler + GLenum pname + const GLint *param + + + + void glSamplerParameterIivOES + GLuint sampler + GLenum pname + const GLint *param + + + + void glSamplerParameterIuiv + GLuint sampler + GLenum pname + const GLuint *param + + + void glSamplerParameterIuivEXT + GLuint sampler + GLenum pname + const GLuint *param + + + + void glSamplerParameterIuivOES + GLuint sampler + GLenum pname + const GLuint *param + + + + void glSamplerParameterf + GLuint sampler + GLenum pname + GLfloat param + + + void glSamplerParameterfv + GLuint sampler + GLenum pname + const GLfloat *param + + + void glSamplerParameteri + GLuint sampler + GLenum pname + GLint param + + + void glSamplerParameteriv + GLuint sampler + GLenum pname + const GLint *param + + + void glScaled + GLdouble x + GLdouble y + GLdouble z + + + + void glScalef + GLfloat x + GLfloat y + GLfloat z + + + + void glScalex + GLfixed x + GLfixed y + GLfixed z + + + void glScalexOES + GLfixed x + GLfixed y + GLfixed z + + + void glScissor + GLint x + GLint y + GLsizei width + GLsizei height + + + + void glScissorArrayv + GLuint first + GLsizei count + const GLint *v + + + void glScissorArrayvNV + GLuint first + GLsizei count + const GLint *v + + + + void glScissorArrayvOES + GLuint first + GLsizei count + const GLint *v + + + + void glScissorExclusiveArrayvNV + GLuint first + GLsizei count + const GLint *v + + + void glScissorExclusiveNV + GLint x + GLint y + GLsizei width + GLsizei height + + + void glScissorIndexed + GLuint index + GLint left + GLint bottom + GLsizei width + GLsizei height + + + void glScissorIndexedNV + GLuint index + GLint left + GLint bottom + GLsizei width + GLsizei height + + + + void glScissorIndexedOES + GLuint index + GLint left + GLint bottom + GLsizei width + GLsizei height + + + + void glScissorIndexedv + GLuint index + const GLint *v + + + void glScissorIndexedvNV + GLuint index + const GLint *v + + + + void glScissorIndexedvOES + GLuint index + const GLint *v + + + + void glSecondaryColor3b + GLbyte red + GLbyte green + GLbyte blue + + + + void glSecondaryColor3bEXT + GLbyte red + GLbyte green + GLbyte blue + + + + + void glSecondaryColor3bv + const GLbyte *v + + + + void glSecondaryColor3bvEXT + const GLbyte *v + + + + + void glSecondaryColor3d + GLdouble red + GLdouble green + GLdouble blue + + + + void glSecondaryColor3dEXT + GLdouble red + GLdouble green + GLdouble blue + + + + + void glSecondaryColor3dv + const GLdouble *v + + + + void glSecondaryColor3dvEXT + const GLdouble *v + + + + + void glSecondaryColor3f + GLfloat red + GLfloat green + GLfloat blue + + + + void glSecondaryColor3fEXT + GLfloat red + GLfloat green + GLfloat blue + + + + + void glSecondaryColor3fv + const GLfloat *v + + + + void glSecondaryColor3fvEXT + const GLfloat *v + + + + + void glSecondaryColor3hNV + GLhalfNV red + GLhalfNV green + GLhalfNV blue + + + + void glSecondaryColor3hvNV + const GLhalfNV *v + + + + void glSecondaryColor3i + GLint red + GLint green + GLint blue + + + + void glSecondaryColor3iEXT + GLint red + GLint green + GLint blue + + + + + void glSecondaryColor3iv + const GLint *v + + + + void glSecondaryColor3ivEXT + const GLint *v + + + + + void glSecondaryColor3s + GLshort red + GLshort green + GLshort blue + + + + void glSecondaryColor3sEXT + GLshort red + GLshort green + GLshort blue + + + + + void glSecondaryColor3sv + const GLshort *v + + + + void glSecondaryColor3svEXT + const GLshort *v + + + + + void glSecondaryColor3ub + GLubyte red + GLubyte green + GLubyte blue + + + + void glSecondaryColor3ubEXT + GLubyte red + GLubyte green + GLubyte blue + + + + + void glSecondaryColor3ubv + const GLubyte *v + + + + void glSecondaryColor3ubvEXT + const GLubyte *v + + + + + void glSecondaryColor3ui + GLuint red + GLuint green + GLuint blue + + + + void glSecondaryColor3uiEXT + GLuint red + GLuint green + GLuint blue + + + + + void glSecondaryColor3uiv + const GLuint *v + + + + void glSecondaryColor3uivEXT + const GLuint *v + + + + + void glSecondaryColor3us + GLushort red + GLushort green + GLushort blue + + + + void glSecondaryColor3usEXT + GLushort red + GLushort green + GLushort blue + + + + + void glSecondaryColor3usv + const GLushort *v + + + + void glSecondaryColor3usvEXT + const GLushort *v + + + + + void glSecondaryColorFormatNV + GLint size + GLenum type + GLsizei stride + + + void glSecondaryColorP3ui + GLenum type + GLuint color + + + void glSecondaryColorP3uiv + GLenum type + const GLuint *color + + + void glSecondaryColorPointer + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glSecondaryColorPointerEXT + GLint size + GLenum type + GLsizei stride + const void *pointer + + + + void glSecondaryColorPointerListIBM + GLint size + GLenum type + GLint stride + const void **pointer + GLint ptrstride + + + void glSelectBuffer + GLsizei size + GLuint *buffer + + + + void glSelectPerfMonitorCountersAMD + GLuint monitor + GLboolean enable + GLuint group + GLint numCounters + GLuint *counterList + + + void glSemaphoreParameterui64vEXT + GLuint semaphore + GLenum pname + const GLuint64 *params + + + void glSeparableFilter2D + GLenum target + GLenum internalformat + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *row + const void *column + + + + + void glSeparableFilter2DEXT + GLenum target + GLenum internalformat + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *row + const void *column + + + + + void glSetFenceAPPLE + GLuint fence + + + void glSetFenceNV + GLuint fence + GLenum condition + + + void glSetFragmentShaderConstantATI + GLuint dst + const GLfloat *value + + + void glSetInvariantEXT + GLuint id + GLenum type + const void *addr + + + void glSetLocalConstantEXT + GLuint id + GLenum type + const void *addr + + + void glSetMultisamplefvAMD + GLenum pname + GLuint index + const GLfloat *val + + + void glShadeModel + GLenum mode + + + + void glShaderBinary + GLsizei count + const GLuint *shaders + GLenum binaryformat + const void *binary + GLsizei length + + + void glShaderOp1EXT + GLenum op + GLuint res + GLuint arg1 + + + void glShaderOp2EXT + GLenum op + GLuint res + GLuint arg1 + GLuint arg2 + + + void glShaderOp3EXT + GLenum op + GLuint res + GLuint arg1 + GLuint arg2 + GLuint arg3 + + + void glShaderSource + GLuint shader + GLsizei count + const GLchar *const*string + const GLint *length + + + void glShaderSourceARB + GLhandleARB shaderObj + GLsizei count + const GLcharARB **string + const GLint *length + + + + void glShaderStorageBlockBinding + GLuint program + GLuint storageBlockIndex + GLuint storageBlockBinding + + + void glShadingRateImageBarrierNV + GLboolean synchronize + + + void glShadingRateImagePaletteNV + GLuint viewport + GLuint first + GLsizei count + const GLenum *rates + + + void glShadingRateSampleOrderNV + GLenum order + + + void glShadingRateSampleOrderCustomNV + GLenum rate + GLuint samples + const GLint *locations + + + void glSharpenTexFuncSGIS + GLenum target + GLsizei n + const GLfloat *points + + + + void glSignalSemaphoreEXT + GLuint semaphore + GLuint numBufferBarriers + const GLuint *buffers + GLuint numTextureBarriers + const GLuint *textures + const GLenum *dstLayouts + + + void glSpecializeShader + GLuint shader + const GLchar *pEntryPoint + GLuint numSpecializationConstants + const GLuint *pConstantIndex + const GLuint *pConstantValue + + + void glSpecializeShaderARB + GLuint shader + const GLchar *pEntryPoint + GLuint numSpecializationConstants + const GLuint *pConstantIndex + const GLuint *pConstantValue + + + + void glSpriteParameterfSGIX + GLenum pname + GLfloat param + + + + void glSpriteParameterfvSGIX + GLenum pname + const GLfloat *params + + + + void glSpriteParameteriSGIX + GLenum pname + GLint param + + + + void glSpriteParameterivSGIX + GLenum pname + const GLint *params + + + + void glStartInstrumentsSGIX + + + + void glStartTilingQCOM + GLuint x + GLuint y + GLuint width + GLuint height + GLbitfield preserveMask + + + void glStateCaptureNV + GLuint state + GLenum mode + + + void glStencilClearTagEXT + GLsizei stencilTagBits + GLuint stencilClearTag + + + + void glStencilFillPathInstancedNV + GLsizei numPaths + GLenum pathNameType + const void *paths + GLuint pathBase + GLenum fillMode + GLuint mask + GLenum transformType + const GLfloat *transformValues + + + void glStencilFillPathNV + GLuint path + GLenum fillMode + GLuint mask + + + void glStencilFunc + GLenum func + GLint ref + GLuint mask + + + + void glStencilFuncSeparate + GLenum face + GLenum func + GLint ref + GLuint mask + + + void glStencilFuncSeparateATI + GLenum frontfunc + GLenum backfunc + GLint ref + GLuint mask + + + void glStencilMask + GLuint mask + + + + void glStencilMaskSeparate + GLenum face + GLuint mask + + + void glStencilOp + GLenum fail + GLenum zfail + GLenum zpass + + + + void glStencilOpSeparate + GLenum face + GLenum sfail + GLenum dpfail + GLenum dppass + + + void glStencilOpSeparateATI + GLenum face + GLenum sfail + GLenum dpfail + GLenum dppass + + + + void glStencilOpValueAMD + GLenum face + GLuint value + + + void glStencilStrokePathInstancedNV + GLsizei numPaths + GLenum pathNameType + const void *paths + GLuint pathBase + GLint reference + GLuint mask + GLenum transformType + const GLfloat *transformValues + + + void glStencilStrokePathNV + GLuint path + GLint reference + GLuint mask + + + void glStencilThenCoverFillPathInstancedNV + GLsizei numPaths + GLenum pathNameType + const void *paths + GLuint pathBase + GLenum fillMode + GLuint mask + GLenum coverMode + GLenum transformType + const GLfloat *transformValues + + + void glStencilThenCoverFillPathNV + GLuint path + GLenum fillMode + GLuint mask + GLenum coverMode + + + void glStencilThenCoverStrokePathInstancedNV + GLsizei numPaths + GLenum pathNameType + const void *paths + GLuint pathBase + GLint reference + GLuint mask + GLenum coverMode + GLenum transformType + const GLfloat *transformValues + + + void glStencilThenCoverStrokePathNV + GLuint path + GLint reference + GLuint mask + GLenum coverMode + + + void glStopInstrumentsSGIX + GLint marker + + + + void glStringMarkerGREMEDY + GLsizei len + const void *string + + + void glSubpixelPrecisionBiasNV + GLuint xbits + GLuint ybits + + + void glSwizzleEXT + GLuint res + GLuint in + GLenum outX + GLenum outY + GLenum outZ + GLenum outW + + + void glSyncTextureINTEL + GLuint texture + + + void glTagSampleBufferSGIX + + + + void glTangent3bEXT + GLbyte tx + GLbyte ty + GLbyte tz + + + + void glTangent3bvEXT + const GLbyte *v + + + void glTangent3dEXT + GLdouble tx + GLdouble ty + GLdouble tz + + + + void glTangent3dvEXT + const GLdouble *v + + + void glTangent3fEXT + GLfloat tx + GLfloat ty + GLfloat tz + + + + void glTangent3fvEXT + const GLfloat *v + + + void glTangent3iEXT + GLint tx + GLint ty + GLint tz + + + + void glTangent3ivEXT + const GLint *v + + + void glTangent3sEXT + GLshort tx + GLshort ty + GLshort tz + + + + void glTangent3svEXT + const GLshort *v + + + void glTangentPointerEXT + GLenum type + GLsizei stride + const void *pointer + + + void glTbufferMask3DFX + GLuint mask + + + void glTessellationFactorAMD + GLfloat factor + + + void glTessellationModeAMD + GLenum mode + + + GLboolean glTestFenceAPPLE + GLuint fence + + + GLboolean glTestFenceNV + GLuint fence + + + + GLboolean glTestObjectAPPLE + GLenum object + GLuint name + + + void glTexAttachMemoryNV + GLenum target + GLuint memory + GLuint64 offset + + + void glTexBuffer + GLenum target + GLenum internalformat + GLuint buffer + + + void glTexBufferARB + GLenum target + GLenum internalformat + GLuint buffer + + + + + void glTexBufferEXT + GLenum target + GLenum internalformat + GLuint buffer + + + + void glTexBufferOES + GLenum target + GLenum internalformat + GLuint buffer + + + + void glTexBufferRange + GLenum target + GLenum internalformat + GLuint buffer + GLintptr offset + GLsizeiptr size + + + void glTexBufferRangeEXT + GLenum target + GLenum internalformat + GLuint buffer + GLintptr offset + GLsizeiptr size + + + + void glTexBufferRangeOES + GLenum target + GLenum internalformat + GLuint buffer + GLintptr offset + GLsizeiptr size + + + + void glTexBumpParameterfvATI + GLenum pname + const GLfloat *param + + + void glTexBumpParameterivATI + GLenum pname + const GLint *param + + + void glTexCoord1bOES + GLbyte s + + + void glTexCoord1bvOES + const GLbyte *coords + + + void glTexCoord1d + GLdouble s + + + + void glTexCoord1dv + const GLdouble *v + + + + void glTexCoord1f + GLfloat s + + + + void glTexCoord1fv + const GLfloat *v + + + + void glTexCoord1hNV + GLhalfNV s + + + + void glTexCoord1hvNV + const GLhalfNV *v + + + + void glTexCoord1i + GLint s + + + + void glTexCoord1iv + const GLint *v + + + + void glTexCoord1s + GLshort s + + + + void glTexCoord1sv + const GLshort *v + + + + void glTexCoord1xOES + GLfixed s + + + void glTexCoord1xvOES + const GLfixed *coords + + + void glTexCoord2bOES + GLbyte s + GLbyte t + + + void glTexCoord2bvOES + const GLbyte *coords + + + void glTexCoord2d + GLdouble s + GLdouble t + + + + void glTexCoord2dv + const GLdouble *v + + + + void glTexCoord2f + GLfloat s + GLfloat t + + + + void glTexCoord2fColor3fVertex3fSUN + GLfloat s + GLfloat t + GLfloat r + GLfloat g + GLfloat b + GLfloat x + GLfloat y + GLfloat z + + + void glTexCoord2fColor3fVertex3fvSUN + const GLfloat *tc + const GLfloat *c + const GLfloat *v + + + void glTexCoord2fColor4fNormal3fVertex3fSUN + GLfloat s + GLfloat t + GLfloat r + GLfloat g + GLfloat b + GLfloat a + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + + + void glTexCoord2fColor4fNormal3fVertex3fvSUN + const GLfloat *tc + const GLfloat *c + const GLfloat *n + const GLfloat *v + + + void glTexCoord2fColor4ubVertex3fSUN + GLfloat s + GLfloat t + GLubyte r + GLubyte g + GLubyte b + GLubyte a + GLfloat x + GLfloat y + GLfloat z + + + void glTexCoord2fColor4ubVertex3fvSUN + const GLfloat *tc + const GLubyte *c + const GLfloat *v + + + void glTexCoord2fNormal3fVertex3fSUN + GLfloat s + GLfloat t + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + + + void glTexCoord2fNormal3fVertex3fvSUN + const GLfloat *tc + const GLfloat *n + const GLfloat *v + + + void glTexCoord2fVertex3fSUN + GLfloat s + GLfloat t + GLfloat x + GLfloat y + GLfloat z + + + void glTexCoord2fVertex3fvSUN + const GLfloat *tc + const GLfloat *v + + + void glTexCoord2fv + const GLfloat *v + + + + void glTexCoord2hNV + GLhalfNV s + GLhalfNV t + + + + void glTexCoord2hvNV + const GLhalfNV *v + + + + void glTexCoord2i + GLint s + GLint t + + + + void glTexCoord2iv + const GLint *v + + + + void glTexCoord2s + GLshort s + GLshort t + + + + void glTexCoord2sv + const GLshort *v + + + + void glTexCoord2xOES + GLfixed s + GLfixed t + + + void glTexCoord2xvOES + const GLfixed *coords + + + void glTexCoord3bOES + GLbyte s + GLbyte t + GLbyte r + + + void glTexCoord3bvOES + const GLbyte *coords + + + void glTexCoord3d + GLdouble s + GLdouble t + GLdouble r + + + + void glTexCoord3dv + const GLdouble *v + + + + void glTexCoord3f + GLfloat s + GLfloat t + GLfloat r + + + + void glTexCoord3fv + const GLfloat *v + + + + void glTexCoord3hNV + GLhalfNV s + GLhalfNV t + GLhalfNV r + + + + void glTexCoord3hvNV + const GLhalfNV *v + + + + void glTexCoord3i + GLint s + GLint t + GLint r + + + + void glTexCoord3iv + const GLint *v + + + + void glTexCoord3s + GLshort s + GLshort t + GLshort r + + + + void glTexCoord3sv + const GLshort *v + + + + void glTexCoord3xOES + GLfixed s + GLfixed t + GLfixed r + + + void glTexCoord3xvOES + const GLfixed *coords + + + void glTexCoord4bOES + GLbyte s + GLbyte t + GLbyte r + GLbyte q + + + void glTexCoord4bvOES + const GLbyte *coords + + + void glTexCoord4d + GLdouble s + GLdouble t + GLdouble r + GLdouble q + + + + void glTexCoord4dv + const GLdouble *v + + + + void glTexCoord4f + GLfloat s + GLfloat t + GLfloat r + GLfloat q + + + + void glTexCoord4fColor4fNormal3fVertex4fSUN + GLfloat s + GLfloat t + GLfloat p + GLfloat q + GLfloat r + GLfloat g + GLfloat b + GLfloat a + GLfloat nx + GLfloat ny + GLfloat nz + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + void glTexCoord4fColor4fNormal3fVertex4fvSUN + const GLfloat *tc + const GLfloat *c + const GLfloat *n + const GLfloat *v + + + void glTexCoord4fVertex4fSUN + GLfloat s + GLfloat t + GLfloat p + GLfloat q + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + void glTexCoord4fVertex4fvSUN + const GLfloat *tc + const GLfloat *v + + + void glTexCoord4fv + const GLfloat *v + + + + void glTexCoord4hNV + GLhalfNV s + GLhalfNV t + GLhalfNV r + GLhalfNV q + + + + void glTexCoord4hvNV + const GLhalfNV *v + + + + void glTexCoord4i + GLint s + GLint t + GLint r + GLint q + + + + void glTexCoord4iv + const GLint *v + + + + void glTexCoord4s + GLshort s + GLshort t + GLshort r + GLshort q + + + + void glTexCoord4sv + const GLshort *v + + + + void glTexCoord4xOES + GLfixed s + GLfixed t + GLfixed r + GLfixed q + + + void glTexCoord4xvOES + const GLfixed *coords + + + void glTexCoordFormatNV + GLint size + GLenum type + GLsizei stride + + + void glTexCoordP1ui + GLenum type + GLuint coords + + + void glTexCoordP1uiv + GLenum type + const GLuint *coords + + + void glTexCoordP2ui + GLenum type + GLuint coords + + + void glTexCoordP2uiv + GLenum type + const GLuint *coords + + + void glTexCoordP3ui + GLenum type + GLuint coords + + + void glTexCoordP3uiv + GLenum type + const GLuint *coords + + + void glTexCoordP4ui + GLenum type + GLuint coords + + + void glTexCoordP4uiv + GLenum type + const GLuint *coords + + + void glTexCoordPointer + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glTexCoordPointerEXT + GLint size + GLenum type + GLsizei stride + GLsizei count + const void *pointer + + + void glTexCoordPointerListIBM + GLint size + GLenum type + GLint stride + const void **pointer + GLint ptrstride + + + void glTexCoordPointervINTEL + GLint size + GLenum type + const void **pointer + + + void glTexEnvf + GLenum target + GLenum pname + GLfloat param + + + + void glTexEnvfv + GLenum target + GLenum pname + const GLfloat *params + + + + void glTexEnvi + GLenum target + GLenum pname + GLint param + + + + void glTexEnviv + GLenum target + GLenum pname + const GLint *params + + + + void glTexEnvx + GLenum target + GLenum pname + GLfixed param + + + void glTexEnvxOES + GLenum target + GLenum pname + GLfixed param + + + void glTexEnvxv + GLenum target + GLenum pname + const GLfixed *params + + + void glTexEnvxvOES + GLenum target + GLenum pname + const GLfixed *params + + + void glTexFilterFuncSGIS + GLenum target + GLenum filter + GLsizei n + const GLfloat *weights + + + + void glTexGend + GLenum coord + GLenum pname + GLdouble param + + + + void glTexGendv + GLenum coord + GLenum pname + const GLdouble *params + + + + void glTexGenf + GLenum coord + GLenum pname + GLfloat param + + + + void glTexGenfOES + GLenum coord + GLenum pname + GLfloat param + + + void glTexGenfv + GLenum coord + GLenum pname + const GLfloat *params + + + + void glTexGenfvOES + GLenum coord + GLenum pname + const GLfloat *params + + + void glTexGeni + GLenum coord + GLenum pname + GLint param + + + + void glTexGeniOES + GLenum coord + GLenum pname + GLint param + + + void glTexGeniv + GLenum coord + GLenum pname + const GLint *params + + + + void glTexGenivOES + GLenum coord + GLenum pname + const GLint *params + + + void glTexGenxOES + GLenum coord + GLenum pname + GLfixed param + + + void glTexGenxvOES + GLenum coord + GLenum pname + const GLfixed *params + + + void glTexImage1D + GLenum target + GLint level + GLint internalformat + GLsizei width + GLint border + GLenum format + GLenum type + const void *pixels + + + + + void glTexImage2D + GLenum target + GLint level + GLint internalformat + GLsizei width + GLsizei height + GLint border + GLenum format + GLenum type + const void *pixels + + + + + void glTexImage2DMultisample + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLboolean fixedsamplelocations + + + void glTexImage2DMultisampleCoverageNV + GLenum target + GLsizei coverageSamples + GLsizei colorSamples + GLint internalFormat + GLsizei width + GLsizei height + GLboolean fixedSampleLocations + + + void glTexImage3D + GLenum target + GLint level + GLint internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLenum format + GLenum type + const void *pixels + + + + + void glTexImage3DEXT + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLenum format + GLenum type + const void *pixels + + + + + void glTexImage3DMultisample + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedsamplelocations + + + void glTexImage3DMultisampleCoverageNV + GLenum target + GLsizei coverageSamples + GLsizei colorSamples + GLint internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedSampleLocations + + + void glTexImage3DOES + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLenum format + GLenum type + const void *pixels + + + void glTexImage4DSGIS + GLenum target + GLint level + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLsizei size4d + GLint border + GLenum format + GLenum type + const void *pixels + + + + void glTexPageCommitmentARB + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLboolean commit + + + void glTexPageCommitmentEXT + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLboolean commit + + + + void glTexParameterIiv + GLenum target + GLenum pname + const GLint *params + + + + void glTexParameterIivEXT + GLenum target + GLenum pname + const GLint *params + + + + void glTexParameterIivOES + GLenum target + GLenum pname + const GLint *params + + + + void glTexParameterIuiv + GLenum target + GLenum pname + const GLuint *params + + + + void glTexParameterIuivEXT + GLenum target + GLenum pname + const GLuint *params + + + + void glTexParameterIuivOES + GLenum target + GLenum pname + const GLuint *params + + + + void glTexParameterf + GLenum target + GLenum pname + GLfloat param + + + + void glTexParameterfv + GLenum target + GLenum pname + const GLfloat *params + + + + void glTexParameteri + GLenum target + GLenum pname + GLint param + + + + void glTexParameteriv + GLenum target + GLenum pname + const GLint *params + + + + void glTexParameterx + GLenum target + GLenum pname + GLfixed param + + + void glTexParameterxOES + GLenum target + GLenum pname + GLfixed param + + + void glTexParameterxv + GLenum target + GLenum pname + const GLfixed *params + + + void glTexParameterxvOES + GLenum target + GLenum pname + const GLfixed *params + + + void glTexRenderbufferNV + GLenum target + GLuint renderbuffer + + + void glTexStorage1D + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + + + void glTexStorage1DEXT + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + + + + void glTexStorage2D + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + GLsizei height + + + void glTexStorage2DEXT + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + GLsizei height + + + + void glTexStorage2DMultisample + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLboolean fixedsamplelocations + + + void glTexStorage3D + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + + + void glTexStorage3DEXT + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + + + + void glTexStorage3DMultisample + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedsamplelocations + + + void glTexStorage3DMultisampleOES + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedsamplelocations + + + + void glTexStorageMem1DEXT + GLenum target + GLsizei levels + GLenum internalFormat + GLsizei width + GLuint memory + GLuint64 offset + + + void glTexStorageMem2DEXT + GLenum target + GLsizei levels + GLenum internalFormat + GLsizei width + GLsizei height + GLuint memory + GLuint64 offset + + + void glTexStorageMem2DMultisampleEXT + GLenum target + GLsizei samples + GLenum internalFormat + GLsizei width + GLsizei height + GLboolean fixedSampleLocations + GLuint memory + GLuint64 offset + + + void glTexStorageMem3DEXT + GLenum target + GLsizei levels + GLenum internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLuint memory + GLuint64 offset + + + void glTexStorageMem3DMultisampleEXT + GLenum target + GLsizei samples + GLenum internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedSampleLocations + GLuint memory + GLuint64 offset + + + void glTexStorageSparseAMD + GLenum target + GLenum internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLsizei layers + GLbitfield flags + + + void glTexSubImage1D + GLenum target + GLint level + GLint xoffset + GLsizei width + GLenum format + GLenum type + const void *pixels + + + + + void glTexSubImage1DEXT + GLenum target + GLint level + GLint xoffset + GLsizei width + GLenum format + GLenum type + const void *pixels + + + + + void glTexSubImage2D + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *pixels + + + + + void glTexSubImage2DEXT + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *pixels + + + + + void glTexSubImage3D + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + const void *pixels + + + + + void glTexSubImage3DEXT + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + const void *pixels + + + + + void glTexSubImage3DOES + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + const void *pixels + + + void glTexSubImage4DSGIS + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLint woffset + GLsizei width + GLsizei height + GLsizei depth + GLsizei size4d + GLenum format + GLenum type + const void *pixels + + + + void glTextureAttachMemoryNV + GLuint texture + GLuint memory + GLuint64 offset + + + void glTextureBarrier + + + void glTextureBarrierNV + + + + void glTextureBuffer + GLuint texture + GLenum internalformat + GLuint buffer + + + void glTextureBufferEXT + GLuint texture + GLenum target + GLenum internalformat + GLuint buffer + + + void glTextureBufferRange + GLuint texture + GLenum internalformat + GLuint buffer + GLintptr offset + GLsizeiptr size + + + void glTextureBufferRangeEXT + GLuint texture + GLenum target + GLenum internalformat + GLuint buffer + GLintptr offset + GLsizeiptr size + + + void glTextureColorMaskSGIS + GLboolean red + GLboolean green + GLboolean blue + GLboolean alpha + + + + void glTextureFoveationParametersQCOM + GLuint texture + GLuint layer + GLuint focalPoint + GLfloat focalX + GLfloat focalY + GLfloat gainX + GLfloat gainY + GLfloat foveaArea + + + void glTextureImage1DEXT + GLuint texture + GLenum target + GLint level + GLint internalformat + GLsizei width + GLint border + GLenum format + GLenum type + const void *pixels + + + void glTextureImage2DEXT + GLuint texture + GLenum target + GLint level + GLint internalformat + GLsizei width + GLsizei height + GLint border + GLenum format + GLenum type + const void *pixels + + + void glTextureImage2DMultisampleCoverageNV + GLuint texture + GLenum target + GLsizei coverageSamples + GLsizei colorSamples + GLint internalFormat + GLsizei width + GLsizei height + GLboolean fixedSampleLocations + + + void glTextureImage2DMultisampleNV + GLuint texture + GLenum target + GLsizei samples + GLint internalFormat + GLsizei width + GLsizei height + GLboolean fixedSampleLocations + + + void glTextureImage3DEXT + GLuint texture + GLenum target + GLint level + GLint internalformat + GLsizei width + GLsizei height + GLsizei depth + GLint border + GLenum format + GLenum type + const void *pixels + + + void glTextureImage3DMultisampleCoverageNV + GLuint texture + GLenum target + GLsizei coverageSamples + GLsizei colorSamples + GLint internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedSampleLocations + + + void glTextureImage3DMultisampleNV + GLuint texture + GLenum target + GLsizei samples + GLint internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedSampleLocations + + + void glTextureLightEXT + GLenum pname + + + void glTextureMaterialEXT + GLenum face + GLenum mode + + + void glTextureNormalEXT + GLenum mode + + + void glTexturePageCommitmentEXT + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLboolean commit + + + void glTextureParameterIiv + GLuint texture + GLenum pname + const GLint *params + + + void glTextureParameterIivEXT + GLuint texture + GLenum target + GLenum pname + const GLint *params + + + void glTextureParameterIuiv + GLuint texture + GLenum pname + const GLuint *params + + + void glTextureParameterIuivEXT + GLuint texture + GLenum target + GLenum pname + const GLuint *params + + + void glTextureParameterf + GLuint texture + GLenum pname + GLfloat param + + + void glTextureParameterfEXT + GLuint texture + GLenum target + GLenum pname + GLfloat param + + + + void glTextureParameterfv + GLuint texture + GLenum pname + const GLfloat *param + + + void glTextureParameterfvEXT + GLuint texture + GLenum target + GLenum pname + const GLfloat *params + + + void glTextureParameteri + GLuint texture + GLenum pname + GLint param + + + void glTextureParameteriEXT + GLuint texture + GLenum target + GLenum pname + GLint param + + + + void glTextureParameteriv + GLuint texture + GLenum pname + const GLint *param + + + void glTextureParameterivEXT + GLuint texture + GLenum target + GLenum pname + const GLint *params + + + void glTextureRangeAPPLE + GLenum target + GLsizei length + const void *pointer + + + void glTextureRenderbufferEXT + GLuint texture + GLenum target + GLuint renderbuffer + + + void glTextureStorage1D + GLuint texture + GLsizei levels + GLenum internalformat + GLsizei width + + + void glTextureStorage1DEXT + GLuint texture + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + + + void glTextureStorage2D + GLuint texture + GLsizei levels + GLenum internalformat + GLsizei width + GLsizei height + + + void glTextureStorage2DEXT + GLuint texture + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + GLsizei height + + + void glTextureStorage2DMultisample + GLuint texture + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLboolean fixedsamplelocations + + + void glTextureStorage2DMultisampleEXT + GLuint texture + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLboolean fixedsamplelocations + + + void glTextureStorage3D + GLuint texture + GLsizei levels + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + + + void glTextureStorage3DEXT + GLuint texture + GLenum target + GLsizei levels + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + + + void glTextureStorage3DMultisample + GLuint texture + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedsamplelocations + + + void glTextureStorage3DMultisampleEXT + GLuint texture + GLenum target + GLsizei samples + GLenum internalformat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedsamplelocations + + + void glTextureStorageMem1DEXT + GLuint texture + GLsizei levels + GLenum internalFormat + GLsizei width + GLuint memory + GLuint64 offset + + + void glTextureStorageMem2DEXT + GLuint texture + GLsizei levels + GLenum internalFormat + GLsizei width + GLsizei height + GLuint memory + GLuint64 offset + + + void glTextureStorageMem2DMultisampleEXT + GLuint texture + GLsizei samples + GLenum internalFormat + GLsizei width + GLsizei height + GLboolean fixedSampleLocations + GLuint memory + GLuint64 offset + + + void glTextureStorageMem3DEXT + GLuint texture + GLsizei levels + GLenum internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLuint memory + GLuint64 offset + + + void glTextureStorageMem3DMultisampleEXT + GLuint texture + GLsizei samples + GLenum internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLboolean fixedSampleLocations + GLuint memory + GLuint64 offset + + + void glTextureStorageSparseAMD + GLuint texture + GLenum target + GLenum internalFormat + GLsizei width + GLsizei height + GLsizei depth + GLsizei layers + GLbitfield flags + + + void glTextureSubImage1D + GLuint texture + GLint level + GLint xoffset + GLsizei width + GLenum format + GLenum type + const void *pixels + + + void glTextureSubImage1DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLsizei width + GLenum format + GLenum type + const void *pixels + + + void glTextureSubImage2D + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *pixels + + + void glTextureSubImage2DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLsizei width + GLsizei height + GLenum format + GLenum type + const void *pixels + + + void glTextureSubImage3D + GLuint texture + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + const void *pixels + + + void glTextureSubImage3DEXT + GLuint texture + GLenum target + GLint level + GLint xoffset + GLint yoffset + GLint zoffset + GLsizei width + GLsizei height + GLsizei depth + GLenum format + GLenum type + const void *pixels + + + void glTextureView + GLuint texture + GLenum target + GLuint origtexture + GLenum internalformat + GLuint minlevel + GLuint numlevels + GLuint minlayer + GLuint numlayers + + + void glTextureViewEXT + GLuint texture + GLenum target + GLuint origtexture + GLenum internalformat + GLuint minlevel + GLuint numlevels + GLuint minlayer + GLuint numlayers + + + + void glTextureViewOES + GLuint texture + GLenum target + GLuint origtexture + GLenum internalformat + GLuint minlevel + GLuint numlevels + GLuint minlayer + GLuint numlayers + + + + void glTrackMatrixNV + GLenum target + GLuint address + GLenum matrix + GLenum transform + + + + void glTransformFeedbackAttribsNV + GLsizei count + const GLint *attribs + GLenum bufferMode + + + void glTransformFeedbackBufferBase + GLuint xfb + GLuint index + GLuint buffer + + + void glTransformFeedbackBufferRange + GLuint xfb + GLuint index + GLuint buffer + GLintptr offset + GLsizeiptr size + + + void glTransformFeedbackStreamAttribsNV + GLsizei count + const GLint *attribs + GLsizei nbuffers + const GLint *bufstreams + GLenum bufferMode + + + void glTransformFeedbackVaryings + GLuint program + GLsizei count + const GLchar *const*varyings + GLenum bufferMode + + + + void glTransformFeedbackVaryingsEXT + GLuint program + GLsizei count + const GLchar *const*varyings + GLenum bufferMode + + + + void glTransformFeedbackVaryingsNV + GLuint program + GLsizei count + const GLint *locations + GLenum bufferMode + + + void glTransformPathNV + GLuint resultPath + GLuint srcPath + GLenum transformType + const GLfloat *transformValues + + + void glTranslated + GLdouble x + GLdouble y + GLdouble z + + + + void glTranslatef + GLfloat x + GLfloat y + GLfloat z + + + + void glTranslatex + GLfixed x + GLfixed y + GLfixed z + + + void glTranslatexOES + GLfixed x + GLfixed y + GLfixed z + + + void glUniform1d + GLint location + GLdouble x + + + void glUniform1dv + GLint location + GLsizei count + const GLdouble *value + + + void glUniform1f + GLint location + GLfloat v0 + + + void glUniform1fARB + GLint location + GLfloat v0 + + + + void glUniform1fv + GLint location + GLsizei count + const GLfloat *value + + + void glUniform1fvARB + GLint location + GLsizei count + const GLfloat *value + + + + void glUniform1i + GLint location + GLint v0 + + + void glUniform1i64ARB + GLint location + GLint64 x + + + void glUniform1i64NV + GLint location + GLint64EXT x + + + void glUniform1i64vARB + GLint location + GLsizei count + const GLint64 *value + + + void glUniform1i64vNV + GLint location + GLsizei count + const GLint64EXT *value + + + void glUniform1iARB + GLint location + GLint v0 + + + + void glUniform1iv + GLint location + GLsizei count + const GLint *value + + + void glUniform1ivARB + GLint location + GLsizei count + const GLint *value + + + + void glUniform1ui + GLint location + GLuint v0 + + + void glUniform1ui64ARB + GLint location + GLuint64 x + + + void glUniform1ui64NV + GLint location + GLuint64EXT x + + + void glUniform1ui64vARB + GLint location + GLsizei count + const GLuint64 *value + + + void glUniform1ui64vNV + GLint location + GLsizei count + const GLuint64EXT *value + + + void glUniform1uiEXT + GLint location + GLuint v0 + + + + void glUniform1uiv + GLint location + GLsizei count + const GLuint *value + + + void glUniform1uivEXT + GLint location + GLsizei count + const GLuint *value + + + + void glUniform2d + GLint location + GLdouble x + GLdouble y + + + void glUniform2dv + GLint location + GLsizei count + const GLdouble *value + + + void glUniform2f + GLint location + GLfloat v0 + GLfloat v1 + + + void glUniform2fARB + GLint location + GLfloat v0 + GLfloat v1 + + + + void glUniform2fv + GLint location + GLsizei count + const GLfloat *value + + + void glUniform2fvARB + GLint location + GLsizei count + const GLfloat *value + + + + void glUniform2i + GLint location + GLint v0 + GLint v1 + + + void glUniform2i64ARB + GLint location + GLint64 x + GLint64 y + + + void glUniform2i64NV + GLint location + GLint64EXT x + GLint64EXT y + + + void glUniform2i64vARB + GLint location + GLsizei count + const GLint64 *value + + + void glUniform2i64vNV + GLint location + GLsizei count + const GLint64EXT *value + + + void glUniform2iARB + GLint location + GLint v0 + GLint v1 + + + + void glUniform2iv + GLint location + GLsizei count + const GLint *value + + + void glUniform2ivARB + GLint location + GLsizei count + const GLint *value + + + + void glUniform2ui + GLint location + GLuint v0 + GLuint v1 + + + void glUniform2ui64ARB + GLint location + GLuint64 x + GLuint64 y + + + void glUniform2ui64NV + GLint location + GLuint64EXT x + GLuint64EXT y + + + void glUniform2ui64vARB + GLint location + GLsizei count + const GLuint64 *value + + + void glUniform2ui64vNV + GLint location + GLsizei count + const GLuint64EXT *value + + + void glUniform2uiEXT + GLint location + GLuint v0 + GLuint v1 + + + + void glUniform2uiv + GLint location + GLsizei count + const GLuint *value + + + void glUniform2uivEXT + GLint location + GLsizei count + const GLuint *value + + + + void glUniform3d + GLint location + GLdouble x + GLdouble y + GLdouble z + + + void glUniform3dv + GLint location + GLsizei count + const GLdouble *value + + + void glUniform3f + GLint location + GLfloat v0 + GLfloat v1 + GLfloat v2 + + + void glUniform3fARB + GLint location + GLfloat v0 + GLfloat v1 + GLfloat v2 + + + + void glUniform3fv + GLint location + GLsizei count + const GLfloat *value + + + void glUniform3fvARB + GLint location + GLsizei count + const GLfloat *value + + + + void glUniform3i + GLint location + GLint v0 + GLint v1 + GLint v2 + + + void glUniform3i64ARB + GLint location + GLint64 x + GLint64 y + GLint64 z + + + void glUniform3i64NV + GLint location + GLint64EXT x + GLint64EXT y + GLint64EXT z + + + void glUniform3i64vARB + GLint location + GLsizei count + const GLint64 *value + + + void glUniform3i64vNV + GLint location + GLsizei count + const GLint64EXT *value + + + void glUniform3iARB + GLint location + GLint v0 + GLint v1 + GLint v2 + + + + void glUniform3iv + GLint location + GLsizei count + const GLint *value + + + void glUniform3ivARB + GLint location + GLsizei count + const GLint *value + + + + void glUniform3ui + GLint location + GLuint v0 + GLuint v1 + GLuint v2 + + + void glUniform3ui64ARB + GLint location + GLuint64 x + GLuint64 y + GLuint64 z + + + void glUniform3ui64NV + GLint location + GLuint64EXT x + GLuint64EXT y + GLuint64EXT z + + + void glUniform3ui64vARB + GLint location + GLsizei count + const GLuint64 *value + + + void glUniform3ui64vNV + GLint location + GLsizei count + const GLuint64EXT *value + + + void glUniform3uiEXT + GLint location + GLuint v0 + GLuint v1 + GLuint v2 + + + + void glUniform3uiv + GLint location + GLsizei count + const GLuint *value + + + void glUniform3uivEXT + GLint location + GLsizei count + const GLuint *value + + + + void glUniform4d + GLint location + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + void glUniform4dv + GLint location + GLsizei count + const GLdouble *value + + + void glUniform4f + GLint location + GLfloat v0 + GLfloat v1 + GLfloat v2 + GLfloat v3 + + + void glUniform4fARB + GLint location + GLfloat v0 + GLfloat v1 + GLfloat v2 + GLfloat v3 + + + + void glUniform4fv + GLint location + GLsizei count + const GLfloat *value + + + void glUniform4fvARB + GLint location + GLsizei count + const GLfloat *value + + + + void glUniform4i + GLint location + GLint v0 + GLint v1 + GLint v2 + GLint v3 + + + void glUniform4i64ARB + GLint location + GLint64 x + GLint64 y + GLint64 z + GLint64 w + + + void glUniform4i64NV + GLint location + GLint64EXT x + GLint64EXT y + GLint64EXT z + GLint64EXT w + + + void glUniform4i64vARB + GLint location + GLsizei count + const GLint64 *value + + + void glUniform4i64vNV + GLint location + GLsizei count + const GLint64EXT *value + + + void glUniform4iARB + GLint location + GLint v0 + GLint v1 + GLint v2 + GLint v3 + + + + void glUniform4iv + GLint location + GLsizei count + const GLint *value + + + void glUniform4ivARB + GLint location + GLsizei count + const GLint *value + + + + void glUniform4ui + GLint location + GLuint v0 + GLuint v1 + GLuint v2 + GLuint v3 + + + void glUniform4ui64ARB + GLint location + GLuint64 x + GLuint64 y + GLuint64 z + GLuint64 w + + + void glUniform4ui64NV + GLint location + GLuint64EXT x + GLuint64EXT y + GLuint64EXT z + GLuint64EXT w + + + void glUniform4ui64vARB + GLint location + GLsizei count + const GLuint64 *value + + + void glUniform4ui64vNV + GLint location + GLsizei count + const GLuint64EXT *value + + + void glUniform4uiEXT + GLint location + GLuint v0 + GLuint v1 + GLuint v2 + GLuint v3 + + + + void glUniform4uiv + GLint location + GLsizei count + const GLuint *value + + + void glUniform4uivEXT + GLint location + GLsizei count + const GLuint *value + + + + void glUniformBlockBinding + GLuint program + GLuint uniformBlockIndex + GLuint uniformBlockBinding + + + + void glUniformBufferEXT + GLuint program + GLint location + GLuint buffer + + + void glUniformHandleui64ARB + GLint location + GLuint64 value + + + void glUniformHandleui64IMG + GLint location + GLuint64 value + + + + void glUniformHandleui64NV + GLint location + GLuint64 value + + + void glUniformHandleui64vARB + GLint location + GLsizei count + const GLuint64 *value + + + void glUniformHandleui64vIMG + GLint location + GLsizei count + const GLuint64 *value + + + + void glUniformHandleui64vNV + GLint location + GLsizei count + const GLuint64 *value + + + void glUniformMatrix2dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix2fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glUniformMatrix2fvARB + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix2x3dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix2x3fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix2x3fvNV + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix2x4dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix2x4fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix2x4fvNV + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix3dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix3fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glUniformMatrix3fvARB + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix3x2dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix3x2fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix3x2fvNV + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix3x4dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix3x4fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix3x4fvNV + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix4dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix4fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + void glUniformMatrix4fvARB + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix4x2dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix4x2fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix4x2fvNV + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix4x3dv + GLint location + GLsizei count + GLboolean transpose + const GLdouble *value + + + void glUniformMatrix4x3fv + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformMatrix4x3fvNV + GLint location + GLsizei count + GLboolean transpose + const GLfloat *value + + + + void glUniformSubroutinesuiv + GLenum shadertype + GLsizei count + const GLuint *indices + + + void glUniformui64NV + GLint location + GLuint64EXT value + + + void glUniformui64vNV + GLint location + GLsizei count + const GLuint64EXT *value + + + void glUnlockArraysEXT + + + GLboolean glUnmapBuffer + GLenum target + + + GLboolean glUnmapBufferARB + GLenum target + + + + GLboolean glUnmapBufferOES + GLenum target + + + + GLboolean glUnmapNamedBuffer + GLuint buffer + + + GLboolean glUnmapNamedBufferEXT + GLuint buffer + + + void glUnmapObjectBufferATI + GLuint buffer + + + void glUnmapTexture2DINTEL + GLuint texture + GLint level + + + void glUpdateObjectBufferATI + GLuint buffer + GLuint offset + GLsizei size + const void *pointer + GLenum preserve + + + void glUseProgram + GLuint program + + + void glUseProgramObjectARB + GLhandleARB programObj + + + + void glUseProgramStages + GLuint pipeline + GLbitfield stages + GLuint program + + + void glUseProgramStagesEXT + GLuint pipeline + GLbitfield stages + GLuint program + + + void glUseShaderProgramEXT + GLenum type + GLuint program + + + void glVDPAUFiniNV + + + void glVDPAUGetSurfaceivNV + GLvdpauSurfaceNV surface + GLenum pname + GLsizei bufSize + GLsizei *length + GLint *values + + + void glVDPAUInitNV + const void *vdpDevice + const void *getProcAddress + + + GLboolean glVDPAUIsSurfaceNV + GLvdpauSurfaceNV surface + + + void glVDPAUMapSurfacesNV + GLsizei numSurfaces + const GLvdpauSurfaceNV *surfaces + + + GLvdpauSurfaceNV glVDPAURegisterOutputSurfaceNV + const void *vdpSurface + GLenum target + GLsizei numTextureNames + const GLuint *textureNames + + + GLvdpauSurfaceNV glVDPAURegisterVideoSurfaceNV + const void *vdpSurface + GLenum target + GLsizei numTextureNames + const GLuint *textureNames + + + GLvdpauSurfaceNV glVDPAURegisterVideoSurfaceWithPictureStructureNV + const void *vdpSurface + GLenum target + GLsizei numTextureNames + const GLuint *textureNames + GLboolean isFrameStructure + + + void glVDPAUSurfaceAccessNV + GLvdpauSurfaceNV surface + GLenum access + + + void glVDPAUUnmapSurfacesNV + GLsizei numSurface + const GLvdpauSurfaceNV *surfaces + + + void glVDPAUUnregisterSurfaceNV + GLvdpauSurfaceNV surface + + + void glValidateProgram + GLuint program + + + void glValidateProgramARB + GLhandleARB programObj + + + + void glValidateProgramPipeline + GLuint pipeline + + + void glValidateProgramPipelineEXT + GLuint pipeline + + + void glVariantArrayObjectATI + GLuint id + GLenum type + GLsizei stride + GLuint buffer + GLuint offset + + + void glVariantPointerEXT + GLuint id + GLenum type + GLuint stride + const void *addr + + + void glVariantbvEXT + GLuint id + const GLbyte *addr + + + void glVariantdvEXT + GLuint id + const GLdouble *addr + + + void glVariantfvEXT + GLuint id + const GLfloat *addr + + + void glVariantivEXT + GLuint id + const GLint *addr + + + void glVariantsvEXT + GLuint id + const GLshort *addr + + + void glVariantubvEXT + GLuint id + const GLubyte *addr + + + void glVariantuivEXT + GLuint id + const GLuint *addr + + + void glVariantusvEXT + GLuint id + const GLushort *addr + + + void glVertex2bOES + GLbyte x + GLbyte y + + + void glVertex2bvOES + const GLbyte *coords + + + void glVertex2d + GLdouble x + GLdouble y + + + + void glVertex2dv + const GLdouble *v + + + + void glVertex2f + GLfloat x + GLfloat y + + + + void glVertex2fv + const GLfloat *v + + + + void glVertex2hNV + GLhalfNV x + GLhalfNV y + + + + void glVertex2hvNV + const GLhalfNV *v + + + + void glVertex2i + GLint x + GLint y + + + + void glVertex2iv + const GLint *v + + + + void glVertex2s + GLshort x + GLshort y + + + + void glVertex2sv + const GLshort *v + + + + void glVertex2xOES + GLfixed x + + + void glVertex2xvOES + const GLfixed *coords + + + void glVertex3bOES + GLbyte x + GLbyte y + GLbyte z + + + void glVertex3bvOES + const GLbyte *coords + + + void glVertex3d + GLdouble x + GLdouble y + GLdouble z + + + + void glVertex3dv + const GLdouble *v + + + + void glVertex3f + GLfloat x + GLfloat y + GLfloat z + + + + void glVertex3fv + const GLfloat *v + + + + void glVertex3hNV + GLhalfNV x + GLhalfNV y + GLhalfNV z + + + + void glVertex3hvNV + const GLhalfNV *v + + + + void glVertex3i + GLint x + GLint y + GLint z + + + + void glVertex3iv + const GLint *v + + + + void glVertex3s + GLshort x + GLshort y + GLshort z + + + + void glVertex3sv + const GLshort *v + + + + void glVertex3xOES + GLfixed x + GLfixed y + + + void glVertex3xvOES + const GLfixed *coords + + + void glVertex4bOES + GLbyte x + GLbyte y + GLbyte z + GLbyte w + + + void glVertex4bvOES + const GLbyte *coords + + + void glVertex4d + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glVertex4dv + const GLdouble *v + + + + void glVertex4f + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glVertex4fv + const GLfloat *v + + + + void glVertex4hNV + GLhalfNV x + GLhalfNV y + GLhalfNV z + GLhalfNV w + + + + void glVertex4hvNV + const GLhalfNV *v + + + + void glVertex4i + GLint x + GLint y + GLint z + GLint w + + + + void glVertex4iv + const GLint *v + + + + void glVertex4s + GLshort x + GLshort y + GLshort z + GLshort w + + + + void glVertex4sv + const GLshort *v + + + + void glVertex4xOES + GLfixed x + GLfixed y + GLfixed z + + + void glVertex4xvOES + const GLfixed *coords + + + void glVertexArrayAttribBinding + GLuint vaobj + GLuint attribindex + GLuint bindingindex + + + void glVertexArrayAttribFormat + GLuint vaobj + GLuint attribindex + GLint size + GLenum type + GLboolean normalized + GLuint relativeoffset + + + void glVertexArrayAttribIFormat + GLuint vaobj + GLuint attribindex + GLint size + GLenum type + GLuint relativeoffset + + + void glVertexArrayAttribLFormat + GLuint vaobj + GLuint attribindex + GLint size + GLenum type + GLuint relativeoffset + + + void glVertexArrayBindVertexBufferEXT + GLuint vaobj + GLuint bindingindex + GLuint buffer + GLintptr offset + GLsizei stride + + + void glVertexArrayBindingDivisor + GLuint vaobj + GLuint bindingindex + GLuint divisor + + + void glVertexArrayColorOffsetEXT + GLuint vaobj + GLuint buffer + GLint size + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayEdgeFlagOffsetEXT + GLuint vaobj + GLuint buffer + GLsizei stride + GLintptr offset + + + void glVertexArrayElementBuffer + GLuint vaobj + GLuint buffer + + + void glVertexArrayFogCoordOffsetEXT + GLuint vaobj + GLuint buffer + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayIndexOffsetEXT + GLuint vaobj + GLuint buffer + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayMultiTexCoordOffsetEXT + GLuint vaobj + GLuint buffer + GLenum texunit + GLint size + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayNormalOffsetEXT + GLuint vaobj + GLuint buffer + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayParameteriAPPLE + GLenum pname + GLint param + + + void glVertexArrayRangeAPPLE + GLsizei length + void *pointer + + + void glVertexArrayRangeNV + GLsizei length + const void *pointer + + + void glVertexArraySecondaryColorOffsetEXT + GLuint vaobj + GLuint buffer + GLint size + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayTexCoordOffsetEXT + GLuint vaobj + GLuint buffer + GLint size + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayVertexAttribBindingEXT + GLuint vaobj + GLuint attribindex + GLuint bindingindex + + + void glVertexArrayVertexAttribDivisorEXT + GLuint vaobj + GLuint index + GLuint divisor + + + void glVertexArrayVertexAttribFormatEXT + GLuint vaobj + GLuint attribindex + GLint size + GLenum type + GLboolean normalized + GLuint relativeoffset + + + void glVertexArrayVertexAttribIFormatEXT + GLuint vaobj + GLuint attribindex + GLint size + GLenum type + GLuint relativeoffset + + + void glVertexArrayVertexAttribIOffsetEXT + GLuint vaobj + GLuint buffer + GLuint index + GLint size + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayVertexAttribLFormatEXT + GLuint vaobj + GLuint attribindex + GLint size + GLenum type + GLuint relativeoffset + + + void glVertexArrayVertexAttribLOffsetEXT + GLuint vaobj + GLuint buffer + GLuint index + GLint size + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexArrayVertexAttribOffsetEXT + GLuint vaobj + GLuint buffer + GLuint index + GLint size + GLenum type + GLboolean normalized + GLsizei stride + GLintptr offset + + + void glVertexArrayVertexBindingDivisorEXT + GLuint vaobj + GLuint bindingindex + GLuint divisor + + + void glVertexArrayVertexBuffer + GLuint vaobj + GLuint bindingindex + GLuint buffer + GLintptr offset + GLsizei stride + + + void glVertexArrayVertexBuffers + GLuint vaobj + GLuint first + GLsizei count + const GLuint *buffers + const GLintptr *offsets + const GLsizei *strides + + + void glVertexArrayVertexOffsetEXT + GLuint vaobj + GLuint buffer + GLint size + GLenum type + GLsizei stride + GLintptr offset + + + void glVertexAttrib1d + GLuint index + GLdouble x + + + + void glVertexAttrib1dARB + GLuint index + GLdouble x + + + + + void glVertexAttrib1dNV + GLuint index + GLdouble x + + + + + void glVertexAttrib1dv + GLuint index + const GLdouble *v + + + + void glVertexAttrib1dvARB + GLuint index + const GLdouble *v + + + + + void glVertexAttrib1dvNV + GLuint index + const GLdouble *v + + + + + void glVertexAttrib1f + GLuint index + GLfloat x + + + + void glVertexAttrib1fARB + GLuint index + GLfloat x + + + + + void glVertexAttrib1fNV + GLuint index + GLfloat x + + + + + void glVertexAttrib1fv + GLuint index + const GLfloat *v + + + + void glVertexAttrib1fvARB + GLuint index + const GLfloat *v + + + + + void glVertexAttrib1fvNV + GLuint index + const GLfloat *v + + + + + void glVertexAttrib1hNV + GLuint index + GLhalfNV x + + + + void glVertexAttrib1hvNV + GLuint index + const GLhalfNV *v + + + + void glVertexAttrib1s + GLuint index + GLshort x + + + + void glVertexAttrib1sARB + GLuint index + GLshort x + + + + + void glVertexAttrib1sNV + GLuint index + GLshort x + + + + + void glVertexAttrib1sv + GLuint index + const GLshort *v + + + + void glVertexAttrib1svARB + GLuint index + const GLshort *v + + + + + void glVertexAttrib1svNV + GLuint index + const GLshort *v + + + + + void glVertexAttrib2d + GLuint index + GLdouble x + GLdouble y + + + + void glVertexAttrib2dARB + GLuint index + GLdouble x + GLdouble y + + + + + void glVertexAttrib2dNV + GLuint index + GLdouble x + GLdouble y + + + + + void glVertexAttrib2dv + GLuint index + const GLdouble *v + + + + void glVertexAttrib2dvARB + GLuint index + const GLdouble *v + + + + + void glVertexAttrib2dvNV + GLuint index + const GLdouble *v + + + + + void glVertexAttrib2f + GLuint index + GLfloat x + GLfloat y + + + + void glVertexAttrib2fARB + GLuint index + GLfloat x + GLfloat y + + + + + void glVertexAttrib2fNV + GLuint index + GLfloat x + GLfloat y + + + + + void glVertexAttrib2fv + GLuint index + const GLfloat *v + + + + void glVertexAttrib2fvARB + GLuint index + const GLfloat *v + + + + + void glVertexAttrib2fvNV + GLuint index + const GLfloat *v + + + + + void glVertexAttrib2hNV + GLuint index + GLhalfNV x + GLhalfNV y + + + + void glVertexAttrib2hvNV + GLuint index + const GLhalfNV *v + + + + void glVertexAttrib2s + GLuint index + GLshort x + GLshort y + + + + void glVertexAttrib2sARB + GLuint index + GLshort x + GLshort y + + + + + void glVertexAttrib2sNV + GLuint index + GLshort x + GLshort y + + + + + void glVertexAttrib2sv + GLuint index + const GLshort *v + + + + void glVertexAttrib2svARB + GLuint index + const GLshort *v + + + + + void glVertexAttrib2svNV + GLuint index + const GLshort *v + + + + + void glVertexAttrib3d + GLuint index + GLdouble x + GLdouble y + GLdouble z + + + + void glVertexAttrib3dARB + GLuint index + GLdouble x + GLdouble y + GLdouble z + + + + + void glVertexAttrib3dNV + GLuint index + GLdouble x + GLdouble y + GLdouble z + + + + + void glVertexAttrib3dv + GLuint index + const GLdouble *v + + + + void glVertexAttrib3dvARB + GLuint index + const GLdouble *v + + + + + void glVertexAttrib3dvNV + GLuint index + const GLdouble *v + + + + + void glVertexAttrib3f + GLuint index + GLfloat x + GLfloat y + GLfloat z + + + + void glVertexAttrib3fARB + GLuint index + GLfloat x + GLfloat y + GLfloat z + + + + + void glVertexAttrib3fNV + GLuint index + GLfloat x + GLfloat y + GLfloat z + + + + + void glVertexAttrib3fv + GLuint index + const GLfloat *v + + + + void glVertexAttrib3fvARB + GLuint index + const GLfloat *v + + + + + void glVertexAttrib3fvNV + GLuint index + const GLfloat *v + + + + + void glVertexAttrib3hNV + GLuint index + GLhalfNV x + GLhalfNV y + GLhalfNV z + + + + void glVertexAttrib3hvNV + GLuint index + const GLhalfNV *v + + + + void glVertexAttrib3s + GLuint index + GLshort x + GLshort y + GLshort z + + + + void glVertexAttrib3sARB + GLuint index + GLshort x + GLshort y + GLshort z + + + + + void glVertexAttrib3sNV + GLuint index + GLshort x + GLshort y + GLshort z + + + + + void glVertexAttrib3sv + GLuint index + const GLshort *v + + + + void glVertexAttrib3svARB + GLuint index + const GLshort *v + + + + + void glVertexAttrib3svNV + GLuint index + const GLshort *v + + + + + void glVertexAttrib4Nbv + GLuint index + const GLbyte *v + + + void glVertexAttrib4NbvARB + GLuint index + const GLbyte *v + + + + void glVertexAttrib4Niv + GLuint index + const GLint *v + + + void glVertexAttrib4NivARB + GLuint index + const GLint *v + + + + void glVertexAttrib4Nsv + GLuint index + const GLshort *v + + + void glVertexAttrib4NsvARB + GLuint index + const GLshort *v + + + + void glVertexAttrib4Nub + GLuint index + GLubyte x + GLubyte y + GLubyte z + GLubyte w + + + void glVertexAttrib4NubARB + GLuint index + GLubyte x + GLubyte y + GLubyte z + GLubyte w + + + + void glVertexAttrib4Nubv + GLuint index + const GLubyte *v + + + + void glVertexAttrib4NubvARB + GLuint index + const GLubyte *v + + + + + void glVertexAttrib4Nuiv + GLuint index + const GLuint *v + + + void glVertexAttrib4NuivARB + GLuint index + const GLuint *v + + + + void glVertexAttrib4Nusv + GLuint index + const GLushort *v + + + void glVertexAttrib4NusvARB + GLuint index + const GLushort *v + + + + void glVertexAttrib4bv + GLuint index + const GLbyte *v + + + void glVertexAttrib4bvARB + GLuint index + const GLbyte *v + + + + void glVertexAttrib4d + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glVertexAttrib4dARB + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + + void glVertexAttrib4dNV + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + + void glVertexAttrib4dv + GLuint index + const GLdouble *v + + + + void glVertexAttrib4dvARB + GLuint index + const GLdouble *v + + + + + void glVertexAttrib4dvNV + GLuint index + const GLdouble *v + + + + + void glVertexAttrib4f + GLuint index + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glVertexAttrib4fARB + GLuint index + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + + void glVertexAttrib4fNV + GLuint index + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + + void glVertexAttrib4fv + GLuint index + const GLfloat *v + + + + void glVertexAttrib4fvARB + GLuint index + const GLfloat *v + + + + + void glVertexAttrib4fvNV + GLuint index + const GLfloat *v + + + + + void glVertexAttrib4hNV + GLuint index + GLhalfNV x + GLhalfNV y + GLhalfNV z + GLhalfNV w + + + + void glVertexAttrib4hvNV + GLuint index + const GLhalfNV *v + + + + void glVertexAttrib4iv + GLuint index + const GLint *v + + + void glVertexAttrib4ivARB + GLuint index + const GLint *v + + + + void glVertexAttrib4s + GLuint index + GLshort x + GLshort y + GLshort z + GLshort w + + + + void glVertexAttrib4sARB + GLuint index + GLshort x + GLshort y + GLshort z + GLshort w + + + + + void glVertexAttrib4sNV + GLuint index + GLshort x + GLshort y + GLshort z + GLshort w + + + + + void glVertexAttrib4sv + GLuint index + const GLshort *v + + + + void glVertexAttrib4svARB + GLuint index + const GLshort *v + + + + + void glVertexAttrib4svNV + GLuint index + const GLshort *v + + + + + void glVertexAttrib4ubNV + GLuint index + GLubyte x + GLubyte y + GLubyte z + GLubyte w + + + + + void glVertexAttrib4ubv + GLuint index + const GLubyte *v + + + void glVertexAttrib4ubvARB + GLuint index + const GLubyte *v + + + + void glVertexAttrib4ubvNV + GLuint index + const GLubyte *v + + + + + void glVertexAttrib4uiv + GLuint index + const GLuint *v + + + void glVertexAttrib4uivARB + GLuint index + const GLuint *v + + + + void glVertexAttrib4usv + GLuint index + const GLushort *v + + + void glVertexAttrib4usvARB + GLuint index + const GLushort *v + + + + void glVertexAttribArrayObjectATI + GLuint index + GLint size + GLenum type + GLboolean normalized + GLsizei stride + GLuint buffer + GLuint offset + + + void glVertexAttribBinding + GLuint attribindex + GLuint bindingindex + + + void glVertexAttribDivisor + GLuint index + GLuint divisor + + + void glVertexAttribDivisorANGLE + GLuint index + GLuint divisor + + + + void glVertexAttribDivisorARB + GLuint index + GLuint divisor + + + + void glVertexAttribDivisorEXT + GLuint index + GLuint divisor + + + + void glVertexAttribDivisorNV + GLuint index + GLuint divisor + + + + void glVertexAttribFormat + GLuint attribindex + GLint size + GLenum type + GLboolean normalized + GLuint relativeoffset + + + void glVertexAttribFormatNV + GLuint index + GLint size + GLenum type + GLboolean normalized + GLsizei stride + + + void glVertexAttribI1i + GLuint index + GLint x + + + + void glVertexAttribI1iEXT + GLuint index + GLint x + + + + + void glVertexAttribI1iv + GLuint index + const GLint *v + + + void glVertexAttribI1ivEXT + GLuint index + const GLint *v + + + + void glVertexAttribI1ui + GLuint index + GLuint x + + + + void glVertexAttribI1uiEXT + GLuint index + GLuint x + + + + + void glVertexAttribI1uiv + GLuint index + const GLuint *v + + + void glVertexAttribI1uivEXT + GLuint index + const GLuint *v + + + + void glVertexAttribI2i + GLuint index + GLint x + GLint y + + + + void glVertexAttribI2iEXT + GLuint index + GLint x + GLint y + + + + + void glVertexAttribI2iv + GLuint index + const GLint *v + + + void glVertexAttribI2ivEXT + GLuint index + const GLint *v + + + + void glVertexAttribI2ui + GLuint index + GLuint x + GLuint y + + + + void glVertexAttribI2uiEXT + GLuint index + GLuint x + GLuint y + + + + + void glVertexAttribI2uiv + GLuint index + const GLuint *v + + + void glVertexAttribI2uivEXT + GLuint index + const GLuint *v + + + + void glVertexAttribI3i + GLuint index + GLint x + GLint y + GLint z + + + + void glVertexAttribI3iEXT + GLuint index + GLint x + GLint y + GLint z + + + + + void glVertexAttribI3iv + GLuint index + const GLint *v + + + void glVertexAttribI3ivEXT + GLuint index + const GLint *v + + + + void glVertexAttribI3ui + GLuint index + GLuint x + GLuint y + GLuint z + + + + void glVertexAttribI3uiEXT + GLuint index + GLuint x + GLuint y + GLuint z + + + + + void glVertexAttribI3uiv + GLuint index + const GLuint *v + + + void glVertexAttribI3uivEXT + GLuint index + const GLuint *v + + + + void glVertexAttribI4bv + GLuint index + const GLbyte *v + + + void glVertexAttribI4bvEXT + GLuint index + const GLbyte *v + + + + void glVertexAttribI4i + GLuint index + GLint x + GLint y + GLint z + GLint w + + + + void glVertexAttribI4iEXT + GLuint index + GLint x + GLint y + GLint z + GLint w + + + + + void glVertexAttribI4iv + GLuint index + const GLint *v + + + void glVertexAttribI4ivEXT + GLuint index + const GLint *v + + + + void glVertexAttribI4sv + GLuint index + const GLshort *v + + + void glVertexAttribI4svEXT + GLuint index + const GLshort *v + + + + void glVertexAttribI4ubv + GLuint index + const GLubyte *v + + + void glVertexAttribI4ubvEXT + GLuint index + const GLubyte *v + + + + void glVertexAttribI4ui + GLuint index + GLuint x + GLuint y + GLuint z + GLuint w + + + + void glVertexAttribI4uiEXT + GLuint index + GLuint x + GLuint y + GLuint z + GLuint w + + + + + void glVertexAttribI4uiv + GLuint index + const GLuint *v + + + void glVertexAttribI4uivEXT + GLuint index + const GLuint *v + + + + void glVertexAttribI4usv + GLuint index + const GLushort *v + + + void glVertexAttribI4usvEXT + GLuint index + const GLushort *v + + + + void glVertexAttribIFormat + GLuint attribindex + GLint size + GLenum type + GLuint relativeoffset + + + void glVertexAttribIFormatNV + GLuint index + GLint size + GLenum type + GLsizei stride + + + void glVertexAttribIPointer + GLuint index + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glVertexAttribIPointerEXT + GLuint index + GLint size + GLenum type + GLsizei stride + const void *pointer + + + + void glVertexAttribL1d + GLuint index + GLdouble x + + + void glVertexAttribL1dEXT + GLuint index + GLdouble x + + + + void glVertexAttribL1dv + GLuint index + const GLdouble *v + + + void glVertexAttribL1dvEXT + GLuint index + const GLdouble *v + + + + void glVertexAttribL1i64NV + GLuint index + GLint64EXT x + + + void glVertexAttribL1i64vNV + GLuint index + const GLint64EXT *v + + + void glVertexAttribL1ui64ARB + GLuint index + GLuint64EXT x + + + void glVertexAttribL1ui64NV + GLuint index + GLuint64EXT x + + + void glVertexAttribL1ui64vARB + GLuint index + const GLuint64EXT *v + + + void glVertexAttribL1ui64vNV + GLuint index + const GLuint64EXT *v + + + void glVertexAttribL2d + GLuint index + GLdouble x + GLdouble y + + + void glVertexAttribL2dEXT + GLuint index + GLdouble x + GLdouble y + + + + void glVertexAttribL2dv + GLuint index + const GLdouble *v + + + void glVertexAttribL2dvEXT + GLuint index + const GLdouble *v + + + + void glVertexAttribL2i64NV + GLuint index + GLint64EXT x + GLint64EXT y + + + void glVertexAttribL2i64vNV + GLuint index + const GLint64EXT *v + + + void glVertexAttribL2ui64NV + GLuint index + GLuint64EXT x + GLuint64EXT y + + + void glVertexAttribL2ui64vNV + GLuint index + const GLuint64EXT *v + + + void glVertexAttribL3d + GLuint index + GLdouble x + GLdouble y + GLdouble z + + + void glVertexAttribL3dEXT + GLuint index + GLdouble x + GLdouble y + GLdouble z + + + + void glVertexAttribL3dv + GLuint index + const GLdouble *v + + + void glVertexAttribL3dvEXT + GLuint index + const GLdouble *v + + + + void glVertexAttribL3i64NV + GLuint index + GLint64EXT x + GLint64EXT y + GLint64EXT z + + + void glVertexAttribL3i64vNV + GLuint index + const GLint64EXT *v + + + void glVertexAttribL3ui64NV + GLuint index + GLuint64EXT x + GLuint64EXT y + GLuint64EXT z + + + void glVertexAttribL3ui64vNV + GLuint index + const GLuint64EXT *v + + + void glVertexAttribL4d + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + void glVertexAttribL4dEXT + GLuint index + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glVertexAttribL4dv + GLuint index + const GLdouble *v + + + void glVertexAttribL4dvEXT + GLuint index + const GLdouble *v + + + + void glVertexAttribL4i64NV + GLuint index + GLint64EXT x + GLint64EXT y + GLint64EXT z + GLint64EXT w + + + void glVertexAttribL4i64vNV + GLuint index + const GLint64EXT *v + + + void glVertexAttribL4ui64NV + GLuint index + GLuint64EXT x + GLuint64EXT y + GLuint64EXT z + GLuint64EXT w + + + void glVertexAttribL4ui64vNV + GLuint index + const GLuint64EXT *v + + + void glVertexAttribLFormat + GLuint attribindex + GLint size + GLenum type + GLuint relativeoffset + + + void glVertexAttribLFormatNV + GLuint index + GLint size + GLenum type + GLsizei stride + + + void glVertexAttribLPointer + GLuint index + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glVertexAttribLPointerEXT + GLuint index + GLint size + GLenum type + GLsizei stride + const void *pointer + + + + void glVertexAttribP1ui + GLuint index + GLenum type + GLboolean normalized + GLuint value + + + void glVertexAttribP1uiv + GLuint index + GLenum type + GLboolean normalized + const GLuint *value + + + void glVertexAttribP2ui + GLuint index + GLenum type + GLboolean normalized + GLuint value + + + void glVertexAttribP2uiv + GLuint index + GLenum type + GLboolean normalized + const GLuint *value + + + void glVertexAttribP3ui + GLuint index + GLenum type + GLboolean normalized + GLuint value + + + void glVertexAttribP3uiv + GLuint index + GLenum type + GLboolean normalized + const GLuint *value + + + void glVertexAttribP4ui + GLuint index + GLenum type + GLboolean normalized + GLuint value + + + void glVertexAttribP4uiv + GLuint index + GLenum type + GLboolean normalized + const GLuint *value + + + void glVertexAttribParameteriAMD + GLuint index + GLenum pname + GLint param + + + void glVertexAttribPointer + GLuint index + GLint size + GLenum type + GLboolean normalized + GLsizei stride + const void *pointer + + + void glVertexAttribPointerARB + GLuint index + GLint size + GLenum type + GLboolean normalized + GLsizei stride + const void *pointer + + + + void glVertexAttribPointerNV + GLuint index + GLint fsize + GLenum type + GLsizei stride + const void *pointer + + + void glVertexAttribs1dvNV + GLuint index + GLsizei count + const GLdouble *v + + + + void glVertexAttribs1fvNV + GLuint index + GLsizei count + const GLfloat *v + + + + void glVertexAttribs1hvNV + GLuint index + GLsizei n + const GLhalfNV *v + + + + void glVertexAttribs1svNV + GLuint index + GLsizei count + const GLshort *v + + + + void glVertexAttribs2dvNV + GLuint index + GLsizei count + const GLdouble *v + + + + void glVertexAttribs2fvNV + GLuint index + GLsizei count + const GLfloat *v + + + + void glVertexAttribs2hvNV + GLuint index + GLsizei n + const GLhalfNV *v + + + + void glVertexAttribs2svNV + GLuint index + GLsizei count + const GLshort *v + + + + void glVertexAttribs3dvNV + GLuint index + GLsizei count + const GLdouble *v + + + + void glVertexAttribs3fvNV + GLuint index + GLsizei count + const GLfloat *v + + + + void glVertexAttribs3hvNV + GLuint index + GLsizei n + const GLhalfNV *v + + + + void glVertexAttribs3svNV + GLuint index + GLsizei count + const GLshort *v + + + + void glVertexAttribs4dvNV + GLuint index + GLsizei count + const GLdouble *v + + + + void glVertexAttribs4fvNV + GLuint index + GLsizei count + const GLfloat *v + + + + void glVertexAttribs4hvNV + GLuint index + GLsizei n + const GLhalfNV *v + + + + void glVertexAttribs4svNV + GLuint index + GLsizei count + const GLshort *v + + + + void glVertexAttribs4ubvNV + GLuint index + GLsizei count + const GLubyte *v + + + + void glVertexBindingDivisor + GLuint bindingindex + GLuint divisor + + + void glVertexBlendARB + GLint count + + + + void glVertexBlendEnvfATI + GLenum pname + GLfloat param + + + void glVertexBlendEnviATI + GLenum pname + GLint param + + + void glVertexFormatNV + GLint size + GLenum type + GLsizei stride + + + void glVertexP2ui + GLenum type + GLuint value + + + void glVertexP2uiv + GLenum type + const GLuint *value + + + void glVertexP3ui + GLenum type + GLuint value + + + void glVertexP3uiv + GLenum type + const GLuint *value + + + void glVertexP4ui + GLenum type + GLuint value + + + void glVertexP4uiv + GLenum type + const GLuint *value + + + void glVertexPointer + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glVertexPointerEXT + GLint size + GLenum type + GLsizei stride + GLsizei count + const void *pointer + + + void glVertexPointerListIBM + GLint size + GLenum type + GLint stride + const void **pointer + GLint ptrstride + + + void glVertexPointervINTEL + GLint size + GLenum type + const void **pointer + + + void glVertexStream1dATI + GLenum stream + GLdouble x + + + void glVertexStream1dvATI + GLenum stream + const GLdouble *coords + + + void glVertexStream1fATI + GLenum stream + GLfloat x + + + void glVertexStream1fvATI + GLenum stream + const GLfloat *coords + + + void glVertexStream1iATI + GLenum stream + GLint x + + + void glVertexStream1ivATI + GLenum stream + const GLint *coords + + + void glVertexStream1sATI + GLenum stream + GLshort x + + + void glVertexStream1svATI + GLenum stream + const GLshort *coords + + + void glVertexStream2dATI + GLenum stream + GLdouble x + GLdouble y + + + void glVertexStream2dvATI + GLenum stream + const GLdouble *coords + + + void glVertexStream2fATI + GLenum stream + GLfloat x + GLfloat y + + + void glVertexStream2fvATI + GLenum stream + const GLfloat *coords + + + void glVertexStream2iATI + GLenum stream + GLint x + GLint y + + + void glVertexStream2ivATI + GLenum stream + const GLint *coords + + + void glVertexStream2sATI + GLenum stream + GLshort x + GLshort y + + + void glVertexStream2svATI + GLenum stream + const GLshort *coords + + + void glVertexStream3dATI + GLenum stream + GLdouble x + GLdouble y + GLdouble z + + + void glVertexStream3dvATI + GLenum stream + const GLdouble *coords + + + void glVertexStream3fATI + GLenum stream + GLfloat x + GLfloat y + GLfloat z + + + void glVertexStream3fvATI + GLenum stream + const GLfloat *coords + + + void glVertexStream3iATI + GLenum stream + GLint x + GLint y + GLint z + + + void glVertexStream3ivATI + GLenum stream + const GLint *coords + + + void glVertexStream3sATI + GLenum stream + GLshort x + GLshort y + GLshort z + + + void glVertexStream3svATI + GLenum stream + const GLshort *coords + + + void glVertexStream4dATI + GLenum stream + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + void glVertexStream4dvATI + GLenum stream + const GLdouble *coords + + + void glVertexStream4fATI + GLenum stream + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + void glVertexStream4fvATI + GLenum stream + const GLfloat *coords + + + void glVertexStream4iATI + GLenum stream + GLint x + GLint y + GLint z + GLint w + + + void glVertexStream4ivATI + GLenum stream + const GLint *coords + + + void glVertexStream4sATI + GLenum stream + GLshort x + GLshort y + GLshort z + GLshort w + + + void glVertexStream4svATI + GLenum stream + const GLshort *coords + + + void glVertexWeightPointerEXT + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glVertexWeightfEXT + GLfloat weight + + + + void glVertexWeightfvEXT + const GLfloat *weight + + + + void glVertexWeighthNV + GLhalfNV weight + + + + void glVertexWeighthvNV + const GLhalfNV *weight + + + + GLenum glVideoCaptureNV + GLuint video_capture_slot + GLuint *sequence_num + GLuint64EXT *capture_time + + + void glVideoCaptureStreamParameterdvNV + GLuint video_capture_slot + GLuint stream + GLenum pname + const GLdouble *params + + + void glVideoCaptureStreamParameterfvNV + GLuint video_capture_slot + GLuint stream + GLenum pname + const GLfloat *params + + + void glVideoCaptureStreamParameterivNV + GLuint video_capture_slot + GLuint stream + GLenum pname + const GLint *params + + + void glViewport + GLint x + GLint y + GLsizei width + GLsizei height + + + + void glViewportArrayv + GLuint first + GLsizei count + const GLfloat *v + + + void glViewportArrayvNV + GLuint first + GLsizei count + const GLfloat *v + + + + void glViewportArrayvOES + GLuint first + GLsizei count + const GLfloat *v + + + + void glViewportIndexedf + GLuint index + GLfloat x + GLfloat y + GLfloat w + GLfloat h + + + void glViewportIndexedfOES + GLuint index + GLfloat x + GLfloat y + GLfloat w + GLfloat h + + + + void glViewportIndexedfNV + GLuint index + GLfloat x + GLfloat y + GLfloat w + GLfloat h + + + + void glViewportIndexedfv + GLuint index + const GLfloat *v + + + void glViewportIndexedfvOES + GLuint index + const GLfloat *v + + + + void glViewportIndexedfvNV + GLuint index + const GLfloat *v + + + + void glViewportPositionWScaleNV + GLuint index + GLfloat xcoeff + GLfloat ycoeff + + + void glViewportSwizzleNV + GLuint index + GLenum swizzlex + GLenum swizzley + GLenum swizzlez + GLenum swizzlew + + + void glWaitSemaphoreEXT + GLuint semaphore + GLuint numBufferBarriers + const GLuint *buffers + GLuint numTextureBarriers + const GLuint *textures + const GLenum *srcLayouts + + + void glWaitSync + GLsync sync + GLbitfield flags + GLuint64 timeout + + + void glWaitSyncAPPLE + GLsync sync + GLbitfield flags + GLuint64 timeout + + + + void glWeightPathsNV + GLuint resultPath + GLsizei numPaths + const GLuint *paths + const GLfloat *weights + + + void glWeightPointerARB + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glWeightPointerOES + GLint size + GLenum type + GLsizei stride + const void *pointer + + + void glWeightbvARB + GLint size + const GLbyte *weights + + + + void glWeightdvARB + GLint size + const GLdouble *weights + + + + void glWeightfvARB + GLint size + const GLfloat *weights + + + + void glWeightivARB + GLint size + const GLint *weights + + + + void glWeightsvARB + GLint size + const GLshort *weights + + + + void glWeightubvARB + GLint size + const GLubyte *weights + + + + void glWeightuivARB + GLint size + const GLuint *weights + + + + void glWeightusvARB + GLint size + const GLushort *weights + + + + void glWindowPos2d + GLdouble x + GLdouble y + + + + void glWindowPos2dARB + GLdouble x + GLdouble y + + + + + void glWindowPos2dMESA + GLdouble x + GLdouble y + + + + + void glWindowPos2dv + const GLdouble *v + + + + void glWindowPos2dvARB + const GLdouble *v + + + + + void glWindowPos2dvMESA + const GLdouble *v + + + + void glWindowPos2f + GLfloat x + GLfloat y + + + + void glWindowPos2fARB + GLfloat x + GLfloat y + + + + + void glWindowPos2fMESA + GLfloat x + GLfloat y + + + + + void glWindowPos2fv + const GLfloat *v + + + + void glWindowPos2fvARB + const GLfloat *v + + + + + void glWindowPos2fvMESA + const GLfloat *v + + + + void glWindowPos2i + GLint x + GLint y + + + + void glWindowPos2iARB + GLint x + GLint y + + + + + void glWindowPos2iMESA + GLint x + GLint y + + + + + void glWindowPos2iv + const GLint *v + + + + void glWindowPos2ivARB + const GLint *v + + + + + void glWindowPos2ivMESA + const GLint *v + + + + void glWindowPos2s + GLshort x + GLshort y + + + + void glWindowPos2sARB + GLshort x + GLshort y + + + + + void glWindowPos2sMESA + GLshort x + GLshort y + + + + + void glWindowPos2sv + const GLshort *v + + + + void glWindowPos2svARB + const GLshort *v + + + + + void glWindowPos2svMESA + const GLshort *v + + + + void glWindowPos3d + GLdouble x + GLdouble y + GLdouble z + + + + void glWindowPos3dARB + GLdouble x + GLdouble y + GLdouble z + + + + + void glWindowPos3dMESA + GLdouble x + GLdouble y + GLdouble z + + + + + void glWindowPos3dv + const GLdouble *v + + + + void glWindowPos3dvARB + const GLdouble *v + + + + + void glWindowPos3dvMESA + const GLdouble *v + + + + void glWindowPos3f + GLfloat x + GLfloat y + GLfloat z + + + + void glWindowPos3fARB + GLfloat x + GLfloat y + GLfloat z + + + + + void glWindowPos3fMESA + GLfloat x + GLfloat y + GLfloat z + + + + + void glWindowPos3fv + const GLfloat *v + + + + void glWindowPos3fvARB + const GLfloat *v + + + + + void glWindowPos3fvMESA + const GLfloat *v + + + + void glWindowPos3i + GLint x + GLint y + GLint z + + + + void glWindowPos3iARB + GLint x + GLint y + GLint z + + + + + void glWindowPos3iMESA + GLint x + GLint y + GLint z + + + + + void glWindowPos3iv + const GLint *v + + + + void glWindowPos3ivARB + const GLint *v + + + + + void glWindowPos3ivMESA + const GLint *v + + + + void glWindowPos3s + GLshort x + GLshort y + GLshort z + + + + void glWindowPos3sARB + GLshort x + GLshort y + GLshort z + + + + + void glWindowPos3sMESA + GLshort x + GLshort y + GLshort z + + + + + void glWindowPos3sv + const GLshort *v + + + + void glWindowPos3svARB + const GLshort *v + + + + + void glWindowPos3svMESA + const GLshort *v + + + + void glWindowPos4dMESA + GLdouble x + GLdouble y + GLdouble z + GLdouble w + + + + void glWindowPos4dvMESA + const GLdouble *v + + + void glWindowPos4fMESA + GLfloat x + GLfloat y + GLfloat z + GLfloat w + + + + void glWindowPos4fvMESA + const GLfloat *v + + + void glWindowPos4iMESA + GLint x + GLint y + GLint z + GLint w + + + + void glWindowPos4ivMESA + const GLint *v + + + void glWindowPos4sMESA + GLshort x + GLshort y + GLshort z + GLshort w + + + + void glWindowPos4svMESA + const GLshort *v + + + void glWindowRectanglesEXT + GLenum mode + GLsizei count + const GLint *box + + + void glWriteMaskEXT + GLuint res + GLuint in + GLenum outX + GLenum outY + GLenum outZ + GLenum outW + + + void glDrawVkImageNV + GLuint64 vkImage + GLuint sampler + GLfloat x0 + GLfloat y0 + GLfloat x1 + GLfloat y1 + GLfloat z + GLfloat s0 + GLfloat t0 + GLfloat s1 + GLfloat t1 + + + GLVULKANPROCNV glGetVkProcAddrNV + const GLchar *name + + + void glWaitVkSemaphoreNV + GLuint64 vkSemaphore + + + void glSignalVkSemaphoreNV + GLuint64 vkSemaphore + + + void glSignalVkFenceNV + GLuint64 vkFence + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/dawn/third_party/khronos/vulkan/vk_icd.h b/third_party/dawn/third_party/khronos/vulkan/vk_icd.h new file mode 100644 index 00000000000..5dff59a16ee --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vk_icd.h @@ -0,0 +1,183 @@ +// +// File: vk_icd.h +// +/* + * Copyright (c) 2015-2016 The Khronos Group Inc. + * Copyright (c) 2015-2016 Valve Corporation + * Copyright (c) 2015-2016 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef VKICD_H +#define VKICD_H + +#include "vulkan.h" +#include + +// Loader-ICD version negotiation API. Versions add the following features: +// Version 0 - Initial. Doesn't support vk_icdGetInstanceProcAddr +// or vk_icdNegotiateLoaderICDInterfaceVersion. +// Version 1 - Add support for vk_icdGetInstanceProcAddr. +// Version 2 - Add Loader/ICD Interface version negotiation +// via vk_icdNegotiateLoaderICDInterfaceVersion. +// Version 3 - Add ICD creation/destruction of KHR_surface objects. +// Version 4 - Add unknown physical device extension qyering via +// vk_icdGetPhysicalDeviceProcAddr. +// Version 5 - Tells ICDs that the loader is now paying attention to the +// application version of Vulkan passed into the ApplicationInfo +// structure during vkCreateInstance. This will tell the ICD +// that if the loader is older, it should automatically fail a +// call for any API version > 1.0. Otherwise, the loader will +// manually determine if it can support the expected version. +#define CURRENT_LOADER_ICD_INTERFACE_VERSION 5 +#define MIN_SUPPORTED_LOADER_ICD_INTERFACE_VERSION 0 +#define MIN_PHYS_DEV_EXTENSION_ICD_INTERFACE_VERSION 4 +typedef VkResult(VKAPI_PTR *PFN_vkNegotiateLoaderICDInterfaceVersion)(uint32_t *pVersion); + +// This is defined in vk_layer.h which will be found by the loader, but if an ICD is building against this +// file directly, it won't be found. +#ifndef PFN_GetPhysicalDeviceProcAddr +typedef PFN_vkVoidFunction(VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char *pName); +#endif + +/* + * The ICD must reserve space for a pointer for the loader's dispatch + * table, at the start of . + * The ICD must initialize this variable using the SET_LOADER_MAGIC_VALUE macro. + */ + +#define ICD_LOADER_MAGIC 0x01CDC0DE + +typedef union { + uintptr_t loaderMagic; + void *loaderData; +} VK_LOADER_DATA; + +static inline void set_loader_magic_value(void *pNewObject) { + VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject; + loader_info->loaderMagic = ICD_LOADER_MAGIC; +} + +static inline bool valid_loader_magic_value(void *pNewObject) { + const VK_LOADER_DATA *loader_info = (VK_LOADER_DATA *)pNewObject; + return (loader_info->loaderMagic & 0xffffffff) == ICD_LOADER_MAGIC; +} + +/* + * Windows and Linux ICDs will treat VkSurfaceKHR as a pointer to a struct that + * contains the platform-specific connection and surface information. + */ +typedef enum { + VK_ICD_WSI_PLATFORM_MIR, + VK_ICD_WSI_PLATFORM_WAYLAND, + VK_ICD_WSI_PLATFORM_WIN32, + VK_ICD_WSI_PLATFORM_XCB, + VK_ICD_WSI_PLATFORM_XLIB, + VK_ICD_WSI_PLATFORM_ANDROID, + VK_ICD_WSI_PLATFORM_MACOS, + VK_ICD_WSI_PLATFORM_IOS, + VK_ICD_WSI_PLATFORM_DISPLAY, + VK_ICD_WSI_PLATFORM_HEADLESS, + VK_ICD_WSI_PLATFORM_METAL, +} VkIcdWsiPlatform; + +typedef struct { + VkIcdWsiPlatform platform; +} VkIcdSurfaceBase; + +#ifdef VK_USE_PLATFORM_MIR_KHR +typedef struct { + VkIcdSurfaceBase base; + MirConnection *connection; + MirSurface *mirSurface; +} VkIcdSurfaceMir; +#endif // VK_USE_PLATFORM_MIR_KHR + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +typedef struct { + VkIcdSurfaceBase base; + struct wl_display *display; + struct wl_surface *surface; +} VkIcdSurfaceWayland; +#endif // VK_USE_PLATFORM_WAYLAND_KHR + +#ifdef VK_USE_PLATFORM_WIN32_KHR +typedef struct { + VkIcdSurfaceBase base; + HINSTANCE hinstance; + HWND hwnd; +} VkIcdSurfaceWin32; +#endif // VK_USE_PLATFORM_WIN32_KHR + +#ifdef VK_USE_PLATFORM_XCB_KHR +typedef struct { + VkIcdSurfaceBase base; + xcb_connection_t *connection; + xcb_window_t window; +} VkIcdSurfaceXcb; +#endif // VK_USE_PLATFORM_XCB_KHR + +#ifdef VK_USE_PLATFORM_XLIB_KHR +typedef struct { + VkIcdSurfaceBase base; + Display *dpy; + Window window; +} VkIcdSurfaceXlib; +#endif // VK_USE_PLATFORM_XLIB_KHR + +#ifdef VK_USE_PLATFORM_ANDROID_KHR +typedef struct { + VkIcdSurfaceBase base; + struct ANativeWindow *window; +} VkIcdSurfaceAndroid; +#endif // VK_USE_PLATFORM_ANDROID_KHR + +#ifdef VK_USE_PLATFORM_MACOS_MVK +typedef struct { + VkIcdSurfaceBase base; + const void *pView; +} VkIcdSurfaceMacOS; +#endif // VK_USE_PLATFORM_MACOS_MVK + +#ifdef VK_USE_PLATFORM_IOS_MVK +typedef struct { + VkIcdSurfaceBase base; + const void *pView; +} VkIcdSurfaceIOS; +#endif // VK_USE_PLATFORM_IOS_MVK + +typedef struct { + VkIcdSurfaceBase base; + VkDisplayModeKHR displayMode; + uint32_t planeIndex; + uint32_t planeStackIndex; + VkSurfaceTransformFlagBitsKHR transform; + float globalAlpha; + VkDisplayPlaneAlphaFlagBitsKHR alphaMode; + VkExtent2D imageExtent; +} VkIcdSurfaceDisplay; + +typedef struct { + VkIcdSurfaceBase base; +} VkIcdSurfaceHeadless; + +#ifdef VK_USE_PLATFORM_METAL_EXT +typedef struct { + VkIcdSurfaceBase base; + const CAMetalLayer *pLayer; +} VkIcdSurfaceMetal; +#endif // VK_USE_PLATFORM_METAL_EXT + +#endif // VKICD_H diff --git a/third_party/dawn/third_party/khronos/vulkan/vk_layer.h b/third_party/dawn/third_party/khronos/vulkan/vk_layer.h new file mode 100644 index 00000000000..fa765200897 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vk_layer.h @@ -0,0 +1,202 @@ +// +// File: vk_layer.h +// +/* + * Copyright (c) 2015-2017 The Khronos Group Inc. + * Copyright (c) 2015-2017 Valve Corporation + * Copyright (c) 2015-2017 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/* Need to define dispatch table + * Core struct can then have ptr to dispatch table at the top + * Along with object ptrs for current and next OBJ + */ +#pragma once + +#include "vulkan.h" +#if defined(__GNUC__) && __GNUC__ >= 4 +#define VK_LAYER_EXPORT __attribute__((visibility("default"))) +#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x590) +#define VK_LAYER_EXPORT __attribute__((visibility("default"))) +#else +#define VK_LAYER_EXPORT +#endif + +#define MAX_NUM_UNKNOWN_EXTS 250 + + // Loader-Layer version negotiation API. Versions add the following features: + // Versions 0/1 - Initial. Doesn't support vk_layerGetPhysicalDeviceProcAddr + // or vk_icdNegotiateLoaderLayerInterfaceVersion. + // Version 2 - Add support for vk_layerGetPhysicalDeviceProcAddr and + // vk_icdNegotiateLoaderLayerInterfaceVersion. +#define CURRENT_LOADER_LAYER_INTERFACE_VERSION 2 +#define MIN_SUPPORTED_LOADER_LAYER_INTERFACE_VERSION 1 + +#define VK_CURRENT_CHAIN_VERSION 1 + +// Typedef for use in the interfaces below +typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_GetPhysicalDeviceProcAddr)(VkInstance instance, const char* pName); + +// Version negotiation values +typedef enum VkNegotiateLayerStructType { + LAYER_NEGOTIATE_UNINTIALIZED = 0, + LAYER_NEGOTIATE_INTERFACE_STRUCT = 1, +} VkNegotiateLayerStructType; + +// Version negotiation structures +typedef struct VkNegotiateLayerInterface { + VkNegotiateLayerStructType sType; + void *pNext; + uint32_t loaderLayerInterfaceVersion; + PFN_vkGetInstanceProcAddr pfnGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr; + PFN_GetPhysicalDeviceProcAddr pfnGetPhysicalDeviceProcAddr; +} VkNegotiateLayerInterface; + +// Version negotiation functions +typedef VkResult (VKAPI_PTR *PFN_vkNegotiateLoaderLayerInterfaceVersion)(VkNegotiateLayerInterface *pVersionStruct); + +// Function prototype for unknown physical device extension command +typedef VkResult(VKAPI_PTR *PFN_PhysDevExt)(VkPhysicalDevice phys_device); + +// ------------------------------------------------------------------------------------------------ +// CreateInstance and CreateDevice support structures + +/* Sub type of structure for instance and device loader ext of CreateInfo. + * When sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO + * or sType == VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO + * then VkLayerFunction indicates struct type pointed to by pNext + */ +typedef enum VkLayerFunction_ { + VK_LAYER_LINK_INFO = 0, + VK_LOADER_DATA_CALLBACK = 1, + VK_LOADER_LAYER_CREATE_DEVICE_CALLBACK = 2 +} VkLayerFunction; + +typedef struct VkLayerInstanceLink_ { + struct VkLayerInstanceLink_ *pNext; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; + PFN_GetPhysicalDeviceProcAddr pfnNextGetPhysicalDeviceProcAddr; +} VkLayerInstanceLink; + +/* + * When creating the device chain the loader needs to pass + * down information about it's device structure needed at + * the end of the chain. Passing the data via the + * VkLayerDeviceInfo avoids issues with finding the + * exact instance being used. + */ +typedef struct VkLayerDeviceInfo_ { + void *device_info; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; +} VkLayerDeviceInfo; + +typedef VkResult (VKAPI_PTR *PFN_vkSetInstanceLoaderData)(VkInstance instance, + void *object); +typedef VkResult (VKAPI_PTR *PFN_vkSetDeviceLoaderData)(VkDevice device, + void *object); +typedef VkResult (VKAPI_PTR *PFN_vkLayerCreateDevice)(VkInstance instance, VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo *pCreateInfo, + const VkAllocationCallbacks *pAllocator, VkDevice *pDevice, PFN_vkGetInstanceProcAddr layerGIPA, PFN_vkGetDeviceProcAddr *nextGDPA); +typedef void (VKAPI_PTR *PFN_vkLayerDestroyDevice)(VkDevice physicalDevice, const VkAllocationCallbacks *pAllocator, PFN_vkDestroyDevice destroyFunction); +typedef struct { + VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO + const void *pNext; + VkLayerFunction function; + union { + VkLayerInstanceLink *pLayerInfo; + PFN_vkSetInstanceLoaderData pfnSetInstanceLoaderData; + struct { + PFN_vkLayerCreateDevice pfnLayerCreateDevice; + PFN_vkLayerDestroyDevice pfnLayerDestroyDevice; + } layerDevice; + } u; +} VkLayerInstanceCreateInfo; + +typedef struct VkLayerDeviceLink_ { + struct VkLayerDeviceLink_ *pNext; + PFN_vkGetInstanceProcAddr pfnNextGetInstanceProcAddr; + PFN_vkGetDeviceProcAddr pfnNextGetDeviceProcAddr; +} VkLayerDeviceLink; + +typedef struct { + VkStructureType sType; // VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO + const void *pNext; + VkLayerFunction function; + union { + VkLayerDeviceLink *pLayerInfo; + PFN_vkSetDeviceLoaderData pfnSetDeviceLoaderData; + } u; +} VkLayerDeviceCreateInfo; + +#ifdef __cplusplus +extern "C" { +#endif + +VKAPI_ATTR VkResult VKAPI_CALL vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface *pVersionStruct); + +typedef enum VkChainType { + VK_CHAIN_TYPE_UNKNOWN = 0, + VK_CHAIN_TYPE_ENUMERATE_INSTANCE_EXTENSION_PROPERTIES = 1, + VK_CHAIN_TYPE_ENUMERATE_INSTANCE_LAYER_PROPERTIES = 2, + VK_CHAIN_TYPE_ENUMERATE_INSTANCE_VERSION = 3, +} VkChainType; + +typedef struct VkChainHeader { + VkChainType type; + uint32_t version; + uint32_t size; +} VkChainHeader; + +typedef struct VkEnumerateInstanceExtensionPropertiesChain { + VkChainHeader header; + VkResult(VKAPI_PTR *pfnNextLayer)(const struct VkEnumerateInstanceExtensionPropertiesChain *, const char *, uint32_t *, + VkExtensionProperties *); + const struct VkEnumerateInstanceExtensionPropertiesChain *pNextLink; + +#if defined(__cplusplus) + inline VkResult CallDown(const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties) const { + return pfnNextLayer(pNextLink, pLayerName, pPropertyCount, pProperties); + } +#endif +} VkEnumerateInstanceExtensionPropertiesChain; + +typedef struct VkEnumerateInstanceLayerPropertiesChain { + VkChainHeader header; + VkResult(VKAPI_PTR *pfnNextLayer)(const struct VkEnumerateInstanceLayerPropertiesChain *, uint32_t *, VkLayerProperties *); + const struct VkEnumerateInstanceLayerPropertiesChain *pNextLink; + +#if defined(__cplusplus) + inline VkResult CallDown(uint32_t *pPropertyCount, VkLayerProperties *pProperties) const { + return pfnNextLayer(pNextLink, pPropertyCount, pProperties); + } +#endif +} VkEnumerateInstanceLayerPropertiesChain; + +typedef struct VkEnumerateInstanceVersionChain { + VkChainHeader header; + VkResult(VKAPI_PTR *pfnNextLayer)(const struct VkEnumerateInstanceVersionChain *, uint32_t *); + const struct VkEnumerateInstanceVersionChain *pNextLink; + +#if defined(__cplusplus) + inline VkResult CallDown(uint32_t *pApiVersion) const { + return pfnNextLayer(pNextLink, pApiVersion); + } +#endif +} VkEnumerateInstanceVersionChain; + +#ifdef __cplusplus +} +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vk_platform.h b/third_party/dawn/third_party/khronos/vulkan/vk_platform.h new file mode 100644 index 00000000000..7289299240a --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vk_platform.h @@ -0,0 +1,92 @@ +// +// File: vk_platform.h +// +/* +** Copyright (c) 2014-2017 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + + +#ifndef VK_PLATFORM_H_ +#define VK_PLATFORM_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif // __cplusplus + +/* +*************************************************************************************************** +* Platform-specific directives and type declarations +*************************************************************************************************** +*/ + +/* Platform-specific calling convention macros. + * + * Platforms should define these so that Vulkan clients call Vulkan commands + * with the same calling conventions that the Vulkan implementation expects. + * + * VKAPI_ATTR - Placed before the return type in function declarations. + * Useful for C++11 and GCC/Clang-style function attribute syntax. + * VKAPI_CALL - Placed after the return type in function declarations. + * Useful for MSVC-style calling convention syntax. + * VKAPI_PTR - Placed between the '(' and '*' in function pointer types. + * + * Function declaration: VKAPI_ATTR void VKAPI_CALL vkCommand(void); + * Function pointer type: typedef void (VKAPI_PTR *PFN_vkCommand)(void); + */ +#if defined(_WIN32) + // On Windows, Vulkan commands use the stdcall convention + #define VKAPI_ATTR + #define VKAPI_CALL __stdcall + #define VKAPI_PTR VKAPI_CALL +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7 + #error "Vulkan isn't supported for the 'armeabi' NDK ABI" +#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE) + // On Android 32-bit ARM targets, Vulkan functions use the "hardfloat" + // calling convention, i.e. float parameters are passed in registers. This + // is true even if the rest of the application passes floats on the stack, + // as it does by default when compiling for the armeabi-v7a NDK ABI. + #define VKAPI_ATTR __attribute__((pcs("aapcs-vfp"))) + #define VKAPI_CALL + #define VKAPI_PTR VKAPI_ATTR +#else + // On other platforms, use the default calling convention + #define VKAPI_ATTR + #define VKAPI_CALL + #define VKAPI_PTR +#endif + +#include + +#if !defined(VK_NO_STDINT_H) + #if defined(_MSC_VER) && (_MSC_VER < 1600) + typedef signed __int8 int8_t; + typedef unsigned __int8 uint8_t; + typedef signed __int16 int16_t; + typedef unsigned __int16 uint16_t; + typedef signed __int32 int32_t; + typedef unsigned __int32 uint32_t; + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; + #else + #include + #endif +#endif // !defined(VK_NO_STDINT_H) + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vk_sdk_platform.h b/third_party/dawn/third_party/khronos/vulkan/vk_sdk_platform.h new file mode 100644 index 00000000000..96d8676949e --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vk_sdk_platform.h @@ -0,0 +1,69 @@ +// +// File: vk_sdk_platform.h +// +/* + * Copyright (c) 2015-2016 The Khronos Group Inc. + * Copyright (c) 2015-2016 Valve Corporation + * Copyright (c) 2015-2016 LunarG, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VK_SDK_PLATFORM_H +#define VK_SDK_PLATFORM_H + +#if defined(_WIN32) +#define NOMINMAX +#ifndef __cplusplus +#undef inline +#define inline __inline +#endif // __cplusplus + +#if (defined(_MSC_VER) && _MSC_VER < 1900 /*vs2015*/) +// C99: +// Microsoft didn't implement C99 in Visual Studio; but started adding it with +// VS2013. However, VS2013 still didn't have snprintf(). The following is a +// work-around (Note: The _CRT_SECURE_NO_WARNINGS macro must be set in the +// "CMakeLists.txt" file). +// NOTE: This is fixed in Visual Studio 2015. +#define snprintf _snprintf +#endif + +#define strdup _strdup + +#endif // _WIN32 + +// Check for noexcept support using clang, with fallback to Windows or GCC version numbers +#ifndef NOEXCEPT +#if defined(__clang__) +#if __has_feature(cxx_noexcept) +#define HAS_NOEXCEPT +#endif +#else +#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ * 10 + __GNUC_MINOR__ >= 46 +#define HAS_NOEXCEPT +#else +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 && defined(_HAS_EXCEPTIONS) && _HAS_EXCEPTIONS +#define HAS_NOEXCEPT +#endif +#endif +#endif + +#ifdef HAS_NOEXCEPT +#define NOEXCEPT noexcept +#else +#define NOEXCEPT +#endif +#endif + +#endif // VK_SDK_PLATFORM_H diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan.h b/third_party/dawn/third_party/khronos/vulkan/vulkan.h new file mode 100644 index 00000000000..5f853f9fc8e --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan.h @@ -0,0 +1,86 @@ +#ifndef VULKAN_H_ +#define VULKAN_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +#include "vk_platform.h" +#include "vulkan_core.h" + +#ifdef VK_USE_PLATFORM_ANDROID_KHR +#include "vulkan_android.h" +#endif + +#ifdef VK_USE_PLATFORM_FUCHSIA +#include +#include "vulkan_fuchsia.h" +#endif + +#ifdef VK_USE_PLATFORM_IOS_MVK +#include "vulkan_ios.h" +#endif + + +#ifdef VK_USE_PLATFORM_MACOS_MVK +#include "vulkan_macos.h" +#endif + +#ifdef VK_USE_PLATFORM_METAL_EXT +#include "vulkan_metal.h" +#endif + +#ifdef VK_USE_PLATFORM_VI_NN +#include "vulkan_vi.h" +#endif + + +#ifdef VK_USE_PLATFORM_WAYLAND_KHR +#include +#include "vulkan_wayland.h" +#endif + + +#ifdef VK_USE_PLATFORM_WIN32_KHR +#include +#include "vulkan_win32.h" +#endif + + +#ifdef VK_USE_PLATFORM_XCB_KHR +#include +#include "vulkan_xcb.h" +#endif + + +#ifdef VK_USE_PLATFORM_XLIB_KHR +#include +#include "vulkan_xlib.h" +#endif + + +#ifdef VK_USE_PLATFORM_XLIB_XRANDR_EXT +#include +#include +#include "vulkan_xlib_xrandr.h" +#endif + + +#ifdef VK_USE_PLATFORM_GGP +#include +#include "vulkan_ggp.h" +#endif + +#endif // VULKAN_H_ diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_android.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_android.h new file mode 100644 index 00000000000..9b8d3e276f8 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_android.h @@ -0,0 +1,122 @@ +#ifndef VULKAN_ANDROID_H_ +#define VULKAN_ANDROID_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_KHR_android_surface 1 +struct ANativeWindow; +#define VK_KHR_ANDROID_SURFACE_SPEC_VERSION 6 +#define VK_KHR_ANDROID_SURFACE_EXTENSION_NAME "VK_KHR_android_surface" +typedef VkFlags VkAndroidSurfaceCreateFlagsKHR; +typedef struct VkAndroidSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkAndroidSurfaceCreateFlagsKHR flags; + struct ANativeWindow* window; +} VkAndroidSurfaceCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateAndroidSurfaceKHR)(VkInstance instance, const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateAndroidSurfaceKHR( + VkInstance instance, + const VkAndroidSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + + +#define VK_ANDROID_external_memory_android_hardware_buffer 1 +struct AHardwareBuffer; +#define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_SPEC_VERSION 3 +#define VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME "VK_ANDROID_external_memory_android_hardware_buffer" +typedef struct VkAndroidHardwareBufferUsageANDROID { + VkStructureType sType; + void* pNext; + uint64_t androidHardwareBufferUsage; +} VkAndroidHardwareBufferUsageANDROID; + +typedef struct VkAndroidHardwareBufferPropertiesANDROID { + VkStructureType sType; + void* pNext; + VkDeviceSize allocationSize; + uint32_t memoryTypeBits; +} VkAndroidHardwareBufferPropertiesANDROID; + +typedef struct VkAndroidHardwareBufferFormatPropertiesANDROID { + VkStructureType sType; + void* pNext; + VkFormat format; + uint64_t externalFormat; + VkFormatFeatureFlags formatFeatures; + VkComponentMapping samplerYcbcrConversionComponents; + VkSamplerYcbcrModelConversion suggestedYcbcrModel; + VkSamplerYcbcrRange suggestedYcbcrRange; + VkChromaLocation suggestedXChromaOffset; + VkChromaLocation suggestedYChromaOffset; +} VkAndroidHardwareBufferFormatPropertiesANDROID; + +typedef struct VkImportAndroidHardwareBufferInfoANDROID { + VkStructureType sType; + const void* pNext; + struct AHardwareBuffer* buffer; +} VkImportAndroidHardwareBufferInfoANDROID; + +typedef struct VkMemoryGetAndroidHardwareBufferInfoANDROID { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; +} VkMemoryGetAndroidHardwareBufferInfoANDROID; + +typedef struct VkExternalFormatANDROID { + VkStructureType sType; + void* pNext; + uint64_t externalFormat; +} VkExternalFormatANDROID; + +typedef VkResult (VKAPI_PTR *PFN_vkGetAndroidHardwareBufferPropertiesANDROID)(VkDevice device, const struct AHardwareBuffer* buffer, VkAndroidHardwareBufferPropertiesANDROID* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryAndroidHardwareBufferANDROID)(VkDevice device, const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, struct AHardwareBuffer** pBuffer); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetAndroidHardwareBufferPropertiesANDROID( + VkDevice device, + const struct AHardwareBuffer* buffer, + VkAndroidHardwareBufferPropertiesANDROID* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryAndroidHardwareBufferANDROID( + VkDevice device, + const VkMemoryGetAndroidHardwareBufferInfoANDROID* pInfo, + struct AHardwareBuffer** pBuffer); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_core.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_core.h new file mode 100644 index 00000000000..9770c3b9be7 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_core.h @@ -0,0 +1,9991 @@ +#ifndef VULKAN_CORE_H_ +#define VULKAN_CORE_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_VERSION_1_0 1 +#include "vk_platform.h" +#define VK_MAKE_VERSION(major, minor, patch) \ + (((major) << 22) | ((minor) << 12) | (patch)) + +// DEPRECATED: This define has been removed. Specific version defines (e.g. VK_API_VERSION_1_0), or the VK_MAKE_VERSION macro, should be used instead. +//#define VK_API_VERSION VK_MAKE_VERSION(1, 0, 0) // Patch version should always be set to 0 + +// Vulkan 1.0 version number +#define VK_API_VERSION_1_0 VK_MAKE_VERSION(1, 0, 0)// Patch version should always be set to 0 + +#define VK_VERSION_MAJOR(version) ((uint32_t)(version) >> 22) +#define VK_VERSION_MINOR(version) (((uint32_t)(version) >> 12) & 0x3ff) +#define VK_VERSION_PATCH(version) ((uint32_t)(version) & 0xfff) +// Version of this file +#define VK_HEADER_VERSION 125 + + +#define VK_NULL_HANDLE 0 + + +#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object; + + +#if !defined(VK_DEFINE_NON_DISPATCHABLE_HANDLE) +#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object; +#else + #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object; +#endif +#endif + +typedef uint32_t VkFlags; +typedef uint32_t VkBool32; +typedef uint64_t VkDeviceSize; +typedef uint32_t VkSampleMask; +VK_DEFINE_HANDLE(VkInstance) +VK_DEFINE_HANDLE(VkPhysicalDevice) +VK_DEFINE_HANDLE(VkDevice) +VK_DEFINE_HANDLE(VkQueue) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSemaphore) +VK_DEFINE_HANDLE(VkCommandBuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFence) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkEvent) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkQueryPool) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferView) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderModule) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineCache) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipelineLayout) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkRenderPass) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPipeline) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSetLayout) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSampler) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorPool) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorSet) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkFramebuffer) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCommandPool) +#define VK_LOD_CLAMP_NONE 1000.0f +#define VK_REMAINING_MIP_LEVELS (~0U) +#define VK_REMAINING_ARRAY_LAYERS (~0U) +#define VK_WHOLE_SIZE (~0ULL) +#define VK_ATTACHMENT_UNUSED (~0U) +#define VK_TRUE 1 +#define VK_FALSE 0 +#define VK_QUEUE_FAMILY_IGNORED (~0U) +#define VK_SUBPASS_EXTERNAL (~0U) +#define VK_MAX_PHYSICAL_DEVICE_NAME_SIZE 256 +#define VK_UUID_SIZE 16 +#define VK_MAX_MEMORY_TYPES 32 +#define VK_MAX_MEMORY_HEAPS 16 +#define VK_MAX_EXTENSION_NAME_SIZE 256 +#define VK_MAX_DESCRIPTION_SIZE 256 + +typedef enum VkPipelineCacheHeaderVersion { + VK_PIPELINE_CACHE_HEADER_VERSION_ONE = 1, + VK_PIPELINE_CACHE_HEADER_VERSION_BEGIN_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, + VK_PIPELINE_CACHE_HEADER_VERSION_END_RANGE = VK_PIPELINE_CACHE_HEADER_VERSION_ONE, + VK_PIPELINE_CACHE_HEADER_VERSION_RANGE_SIZE = (VK_PIPELINE_CACHE_HEADER_VERSION_ONE - VK_PIPELINE_CACHE_HEADER_VERSION_ONE + 1), + VK_PIPELINE_CACHE_HEADER_VERSION_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCacheHeaderVersion; + +typedef enum VkResult { + VK_SUCCESS = 0, + VK_NOT_READY = 1, + VK_TIMEOUT = 2, + VK_EVENT_SET = 3, + VK_EVENT_RESET = 4, + VK_INCOMPLETE = 5, + VK_ERROR_OUT_OF_HOST_MEMORY = -1, + VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, + VK_ERROR_INITIALIZATION_FAILED = -3, + VK_ERROR_DEVICE_LOST = -4, + VK_ERROR_MEMORY_MAP_FAILED = -5, + VK_ERROR_LAYER_NOT_PRESENT = -6, + VK_ERROR_EXTENSION_NOT_PRESENT = -7, + VK_ERROR_FEATURE_NOT_PRESENT = -8, + VK_ERROR_INCOMPATIBLE_DRIVER = -9, + VK_ERROR_TOO_MANY_OBJECTS = -10, + VK_ERROR_FORMAT_NOT_SUPPORTED = -11, + VK_ERROR_FRAGMENTED_POOL = -12, + VK_ERROR_OUT_OF_POOL_MEMORY = -1000069000, + VK_ERROR_INVALID_EXTERNAL_HANDLE = -1000072003, + VK_ERROR_SURFACE_LOST_KHR = -1000000000, + VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, + VK_SUBOPTIMAL_KHR = 1000001003, + VK_ERROR_OUT_OF_DATE_KHR = -1000001004, + VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, + VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_ERROR_INVALID_SHADER_NV = -1000012000, + VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT = -1000158000, + VK_ERROR_FRAGMENTATION_EXT = -1000161000, + VK_ERROR_NOT_PERMITTED_EXT = -1000174001, + VK_ERROR_INVALID_DEVICE_ADDRESS_EXT = -1000244000, + VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT = -1000255000, + VK_ERROR_OUT_OF_POOL_MEMORY_KHR = VK_ERROR_OUT_OF_POOL_MEMORY, + VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR = VK_ERROR_INVALID_EXTERNAL_HANDLE, + VK_RESULT_BEGIN_RANGE = VK_ERROR_FRAGMENTED_POOL, + VK_RESULT_END_RANGE = VK_INCOMPLETE, + VK_RESULT_RANGE_SIZE = (VK_INCOMPLETE - VK_ERROR_FRAGMENTED_POOL + 1), + VK_RESULT_MAX_ENUM = 0x7FFFFFFF +} VkResult; + +typedef enum VkStructureType { + VK_STRUCTURE_TYPE_APPLICATION_INFO = 0, + VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO = 1, + VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO = 2, + VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO = 3, + VK_STRUCTURE_TYPE_SUBMIT_INFO = 4, + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO = 5, + VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE = 6, + VK_STRUCTURE_TYPE_BIND_SPARSE_INFO = 7, + VK_STRUCTURE_TYPE_FENCE_CREATE_INFO = 8, + VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO = 9, + VK_STRUCTURE_TYPE_EVENT_CREATE_INFO = 10, + VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO = 11, + VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO = 12, + VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO = 13, + VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO = 14, + VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO = 15, + VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO = 16, + VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO = 17, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO = 18, + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO = 19, + VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO = 20, + VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO = 21, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO = 22, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO = 23, + VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO = 24, + VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO = 25, + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO = 26, + VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO = 27, + VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO = 28, + VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO = 29, + VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO = 30, + VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO = 31, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO = 32, + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO = 33, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO = 34, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET = 35, + VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET = 36, + VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO = 37, + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO = 38, + VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO = 39, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO = 40, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO = 41, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO = 42, + VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO = 43, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER = 44, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER = 45, + VK_STRUCTURE_TYPE_MEMORY_BARRIER = 46, + VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO = 47, + VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO = 48, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES = 1000094000, + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO = 1000157000, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO = 1000157001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES = 1000083000, + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS = 1000127000, + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO = 1000127001, + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO = 1000060000, + VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO = 1000060003, + VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO = 1000060004, + VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO = 1000060005, + VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO = 1000060006, + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO = 1000060013, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO = 1000060014, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES = 1000070000, + VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO = 1000070001, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2 = 1000146000, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2 = 1000146001, + VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2 = 1000146002, + VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2 = 1000146003, + VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2 = 1000146004, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 = 1000059000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2 = 1000059001, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2 = 1000059002, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2 = 1000059003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2 = 1000059004, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2 = 1000059005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2 = 1000059006, + VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2 = 1000059007, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2 = 1000059008, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES = 1000117000, + VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO = 1000117001, + VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO = 1000117002, + VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO = 1000117003, + VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO = 1000053000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES = 1000053001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES = 1000053002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES = 1000120000, + VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO = 1000145000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES = 1000145001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES = 1000145002, + VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2 = 1000145003, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO = 1000156000, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO = 1000156001, + VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO = 1000156002, + VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO = 1000156003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES = 1000156004, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES = 1000156005, + VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO = 1000085000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO = 1000071000, + VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES = 1000071001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO = 1000071002, + VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES = 1000071003, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES = 1000071004, + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO = 1000072000, + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO = 1000072001, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO = 1000072002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO = 1000112000, + VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES = 1000112001, + VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO = 1000113000, + VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO = 1000077000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO = 1000076000, + VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES = 1000076001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES = 1000168000, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT = 1000168001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES = 1000063000, + VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR = 1000001000, + VK_STRUCTURE_TYPE_PRESENT_INFO_KHR = 1000001001, + VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_CAPABILITIES_KHR = 1000060007, + VK_STRUCTURE_TYPE_IMAGE_SWAPCHAIN_CREATE_INFO_KHR = 1000060008, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR = 1000060009, + VK_STRUCTURE_TYPE_ACQUIRE_NEXT_IMAGE_INFO_KHR = 1000060010, + VK_STRUCTURE_TYPE_DEVICE_GROUP_PRESENT_INFO_KHR = 1000060011, + VK_STRUCTURE_TYPE_DEVICE_GROUP_SWAPCHAIN_CREATE_INFO_KHR = 1000060012, + VK_STRUCTURE_TYPE_DISPLAY_MODE_CREATE_INFO_KHR = 1000002000, + VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR = 1000002001, + VK_STRUCTURE_TYPE_DISPLAY_PRESENT_INFO_KHR = 1000003000, + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, + VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, + VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR = 1000008000, + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT = 1000011000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_RASTERIZATION_ORDER_AMD = 1000018000, + VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT = 1000022000, + VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT = 1000022001, + VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT = 1000022002, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_IMAGE_CREATE_INFO_NV = 1000026000, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_BUFFER_CREATE_INFO_NV = 1000026001, + VK_STRUCTURE_TYPE_DEDICATED_ALLOCATION_MEMORY_ALLOCATE_INFO_NV = 1000026002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT = 1000028000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT = 1000028001, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT = 1000028002, + VK_STRUCTURE_TYPE_IMAGE_VIEW_HANDLE_INFO_NVX = 1000030000, + VK_STRUCTURE_TYPE_TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD = 1000041000, + VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP = 1000049000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CORNER_SAMPLED_IMAGE_FEATURES_NV = 1000050000, + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_NV = 1000056000, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_NV = 1000056001, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057000, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_NV = 1000057001, + VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_NV = 1000058000, + VK_STRUCTURE_TYPE_VALIDATION_FLAGS_EXT = 1000061000, + VK_STRUCTURE_TYPE_VI_SURFACE_CREATE_INFO_NN = 1000062000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXTURE_COMPRESSION_ASTC_HDR_FEATURES_EXT = 1000066000, + VK_STRUCTURE_TYPE_IMAGE_VIEW_ASTC_DECODE_MODE_EXT = 1000067000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ASTC_DECODE_FEATURES_EXT = 1000067001, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073000, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_WIN32_HANDLE_INFO_KHR = 1000073001, + VK_STRUCTURE_TYPE_MEMORY_WIN32_HANDLE_PROPERTIES_KHR = 1000073002, + VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR = 1000073003, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR = 1000074000, + VK_STRUCTURE_TYPE_MEMORY_FD_PROPERTIES_KHR = 1000074001, + VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR = 1000074002, + VK_STRUCTURE_TYPE_WIN32_KEYED_MUTEX_ACQUIRE_RELEASE_INFO_KHR = 1000075000, + VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR = 1000078000, + VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR = 1000078001, + VK_STRUCTURE_TYPE_D3D12_FENCE_SUBMIT_INFO_KHR = 1000078002, + VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR = 1000078003, + VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR = 1000079000, + VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR = 1000079001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR = 1000080000, + VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_CONDITIONAL_RENDERING_INFO_EXT = 1000081000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONDITIONAL_RENDERING_FEATURES_EXT = 1000081001, + VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT = 1000081002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR = 1000082000, + VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR = 1000084000, + VK_STRUCTURE_TYPE_OBJECT_TABLE_CREATE_INFO_NVX = 1000086000, + VK_STRUCTURE_TYPE_INDIRECT_COMMANDS_LAYOUT_CREATE_INFO_NVX = 1000086001, + VK_STRUCTURE_TYPE_CMD_PROCESS_COMMANDS_INFO_NVX = 1000086002, + VK_STRUCTURE_TYPE_CMD_RESERVE_SPACE_FOR_COMMANDS_INFO_NVX = 1000086003, + VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_LIMITS_NVX = 1000086004, + VK_STRUCTURE_TYPE_DEVICE_GENERATED_COMMANDS_FEATURES_NVX = 1000086005, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_W_SCALING_STATE_CREATE_INFO_NV = 1000087000, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT = 1000090000, + VK_STRUCTURE_TYPE_DISPLAY_POWER_INFO_EXT = 1000091000, + VK_STRUCTURE_TYPE_DEVICE_EVENT_INFO_EXT = 1000091001, + VK_STRUCTURE_TYPE_DISPLAY_EVENT_INFO_EXT = 1000091002, + VK_STRUCTURE_TYPE_SWAPCHAIN_COUNTER_CREATE_INFO_EXT = 1000091003, + VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE = 1000092000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_ATTRIBUTES_PROPERTIES_NVX = 1000097000, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SWIZZLE_STATE_CREATE_INFO_NV = 1000098000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DISCARD_RECTANGLE_PROPERTIES_EXT = 1000099000, + VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT = 1000099001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT = 1000101000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT = 1000101001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT = 1000102000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT = 1000102001, + VK_STRUCTURE_TYPE_HDR_METADATA_EXT = 1000105000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGELESS_FRAMEBUFFER_FEATURES_KHR = 1000108000, + VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENTS_CREATE_INFO_KHR = 1000108001, + VK_STRUCTURE_TYPE_FRAMEBUFFER_ATTACHMENT_IMAGE_INFO_KHR = 1000108002, + VK_STRUCTURE_TYPE_RENDER_PASS_ATTACHMENT_BEGIN_INFO_KHR = 1000108003, + VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR = 1000109000, + VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR = 1000109001, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR = 1000109002, + VK_STRUCTURE_TYPE_SUBPASS_DEPENDENCY_2_KHR = 1000109003, + VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR = 1000109004, + VK_STRUCTURE_TYPE_SUBPASS_BEGIN_INFO_KHR = 1000109005, + VK_STRUCTURE_TYPE_SUBPASS_END_INFO_KHR = 1000109006, + VK_STRUCTURE_TYPE_SHARED_PRESENT_SURFACE_CAPABILITIES_KHR = 1000111000, + VK_STRUCTURE_TYPE_IMPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114000, + VK_STRUCTURE_TYPE_EXPORT_FENCE_WIN32_HANDLE_INFO_KHR = 1000114001, + VK_STRUCTURE_TYPE_FENCE_GET_WIN32_HANDLE_INFO_KHR = 1000114002, + VK_STRUCTURE_TYPE_IMPORT_FENCE_FD_INFO_KHR = 1000115000, + VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR = 1000115001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR = 1000119000, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR = 1000119001, + VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR = 1000119002, + VK_STRUCTURE_TYPE_DISPLAY_PROPERTIES_2_KHR = 1000121000, + VK_STRUCTURE_TYPE_DISPLAY_PLANE_PROPERTIES_2_KHR = 1000121001, + VK_STRUCTURE_TYPE_DISPLAY_MODE_PROPERTIES_2_KHR = 1000121002, + VK_STRUCTURE_TYPE_DISPLAY_PLANE_INFO_2_KHR = 1000121003, + VK_STRUCTURE_TYPE_DISPLAY_PLANE_CAPABILITIES_2_KHR = 1000121004, + VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK = 1000122000, + VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK = 1000123000, + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000128000, + VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT = 1000128001, + VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT = 1000128002, + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT = 1000128003, + VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT = 1000128004, + VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID = 1000129000, + VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID = 1000129001, + VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID = 1000129002, + VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129003, + VK_STRUCTURE_TYPE_MEMORY_GET_ANDROID_HARDWARE_BUFFER_INFO_ANDROID = 1000129004, + VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID = 1000129005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_FILTER_MINMAX_PROPERTIES_EXT = 1000130000, + VK_STRUCTURE_TYPE_SAMPLER_REDUCTION_MODE_CREATE_INFO_EXT = 1000130001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT = 1000138000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_PROPERTIES_EXT = 1000138001, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT = 1000138002, + VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_INLINE_UNIFORM_BLOCK_CREATE_INFO_EXT = 1000138003, + VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT = 1000143000, + VK_STRUCTURE_TYPE_RENDER_PASS_SAMPLE_LOCATIONS_BEGIN_INFO_EXT = 1000143001, + VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT = 1000143002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLE_LOCATIONS_PROPERTIES_EXT = 1000143003, + VK_STRUCTURE_TYPE_MULTISAMPLE_PROPERTIES_EXT = 1000143004, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR = 1000147000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_FEATURES_EXT = 1000148000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BLEND_OPERATION_ADVANCED_PROPERTIES_EXT = 1000148001, + VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_ADVANCED_STATE_CREATE_INFO_EXT = 1000148002, + VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_TO_COLOR_STATE_CREATE_INFO_NV = 1000149000, + VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_MODULATION_STATE_CREATE_INFO_NV = 1000152000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_FEATURES_NV = 1000154000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SM_BUILTINS_PROPERTIES_NV = 1000154001, + VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_LIST_EXT = 1000158000, + VK_STRUCTURE_TYPE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT = 1000158001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT = 1000158002, + VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT = 1000158003, + VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_EXPLICIT_CREATE_INFO_EXT = 1000158004, + VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT = 1000158005, + VK_STRUCTURE_TYPE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160000, + VK_STRUCTURE_TYPE_SHADER_MODULE_VALIDATION_CACHE_CREATE_INFO_EXT = 1000160001, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT = 1000161000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT = 1000161001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_PROPERTIES_EXT = 1000161002, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO_EXT = 1000161003, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_LAYOUT_SUPPORT_EXT = 1000161004, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_SHADING_RATE_IMAGE_STATE_CREATE_INFO_NV = 1000164000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_FEATURES_NV = 1000164001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADING_RATE_IMAGE_PROPERTIES_NV = 1000164002, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_COARSE_SAMPLE_ORDER_STATE_CREATE_INFO_NV = 1000164005, + VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_NV = 1000165000, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_NV = 1000165001, + VK_STRUCTURE_TYPE_GEOMETRY_NV = 1000165003, + VK_STRUCTURE_TYPE_GEOMETRY_TRIANGLES_NV = 1000165004, + VK_STRUCTURE_TYPE_GEOMETRY_AABB_NV = 1000165005, + VK_STRUCTURE_TYPE_BIND_ACCELERATION_STRUCTURE_MEMORY_INFO_NV = 1000165006, + VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_NV = 1000165007, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_INFO_NV = 1000165008, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PROPERTIES_NV = 1000165009, + VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_NV = 1000165011, + VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_INFO_NV = 1000165012, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_REPRESENTATIVE_FRAGMENT_TEST_FEATURES_NV = 1000166000, + VK_STRUCTURE_TYPE_PIPELINE_REPRESENTATIVE_FRAGMENT_TEST_STATE_CREATE_INFO_NV = 1000166001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_IMAGE_FORMAT_INFO_EXT = 1000170000, + VK_STRUCTURE_TYPE_FILTER_CUBIC_IMAGE_VIEW_IMAGE_FORMAT_PROPERTIES_EXT = 1000170001, + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT = 1000174000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_SUBGROUP_EXTENDED_TYPES_FEATURES_KHR = 1000175000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR = 1000177000, + VK_STRUCTURE_TYPE_IMPORT_MEMORY_HOST_POINTER_INFO_EXT = 1000178000, + VK_STRUCTURE_TYPE_MEMORY_HOST_POINTER_PROPERTIES_EXT = 1000178001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT = 1000178002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_ATOMIC_INT64_FEATURES_KHR = 1000180000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CLOCK_FEATURES_KHR = 1000181000, + VK_STRUCTURE_TYPE_PIPELINE_COMPILER_CONTROL_CREATE_INFO_AMD = 1000183000, + VK_STRUCTURE_TYPE_CALIBRATED_TIMESTAMP_INFO_EXT = 1000184000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD = 1000185000, + VK_STRUCTURE_TYPE_DEVICE_MEMORY_OVERALLOCATION_CREATE_INFO_AMD = 1000189000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT = 1000190000, + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT = 1000190001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT = 1000190002, + VK_STRUCTURE_TYPE_PRESENT_FRAME_TOKEN_GGP = 1000191000, + VK_STRUCTURE_TYPE_PIPELINE_CREATION_FEEDBACK_CREATE_INFO_EXT = 1000192000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR = 1000196000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR = 1000197000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES_KHR = 1000199000, + VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR = 1000199001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COMPUTE_SHADER_DERIVATIVES_FEATURES_NV = 1000201000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV = 1000202000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV = 1000202001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_BARYCENTRIC_FEATURES_NV = 1000203000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_IMAGE_FOOTPRINT_FEATURES_NV = 1000204000, + VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_EXCLUSIVE_SCISSOR_STATE_CREATE_INFO_NV = 1000205000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXCLUSIVE_SCISSOR_FEATURES_NV = 1000205002, + VK_STRUCTURE_TYPE_CHECKPOINT_DATA_NV = 1000206000, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_CHECKPOINT_PROPERTIES_NV = 1000206001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR = 1000207000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR = 1000207001, + VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR = 1000207002, + VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR = 1000207003, + VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR = 1000207004, + VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO_KHR = 1000207005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL = 1000209000, + VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO_INTEL = 1000210000, + VK_STRUCTURE_TYPE_INITIALIZE_PERFORMANCE_API_INFO_INTEL = 1000210001, + VK_STRUCTURE_TYPE_PERFORMANCE_MARKER_INFO_INTEL = 1000210002, + VK_STRUCTURE_TYPE_PERFORMANCE_STREAM_MARKER_INFO_INTEL = 1000210003, + VK_STRUCTURE_TYPE_PERFORMANCE_OVERRIDE_INFO_INTEL = 1000210004, + VK_STRUCTURE_TYPE_PERFORMANCE_CONFIGURATION_ACQUIRE_INFO_INTEL = 1000210005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES_KHR = 1000211000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT = 1000212000, + VK_STRUCTURE_TYPE_DISPLAY_NATIVE_HDR_SURFACE_CAPABILITIES_AMD = 1000213000, + VK_STRUCTURE_TYPE_SWAPCHAIN_DISPLAY_NATIVE_HDR_CREATE_INFO_AMD = 1000213001, + VK_STRUCTURE_TYPE_IMAGEPIPE_SURFACE_CREATE_INFO_FUCHSIA = 1000214000, + VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT = 1000217000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT = 1000218000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT = 1000218001, + VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT = 1000218002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT = 1000221000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT = 1000225000, + VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT = 1000225001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT = 1000225002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_2_AMD = 1000227000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COHERENT_MEMORY_FEATURES_AMD = 1000229000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT = 1000237000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT = 1000238000, + VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT = 1000238001, + VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR = 1000239000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEDICATED_ALLOCATION_IMAGE_ALIASING_FEATURES_NV = 1000240000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT = 1000244000, + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO_EXT = 1000244001, + VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_CREATE_INFO_EXT = 1000244002, + VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO_EXT = 1000246000, + VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT = 1000247000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_FEATURES_NV = 1000249000, + VK_STRUCTURE_TYPE_COOPERATIVE_MATRIX_PROPERTIES_NV = 1000249001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COOPERATIVE_MATRIX_PROPERTIES_NV = 1000249002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_COVERAGE_REDUCTION_MODE_FEATURES_NV = 1000250000, + VK_STRUCTURE_TYPE_PIPELINE_COVERAGE_REDUCTION_STATE_CREATE_INFO_NV = 1000250001, + VK_STRUCTURE_TYPE_FRAMEBUFFER_MIXED_SAMPLES_COMBINATION_NV = 1000250002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT = 1000251000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_YCBCR_IMAGE_ARRAYS_FEATURES_EXT = 1000252000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR = 1000253000, + VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_INFO_EXT = 1000255000, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_FULL_SCREEN_EXCLUSIVE_EXT = 1000255002, + VK_STRUCTURE_TYPE_SURFACE_FULL_SCREEN_EXCLUSIVE_WIN32_INFO_EXT = 1000255001, + VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT = 1000256000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_FEATURES_EXT = 1000259000, + VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO_EXT = 1000259001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINE_RASTERIZATION_PROPERTIES_EXT = 1000259002, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT = 1000261000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INDEX_TYPE_UINT8_FEATURES_EXT = 1000265000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_EXECUTABLE_PROPERTIES_FEATURES_KHR = 1000269000, + VK_STRUCTURE_TYPE_PIPELINE_INFO_KHR = 1000269001, + VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_PROPERTIES_KHR = 1000269002, + VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INFO_KHR = 1000269003, + VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_STATISTIC_KHR = 1000269004, + VK_STRUCTURE_TYPE_PIPELINE_EXECUTABLE_INTERNAL_REPRESENTATION_KHR = 1000269005, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT = 1000276000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT = 1000281000, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT = 1000281001, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETER_FEATURES = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES, + VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, + VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, + VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, + VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2_KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_FORMAT_PROPERTIES_2, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SPARSE_IMAGE_FORMAT_INFO_2, + VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO, + VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_GROUP_RENDER_PASS_BEGIN_INFO, + VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_GROUP_COMMAND_BUFFER_BEGIN_INFO, + VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_GROUP_SUBMIT_INFO, + VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_GROUP_BIND_SPARSE_INFO, + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO_KHR = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_DEVICE_GROUP_INFO, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_DEVICE_GROUP_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_GROUP_PROPERTIES, + VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, + VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO, + VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_BUFFER_PROPERTIES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES, + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO, + VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, + VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, + VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES, + VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO, + VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES2_EXT = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_EXT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_FENCE_INFO, + VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES_KHR = VK_STRUCTURE_TYPE_EXTERNAL_FENCE_PROPERTIES, + VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES, + VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO, + VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, + VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTERS_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES, + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS, + VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2, + VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2, + VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2_KHR = VK_STRUCTURE_TYPE_IMAGE_SPARSE_MEMORY_REQUIREMENTS_INFO_2, + VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2, + VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2_KHR = VK_STRUCTURE_TYPE_SPARSE_IMAGE_MEMORY_REQUIREMENTS_2, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO, + VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_PLANE_MEMORY_INFO, + VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO_KHR = VK_STRUCTURE_TYPE_IMAGE_PLANE_MEMORY_REQUIREMENTS_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES, + VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES_KHR = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_IMAGE_FORMAT_PROPERTIES, + VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO, + VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR = VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES_KHR = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES, + VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT_KHR = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_SUPPORT, + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_ADDRESS_FEATURES_EXT = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT, + VK_STRUCTURE_TYPE_BEGIN_RANGE = VK_STRUCTURE_TYPE_APPLICATION_INFO, + VK_STRUCTURE_TYPE_END_RANGE = VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO, + VK_STRUCTURE_TYPE_RANGE_SIZE = (VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO - VK_STRUCTURE_TYPE_APPLICATION_INFO + 1), + VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkStructureType; + +typedef enum VkSystemAllocationScope { + VK_SYSTEM_ALLOCATION_SCOPE_COMMAND = 0, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT = 1, + VK_SYSTEM_ALLOCATION_SCOPE_CACHE = 2, + VK_SYSTEM_ALLOCATION_SCOPE_DEVICE = 3, + VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE = 4, + VK_SYSTEM_ALLOCATION_SCOPE_BEGIN_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_COMMAND, + VK_SYSTEM_ALLOCATION_SCOPE_END_RANGE = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE, + VK_SYSTEM_ALLOCATION_SCOPE_RANGE_SIZE = (VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE - VK_SYSTEM_ALLOCATION_SCOPE_COMMAND + 1), + VK_SYSTEM_ALLOCATION_SCOPE_MAX_ENUM = 0x7FFFFFFF +} VkSystemAllocationScope; + +typedef enum VkInternalAllocationType { + VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE = 0, + VK_INTERNAL_ALLOCATION_TYPE_BEGIN_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE, + VK_INTERNAL_ALLOCATION_TYPE_END_RANGE = VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE, + VK_INTERNAL_ALLOCATION_TYPE_RANGE_SIZE = (VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE - VK_INTERNAL_ALLOCATION_TYPE_EXECUTABLE + 1), + VK_INTERNAL_ALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkInternalAllocationType; + +typedef enum VkFormat { + VK_FORMAT_UNDEFINED = 0, + VK_FORMAT_R4G4_UNORM_PACK8 = 1, + VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2, + VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3, + VK_FORMAT_R5G6B5_UNORM_PACK16 = 4, + VK_FORMAT_B5G6R5_UNORM_PACK16 = 5, + VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6, + VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7, + VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8, + VK_FORMAT_R8_UNORM = 9, + VK_FORMAT_R8_SNORM = 10, + VK_FORMAT_R8_USCALED = 11, + VK_FORMAT_R8_SSCALED = 12, + VK_FORMAT_R8_UINT = 13, + VK_FORMAT_R8_SINT = 14, + VK_FORMAT_R8_SRGB = 15, + VK_FORMAT_R8G8_UNORM = 16, + VK_FORMAT_R8G8_SNORM = 17, + VK_FORMAT_R8G8_USCALED = 18, + VK_FORMAT_R8G8_SSCALED = 19, + VK_FORMAT_R8G8_UINT = 20, + VK_FORMAT_R8G8_SINT = 21, + VK_FORMAT_R8G8_SRGB = 22, + VK_FORMAT_R8G8B8_UNORM = 23, + VK_FORMAT_R8G8B8_SNORM = 24, + VK_FORMAT_R8G8B8_USCALED = 25, + VK_FORMAT_R8G8B8_SSCALED = 26, + VK_FORMAT_R8G8B8_UINT = 27, + VK_FORMAT_R8G8B8_SINT = 28, + VK_FORMAT_R8G8B8_SRGB = 29, + VK_FORMAT_B8G8R8_UNORM = 30, + VK_FORMAT_B8G8R8_SNORM = 31, + VK_FORMAT_B8G8R8_USCALED = 32, + VK_FORMAT_B8G8R8_SSCALED = 33, + VK_FORMAT_B8G8R8_UINT = 34, + VK_FORMAT_B8G8R8_SINT = 35, + VK_FORMAT_B8G8R8_SRGB = 36, + VK_FORMAT_R8G8B8A8_UNORM = 37, + VK_FORMAT_R8G8B8A8_SNORM = 38, + VK_FORMAT_R8G8B8A8_USCALED = 39, + VK_FORMAT_R8G8B8A8_SSCALED = 40, + VK_FORMAT_R8G8B8A8_UINT = 41, + VK_FORMAT_R8G8B8A8_SINT = 42, + VK_FORMAT_R8G8B8A8_SRGB = 43, + VK_FORMAT_B8G8R8A8_UNORM = 44, + VK_FORMAT_B8G8R8A8_SNORM = 45, + VK_FORMAT_B8G8R8A8_USCALED = 46, + VK_FORMAT_B8G8R8A8_SSCALED = 47, + VK_FORMAT_B8G8R8A8_UINT = 48, + VK_FORMAT_B8G8R8A8_SINT = 49, + VK_FORMAT_B8G8R8A8_SRGB = 50, + VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51, + VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52, + VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53, + VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54, + VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55, + VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56, + VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57, + VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58, + VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59, + VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60, + VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61, + VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62, + VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63, + VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64, + VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65, + VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66, + VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67, + VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68, + VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69, + VK_FORMAT_R16_UNORM = 70, + VK_FORMAT_R16_SNORM = 71, + VK_FORMAT_R16_USCALED = 72, + VK_FORMAT_R16_SSCALED = 73, + VK_FORMAT_R16_UINT = 74, + VK_FORMAT_R16_SINT = 75, + VK_FORMAT_R16_SFLOAT = 76, + VK_FORMAT_R16G16_UNORM = 77, + VK_FORMAT_R16G16_SNORM = 78, + VK_FORMAT_R16G16_USCALED = 79, + VK_FORMAT_R16G16_SSCALED = 80, + VK_FORMAT_R16G16_UINT = 81, + VK_FORMAT_R16G16_SINT = 82, + VK_FORMAT_R16G16_SFLOAT = 83, + VK_FORMAT_R16G16B16_UNORM = 84, + VK_FORMAT_R16G16B16_SNORM = 85, + VK_FORMAT_R16G16B16_USCALED = 86, + VK_FORMAT_R16G16B16_SSCALED = 87, + VK_FORMAT_R16G16B16_UINT = 88, + VK_FORMAT_R16G16B16_SINT = 89, + VK_FORMAT_R16G16B16_SFLOAT = 90, + VK_FORMAT_R16G16B16A16_UNORM = 91, + VK_FORMAT_R16G16B16A16_SNORM = 92, + VK_FORMAT_R16G16B16A16_USCALED = 93, + VK_FORMAT_R16G16B16A16_SSCALED = 94, + VK_FORMAT_R16G16B16A16_UINT = 95, + VK_FORMAT_R16G16B16A16_SINT = 96, + VK_FORMAT_R16G16B16A16_SFLOAT = 97, + VK_FORMAT_R32_UINT = 98, + VK_FORMAT_R32_SINT = 99, + VK_FORMAT_R32_SFLOAT = 100, + VK_FORMAT_R32G32_UINT = 101, + VK_FORMAT_R32G32_SINT = 102, + VK_FORMAT_R32G32_SFLOAT = 103, + VK_FORMAT_R32G32B32_UINT = 104, + VK_FORMAT_R32G32B32_SINT = 105, + VK_FORMAT_R32G32B32_SFLOAT = 106, + VK_FORMAT_R32G32B32A32_UINT = 107, + VK_FORMAT_R32G32B32A32_SINT = 108, + VK_FORMAT_R32G32B32A32_SFLOAT = 109, + VK_FORMAT_R64_UINT = 110, + VK_FORMAT_R64_SINT = 111, + VK_FORMAT_R64_SFLOAT = 112, + VK_FORMAT_R64G64_UINT = 113, + VK_FORMAT_R64G64_SINT = 114, + VK_FORMAT_R64G64_SFLOAT = 115, + VK_FORMAT_R64G64B64_UINT = 116, + VK_FORMAT_R64G64B64_SINT = 117, + VK_FORMAT_R64G64B64_SFLOAT = 118, + VK_FORMAT_R64G64B64A64_UINT = 119, + VK_FORMAT_R64G64B64A64_SINT = 120, + VK_FORMAT_R64G64B64A64_SFLOAT = 121, + VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122, + VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123, + VK_FORMAT_D16_UNORM = 124, + VK_FORMAT_X8_D24_UNORM_PACK32 = 125, + VK_FORMAT_D32_SFLOAT = 126, + VK_FORMAT_S8_UINT = 127, + VK_FORMAT_D16_UNORM_S8_UINT = 128, + VK_FORMAT_D24_UNORM_S8_UINT = 129, + VK_FORMAT_D32_SFLOAT_S8_UINT = 130, + VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131, + VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132, + VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133, + VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134, + VK_FORMAT_BC2_UNORM_BLOCK = 135, + VK_FORMAT_BC2_SRGB_BLOCK = 136, + VK_FORMAT_BC3_UNORM_BLOCK = 137, + VK_FORMAT_BC3_SRGB_BLOCK = 138, + VK_FORMAT_BC4_UNORM_BLOCK = 139, + VK_FORMAT_BC4_SNORM_BLOCK = 140, + VK_FORMAT_BC5_UNORM_BLOCK = 141, + VK_FORMAT_BC5_SNORM_BLOCK = 142, + VK_FORMAT_BC6H_UFLOAT_BLOCK = 143, + VK_FORMAT_BC6H_SFLOAT_BLOCK = 144, + VK_FORMAT_BC7_UNORM_BLOCK = 145, + VK_FORMAT_BC7_SRGB_BLOCK = 146, + VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147, + VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148, + VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149, + VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150, + VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151, + VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152, + VK_FORMAT_EAC_R11_UNORM_BLOCK = 153, + VK_FORMAT_EAC_R11_SNORM_BLOCK = 154, + VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155, + VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156, + VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157, + VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158, + VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159, + VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160, + VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161, + VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162, + VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163, + VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164, + VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165, + VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166, + VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167, + VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168, + VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169, + VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170, + VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171, + VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172, + VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173, + VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174, + VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175, + VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176, + VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177, + VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178, + VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179, + VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180, + VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181, + VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182, + VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183, + VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184, + VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000, + VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006, + VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016, + VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026, + VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027, + VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033, + VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000, + VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001, + VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002, + VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003, + VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004, + VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005, + VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006, + VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007, + VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = 1000066000, + VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = 1000066001, + VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = 1000066002, + VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = 1000066003, + VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = 1000066004, + VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = 1000066005, + VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = 1000066006, + VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = 1000066007, + VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = 1000066008, + VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = 1000066009, + VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = 1000066010, + VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = 1000066011, + VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = 1000066012, + VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = 1000066013, + VK_FORMAT_G8B8G8R8_422_UNORM_KHR = VK_FORMAT_G8B8G8R8_422_UNORM, + VK_FORMAT_B8G8R8G8_422_UNORM_KHR = VK_FORMAT_B8G8R8G8_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_420_UNORM_KHR = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM, + VK_FORMAT_G8_B8R8_2PLANE_422_UNORM_KHR = VK_FORMAT_G8_B8R8_2PLANE_422_UNORM, + VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM_KHR = VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM, + VK_FORMAT_R10X6_UNORM_PACK16_KHR = VK_FORMAT_R10X6_UNORM_PACK16, + VK_FORMAT_R10X6G10X6_UNORM_2PACK16_KHR = VK_FORMAT_R10X6G10X6_UNORM_2PACK16, + VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16_KHR = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16, + VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16_KHR = VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16, + VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16_KHR = VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16, + VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16_KHR = VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16, + VK_FORMAT_R12X4_UNORM_PACK16_KHR = VK_FORMAT_R12X4_UNORM_PACK16, + VK_FORMAT_R12X4G12X4_UNORM_2PACK16_KHR = VK_FORMAT_R12X4G12X4_UNORM_2PACK16, + VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16_KHR = VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16, + VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16_KHR = VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16, + VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16_KHR = VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16, + VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16_KHR = VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16, + VK_FORMAT_G16B16G16R16_422_UNORM_KHR = VK_FORMAT_G16B16G16R16_422_UNORM, + VK_FORMAT_B16G16R16G16_422_UNORM_KHR = VK_FORMAT_B16G16R16G16_422_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM, + VK_FORMAT_G16_B16R16_2PLANE_420_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_420_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM, + VK_FORMAT_G16_B16R16_2PLANE_422_UNORM_KHR = VK_FORMAT_G16_B16R16_2PLANE_422_UNORM, + VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM_KHR = VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM, + VK_FORMAT_BEGIN_RANGE = VK_FORMAT_UNDEFINED, + VK_FORMAT_END_RANGE = VK_FORMAT_ASTC_12x12_SRGB_BLOCK, + VK_FORMAT_RANGE_SIZE = (VK_FORMAT_ASTC_12x12_SRGB_BLOCK - VK_FORMAT_UNDEFINED + 1), + VK_FORMAT_MAX_ENUM = 0x7FFFFFFF +} VkFormat; + +typedef enum VkImageType { + VK_IMAGE_TYPE_1D = 0, + VK_IMAGE_TYPE_2D = 1, + VK_IMAGE_TYPE_3D = 2, + VK_IMAGE_TYPE_BEGIN_RANGE = VK_IMAGE_TYPE_1D, + VK_IMAGE_TYPE_END_RANGE = VK_IMAGE_TYPE_3D, + VK_IMAGE_TYPE_RANGE_SIZE = (VK_IMAGE_TYPE_3D - VK_IMAGE_TYPE_1D + 1), + VK_IMAGE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkImageType; + +typedef enum VkImageTiling { + VK_IMAGE_TILING_OPTIMAL = 0, + VK_IMAGE_TILING_LINEAR = 1, + VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT = 1000158000, + VK_IMAGE_TILING_BEGIN_RANGE = VK_IMAGE_TILING_OPTIMAL, + VK_IMAGE_TILING_END_RANGE = VK_IMAGE_TILING_LINEAR, + VK_IMAGE_TILING_RANGE_SIZE = (VK_IMAGE_TILING_LINEAR - VK_IMAGE_TILING_OPTIMAL + 1), + VK_IMAGE_TILING_MAX_ENUM = 0x7FFFFFFF +} VkImageTiling; + +typedef enum VkPhysicalDeviceType { + VK_PHYSICAL_DEVICE_TYPE_OTHER = 0, + VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1, + VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2, + VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU = 3, + VK_PHYSICAL_DEVICE_TYPE_CPU = 4, + VK_PHYSICAL_DEVICE_TYPE_BEGIN_RANGE = VK_PHYSICAL_DEVICE_TYPE_OTHER, + VK_PHYSICAL_DEVICE_TYPE_END_RANGE = VK_PHYSICAL_DEVICE_TYPE_CPU, + VK_PHYSICAL_DEVICE_TYPE_RANGE_SIZE = (VK_PHYSICAL_DEVICE_TYPE_CPU - VK_PHYSICAL_DEVICE_TYPE_OTHER + 1), + VK_PHYSICAL_DEVICE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkPhysicalDeviceType; + +typedef enum VkQueryType { + VK_QUERY_TYPE_OCCLUSION = 0, + VK_QUERY_TYPE_PIPELINE_STATISTICS = 1, + VK_QUERY_TYPE_TIMESTAMP = 2, + VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT = 1000028004, + VK_QUERY_TYPE_ACCELERATION_STRUCTURE_COMPACTED_SIZE_NV = 1000165000, + VK_QUERY_TYPE_PERFORMANCE_QUERY_INTEL = 1000210000, + VK_QUERY_TYPE_BEGIN_RANGE = VK_QUERY_TYPE_OCCLUSION, + VK_QUERY_TYPE_END_RANGE = VK_QUERY_TYPE_TIMESTAMP, + VK_QUERY_TYPE_RANGE_SIZE = (VK_QUERY_TYPE_TIMESTAMP - VK_QUERY_TYPE_OCCLUSION + 1), + VK_QUERY_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkQueryType; + +typedef enum VkSharingMode { + VK_SHARING_MODE_EXCLUSIVE = 0, + VK_SHARING_MODE_CONCURRENT = 1, + VK_SHARING_MODE_BEGIN_RANGE = VK_SHARING_MODE_EXCLUSIVE, + VK_SHARING_MODE_END_RANGE = VK_SHARING_MODE_CONCURRENT, + VK_SHARING_MODE_RANGE_SIZE = (VK_SHARING_MODE_CONCURRENT - VK_SHARING_MODE_EXCLUSIVE + 1), + VK_SHARING_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSharingMode; + +typedef enum VkImageLayout { + VK_IMAGE_LAYOUT_UNDEFINED = 0, + VK_IMAGE_LAYOUT_GENERAL = 1, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL = 2, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL = 3, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL = 4, + VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL = 5, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL = 6, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL = 7, + VK_IMAGE_LAYOUT_PREINITIALIZED = 8, + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL = 1000117000, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL = 1000117001, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR = 1000001002, + VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR = 1000111000, + VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV = 1000164003, + VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT = 1000218000, + VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL_KHR = VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL, + VK_IMAGE_LAYOUT_BEGIN_RANGE = VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_END_RANGE = VK_IMAGE_LAYOUT_PREINITIALIZED, + VK_IMAGE_LAYOUT_RANGE_SIZE = (VK_IMAGE_LAYOUT_PREINITIALIZED - VK_IMAGE_LAYOUT_UNDEFINED + 1), + VK_IMAGE_LAYOUT_MAX_ENUM = 0x7FFFFFFF +} VkImageLayout; + +typedef enum VkImageViewType { + VK_IMAGE_VIEW_TYPE_1D = 0, + VK_IMAGE_VIEW_TYPE_2D = 1, + VK_IMAGE_VIEW_TYPE_3D = 2, + VK_IMAGE_VIEW_TYPE_CUBE = 3, + VK_IMAGE_VIEW_TYPE_1D_ARRAY = 4, + VK_IMAGE_VIEW_TYPE_2D_ARRAY = 5, + VK_IMAGE_VIEW_TYPE_CUBE_ARRAY = 6, + VK_IMAGE_VIEW_TYPE_BEGIN_RANGE = VK_IMAGE_VIEW_TYPE_1D, + VK_IMAGE_VIEW_TYPE_END_RANGE = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, + VK_IMAGE_VIEW_TYPE_RANGE_SIZE = (VK_IMAGE_VIEW_TYPE_CUBE_ARRAY - VK_IMAGE_VIEW_TYPE_1D + 1), + VK_IMAGE_VIEW_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkImageViewType; + +typedef enum VkComponentSwizzle { + VK_COMPONENT_SWIZZLE_IDENTITY = 0, + VK_COMPONENT_SWIZZLE_ZERO = 1, + VK_COMPONENT_SWIZZLE_ONE = 2, + VK_COMPONENT_SWIZZLE_R = 3, + VK_COMPONENT_SWIZZLE_G = 4, + VK_COMPONENT_SWIZZLE_B = 5, + VK_COMPONENT_SWIZZLE_A = 6, + VK_COMPONENT_SWIZZLE_BEGIN_RANGE = VK_COMPONENT_SWIZZLE_IDENTITY, + VK_COMPONENT_SWIZZLE_END_RANGE = VK_COMPONENT_SWIZZLE_A, + VK_COMPONENT_SWIZZLE_RANGE_SIZE = (VK_COMPONENT_SWIZZLE_A - VK_COMPONENT_SWIZZLE_IDENTITY + 1), + VK_COMPONENT_SWIZZLE_MAX_ENUM = 0x7FFFFFFF +} VkComponentSwizzle; + +typedef enum VkVertexInputRate { + VK_VERTEX_INPUT_RATE_VERTEX = 0, + VK_VERTEX_INPUT_RATE_INSTANCE = 1, + VK_VERTEX_INPUT_RATE_BEGIN_RANGE = VK_VERTEX_INPUT_RATE_VERTEX, + VK_VERTEX_INPUT_RATE_END_RANGE = VK_VERTEX_INPUT_RATE_INSTANCE, + VK_VERTEX_INPUT_RATE_RANGE_SIZE = (VK_VERTEX_INPUT_RATE_INSTANCE - VK_VERTEX_INPUT_RATE_VERTEX + 1), + VK_VERTEX_INPUT_RATE_MAX_ENUM = 0x7FFFFFFF +} VkVertexInputRate; + +typedef enum VkPrimitiveTopology { + VK_PRIMITIVE_TOPOLOGY_POINT_LIST = 0, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST = 1, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP = 2, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST = 3, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP = 4, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN = 5, + VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY = 6, + VK_PRIMITIVE_TOPOLOGY_LINE_STRIP_WITH_ADJACENCY = 7, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY = 8, + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP_WITH_ADJACENCY = 9, + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST = 10, + VK_PRIMITIVE_TOPOLOGY_BEGIN_RANGE = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, + VK_PRIMITIVE_TOPOLOGY_END_RANGE = VK_PRIMITIVE_TOPOLOGY_PATCH_LIST, + VK_PRIMITIVE_TOPOLOGY_RANGE_SIZE = (VK_PRIMITIVE_TOPOLOGY_PATCH_LIST - VK_PRIMITIVE_TOPOLOGY_POINT_LIST + 1), + VK_PRIMITIVE_TOPOLOGY_MAX_ENUM = 0x7FFFFFFF +} VkPrimitiveTopology; + +typedef enum VkPolygonMode { + VK_POLYGON_MODE_FILL = 0, + VK_POLYGON_MODE_LINE = 1, + VK_POLYGON_MODE_POINT = 2, + VK_POLYGON_MODE_FILL_RECTANGLE_NV = 1000153000, + VK_POLYGON_MODE_BEGIN_RANGE = VK_POLYGON_MODE_FILL, + VK_POLYGON_MODE_END_RANGE = VK_POLYGON_MODE_POINT, + VK_POLYGON_MODE_RANGE_SIZE = (VK_POLYGON_MODE_POINT - VK_POLYGON_MODE_FILL + 1), + VK_POLYGON_MODE_MAX_ENUM = 0x7FFFFFFF +} VkPolygonMode; + +typedef enum VkFrontFace { + VK_FRONT_FACE_COUNTER_CLOCKWISE = 0, + VK_FRONT_FACE_CLOCKWISE = 1, + VK_FRONT_FACE_BEGIN_RANGE = VK_FRONT_FACE_COUNTER_CLOCKWISE, + VK_FRONT_FACE_END_RANGE = VK_FRONT_FACE_CLOCKWISE, + VK_FRONT_FACE_RANGE_SIZE = (VK_FRONT_FACE_CLOCKWISE - VK_FRONT_FACE_COUNTER_CLOCKWISE + 1), + VK_FRONT_FACE_MAX_ENUM = 0x7FFFFFFF +} VkFrontFace; + +typedef enum VkCompareOp { + VK_COMPARE_OP_NEVER = 0, + VK_COMPARE_OP_LESS = 1, + VK_COMPARE_OP_EQUAL = 2, + VK_COMPARE_OP_LESS_OR_EQUAL = 3, + VK_COMPARE_OP_GREATER = 4, + VK_COMPARE_OP_NOT_EQUAL = 5, + VK_COMPARE_OP_GREATER_OR_EQUAL = 6, + VK_COMPARE_OP_ALWAYS = 7, + VK_COMPARE_OP_BEGIN_RANGE = VK_COMPARE_OP_NEVER, + VK_COMPARE_OP_END_RANGE = VK_COMPARE_OP_ALWAYS, + VK_COMPARE_OP_RANGE_SIZE = (VK_COMPARE_OP_ALWAYS - VK_COMPARE_OP_NEVER + 1), + VK_COMPARE_OP_MAX_ENUM = 0x7FFFFFFF +} VkCompareOp; + +typedef enum VkStencilOp { + VK_STENCIL_OP_KEEP = 0, + VK_STENCIL_OP_ZERO = 1, + VK_STENCIL_OP_REPLACE = 2, + VK_STENCIL_OP_INCREMENT_AND_CLAMP = 3, + VK_STENCIL_OP_DECREMENT_AND_CLAMP = 4, + VK_STENCIL_OP_INVERT = 5, + VK_STENCIL_OP_INCREMENT_AND_WRAP = 6, + VK_STENCIL_OP_DECREMENT_AND_WRAP = 7, + VK_STENCIL_OP_BEGIN_RANGE = VK_STENCIL_OP_KEEP, + VK_STENCIL_OP_END_RANGE = VK_STENCIL_OP_DECREMENT_AND_WRAP, + VK_STENCIL_OP_RANGE_SIZE = (VK_STENCIL_OP_DECREMENT_AND_WRAP - VK_STENCIL_OP_KEEP + 1), + VK_STENCIL_OP_MAX_ENUM = 0x7FFFFFFF +} VkStencilOp; + +typedef enum VkLogicOp { + VK_LOGIC_OP_CLEAR = 0, + VK_LOGIC_OP_AND = 1, + VK_LOGIC_OP_AND_REVERSE = 2, + VK_LOGIC_OP_COPY = 3, + VK_LOGIC_OP_AND_INVERTED = 4, + VK_LOGIC_OP_NO_OP = 5, + VK_LOGIC_OP_XOR = 6, + VK_LOGIC_OP_OR = 7, + VK_LOGIC_OP_NOR = 8, + VK_LOGIC_OP_EQUIVALENT = 9, + VK_LOGIC_OP_INVERT = 10, + VK_LOGIC_OP_OR_REVERSE = 11, + VK_LOGIC_OP_COPY_INVERTED = 12, + VK_LOGIC_OP_OR_INVERTED = 13, + VK_LOGIC_OP_NAND = 14, + VK_LOGIC_OP_SET = 15, + VK_LOGIC_OP_BEGIN_RANGE = VK_LOGIC_OP_CLEAR, + VK_LOGIC_OP_END_RANGE = VK_LOGIC_OP_SET, + VK_LOGIC_OP_RANGE_SIZE = (VK_LOGIC_OP_SET - VK_LOGIC_OP_CLEAR + 1), + VK_LOGIC_OP_MAX_ENUM = 0x7FFFFFFF +} VkLogicOp; + +typedef enum VkBlendFactor { + VK_BLEND_FACTOR_ZERO = 0, + VK_BLEND_FACTOR_ONE = 1, + VK_BLEND_FACTOR_SRC_COLOR = 2, + VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR = 3, + VK_BLEND_FACTOR_DST_COLOR = 4, + VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR = 5, + VK_BLEND_FACTOR_SRC_ALPHA = 6, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = 7, + VK_BLEND_FACTOR_DST_ALPHA = 8, + VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA = 9, + VK_BLEND_FACTOR_CONSTANT_COLOR = 10, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR = 11, + VK_BLEND_FACTOR_CONSTANT_ALPHA = 12, + VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = 13, + VK_BLEND_FACTOR_SRC_ALPHA_SATURATE = 14, + VK_BLEND_FACTOR_SRC1_COLOR = 15, + VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR = 16, + VK_BLEND_FACTOR_SRC1_ALPHA = 17, + VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA = 18, + VK_BLEND_FACTOR_BEGIN_RANGE = VK_BLEND_FACTOR_ZERO, + VK_BLEND_FACTOR_END_RANGE = VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, + VK_BLEND_FACTOR_RANGE_SIZE = (VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA - VK_BLEND_FACTOR_ZERO + 1), + VK_BLEND_FACTOR_MAX_ENUM = 0x7FFFFFFF +} VkBlendFactor; + +typedef enum VkBlendOp { + VK_BLEND_OP_ADD = 0, + VK_BLEND_OP_SUBTRACT = 1, + VK_BLEND_OP_REVERSE_SUBTRACT = 2, + VK_BLEND_OP_MIN = 3, + VK_BLEND_OP_MAX = 4, + VK_BLEND_OP_ZERO_EXT = 1000148000, + VK_BLEND_OP_SRC_EXT = 1000148001, + VK_BLEND_OP_DST_EXT = 1000148002, + VK_BLEND_OP_SRC_OVER_EXT = 1000148003, + VK_BLEND_OP_DST_OVER_EXT = 1000148004, + VK_BLEND_OP_SRC_IN_EXT = 1000148005, + VK_BLEND_OP_DST_IN_EXT = 1000148006, + VK_BLEND_OP_SRC_OUT_EXT = 1000148007, + VK_BLEND_OP_DST_OUT_EXT = 1000148008, + VK_BLEND_OP_SRC_ATOP_EXT = 1000148009, + VK_BLEND_OP_DST_ATOP_EXT = 1000148010, + VK_BLEND_OP_XOR_EXT = 1000148011, + VK_BLEND_OP_MULTIPLY_EXT = 1000148012, + VK_BLEND_OP_SCREEN_EXT = 1000148013, + VK_BLEND_OP_OVERLAY_EXT = 1000148014, + VK_BLEND_OP_DARKEN_EXT = 1000148015, + VK_BLEND_OP_LIGHTEN_EXT = 1000148016, + VK_BLEND_OP_COLORDODGE_EXT = 1000148017, + VK_BLEND_OP_COLORBURN_EXT = 1000148018, + VK_BLEND_OP_HARDLIGHT_EXT = 1000148019, + VK_BLEND_OP_SOFTLIGHT_EXT = 1000148020, + VK_BLEND_OP_DIFFERENCE_EXT = 1000148021, + VK_BLEND_OP_EXCLUSION_EXT = 1000148022, + VK_BLEND_OP_INVERT_EXT = 1000148023, + VK_BLEND_OP_INVERT_RGB_EXT = 1000148024, + VK_BLEND_OP_LINEARDODGE_EXT = 1000148025, + VK_BLEND_OP_LINEARBURN_EXT = 1000148026, + VK_BLEND_OP_VIVIDLIGHT_EXT = 1000148027, + VK_BLEND_OP_LINEARLIGHT_EXT = 1000148028, + VK_BLEND_OP_PINLIGHT_EXT = 1000148029, + VK_BLEND_OP_HARDMIX_EXT = 1000148030, + VK_BLEND_OP_HSL_HUE_EXT = 1000148031, + VK_BLEND_OP_HSL_SATURATION_EXT = 1000148032, + VK_BLEND_OP_HSL_COLOR_EXT = 1000148033, + VK_BLEND_OP_HSL_LUMINOSITY_EXT = 1000148034, + VK_BLEND_OP_PLUS_EXT = 1000148035, + VK_BLEND_OP_PLUS_CLAMPED_EXT = 1000148036, + VK_BLEND_OP_PLUS_CLAMPED_ALPHA_EXT = 1000148037, + VK_BLEND_OP_PLUS_DARKER_EXT = 1000148038, + VK_BLEND_OP_MINUS_EXT = 1000148039, + VK_BLEND_OP_MINUS_CLAMPED_EXT = 1000148040, + VK_BLEND_OP_CONTRAST_EXT = 1000148041, + VK_BLEND_OP_INVERT_OVG_EXT = 1000148042, + VK_BLEND_OP_RED_EXT = 1000148043, + VK_BLEND_OP_GREEN_EXT = 1000148044, + VK_BLEND_OP_BLUE_EXT = 1000148045, + VK_BLEND_OP_BEGIN_RANGE = VK_BLEND_OP_ADD, + VK_BLEND_OP_END_RANGE = VK_BLEND_OP_MAX, + VK_BLEND_OP_RANGE_SIZE = (VK_BLEND_OP_MAX - VK_BLEND_OP_ADD + 1), + VK_BLEND_OP_MAX_ENUM = 0x7FFFFFFF +} VkBlendOp; + +typedef enum VkDynamicState { + VK_DYNAMIC_STATE_VIEWPORT = 0, + VK_DYNAMIC_STATE_SCISSOR = 1, + VK_DYNAMIC_STATE_LINE_WIDTH = 2, + VK_DYNAMIC_STATE_DEPTH_BIAS = 3, + VK_DYNAMIC_STATE_BLEND_CONSTANTS = 4, + VK_DYNAMIC_STATE_DEPTH_BOUNDS = 5, + VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK = 6, + VK_DYNAMIC_STATE_STENCIL_WRITE_MASK = 7, + VK_DYNAMIC_STATE_STENCIL_REFERENCE = 8, + VK_DYNAMIC_STATE_VIEWPORT_W_SCALING_NV = 1000087000, + VK_DYNAMIC_STATE_DISCARD_RECTANGLE_EXT = 1000099000, + VK_DYNAMIC_STATE_SAMPLE_LOCATIONS_EXT = 1000143000, + VK_DYNAMIC_STATE_VIEWPORT_SHADING_RATE_PALETTE_NV = 1000164004, + VK_DYNAMIC_STATE_VIEWPORT_COARSE_SAMPLE_ORDER_NV = 1000164006, + VK_DYNAMIC_STATE_EXCLUSIVE_SCISSOR_NV = 1000205001, + VK_DYNAMIC_STATE_LINE_STIPPLE_EXT = 1000259000, + VK_DYNAMIC_STATE_BEGIN_RANGE = VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_END_RANGE = VK_DYNAMIC_STATE_STENCIL_REFERENCE, + VK_DYNAMIC_STATE_RANGE_SIZE = (VK_DYNAMIC_STATE_STENCIL_REFERENCE - VK_DYNAMIC_STATE_VIEWPORT + 1), + VK_DYNAMIC_STATE_MAX_ENUM = 0x7FFFFFFF +} VkDynamicState; + +typedef enum VkFilter { + VK_FILTER_NEAREST = 0, + VK_FILTER_LINEAR = 1, + VK_FILTER_CUBIC_IMG = 1000015000, + VK_FILTER_CUBIC_EXT = VK_FILTER_CUBIC_IMG, + VK_FILTER_BEGIN_RANGE = VK_FILTER_NEAREST, + VK_FILTER_END_RANGE = VK_FILTER_LINEAR, + VK_FILTER_RANGE_SIZE = (VK_FILTER_LINEAR - VK_FILTER_NEAREST + 1), + VK_FILTER_MAX_ENUM = 0x7FFFFFFF +} VkFilter; + +typedef enum VkSamplerMipmapMode { + VK_SAMPLER_MIPMAP_MODE_NEAREST = 0, + VK_SAMPLER_MIPMAP_MODE_LINEAR = 1, + VK_SAMPLER_MIPMAP_MODE_BEGIN_RANGE = VK_SAMPLER_MIPMAP_MODE_NEAREST, + VK_SAMPLER_MIPMAP_MODE_END_RANGE = VK_SAMPLER_MIPMAP_MODE_LINEAR, + VK_SAMPLER_MIPMAP_MODE_RANGE_SIZE = (VK_SAMPLER_MIPMAP_MODE_LINEAR - VK_SAMPLER_MIPMAP_MODE_NEAREST + 1), + VK_SAMPLER_MIPMAP_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerMipmapMode; + +typedef enum VkSamplerAddressMode { + VK_SAMPLER_ADDRESS_MODE_REPEAT = 0, + VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT = 1, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE = 2, + VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER = 3, + VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE = 4, + VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE_KHR = VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE, + VK_SAMPLER_ADDRESS_MODE_BEGIN_RANGE = VK_SAMPLER_ADDRESS_MODE_REPEAT, + VK_SAMPLER_ADDRESS_MODE_END_RANGE = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + VK_SAMPLER_ADDRESS_MODE_RANGE_SIZE = (VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER - VK_SAMPLER_ADDRESS_MODE_REPEAT + 1), + VK_SAMPLER_ADDRESS_MODE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerAddressMode; + +typedef enum VkBorderColor { + VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK = 0, + VK_BORDER_COLOR_INT_TRANSPARENT_BLACK = 1, + VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK = 2, + VK_BORDER_COLOR_INT_OPAQUE_BLACK = 3, + VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE = 4, + VK_BORDER_COLOR_INT_OPAQUE_WHITE = 5, + VK_BORDER_COLOR_BEGIN_RANGE = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK, + VK_BORDER_COLOR_END_RANGE = VK_BORDER_COLOR_INT_OPAQUE_WHITE, + VK_BORDER_COLOR_RANGE_SIZE = (VK_BORDER_COLOR_INT_OPAQUE_WHITE - VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK + 1), + VK_BORDER_COLOR_MAX_ENUM = 0x7FFFFFFF +} VkBorderColor; + +typedef enum VkDescriptorType { + VK_DESCRIPTOR_TYPE_SAMPLER = 0, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER = 1, + VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE = 2, + VK_DESCRIPTOR_TYPE_STORAGE_IMAGE = 3, + VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER = 4, + VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER = 5, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER = 6, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER = 7, + VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC = 8, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC = 9, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT = 10, + VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT = 1000138000, + VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, + VK_DESCRIPTOR_TYPE_BEGIN_RANGE = VK_DESCRIPTOR_TYPE_SAMPLER, + VK_DESCRIPTOR_TYPE_END_RANGE = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, + VK_DESCRIPTOR_TYPE_RANGE_SIZE = (VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT - VK_DESCRIPTOR_TYPE_SAMPLER + 1), + VK_DESCRIPTOR_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorType; + +typedef enum VkAttachmentLoadOp { + VK_ATTACHMENT_LOAD_OP_LOAD = 0, + VK_ATTACHMENT_LOAD_OP_CLEAR = 1, + VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2, + VK_ATTACHMENT_LOAD_OP_BEGIN_RANGE = VK_ATTACHMENT_LOAD_OP_LOAD, + VK_ATTACHMENT_LOAD_OP_END_RANGE = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + VK_ATTACHMENT_LOAD_OP_RANGE_SIZE = (VK_ATTACHMENT_LOAD_OP_DONT_CARE - VK_ATTACHMENT_LOAD_OP_LOAD + 1), + VK_ATTACHMENT_LOAD_OP_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentLoadOp; + +typedef enum VkAttachmentStoreOp { + VK_ATTACHMENT_STORE_OP_STORE = 0, + VK_ATTACHMENT_STORE_OP_DONT_CARE = 1, + VK_ATTACHMENT_STORE_OP_BEGIN_RANGE = VK_ATTACHMENT_STORE_OP_STORE, + VK_ATTACHMENT_STORE_OP_END_RANGE = VK_ATTACHMENT_STORE_OP_DONT_CARE, + VK_ATTACHMENT_STORE_OP_RANGE_SIZE = (VK_ATTACHMENT_STORE_OP_DONT_CARE - VK_ATTACHMENT_STORE_OP_STORE + 1), + VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentStoreOp; + +typedef enum VkPipelineBindPoint { + VK_PIPELINE_BIND_POINT_GRAPHICS = 0, + VK_PIPELINE_BIND_POINT_COMPUTE = 1, + VK_PIPELINE_BIND_POINT_RAY_TRACING_NV = 1000165000, + VK_PIPELINE_BIND_POINT_BEGIN_RANGE = VK_PIPELINE_BIND_POINT_GRAPHICS, + VK_PIPELINE_BIND_POINT_END_RANGE = VK_PIPELINE_BIND_POINT_COMPUTE, + VK_PIPELINE_BIND_POINT_RANGE_SIZE = (VK_PIPELINE_BIND_POINT_COMPUTE - VK_PIPELINE_BIND_POINT_GRAPHICS + 1), + VK_PIPELINE_BIND_POINT_MAX_ENUM = 0x7FFFFFFF +} VkPipelineBindPoint; + +typedef enum VkCommandBufferLevel { + VK_COMMAND_BUFFER_LEVEL_PRIMARY = 0, + VK_COMMAND_BUFFER_LEVEL_SECONDARY = 1, + VK_COMMAND_BUFFER_LEVEL_BEGIN_RANGE = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + VK_COMMAND_BUFFER_LEVEL_END_RANGE = VK_COMMAND_BUFFER_LEVEL_SECONDARY, + VK_COMMAND_BUFFER_LEVEL_RANGE_SIZE = (VK_COMMAND_BUFFER_LEVEL_SECONDARY - VK_COMMAND_BUFFER_LEVEL_PRIMARY + 1), + VK_COMMAND_BUFFER_LEVEL_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferLevel; + +typedef enum VkIndexType { + VK_INDEX_TYPE_UINT16 = 0, + VK_INDEX_TYPE_UINT32 = 1, + VK_INDEX_TYPE_NONE_NV = 1000165000, + VK_INDEX_TYPE_UINT8_EXT = 1000265000, + VK_INDEX_TYPE_BEGIN_RANGE = VK_INDEX_TYPE_UINT16, + VK_INDEX_TYPE_END_RANGE = VK_INDEX_TYPE_UINT32, + VK_INDEX_TYPE_RANGE_SIZE = (VK_INDEX_TYPE_UINT32 - VK_INDEX_TYPE_UINT16 + 1), + VK_INDEX_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkIndexType; + +typedef enum VkSubpassContents { + VK_SUBPASS_CONTENTS_INLINE = 0, + VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS = 1, + VK_SUBPASS_CONTENTS_BEGIN_RANGE = VK_SUBPASS_CONTENTS_INLINE, + VK_SUBPASS_CONTENTS_END_RANGE = VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, + VK_SUBPASS_CONTENTS_RANGE_SIZE = (VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS - VK_SUBPASS_CONTENTS_INLINE + 1), + VK_SUBPASS_CONTENTS_MAX_ENUM = 0x7FFFFFFF +} VkSubpassContents; + +typedef enum VkObjectType { + VK_OBJECT_TYPE_UNKNOWN = 0, + VK_OBJECT_TYPE_INSTANCE = 1, + VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2, + VK_OBJECT_TYPE_DEVICE = 3, + VK_OBJECT_TYPE_QUEUE = 4, + VK_OBJECT_TYPE_SEMAPHORE = 5, + VK_OBJECT_TYPE_COMMAND_BUFFER = 6, + VK_OBJECT_TYPE_FENCE = 7, + VK_OBJECT_TYPE_DEVICE_MEMORY = 8, + VK_OBJECT_TYPE_BUFFER = 9, + VK_OBJECT_TYPE_IMAGE = 10, + VK_OBJECT_TYPE_EVENT = 11, + VK_OBJECT_TYPE_QUERY_POOL = 12, + VK_OBJECT_TYPE_BUFFER_VIEW = 13, + VK_OBJECT_TYPE_IMAGE_VIEW = 14, + VK_OBJECT_TYPE_SHADER_MODULE = 15, + VK_OBJECT_TYPE_PIPELINE_CACHE = 16, + VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17, + VK_OBJECT_TYPE_RENDER_PASS = 18, + VK_OBJECT_TYPE_PIPELINE = 19, + VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20, + VK_OBJECT_TYPE_SAMPLER = 21, + VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22, + VK_OBJECT_TYPE_DESCRIPTOR_SET = 23, + VK_OBJECT_TYPE_FRAMEBUFFER = 24, + VK_OBJECT_TYPE_COMMAND_POOL = 25, + VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000, + VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000, + VK_OBJECT_TYPE_SURFACE_KHR = 1000000000, + VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000, + VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000, + VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001, + VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000, + VK_OBJECT_TYPE_OBJECT_TABLE_NVX = 1000086000, + VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX = 1000086001, + VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000, + VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000, + VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000, + VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL = 1000210000, + VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR = VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE, + VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR = VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION, + VK_OBJECT_TYPE_BEGIN_RANGE = VK_OBJECT_TYPE_UNKNOWN, + VK_OBJECT_TYPE_END_RANGE = VK_OBJECT_TYPE_COMMAND_POOL, + VK_OBJECT_TYPE_RANGE_SIZE = (VK_OBJECT_TYPE_COMMAND_POOL - VK_OBJECT_TYPE_UNKNOWN + 1), + VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkObjectType; + +typedef enum VkVendorId { + VK_VENDOR_ID_VIV = 0x10001, + VK_VENDOR_ID_VSI = 0x10002, + VK_VENDOR_ID_KAZAN = 0x10003, + VK_VENDOR_ID_BEGIN_RANGE = VK_VENDOR_ID_VIV, + VK_VENDOR_ID_END_RANGE = VK_VENDOR_ID_KAZAN, + VK_VENDOR_ID_RANGE_SIZE = (VK_VENDOR_ID_KAZAN - VK_VENDOR_ID_VIV + 1), + VK_VENDOR_ID_MAX_ENUM = 0x7FFFFFFF +} VkVendorId; +typedef VkFlags VkInstanceCreateFlags; + +typedef enum VkFormatFeatureFlagBits { + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT = 0x00000001, + VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT = 0x00000002, + VK_FORMAT_FEATURE_STORAGE_IMAGE_ATOMIC_BIT = 0x00000004, + VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000008, + VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT = 0x00000010, + VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_ATOMIC_BIT = 0x00000020, + VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT = 0x00000040, + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT = 0x00000080, + VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT = 0x00000100, + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000200, + VK_FORMAT_FEATURE_BLIT_SRC_BIT = 0x00000400, + VK_FORMAT_FEATURE_BLIT_DST_BIT = 0x00000800, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT = 0x00001000, + VK_FORMAT_FEATURE_TRANSFER_SRC_BIT = 0x00004000, + VK_FORMAT_FEATURE_TRANSFER_DST_BIT = 0x00008000, + VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT = 0x00020000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT = 0x00040000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT = 0x00080000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT = 0x00100000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT = 0x00200000, + VK_FORMAT_FEATURE_DISJOINT_BIT = 0x00400000, + VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT = 0x00800000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG = 0x00002000, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_MINMAX_BIT_EXT = 0x00010000, + VK_FORMAT_FEATURE_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x01000000, + VK_FORMAT_FEATURE_TRANSFER_SRC_BIT_KHR = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT, + VK_FORMAT_FEATURE_TRANSFER_DST_BIT_KHR = VK_FORMAT_FEATURE_TRANSFER_DST_BIT, + VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT_KHR = VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT_KHR = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT_KHR = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT_KHR = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_BIT, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT_KHR = VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_CHROMA_RECONSTRUCTION_EXPLICIT_FORCEABLE_BIT, + VK_FORMAT_FEATURE_DISJOINT_BIT_KHR = VK_FORMAT_FEATURE_DISJOINT_BIT, + VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT_KHR = VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT, + VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT = VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_IMG, + VK_FORMAT_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFormatFeatureFlagBits; +typedef VkFlags VkFormatFeatureFlags; + +typedef enum VkImageUsageFlagBits { + VK_IMAGE_USAGE_TRANSFER_SRC_BIT = 0x00000001, + VK_IMAGE_USAGE_TRANSFER_DST_BIT = 0x00000002, + VK_IMAGE_USAGE_SAMPLED_BIT = 0x00000004, + VK_IMAGE_USAGE_STORAGE_BIT = 0x00000008, + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT = 0x00000010, + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT = 0x00000020, + VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT = 0x00000040, + VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT = 0x00000080, + VK_IMAGE_USAGE_SHADING_RATE_IMAGE_BIT_NV = 0x00000100, + VK_IMAGE_USAGE_FRAGMENT_DENSITY_MAP_BIT_EXT = 0x00000200, + VK_IMAGE_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageUsageFlagBits; +typedef VkFlags VkImageUsageFlags; + +typedef enum VkImageCreateFlagBits { + VK_IMAGE_CREATE_SPARSE_BINDING_BIT = 0x00000001, + VK_IMAGE_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, + VK_IMAGE_CREATE_SPARSE_ALIASED_BIT = 0x00000004, + VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT = 0x00000008, + VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT = 0x00000010, + VK_IMAGE_CREATE_ALIAS_BIT = 0x00000400, + VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT = 0x00000040, + VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT = 0x00000020, + VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT = 0x00000080, + VK_IMAGE_CREATE_EXTENDED_USAGE_BIT = 0x00000100, + VK_IMAGE_CREATE_PROTECTED_BIT = 0x00000800, + VK_IMAGE_CREATE_DISJOINT_BIT = 0x00000200, + VK_IMAGE_CREATE_CORNER_SAMPLED_BIT_NV = 0x00002000, + VK_IMAGE_CREATE_SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_BIT_EXT = 0x00001000, + VK_IMAGE_CREATE_SUBSAMPLED_BIT_EXT = 0x00004000, + VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = VK_IMAGE_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT, + VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT, + VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT_KHR = VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT, + VK_IMAGE_CREATE_EXTENDED_USAGE_BIT_KHR = VK_IMAGE_CREATE_EXTENDED_USAGE_BIT, + VK_IMAGE_CREATE_DISJOINT_BIT_KHR = VK_IMAGE_CREATE_DISJOINT_BIT, + VK_IMAGE_CREATE_ALIAS_BIT_KHR = VK_IMAGE_CREATE_ALIAS_BIT, + VK_IMAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageCreateFlagBits; +typedef VkFlags VkImageCreateFlags; + +typedef enum VkSampleCountFlagBits { + VK_SAMPLE_COUNT_1_BIT = 0x00000001, + VK_SAMPLE_COUNT_2_BIT = 0x00000002, + VK_SAMPLE_COUNT_4_BIT = 0x00000004, + VK_SAMPLE_COUNT_8_BIT = 0x00000008, + VK_SAMPLE_COUNT_16_BIT = 0x00000010, + VK_SAMPLE_COUNT_32_BIT = 0x00000020, + VK_SAMPLE_COUNT_64_BIT = 0x00000040, + VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSampleCountFlagBits; +typedef VkFlags VkSampleCountFlags; + +typedef enum VkQueueFlagBits { + VK_QUEUE_GRAPHICS_BIT = 0x00000001, + VK_QUEUE_COMPUTE_BIT = 0x00000002, + VK_QUEUE_TRANSFER_BIT = 0x00000004, + VK_QUEUE_SPARSE_BINDING_BIT = 0x00000008, + VK_QUEUE_PROTECTED_BIT = 0x00000010, + VK_QUEUE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueueFlagBits; +typedef VkFlags VkQueueFlags; + +typedef enum VkMemoryPropertyFlagBits { + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT = 0x00000001, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT = 0x00000002, + VK_MEMORY_PROPERTY_HOST_COHERENT_BIT = 0x00000004, + VK_MEMORY_PROPERTY_HOST_CACHED_BIT = 0x00000008, + VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT = 0x00000010, + VK_MEMORY_PROPERTY_PROTECTED_BIT = 0x00000020, + VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD = 0x00000040, + VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD = 0x00000080, + VK_MEMORY_PROPERTY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkMemoryPropertyFlagBits; +typedef VkFlags VkMemoryPropertyFlags; + +typedef enum VkMemoryHeapFlagBits { + VK_MEMORY_HEAP_DEVICE_LOCAL_BIT = 0x00000001, + VK_MEMORY_HEAP_MULTI_INSTANCE_BIT = 0x00000002, + VK_MEMORY_HEAP_MULTI_INSTANCE_BIT_KHR = VK_MEMORY_HEAP_MULTI_INSTANCE_BIT, + VK_MEMORY_HEAP_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkMemoryHeapFlagBits; +typedef VkFlags VkMemoryHeapFlags; +typedef VkFlags VkDeviceCreateFlags; + +typedef enum VkDeviceQueueCreateFlagBits { + VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT = 0x00000001, + VK_DEVICE_QUEUE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDeviceQueueCreateFlagBits; +typedef VkFlags VkDeviceQueueCreateFlags; + +typedef enum VkPipelineStageFlagBits { + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT = 0x00000001, + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT = 0x00000002, + VK_PIPELINE_STAGE_VERTEX_INPUT_BIT = 0x00000004, + VK_PIPELINE_STAGE_VERTEX_SHADER_BIT = 0x00000008, + VK_PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT = 0x00000010, + VK_PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT = 0x00000020, + VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT = 0x00000040, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT = 0x00000080, + VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT = 0x00000100, + VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT = 0x00000200, + VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT = 0x00000400, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT = 0x00000800, + VK_PIPELINE_STAGE_TRANSFER_BIT = 0x00001000, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT = 0x00002000, + VK_PIPELINE_STAGE_HOST_BIT = 0x00004000, + VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT = 0x00008000, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT = 0x00010000, + VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT = 0x01000000, + VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT = 0x00040000, + VK_PIPELINE_STAGE_COMMAND_PROCESS_BIT_NVX = 0x00020000, + VK_PIPELINE_STAGE_SHADING_RATE_IMAGE_BIT_NV = 0x00400000, + VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_NV = 0x00200000, + VK_PIPELINE_STAGE_ACCELERATION_STRUCTURE_BUILD_BIT_NV = 0x02000000, + VK_PIPELINE_STAGE_TASK_SHADER_BIT_NV = 0x00080000, + VK_PIPELINE_STAGE_MESH_SHADER_BIT_NV = 0x00100000, + VK_PIPELINE_STAGE_FRAGMENT_DENSITY_PROCESS_BIT_EXT = 0x00800000, + VK_PIPELINE_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineStageFlagBits; +typedef VkFlags VkPipelineStageFlags; +typedef VkFlags VkMemoryMapFlags; + +typedef enum VkImageAspectFlagBits { + VK_IMAGE_ASPECT_COLOR_BIT = 0x00000001, + VK_IMAGE_ASPECT_DEPTH_BIT = 0x00000002, + VK_IMAGE_ASPECT_STENCIL_BIT = 0x00000004, + VK_IMAGE_ASPECT_METADATA_BIT = 0x00000008, + VK_IMAGE_ASPECT_PLANE_0_BIT = 0x00000010, + VK_IMAGE_ASPECT_PLANE_1_BIT = 0x00000020, + VK_IMAGE_ASPECT_PLANE_2_BIT = 0x00000040, + VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT = 0x00000080, + VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT = 0x00000100, + VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT = 0x00000200, + VK_IMAGE_ASPECT_MEMORY_PLANE_3_BIT_EXT = 0x00000400, + VK_IMAGE_ASPECT_PLANE_0_BIT_KHR = VK_IMAGE_ASPECT_PLANE_0_BIT, + VK_IMAGE_ASPECT_PLANE_1_BIT_KHR = VK_IMAGE_ASPECT_PLANE_1_BIT, + VK_IMAGE_ASPECT_PLANE_2_BIT_KHR = VK_IMAGE_ASPECT_PLANE_2_BIT, + VK_IMAGE_ASPECT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageAspectFlagBits; +typedef VkFlags VkImageAspectFlags; + +typedef enum VkSparseImageFormatFlagBits { + VK_SPARSE_IMAGE_FORMAT_SINGLE_MIPTAIL_BIT = 0x00000001, + VK_SPARSE_IMAGE_FORMAT_ALIGNED_MIP_SIZE_BIT = 0x00000002, + VK_SPARSE_IMAGE_FORMAT_NONSTANDARD_BLOCK_SIZE_BIT = 0x00000004, + VK_SPARSE_IMAGE_FORMAT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSparseImageFormatFlagBits; +typedef VkFlags VkSparseImageFormatFlags; + +typedef enum VkSparseMemoryBindFlagBits { + VK_SPARSE_MEMORY_BIND_METADATA_BIT = 0x00000001, + VK_SPARSE_MEMORY_BIND_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSparseMemoryBindFlagBits; +typedef VkFlags VkSparseMemoryBindFlags; + +typedef enum VkFenceCreateFlagBits { + VK_FENCE_CREATE_SIGNALED_BIT = 0x00000001, + VK_FENCE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFenceCreateFlagBits; +typedef VkFlags VkFenceCreateFlags; +typedef VkFlags VkSemaphoreCreateFlags; +typedef VkFlags VkEventCreateFlags; +typedef VkFlags VkQueryPoolCreateFlags; + +typedef enum VkQueryPipelineStatisticFlagBits { + VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT = 0x00000001, + VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT = 0x00000002, + VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT = 0x00000004, + VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT = 0x00000008, + VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT = 0x00000010, + VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT = 0x00000020, + VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT = 0x00000040, + VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT = 0x00000080, + VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT = 0x00000100, + VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT = 0x00000200, + VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT = 0x00000400, + VK_QUERY_PIPELINE_STATISTIC_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryPipelineStatisticFlagBits; +typedef VkFlags VkQueryPipelineStatisticFlags; + +typedef enum VkQueryResultFlagBits { + VK_QUERY_RESULT_64_BIT = 0x00000001, + VK_QUERY_RESULT_WAIT_BIT = 0x00000002, + VK_QUERY_RESULT_WITH_AVAILABILITY_BIT = 0x00000004, + VK_QUERY_RESULT_PARTIAL_BIT = 0x00000008, + VK_QUERY_RESULT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryResultFlagBits; +typedef VkFlags VkQueryResultFlags; + +typedef enum VkBufferCreateFlagBits { + VK_BUFFER_CREATE_SPARSE_BINDING_BIT = 0x00000001, + VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT = 0x00000002, + VK_BUFFER_CREATE_SPARSE_ALIASED_BIT = 0x00000004, + VK_BUFFER_CREATE_PROTECTED_BIT = 0x00000008, + VK_BUFFER_CREATE_DEVICE_ADDRESS_CAPTURE_REPLAY_BIT_EXT = 0x00000010, + VK_BUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkBufferCreateFlagBits; +typedef VkFlags VkBufferCreateFlags; + +typedef enum VkBufferUsageFlagBits { + VK_BUFFER_USAGE_TRANSFER_SRC_BIT = 0x00000001, + VK_BUFFER_USAGE_TRANSFER_DST_BIT = 0x00000002, + VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT = 0x00000004, + VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT = 0x00000008, + VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT = 0x00000010, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT = 0x00000020, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT = 0x00000040, + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT = 0x00000080, + VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT = 0x00000100, + VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_BUFFER_BIT_EXT = 0x00000800, + VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT = 0x00001000, + VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT = 0x00000200, + VK_BUFFER_USAGE_RAY_TRACING_BIT_NV = 0x00000400, + VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT = 0x00020000, + VK_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkBufferUsageFlagBits; +typedef VkFlags VkBufferUsageFlags; +typedef VkFlags VkBufferViewCreateFlags; + +typedef enum VkImageViewCreateFlagBits { + VK_IMAGE_VIEW_CREATE_FRAGMENT_DENSITY_MAP_DYNAMIC_BIT_EXT = 0x00000001, + VK_IMAGE_VIEW_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkImageViewCreateFlagBits; +typedef VkFlags VkImageViewCreateFlags; + +typedef enum VkShaderModuleCreateFlagBits { + VK_SHADER_MODULE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkShaderModuleCreateFlagBits; +typedef VkFlags VkShaderModuleCreateFlags; +typedef VkFlags VkPipelineCacheCreateFlags; + +typedef enum VkPipelineCreateFlagBits { + VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT = 0x00000001, + VK_PIPELINE_CREATE_ALLOW_DERIVATIVES_BIT = 0x00000002, + VK_PIPELINE_CREATE_DERIVATIVE_BIT = 0x00000004, + VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT = 0x00000008, + VK_PIPELINE_CREATE_DISPATCH_BASE = 0x00000010, + VK_PIPELINE_CREATE_DEFER_COMPILE_BIT_NV = 0x00000020, + VK_PIPELINE_CREATE_CAPTURE_STATISTICS_BIT_KHR = 0x00000040, + VK_PIPELINE_CREATE_CAPTURE_INTERNAL_REPRESENTATIONS_BIT_KHR = 0x00000080, + VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT_KHR = VK_PIPELINE_CREATE_VIEW_INDEX_FROM_DEVICE_INDEX_BIT, + VK_PIPELINE_CREATE_DISPATCH_BASE_KHR = VK_PIPELINE_CREATE_DISPATCH_BASE, + VK_PIPELINE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineCreateFlagBits; +typedef VkFlags VkPipelineCreateFlags; + +typedef enum VkPipelineShaderStageCreateFlagBits { + VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT = 0x00000001, + VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT = 0x00000002, + VK_PIPELINE_SHADER_STAGE_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPipelineShaderStageCreateFlagBits; +typedef VkFlags VkPipelineShaderStageCreateFlags; + +typedef enum VkShaderStageFlagBits { + VK_SHADER_STAGE_VERTEX_BIT = 0x00000001, + VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT = 0x00000002, + VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT = 0x00000004, + VK_SHADER_STAGE_GEOMETRY_BIT = 0x00000008, + VK_SHADER_STAGE_FRAGMENT_BIT = 0x00000010, + VK_SHADER_STAGE_COMPUTE_BIT = 0x00000020, + VK_SHADER_STAGE_ALL_GRAPHICS = 0x0000001F, + VK_SHADER_STAGE_ALL = 0x7FFFFFFF, + VK_SHADER_STAGE_RAYGEN_BIT_NV = 0x00000100, + VK_SHADER_STAGE_ANY_HIT_BIT_NV = 0x00000200, + VK_SHADER_STAGE_CLOSEST_HIT_BIT_NV = 0x00000400, + VK_SHADER_STAGE_MISS_BIT_NV = 0x00000800, + VK_SHADER_STAGE_INTERSECTION_BIT_NV = 0x00001000, + VK_SHADER_STAGE_CALLABLE_BIT_NV = 0x00002000, + VK_SHADER_STAGE_TASK_BIT_NV = 0x00000040, + VK_SHADER_STAGE_MESH_BIT_NV = 0x00000080, + VK_SHADER_STAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkShaderStageFlagBits; +typedef VkFlags VkPipelineVertexInputStateCreateFlags; +typedef VkFlags VkPipelineInputAssemblyStateCreateFlags; +typedef VkFlags VkPipelineTessellationStateCreateFlags; +typedef VkFlags VkPipelineViewportStateCreateFlags; +typedef VkFlags VkPipelineRasterizationStateCreateFlags; + +typedef enum VkCullModeFlagBits { + VK_CULL_MODE_NONE = 0, + VK_CULL_MODE_FRONT_BIT = 0x00000001, + VK_CULL_MODE_BACK_BIT = 0x00000002, + VK_CULL_MODE_FRONT_AND_BACK = 0x00000003, + VK_CULL_MODE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCullModeFlagBits; +typedef VkFlags VkCullModeFlags; +typedef VkFlags VkPipelineMultisampleStateCreateFlags; +typedef VkFlags VkPipelineDepthStencilStateCreateFlags; +typedef VkFlags VkPipelineColorBlendStateCreateFlags; + +typedef enum VkColorComponentFlagBits { + VK_COLOR_COMPONENT_R_BIT = 0x00000001, + VK_COLOR_COMPONENT_G_BIT = 0x00000002, + VK_COLOR_COMPONENT_B_BIT = 0x00000004, + VK_COLOR_COMPONENT_A_BIT = 0x00000008, + VK_COLOR_COMPONENT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkColorComponentFlagBits; +typedef VkFlags VkColorComponentFlags; +typedef VkFlags VkPipelineDynamicStateCreateFlags; +typedef VkFlags VkPipelineLayoutCreateFlags; +typedef VkFlags VkShaderStageFlags; + +typedef enum VkSamplerCreateFlagBits { + VK_SAMPLER_CREATE_SUBSAMPLED_BIT_EXT = 0x00000001, + VK_SAMPLER_CREATE_SUBSAMPLED_COARSE_RECONSTRUCTION_BIT_EXT = 0x00000002, + VK_SAMPLER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSamplerCreateFlagBits; +typedef VkFlags VkSamplerCreateFlags; + +typedef enum VkDescriptorSetLayoutCreateFlagBits { + VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR = 0x00000001, + VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT = 0x00000002, + VK_DESCRIPTOR_SET_LAYOUT_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorSetLayoutCreateFlagBits; +typedef VkFlags VkDescriptorSetLayoutCreateFlags; + +typedef enum VkDescriptorPoolCreateFlagBits { + VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT = 0x00000001, + VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT = 0x00000002, + VK_DESCRIPTOR_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorPoolCreateFlagBits; +typedef VkFlags VkDescriptorPoolCreateFlags; +typedef VkFlags VkDescriptorPoolResetFlags; + +typedef enum VkFramebufferCreateFlagBits { + VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT_KHR = 0x00000001, + VK_FRAMEBUFFER_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFramebufferCreateFlagBits; +typedef VkFlags VkFramebufferCreateFlags; + +typedef enum VkRenderPassCreateFlagBits { + VK_RENDER_PASS_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkRenderPassCreateFlagBits; +typedef VkFlags VkRenderPassCreateFlags; + +typedef enum VkAttachmentDescriptionFlagBits { + VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 0x00000001, + VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkAttachmentDescriptionFlagBits; +typedef VkFlags VkAttachmentDescriptionFlags; + +typedef enum VkSubpassDescriptionFlagBits { + VK_SUBPASS_DESCRIPTION_PER_VIEW_ATTRIBUTES_BIT_NVX = 0x00000001, + VK_SUBPASS_DESCRIPTION_PER_VIEW_POSITION_X_ONLY_BIT_NVX = 0x00000002, + VK_SUBPASS_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSubpassDescriptionFlagBits; +typedef VkFlags VkSubpassDescriptionFlags; + +typedef enum VkAccessFlagBits { + VK_ACCESS_INDIRECT_COMMAND_READ_BIT = 0x00000001, + VK_ACCESS_INDEX_READ_BIT = 0x00000002, + VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT = 0x00000004, + VK_ACCESS_UNIFORM_READ_BIT = 0x00000008, + VK_ACCESS_INPUT_ATTACHMENT_READ_BIT = 0x00000010, + VK_ACCESS_SHADER_READ_BIT = 0x00000020, + VK_ACCESS_SHADER_WRITE_BIT = 0x00000040, + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT = 0x00000080, + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT = 0x00000100, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT = 0x00000200, + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT = 0x00000400, + VK_ACCESS_TRANSFER_READ_BIT = 0x00000800, + VK_ACCESS_TRANSFER_WRITE_BIT = 0x00001000, + VK_ACCESS_HOST_READ_BIT = 0x00002000, + VK_ACCESS_HOST_WRITE_BIT = 0x00004000, + VK_ACCESS_MEMORY_READ_BIT = 0x00008000, + VK_ACCESS_MEMORY_WRITE_BIT = 0x00010000, + VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT = 0x02000000, + VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT = 0x04000000, + VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT = 0x08000000, + VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT = 0x00100000, + VK_ACCESS_COMMAND_PROCESS_READ_BIT_NVX = 0x00020000, + VK_ACCESS_COMMAND_PROCESS_WRITE_BIT_NVX = 0x00040000, + VK_ACCESS_COLOR_ATTACHMENT_READ_NONCOHERENT_BIT_EXT = 0x00080000, + VK_ACCESS_SHADING_RATE_IMAGE_READ_BIT_NV = 0x00800000, + VK_ACCESS_ACCELERATION_STRUCTURE_READ_BIT_NV = 0x00200000, + VK_ACCESS_ACCELERATION_STRUCTURE_WRITE_BIT_NV = 0x00400000, + VK_ACCESS_FRAGMENT_DENSITY_MAP_READ_BIT_EXT = 0x01000000, + VK_ACCESS_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkAccessFlagBits; +typedef VkFlags VkAccessFlags; + +typedef enum VkDependencyFlagBits { + VK_DEPENDENCY_BY_REGION_BIT = 0x00000001, + VK_DEPENDENCY_DEVICE_GROUP_BIT = 0x00000004, + VK_DEPENDENCY_VIEW_LOCAL_BIT = 0x00000002, + VK_DEPENDENCY_VIEW_LOCAL_BIT_KHR = VK_DEPENDENCY_VIEW_LOCAL_BIT, + VK_DEPENDENCY_DEVICE_GROUP_BIT_KHR = VK_DEPENDENCY_DEVICE_GROUP_BIT, + VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkDependencyFlagBits; +typedef VkFlags VkDependencyFlags; + +typedef enum VkCommandPoolCreateFlagBits { + VK_COMMAND_POOL_CREATE_TRANSIENT_BIT = 0x00000001, + VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT = 0x00000002, + VK_COMMAND_POOL_CREATE_PROTECTED_BIT = 0x00000004, + VK_COMMAND_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandPoolCreateFlagBits; +typedef VkFlags VkCommandPoolCreateFlags; + +typedef enum VkCommandPoolResetFlagBits { + VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT = 0x00000001, + VK_COMMAND_POOL_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandPoolResetFlagBits; +typedef VkFlags VkCommandPoolResetFlags; + +typedef enum VkCommandBufferUsageFlagBits { + VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT = 0x00000001, + VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT = 0x00000002, + VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT = 0x00000004, + VK_COMMAND_BUFFER_USAGE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferUsageFlagBits; +typedef VkFlags VkCommandBufferUsageFlags; + +typedef enum VkQueryControlFlagBits { + VK_QUERY_CONTROL_PRECISE_BIT = 0x00000001, + VK_QUERY_CONTROL_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkQueryControlFlagBits; +typedef VkFlags VkQueryControlFlags; + +typedef enum VkCommandBufferResetFlagBits { + VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT = 0x00000001, + VK_COMMAND_BUFFER_RESET_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkCommandBufferResetFlagBits; +typedef VkFlags VkCommandBufferResetFlags; + +typedef enum VkStencilFaceFlagBits { + VK_STENCIL_FACE_FRONT_BIT = 0x00000001, + VK_STENCIL_FACE_BACK_BIT = 0x00000002, + VK_STENCIL_FACE_FRONT_AND_BACK = 0x00000003, + VK_STENCIL_FRONT_AND_BACK = VK_STENCIL_FACE_FRONT_AND_BACK, + VK_STENCIL_FACE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkStencilFaceFlagBits; +typedef VkFlags VkStencilFaceFlags; +typedef struct VkApplicationInfo { + VkStructureType sType; + const void* pNext; + const char* pApplicationName; + uint32_t applicationVersion; + const char* pEngineName; + uint32_t engineVersion; + uint32_t apiVersion; +} VkApplicationInfo; + +typedef struct VkInstanceCreateInfo { + VkStructureType sType; + const void* pNext; + VkInstanceCreateFlags flags; + const VkApplicationInfo* pApplicationInfo; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; +} VkInstanceCreateInfo; + +typedef void* (VKAPI_PTR *PFN_vkAllocationFunction)( + void* pUserData, + size_t size, + size_t alignment, + VkSystemAllocationScope allocationScope); + +typedef void* (VKAPI_PTR *PFN_vkReallocationFunction)( + void* pUserData, + void* pOriginal, + size_t size, + size_t alignment, + VkSystemAllocationScope allocationScope); + +typedef void (VKAPI_PTR *PFN_vkFreeFunction)( + void* pUserData, + void* pMemory); + +typedef void (VKAPI_PTR *PFN_vkInternalAllocationNotification)( + void* pUserData, + size_t size, + VkInternalAllocationType allocationType, + VkSystemAllocationScope allocationScope); + +typedef void (VKAPI_PTR *PFN_vkInternalFreeNotification)( + void* pUserData, + size_t size, + VkInternalAllocationType allocationType, + VkSystemAllocationScope allocationScope); + +typedef struct VkAllocationCallbacks { + void* pUserData; + PFN_vkAllocationFunction pfnAllocation; + PFN_vkReallocationFunction pfnReallocation; + PFN_vkFreeFunction pfnFree; + PFN_vkInternalAllocationNotification pfnInternalAllocation; + PFN_vkInternalFreeNotification pfnInternalFree; +} VkAllocationCallbacks; + +typedef struct VkPhysicalDeviceFeatures { + VkBool32 robustBufferAccess; + VkBool32 fullDrawIndexUint32; + VkBool32 imageCubeArray; + VkBool32 independentBlend; + VkBool32 geometryShader; + VkBool32 tessellationShader; + VkBool32 sampleRateShading; + VkBool32 dualSrcBlend; + VkBool32 logicOp; + VkBool32 multiDrawIndirect; + VkBool32 drawIndirectFirstInstance; + VkBool32 depthClamp; + VkBool32 depthBiasClamp; + VkBool32 fillModeNonSolid; + VkBool32 depthBounds; + VkBool32 wideLines; + VkBool32 largePoints; + VkBool32 alphaToOne; + VkBool32 multiViewport; + VkBool32 samplerAnisotropy; + VkBool32 textureCompressionETC2; + VkBool32 textureCompressionASTC_LDR; + VkBool32 textureCompressionBC; + VkBool32 occlusionQueryPrecise; + VkBool32 pipelineStatisticsQuery; + VkBool32 vertexPipelineStoresAndAtomics; + VkBool32 fragmentStoresAndAtomics; + VkBool32 shaderTessellationAndGeometryPointSize; + VkBool32 shaderImageGatherExtended; + VkBool32 shaderStorageImageExtendedFormats; + VkBool32 shaderStorageImageMultisample; + VkBool32 shaderStorageImageReadWithoutFormat; + VkBool32 shaderStorageImageWriteWithoutFormat; + VkBool32 shaderUniformBufferArrayDynamicIndexing; + VkBool32 shaderSampledImageArrayDynamicIndexing; + VkBool32 shaderStorageBufferArrayDynamicIndexing; + VkBool32 shaderStorageImageArrayDynamicIndexing; + VkBool32 shaderClipDistance; + VkBool32 shaderCullDistance; + VkBool32 shaderFloat64; + VkBool32 shaderInt64; + VkBool32 shaderInt16; + VkBool32 shaderResourceResidency; + VkBool32 shaderResourceMinLod; + VkBool32 sparseBinding; + VkBool32 sparseResidencyBuffer; + VkBool32 sparseResidencyImage2D; + VkBool32 sparseResidencyImage3D; + VkBool32 sparseResidency2Samples; + VkBool32 sparseResidency4Samples; + VkBool32 sparseResidency8Samples; + VkBool32 sparseResidency16Samples; + VkBool32 sparseResidencyAliased; + VkBool32 variableMultisampleRate; + VkBool32 inheritedQueries; +} VkPhysicalDeviceFeatures; + +typedef struct VkFormatProperties { + VkFormatFeatureFlags linearTilingFeatures; + VkFormatFeatureFlags optimalTilingFeatures; + VkFormatFeatureFlags bufferFeatures; +} VkFormatProperties; + +typedef struct VkExtent3D { + uint32_t width; + uint32_t height; + uint32_t depth; +} VkExtent3D; + +typedef struct VkImageFormatProperties { + VkExtent3D maxExtent; + uint32_t maxMipLevels; + uint32_t maxArrayLayers; + VkSampleCountFlags sampleCounts; + VkDeviceSize maxResourceSize; +} VkImageFormatProperties; + +typedef struct VkPhysicalDeviceLimits { + uint32_t maxImageDimension1D; + uint32_t maxImageDimension2D; + uint32_t maxImageDimension3D; + uint32_t maxImageDimensionCube; + uint32_t maxImageArrayLayers; + uint32_t maxTexelBufferElements; + uint32_t maxUniformBufferRange; + uint32_t maxStorageBufferRange; + uint32_t maxPushConstantsSize; + uint32_t maxMemoryAllocationCount; + uint32_t maxSamplerAllocationCount; + VkDeviceSize bufferImageGranularity; + VkDeviceSize sparseAddressSpaceSize; + uint32_t maxBoundDescriptorSets; + uint32_t maxPerStageDescriptorSamplers; + uint32_t maxPerStageDescriptorUniformBuffers; + uint32_t maxPerStageDescriptorStorageBuffers; + uint32_t maxPerStageDescriptorSampledImages; + uint32_t maxPerStageDescriptorStorageImages; + uint32_t maxPerStageDescriptorInputAttachments; + uint32_t maxPerStageResources; + uint32_t maxDescriptorSetSamplers; + uint32_t maxDescriptorSetUniformBuffers; + uint32_t maxDescriptorSetUniformBuffersDynamic; + uint32_t maxDescriptorSetStorageBuffers; + uint32_t maxDescriptorSetStorageBuffersDynamic; + uint32_t maxDescriptorSetSampledImages; + uint32_t maxDescriptorSetStorageImages; + uint32_t maxDescriptorSetInputAttachments; + uint32_t maxVertexInputAttributes; + uint32_t maxVertexInputBindings; + uint32_t maxVertexInputAttributeOffset; + uint32_t maxVertexInputBindingStride; + uint32_t maxVertexOutputComponents; + uint32_t maxTessellationGenerationLevel; + uint32_t maxTessellationPatchSize; + uint32_t maxTessellationControlPerVertexInputComponents; + uint32_t maxTessellationControlPerVertexOutputComponents; + uint32_t maxTessellationControlPerPatchOutputComponents; + uint32_t maxTessellationControlTotalOutputComponents; + uint32_t maxTessellationEvaluationInputComponents; + uint32_t maxTessellationEvaluationOutputComponents; + uint32_t maxGeometryShaderInvocations; + uint32_t maxGeometryInputComponents; + uint32_t maxGeometryOutputComponents; + uint32_t maxGeometryOutputVertices; + uint32_t maxGeometryTotalOutputComponents; + uint32_t maxFragmentInputComponents; + uint32_t maxFragmentOutputAttachments; + uint32_t maxFragmentDualSrcAttachments; + uint32_t maxFragmentCombinedOutputResources; + uint32_t maxComputeSharedMemorySize; + uint32_t maxComputeWorkGroupCount[3]; + uint32_t maxComputeWorkGroupInvocations; + uint32_t maxComputeWorkGroupSize[3]; + uint32_t subPixelPrecisionBits; + uint32_t subTexelPrecisionBits; + uint32_t mipmapPrecisionBits; + uint32_t maxDrawIndexedIndexValue; + uint32_t maxDrawIndirectCount; + float maxSamplerLodBias; + float maxSamplerAnisotropy; + uint32_t maxViewports; + uint32_t maxViewportDimensions[2]; + float viewportBoundsRange[2]; + uint32_t viewportSubPixelBits; + size_t minMemoryMapAlignment; + VkDeviceSize minTexelBufferOffsetAlignment; + VkDeviceSize minUniformBufferOffsetAlignment; + VkDeviceSize minStorageBufferOffsetAlignment; + int32_t minTexelOffset; + uint32_t maxTexelOffset; + int32_t minTexelGatherOffset; + uint32_t maxTexelGatherOffset; + float minInterpolationOffset; + float maxInterpolationOffset; + uint32_t subPixelInterpolationOffsetBits; + uint32_t maxFramebufferWidth; + uint32_t maxFramebufferHeight; + uint32_t maxFramebufferLayers; + VkSampleCountFlags framebufferColorSampleCounts; + VkSampleCountFlags framebufferDepthSampleCounts; + VkSampleCountFlags framebufferStencilSampleCounts; + VkSampleCountFlags framebufferNoAttachmentsSampleCounts; + uint32_t maxColorAttachments; + VkSampleCountFlags sampledImageColorSampleCounts; + VkSampleCountFlags sampledImageIntegerSampleCounts; + VkSampleCountFlags sampledImageDepthSampleCounts; + VkSampleCountFlags sampledImageStencilSampleCounts; + VkSampleCountFlags storageImageSampleCounts; + uint32_t maxSampleMaskWords; + VkBool32 timestampComputeAndGraphics; + float timestampPeriod; + uint32_t maxClipDistances; + uint32_t maxCullDistances; + uint32_t maxCombinedClipAndCullDistances; + uint32_t discreteQueuePriorities; + float pointSizeRange[2]; + float lineWidthRange[2]; + float pointSizeGranularity; + float lineWidthGranularity; + VkBool32 strictLines; + VkBool32 standardSampleLocations; + VkDeviceSize optimalBufferCopyOffsetAlignment; + VkDeviceSize optimalBufferCopyRowPitchAlignment; + VkDeviceSize nonCoherentAtomSize; +} VkPhysicalDeviceLimits; + +typedef struct VkPhysicalDeviceSparseProperties { + VkBool32 residencyStandard2DBlockShape; + VkBool32 residencyStandard2DMultisampleBlockShape; + VkBool32 residencyStandard3DBlockShape; + VkBool32 residencyAlignedMipSize; + VkBool32 residencyNonResidentStrict; +} VkPhysicalDeviceSparseProperties; + +typedef struct VkPhysicalDeviceProperties { + uint32_t apiVersion; + uint32_t driverVersion; + uint32_t vendorID; + uint32_t deviceID; + VkPhysicalDeviceType deviceType; + char deviceName[VK_MAX_PHYSICAL_DEVICE_NAME_SIZE]; + uint8_t pipelineCacheUUID[VK_UUID_SIZE]; + VkPhysicalDeviceLimits limits; + VkPhysicalDeviceSparseProperties sparseProperties; +} VkPhysicalDeviceProperties; + +typedef struct VkQueueFamilyProperties { + VkQueueFlags queueFlags; + uint32_t queueCount; + uint32_t timestampValidBits; + VkExtent3D minImageTransferGranularity; +} VkQueueFamilyProperties; + +typedef struct VkMemoryType { + VkMemoryPropertyFlags propertyFlags; + uint32_t heapIndex; +} VkMemoryType; + +typedef struct VkMemoryHeap { + VkDeviceSize size; + VkMemoryHeapFlags flags; +} VkMemoryHeap; + +typedef struct VkPhysicalDeviceMemoryProperties { + uint32_t memoryTypeCount; + VkMemoryType memoryTypes[VK_MAX_MEMORY_TYPES]; + uint32_t memoryHeapCount; + VkMemoryHeap memoryHeaps[VK_MAX_MEMORY_HEAPS]; +} VkPhysicalDeviceMemoryProperties; + +typedef void (VKAPI_PTR *PFN_vkVoidFunction)(void); +typedef struct VkDeviceQueueCreateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceQueueCreateFlags flags; + uint32_t queueFamilyIndex; + uint32_t queueCount; + const float* pQueuePriorities; +} VkDeviceQueueCreateInfo; + +typedef struct VkDeviceCreateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceCreateFlags flags; + uint32_t queueCreateInfoCount; + const VkDeviceQueueCreateInfo* pQueueCreateInfos; + uint32_t enabledLayerCount; + const char* const* ppEnabledLayerNames; + uint32_t enabledExtensionCount; + const char* const* ppEnabledExtensionNames; + const VkPhysicalDeviceFeatures* pEnabledFeatures; +} VkDeviceCreateInfo; + +typedef struct VkExtensionProperties { + char extensionName[VK_MAX_EXTENSION_NAME_SIZE]; + uint32_t specVersion; +} VkExtensionProperties; + +typedef struct VkLayerProperties { + char layerName[VK_MAX_EXTENSION_NAME_SIZE]; + uint32_t specVersion; + uint32_t implementationVersion; + char description[VK_MAX_DESCRIPTION_SIZE]; +} VkLayerProperties; + +typedef struct VkSubmitInfo { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore* pWaitSemaphores; + const VkPipelineStageFlags* pWaitDstStageMask; + uint32_t commandBufferCount; + const VkCommandBuffer* pCommandBuffers; + uint32_t signalSemaphoreCount; + const VkSemaphore* pSignalSemaphores; +} VkSubmitInfo; + +typedef struct VkMemoryAllocateInfo { + VkStructureType sType; + const void* pNext; + VkDeviceSize allocationSize; + uint32_t memoryTypeIndex; +} VkMemoryAllocateInfo; + +typedef struct VkMappedMemoryRange { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; + VkDeviceSize offset; + VkDeviceSize size; +} VkMappedMemoryRange; + +typedef struct VkMemoryRequirements { + VkDeviceSize size; + VkDeviceSize alignment; + uint32_t memoryTypeBits; +} VkMemoryRequirements; + +typedef struct VkSparseImageFormatProperties { + VkImageAspectFlags aspectMask; + VkExtent3D imageGranularity; + VkSparseImageFormatFlags flags; +} VkSparseImageFormatProperties; + +typedef struct VkSparseImageMemoryRequirements { + VkSparseImageFormatProperties formatProperties; + uint32_t imageMipTailFirstLod; + VkDeviceSize imageMipTailSize; + VkDeviceSize imageMipTailOffset; + VkDeviceSize imageMipTailStride; +} VkSparseImageMemoryRequirements; + +typedef struct VkSparseMemoryBind { + VkDeviceSize resourceOffset; + VkDeviceSize size; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + VkSparseMemoryBindFlags flags; +} VkSparseMemoryBind; + +typedef struct VkSparseBufferMemoryBindInfo { + VkBuffer buffer; + uint32_t bindCount; + const VkSparseMemoryBind* pBinds; +} VkSparseBufferMemoryBindInfo; + +typedef struct VkSparseImageOpaqueMemoryBindInfo { + VkImage image; + uint32_t bindCount; + const VkSparseMemoryBind* pBinds; +} VkSparseImageOpaqueMemoryBindInfo; + +typedef struct VkImageSubresource { + VkImageAspectFlags aspectMask; + uint32_t mipLevel; + uint32_t arrayLayer; +} VkImageSubresource; + +typedef struct VkOffset3D { + int32_t x; + int32_t y; + int32_t z; +} VkOffset3D; + +typedef struct VkSparseImageMemoryBind { + VkImageSubresource subresource; + VkOffset3D offset; + VkExtent3D extent; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + VkSparseMemoryBindFlags flags; +} VkSparseImageMemoryBind; + +typedef struct VkSparseImageMemoryBindInfo { + VkImage image; + uint32_t bindCount; + const VkSparseImageMemoryBind* pBinds; +} VkSparseImageMemoryBindInfo; + +typedef struct VkBindSparseInfo { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore* pWaitSemaphores; + uint32_t bufferBindCount; + const VkSparseBufferMemoryBindInfo* pBufferBinds; + uint32_t imageOpaqueBindCount; + const VkSparseImageOpaqueMemoryBindInfo* pImageOpaqueBinds; + uint32_t imageBindCount; + const VkSparseImageMemoryBindInfo* pImageBinds; + uint32_t signalSemaphoreCount; + const VkSemaphore* pSignalSemaphores; +} VkBindSparseInfo; + +typedef struct VkFenceCreateInfo { + VkStructureType sType; + const void* pNext; + VkFenceCreateFlags flags; +} VkFenceCreateInfo; + +typedef struct VkSemaphoreCreateInfo { + VkStructureType sType; + const void* pNext; + VkSemaphoreCreateFlags flags; +} VkSemaphoreCreateInfo; + +typedef struct VkEventCreateInfo { + VkStructureType sType; + const void* pNext; + VkEventCreateFlags flags; +} VkEventCreateInfo; + +typedef struct VkQueryPoolCreateInfo { + VkStructureType sType; + const void* pNext; + VkQueryPoolCreateFlags flags; + VkQueryType queryType; + uint32_t queryCount; + VkQueryPipelineStatisticFlags pipelineStatistics; +} VkQueryPoolCreateInfo; + +typedef struct VkBufferCreateInfo { + VkStructureType sType; + const void* pNext; + VkBufferCreateFlags flags; + VkDeviceSize size; + VkBufferUsageFlags usage; + VkSharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; +} VkBufferCreateInfo; + +typedef struct VkBufferViewCreateInfo { + VkStructureType sType; + const void* pNext; + VkBufferViewCreateFlags flags; + VkBuffer buffer; + VkFormat format; + VkDeviceSize offset; + VkDeviceSize range; +} VkBufferViewCreateInfo; + +typedef struct VkImageCreateInfo { + VkStructureType sType; + const void* pNext; + VkImageCreateFlags flags; + VkImageType imageType; + VkFormat format; + VkExtent3D extent; + uint32_t mipLevels; + uint32_t arrayLayers; + VkSampleCountFlagBits samples; + VkImageTiling tiling; + VkImageUsageFlags usage; + VkSharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; + VkImageLayout initialLayout; +} VkImageCreateInfo; + +typedef struct VkSubresourceLayout { + VkDeviceSize offset; + VkDeviceSize size; + VkDeviceSize rowPitch; + VkDeviceSize arrayPitch; + VkDeviceSize depthPitch; +} VkSubresourceLayout; + +typedef struct VkComponentMapping { + VkComponentSwizzle r; + VkComponentSwizzle g; + VkComponentSwizzle b; + VkComponentSwizzle a; +} VkComponentMapping; + +typedef struct VkImageSubresourceRange { + VkImageAspectFlags aspectMask; + uint32_t baseMipLevel; + uint32_t levelCount; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkImageSubresourceRange; + +typedef struct VkImageViewCreateInfo { + VkStructureType sType; + const void* pNext; + VkImageViewCreateFlags flags; + VkImage image; + VkImageViewType viewType; + VkFormat format; + VkComponentMapping components; + VkImageSubresourceRange subresourceRange; +} VkImageViewCreateInfo; + +typedef struct VkShaderModuleCreateInfo { + VkStructureType sType; + const void* pNext; + VkShaderModuleCreateFlags flags; + size_t codeSize; + const uint32_t* pCode; +} VkShaderModuleCreateInfo; + +typedef struct VkPipelineCacheCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineCacheCreateFlags flags; + size_t initialDataSize; + const void* pInitialData; +} VkPipelineCacheCreateInfo; + +typedef struct VkSpecializationMapEntry { + uint32_t constantID; + uint32_t offset; + size_t size; +} VkSpecializationMapEntry; + +typedef struct VkSpecializationInfo { + uint32_t mapEntryCount; + const VkSpecializationMapEntry* pMapEntries; + size_t dataSize; + const void* pData; +} VkSpecializationInfo; + +typedef struct VkPipelineShaderStageCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineShaderStageCreateFlags flags; + VkShaderStageFlagBits stage; + VkShaderModule module; + const char* pName; + const VkSpecializationInfo* pSpecializationInfo; +} VkPipelineShaderStageCreateInfo; + +typedef struct VkVertexInputBindingDescription { + uint32_t binding; + uint32_t stride; + VkVertexInputRate inputRate; +} VkVertexInputBindingDescription; + +typedef struct VkVertexInputAttributeDescription { + uint32_t location; + uint32_t binding; + VkFormat format; + uint32_t offset; +} VkVertexInputAttributeDescription; + +typedef struct VkPipelineVertexInputStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineVertexInputStateCreateFlags flags; + uint32_t vertexBindingDescriptionCount; + const VkVertexInputBindingDescription* pVertexBindingDescriptions; + uint32_t vertexAttributeDescriptionCount; + const VkVertexInputAttributeDescription* pVertexAttributeDescriptions; +} VkPipelineVertexInputStateCreateInfo; + +typedef struct VkPipelineInputAssemblyStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineInputAssemblyStateCreateFlags flags; + VkPrimitiveTopology topology; + VkBool32 primitiveRestartEnable; +} VkPipelineInputAssemblyStateCreateInfo; + +typedef struct VkPipelineTessellationStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineTessellationStateCreateFlags flags; + uint32_t patchControlPoints; +} VkPipelineTessellationStateCreateInfo; + +typedef struct VkViewport { + float x; + float y; + float width; + float height; + float minDepth; + float maxDepth; +} VkViewport; + +typedef struct VkOffset2D { + int32_t x; + int32_t y; +} VkOffset2D; + +typedef struct VkExtent2D { + uint32_t width; + uint32_t height; +} VkExtent2D; + +typedef struct VkRect2D { + VkOffset2D offset; + VkExtent2D extent; +} VkRect2D; + +typedef struct VkPipelineViewportStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineViewportStateCreateFlags flags; + uint32_t viewportCount; + const VkViewport* pViewports; + uint32_t scissorCount; + const VkRect2D* pScissors; +} VkPipelineViewportStateCreateInfo; + +typedef struct VkPipelineRasterizationStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineRasterizationStateCreateFlags flags; + VkBool32 depthClampEnable; + VkBool32 rasterizerDiscardEnable; + VkPolygonMode polygonMode; + VkCullModeFlags cullMode; + VkFrontFace frontFace; + VkBool32 depthBiasEnable; + float depthBiasConstantFactor; + float depthBiasClamp; + float depthBiasSlopeFactor; + float lineWidth; +} VkPipelineRasterizationStateCreateInfo; + +typedef struct VkPipelineMultisampleStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineMultisampleStateCreateFlags flags; + VkSampleCountFlagBits rasterizationSamples; + VkBool32 sampleShadingEnable; + float minSampleShading; + const VkSampleMask* pSampleMask; + VkBool32 alphaToCoverageEnable; + VkBool32 alphaToOneEnable; +} VkPipelineMultisampleStateCreateInfo; + +typedef struct VkStencilOpState { + VkStencilOp failOp; + VkStencilOp passOp; + VkStencilOp depthFailOp; + VkCompareOp compareOp; + uint32_t compareMask; + uint32_t writeMask; + uint32_t reference; +} VkStencilOpState; + +typedef struct VkPipelineDepthStencilStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineDepthStencilStateCreateFlags flags; + VkBool32 depthTestEnable; + VkBool32 depthWriteEnable; + VkCompareOp depthCompareOp; + VkBool32 depthBoundsTestEnable; + VkBool32 stencilTestEnable; + VkStencilOpState front; + VkStencilOpState back; + float minDepthBounds; + float maxDepthBounds; +} VkPipelineDepthStencilStateCreateInfo; + +typedef struct VkPipelineColorBlendAttachmentState { + VkBool32 blendEnable; + VkBlendFactor srcColorBlendFactor; + VkBlendFactor dstColorBlendFactor; + VkBlendOp colorBlendOp; + VkBlendFactor srcAlphaBlendFactor; + VkBlendFactor dstAlphaBlendFactor; + VkBlendOp alphaBlendOp; + VkColorComponentFlags colorWriteMask; +} VkPipelineColorBlendAttachmentState; + +typedef struct VkPipelineColorBlendStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineColorBlendStateCreateFlags flags; + VkBool32 logicOpEnable; + VkLogicOp logicOp; + uint32_t attachmentCount; + const VkPipelineColorBlendAttachmentState* pAttachments; + float blendConstants[4]; +} VkPipelineColorBlendStateCreateInfo; + +typedef struct VkPipelineDynamicStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineDynamicStateCreateFlags flags; + uint32_t dynamicStateCount; + const VkDynamicState* pDynamicStates; +} VkPipelineDynamicStateCreateInfo; + +typedef struct VkGraphicsPipelineCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineCreateFlags flags; + uint32_t stageCount; + const VkPipelineShaderStageCreateInfo* pStages; + const VkPipelineVertexInputStateCreateInfo* pVertexInputState; + const VkPipelineInputAssemblyStateCreateInfo* pInputAssemblyState; + const VkPipelineTessellationStateCreateInfo* pTessellationState; + const VkPipelineViewportStateCreateInfo* pViewportState; + const VkPipelineRasterizationStateCreateInfo* pRasterizationState; + const VkPipelineMultisampleStateCreateInfo* pMultisampleState; + const VkPipelineDepthStencilStateCreateInfo* pDepthStencilState; + const VkPipelineColorBlendStateCreateInfo* pColorBlendState; + const VkPipelineDynamicStateCreateInfo* pDynamicState; + VkPipelineLayout layout; + VkRenderPass renderPass; + uint32_t subpass; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkGraphicsPipelineCreateInfo; + +typedef struct VkComputePipelineCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineCreateFlags flags; + VkPipelineShaderStageCreateInfo stage; + VkPipelineLayout layout; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkComputePipelineCreateInfo; + +typedef struct VkPushConstantRange { + VkShaderStageFlags stageFlags; + uint32_t offset; + uint32_t size; +} VkPushConstantRange; + +typedef struct VkPipelineLayoutCreateInfo { + VkStructureType sType; + const void* pNext; + VkPipelineLayoutCreateFlags flags; + uint32_t setLayoutCount; + const VkDescriptorSetLayout* pSetLayouts; + uint32_t pushConstantRangeCount; + const VkPushConstantRange* pPushConstantRanges; +} VkPipelineLayoutCreateInfo; + +typedef struct VkSamplerCreateInfo { + VkStructureType sType; + const void* pNext; + VkSamplerCreateFlags flags; + VkFilter magFilter; + VkFilter minFilter; + VkSamplerMipmapMode mipmapMode; + VkSamplerAddressMode addressModeU; + VkSamplerAddressMode addressModeV; + VkSamplerAddressMode addressModeW; + float mipLodBias; + VkBool32 anisotropyEnable; + float maxAnisotropy; + VkBool32 compareEnable; + VkCompareOp compareOp; + float minLod; + float maxLod; + VkBorderColor borderColor; + VkBool32 unnormalizedCoordinates; +} VkSamplerCreateInfo; + +typedef struct VkDescriptorSetLayoutBinding { + uint32_t binding; + VkDescriptorType descriptorType; + uint32_t descriptorCount; + VkShaderStageFlags stageFlags; + const VkSampler* pImmutableSamplers; +} VkDescriptorSetLayoutBinding; + +typedef struct VkDescriptorSetLayoutCreateInfo { + VkStructureType sType; + const void* pNext; + VkDescriptorSetLayoutCreateFlags flags; + uint32_t bindingCount; + const VkDescriptorSetLayoutBinding* pBindings; +} VkDescriptorSetLayoutCreateInfo; + +typedef struct VkDescriptorPoolSize { + VkDescriptorType type; + uint32_t descriptorCount; +} VkDescriptorPoolSize; + +typedef struct VkDescriptorPoolCreateInfo { + VkStructureType sType; + const void* pNext; + VkDescriptorPoolCreateFlags flags; + uint32_t maxSets; + uint32_t poolSizeCount; + const VkDescriptorPoolSize* pPoolSizes; +} VkDescriptorPoolCreateInfo; + +typedef struct VkDescriptorSetAllocateInfo { + VkStructureType sType; + const void* pNext; + VkDescriptorPool descriptorPool; + uint32_t descriptorSetCount; + const VkDescriptorSetLayout* pSetLayouts; +} VkDescriptorSetAllocateInfo; + +typedef struct VkDescriptorImageInfo { + VkSampler sampler; + VkImageView imageView; + VkImageLayout imageLayout; +} VkDescriptorImageInfo; + +typedef struct VkDescriptorBufferInfo { + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize range; +} VkDescriptorBufferInfo; + +typedef struct VkWriteDescriptorSet { + VkStructureType sType; + const void* pNext; + VkDescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; + VkDescriptorType descriptorType; + const VkDescriptorImageInfo* pImageInfo; + const VkDescriptorBufferInfo* pBufferInfo; + const VkBufferView* pTexelBufferView; +} VkWriteDescriptorSet; + +typedef struct VkCopyDescriptorSet { + VkStructureType sType; + const void* pNext; + VkDescriptorSet srcSet; + uint32_t srcBinding; + uint32_t srcArrayElement; + VkDescriptorSet dstSet; + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; +} VkCopyDescriptorSet; + +typedef struct VkFramebufferCreateInfo { + VkStructureType sType; + const void* pNext; + VkFramebufferCreateFlags flags; + VkRenderPass renderPass; + uint32_t attachmentCount; + const VkImageView* pAttachments; + uint32_t width; + uint32_t height; + uint32_t layers; +} VkFramebufferCreateInfo; + +typedef struct VkAttachmentDescription { + VkAttachmentDescriptionFlags flags; + VkFormat format; + VkSampleCountFlagBits samples; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkAttachmentLoadOp stencilLoadOp; + VkAttachmentStoreOp stencilStoreOp; + VkImageLayout initialLayout; + VkImageLayout finalLayout; +} VkAttachmentDescription; + +typedef struct VkAttachmentReference { + uint32_t attachment; + VkImageLayout layout; +} VkAttachmentReference; + +typedef struct VkSubpassDescription { + VkSubpassDescriptionFlags flags; + VkPipelineBindPoint pipelineBindPoint; + uint32_t inputAttachmentCount; + const VkAttachmentReference* pInputAttachments; + uint32_t colorAttachmentCount; + const VkAttachmentReference* pColorAttachments; + const VkAttachmentReference* pResolveAttachments; + const VkAttachmentReference* pDepthStencilAttachment; + uint32_t preserveAttachmentCount; + const uint32_t* pPreserveAttachments; +} VkSubpassDescription; + +typedef struct VkSubpassDependency { + uint32_t srcSubpass; + uint32_t dstSubpass; + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkDependencyFlags dependencyFlags; +} VkSubpassDependency; + +typedef struct VkRenderPassCreateInfo { + VkStructureType sType; + const void* pNext; + VkRenderPassCreateFlags flags; + uint32_t attachmentCount; + const VkAttachmentDescription* pAttachments; + uint32_t subpassCount; + const VkSubpassDescription* pSubpasses; + uint32_t dependencyCount; + const VkSubpassDependency* pDependencies; +} VkRenderPassCreateInfo; + +typedef struct VkCommandPoolCreateInfo { + VkStructureType sType; + const void* pNext; + VkCommandPoolCreateFlags flags; + uint32_t queueFamilyIndex; +} VkCommandPoolCreateInfo; + +typedef struct VkCommandBufferAllocateInfo { + VkStructureType sType; + const void* pNext; + VkCommandPool commandPool; + VkCommandBufferLevel level; + uint32_t commandBufferCount; +} VkCommandBufferAllocateInfo; + +typedef struct VkCommandBufferInheritanceInfo { + VkStructureType sType; + const void* pNext; + VkRenderPass renderPass; + uint32_t subpass; + VkFramebuffer framebuffer; + VkBool32 occlusionQueryEnable; + VkQueryControlFlags queryFlags; + VkQueryPipelineStatisticFlags pipelineStatistics; +} VkCommandBufferInheritanceInfo; + +typedef struct VkCommandBufferBeginInfo { + VkStructureType sType; + const void* pNext; + VkCommandBufferUsageFlags flags; + const VkCommandBufferInheritanceInfo* pInheritanceInfo; +} VkCommandBufferBeginInfo; + +typedef struct VkBufferCopy { + VkDeviceSize srcOffset; + VkDeviceSize dstOffset; + VkDeviceSize size; +} VkBufferCopy; + +typedef struct VkImageSubresourceLayers { + VkImageAspectFlags aspectMask; + uint32_t mipLevel; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkImageSubresourceLayers; + +typedef struct VkImageCopy { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageCopy; + +typedef struct VkImageBlit { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffsets[2]; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffsets[2]; +} VkImageBlit; + +typedef struct VkBufferImageCopy { + VkDeviceSize bufferOffset; + uint32_t bufferRowLength; + uint32_t bufferImageHeight; + VkImageSubresourceLayers imageSubresource; + VkOffset3D imageOffset; + VkExtent3D imageExtent; +} VkBufferImageCopy; + +typedef union VkClearColorValue { + float float32[4]; + int32_t int32[4]; + uint32_t uint32[4]; +} VkClearColorValue; + +typedef struct VkClearDepthStencilValue { + float depth; + uint32_t stencil; +} VkClearDepthStencilValue; + +typedef union VkClearValue { + VkClearColorValue color; + VkClearDepthStencilValue depthStencil; +} VkClearValue; + +typedef struct VkClearAttachment { + VkImageAspectFlags aspectMask; + uint32_t colorAttachment; + VkClearValue clearValue; +} VkClearAttachment; + +typedef struct VkClearRect { + VkRect2D rect; + uint32_t baseArrayLayer; + uint32_t layerCount; +} VkClearRect; + +typedef struct VkImageResolve { + VkImageSubresourceLayers srcSubresource; + VkOffset3D srcOffset; + VkImageSubresourceLayers dstSubresource; + VkOffset3D dstOffset; + VkExtent3D extent; +} VkImageResolve; + +typedef struct VkMemoryBarrier { + VkStructureType sType; + const void* pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; +} VkMemoryBarrier; + +typedef struct VkBufferMemoryBarrier { + VkStructureType sType; + const void* pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkBuffer buffer; + VkDeviceSize offset; + VkDeviceSize size; +} VkBufferMemoryBarrier; + +typedef struct VkImageMemoryBarrier { + VkStructureType sType; + const void* pNext; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkImageLayout oldLayout; + VkImageLayout newLayout; + uint32_t srcQueueFamilyIndex; + uint32_t dstQueueFamilyIndex; + VkImage image; + VkImageSubresourceRange subresourceRange; +} VkImageMemoryBarrier; + +typedef struct VkRenderPassBeginInfo { + VkStructureType sType; + const void* pNext; + VkRenderPass renderPass; + VkFramebuffer framebuffer; + VkRect2D renderArea; + uint32_t clearValueCount; + const VkClearValue* pClearValues; +} VkRenderPassBeginInfo; + +typedef struct VkDispatchIndirectCommand { + uint32_t x; + uint32_t y; + uint32_t z; +} VkDispatchIndirectCommand; + +typedef struct VkDrawIndexedIndirectCommand { + uint32_t indexCount; + uint32_t instanceCount; + uint32_t firstIndex; + int32_t vertexOffset; + uint32_t firstInstance; +} VkDrawIndexedIndirectCommand; + +typedef struct VkDrawIndirectCommand { + uint32_t vertexCount; + uint32_t instanceCount; + uint32_t firstVertex; + uint32_t firstInstance; +} VkDrawIndirectCommand; + +typedef struct VkBaseOutStructure { + VkStructureType sType; + struct VkBaseOutStructure* pNext; +} VkBaseOutStructure; + +typedef struct VkBaseInStructure { + VkStructureType sType; + const struct VkBaseInStructure* pNext; +} VkBaseInStructure; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateInstance)(const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance); +typedef void (VKAPI_PTR *PFN_vkDestroyInstance)(VkInstance instance, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDevices)(VkInstance instance, uint32_t* pPhysicalDeviceCount, VkPhysicalDevice* pPhysicalDevices); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures* pFeatures); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties* pFormatProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkImageFormatProperties* pImageFormatProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties); +typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetInstanceProcAddr)(VkInstance instance, const char* pName); +typedef PFN_vkVoidFunction (VKAPI_PTR *PFN_vkGetDeviceProcAddr)(VkDevice device, const char* pName); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDevice)(VkPhysicalDevice physicalDevice, const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice); +typedef void (VKAPI_PTR *PFN_vkDestroyDevice)(VkDevice device, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceExtensionProperties)(const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateDeviceExtensionProperties)(VkPhysicalDevice physicalDevice, const char* pLayerName, uint32_t* pPropertyCount, VkExtensionProperties* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceLayerProperties)(uint32_t* pPropertyCount, VkLayerProperties* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateDeviceLayerProperties)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkLayerProperties* pProperties); +typedef void (VKAPI_PTR *PFN_vkGetDeviceQueue)(VkDevice device, uint32_t queueFamilyIndex, uint32_t queueIndex, VkQueue* pQueue); +typedef VkResult (VKAPI_PTR *PFN_vkQueueSubmit)(VkQueue queue, uint32_t submitCount, const VkSubmitInfo* pSubmits, VkFence fence); +typedef VkResult (VKAPI_PTR *PFN_vkQueueWaitIdle)(VkQueue queue); +typedef VkResult (VKAPI_PTR *PFN_vkDeviceWaitIdle)(VkDevice device); +typedef VkResult (VKAPI_PTR *PFN_vkAllocateMemory)(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory); +typedef void (VKAPI_PTR *PFN_vkFreeMemory)(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkMapMemory)(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData); +typedef void (VKAPI_PTR *PFN_vkUnmapMemory)(VkDevice device, VkDeviceMemory memory); +typedef VkResult (VKAPI_PTR *PFN_vkFlushMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges); +typedef VkResult (VKAPI_PTR *PFN_vkInvalidateMappedMemoryRanges)(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges); +typedef void (VKAPI_PTR *PFN_vkGetDeviceMemoryCommitment)(VkDevice device, VkDeviceMemory memory, VkDeviceSize* pCommittedMemoryInBytes); +typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory)(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset); +typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory)(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset); +typedef void (VKAPI_PTR *PFN_vkGetBufferMemoryRequirements)(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements)(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetImageSparseMemoryRequirements)(VkDevice device, VkImage image, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements* pSparseMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkSampleCountFlagBits samples, VkImageUsageFlags usage, VkImageTiling tiling, uint32_t* pPropertyCount, VkSparseImageFormatProperties* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkQueueBindSparse)(VkQueue queue, uint32_t bindInfoCount, const VkBindSparseInfo* pBindInfo, VkFence fence); +typedef VkResult (VKAPI_PTR *PFN_vkCreateFence)(VkDevice device, const VkFenceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); +typedef void (VKAPI_PTR *PFN_vkDestroyFence)(VkDevice device, VkFence fence, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkResetFences)(VkDevice device, uint32_t fenceCount, const VkFence* pFences); +typedef VkResult (VKAPI_PTR *PFN_vkGetFenceStatus)(VkDevice device, VkFence fence); +typedef VkResult (VKAPI_PTR *PFN_vkWaitForFences)(VkDevice device, uint32_t fenceCount, const VkFence* pFences, VkBool32 waitAll, uint64_t timeout); +typedef VkResult (VKAPI_PTR *PFN_vkCreateSemaphore)(VkDevice device, const VkSemaphoreCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSemaphore* pSemaphore); +typedef void (VKAPI_PTR *PFN_vkDestroySemaphore)(VkDevice device, VkSemaphore semaphore, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateEvent)(VkDevice device, const VkEventCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkEvent* pEvent); +typedef void (VKAPI_PTR *PFN_vkDestroyEvent)(VkDevice device, VkEvent event, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetEventStatus)(VkDevice device, VkEvent event); +typedef VkResult (VKAPI_PTR *PFN_vkSetEvent)(VkDevice device, VkEvent event); +typedef VkResult (VKAPI_PTR *PFN_vkResetEvent)(VkDevice device, VkEvent event); +typedef VkResult (VKAPI_PTR *PFN_vkCreateQueryPool)(VkDevice device, const VkQueryPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkQueryPool* pQueryPool); +typedef void (VKAPI_PTR *PFN_vkDestroyQueryPool)(VkDevice device, VkQueryPool queryPool, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetQueryPoolResults)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, size_t dataSize, void* pData, VkDeviceSize stride, VkQueryResultFlags flags); +typedef VkResult (VKAPI_PTR *PFN_vkCreateBuffer)(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer); +typedef void (VKAPI_PTR *PFN_vkDestroyBuffer)(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateBufferView)(VkDevice device, const VkBufferViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBufferView* pView); +typedef void (VKAPI_PTR *PFN_vkDestroyBufferView)(VkDevice device, VkBufferView bufferView, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateImage)(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage); +typedef void (VKAPI_PTR *PFN_vkDestroyImage)(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkGetImageSubresourceLayout)(VkDevice device, VkImage image, const VkImageSubresource* pSubresource, VkSubresourceLayout* pLayout); +typedef VkResult (VKAPI_PTR *PFN_vkCreateImageView)(VkDevice device, const VkImageViewCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImageView* pView); +typedef void (VKAPI_PTR *PFN_vkDestroyImageView)(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateShaderModule)(VkDevice device, const VkShaderModuleCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkShaderModule* pShaderModule); +typedef void (VKAPI_PTR *PFN_vkDestroyShaderModule)(VkDevice device, VkShaderModule shaderModule, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreatePipelineCache)(VkDevice device, const VkPipelineCacheCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineCache* pPipelineCache); +typedef void (VKAPI_PTR *PFN_vkDestroyPipelineCache)(VkDevice device, VkPipelineCache pipelineCache, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineCacheData)(VkDevice device, VkPipelineCache pipelineCache, size_t* pDataSize, void* pData); +typedef VkResult (VKAPI_PTR *PFN_vkMergePipelineCaches)(VkDevice device, VkPipelineCache dstCache, uint32_t srcCacheCount, const VkPipelineCache* pSrcCaches); +typedef VkResult (VKAPI_PTR *PFN_vkCreateGraphicsPipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkGraphicsPipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); +typedef VkResult (VKAPI_PTR *PFN_vkCreateComputePipelines)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkComputePipelineCreateInfo* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); +typedef void (VKAPI_PTR *PFN_vkDestroyPipeline)(VkDevice device, VkPipeline pipeline, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreatePipelineLayout)(VkDevice device, const VkPipelineLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkPipelineLayout* pPipelineLayout); +typedef void (VKAPI_PTR *PFN_vkDestroyPipelineLayout)(VkDevice device, VkPipelineLayout pipelineLayout, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateSampler)(VkDevice device, const VkSamplerCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSampler* pSampler); +typedef void (VKAPI_PTR *PFN_vkDestroySampler)(VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorSetLayout)(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorSetLayout* pSetLayout); +typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorSetLayout)(VkDevice device, VkDescriptorSetLayout descriptorSetLayout, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorPool)(VkDevice device, const VkDescriptorPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorPool* pDescriptorPool); +typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkResetDescriptorPool)(VkDevice device, VkDescriptorPool descriptorPool, VkDescriptorPoolResetFlags flags); +typedef VkResult (VKAPI_PTR *PFN_vkAllocateDescriptorSets)(VkDevice device, const VkDescriptorSetAllocateInfo* pAllocateInfo, VkDescriptorSet* pDescriptorSets); +typedef VkResult (VKAPI_PTR *PFN_vkFreeDescriptorSets)(VkDevice device, VkDescriptorPool descriptorPool, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets); +typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSets)(VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet* pDescriptorCopies); +typedef VkResult (VKAPI_PTR *PFN_vkCreateFramebuffer)(VkDevice device, const VkFramebufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkFramebuffer* pFramebuffer); +typedef void (VKAPI_PTR *PFN_vkDestroyFramebuffer)(VkDevice device, VkFramebuffer framebuffer, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateRenderPass)(VkDevice device, const VkRenderPassCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); +typedef void (VKAPI_PTR *PFN_vkDestroyRenderPass)(VkDevice device, VkRenderPass renderPass, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkGetRenderAreaGranularity)(VkDevice device, VkRenderPass renderPass, VkExtent2D* pGranularity); +typedef VkResult (VKAPI_PTR *PFN_vkCreateCommandPool)(VkDevice device, const VkCommandPoolCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkCommandPool* pCommandPool); +typedef void (VKAPI_PTR *PFN_vkDestroyCommandPool)(VkDevice device, VkCommandPool commandPool, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkResetCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolResetFlags flags); +typedef VkResult (VKAPI_PTR *PFN_vkAllocateCommandBuffers)(VkDevice device, const VkCommandBufferAllocateInfo* pAllocateInfo, VkCommandBuffer* pCommandBuffers); +typedef void (VKAPI_PTR *PFN_vkFreeCommandBuffers)(VkDevice device, VkCommandPool commandPool, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); +typedef VkResult (VKAPI_PTR *PFN_vkBeginCommandBuffer)(VkCommandBuffer commandBuffer, const VkCommandBufferBeginInfo* pBeginInfo); +typedef VkResult (VKAPI_PTR *PFN_vkEndCommandBuffer)(VkCommandBuffer commandBuffer); +typedef VkResult (VKAPI_PTR *PFN_vkResetCommandBuffer)(VkCommandBuffer commandBuffer, VkCommandBufferResetFlags flags); +typedef void (VKAPI_PTR *PFN_vkCmdBindPipeline)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipeline pipeline); +typedef void (VKAPI_PTR *PFN_vkCmdSetViewport)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewport* pViewports); +typedef void (VKAPI_PTR *PFN_vkCmdSetScissor)(VkCommandBuffer commandBuffer, uint32_t firstScissor, uint32_t scissorCount, const VkRect2D* pScissors); +typedef void (VKAPI_PTR *PFN_vkCmdSetLineWidth)(VkCommandBuffer commandBuffer, float lineWidth); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBias)(VkCommandBuffer commandBuffer, float depthBiasConstantFactor, float depthBiasClamp, float depthBiasSlopeFactor); +typedef void (VKAPI_PTR *PFN_vkCmdSetBlendConstants)(VkCommandBuffer commandBuffer, const float blendConstants[4]); +typedef void (VKAPI_PTR *PFN_vkCmdSetDepthBounds)(VkCommandBuffer commandBuffer, float minDepthBounds, float maxDepthBounds); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilCompareMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t compareMask); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilWriteMask)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t writeMask); +typedef void (VKAPI_PTR *PFN_vkCmdSetStencilReference)(VkCommandBuffer commandBuffer, VkStencilFaceFlags faceMask, uint32_t reference); +typedef void (VKAPI_PTR *PFN_vkCmdBindDescriptorSets)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t firstSet, uint32_t descriptorSetCount, const VkDescriptorSet* pDescriptorSets, uint32_t dynamicOffsetCount, const uint32_t* pDynamicOffsets); +typedef void (VKAPI_PTR *PFN_vkCmdBindIndexBuffer)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkIndexType indexType); +typedef void (VKAPI_PTR *PFN_vkCmdBindVertexBuffers)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets); +typedef void (VKAPI_PTR *PFN_vkCmdDraw)(VkCommandBuffer commandBuffer, uint32_t vertexCount, uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexed)(VkCommandBuffer commandBuffer, uint32_t indexCount, uint32_t instanceCount, uint32_t firstIndex, int32_t vertexOffset, uint32_t firstInstance); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDispatch)(VkCommandBuffer commandBuffer, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); +typedef void (VKAPI_PTR *PFN_vkCmdDispatchIndirect)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBuffer)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferCopy* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageCopy* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdBlitImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter); +typedef void (VKAPI_PTR *PFN_vkCmdCopyBufferToImage)(VkCommandBuffer commandBuffer, VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkBufferImageCopy* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdCopyImageToBuffer)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkBuffer dstBuffer, uint32_t regionCount, const VkBufferImageCopy* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdUpdateBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize dataSize, const void* pData); +typedef void (VKAPI_PTR *PFN_vkCmdFillBuffer)(VkCommandBuffer commandBuffer, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize size, uint32_t data); +typedef void (VKAPI_PTR *PFN_vkCmdClearColorImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearColorValue* pColor, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); +typedef void (VKAPI_PTR *PFN_vkCmdClearDepthStencilImage)(VkCommandBuffer commandBuffer, VkImage image, VkImageLayout imageLayout, const VkClearDepthStencilValue* pDepthStencil, uint32_t rangeCount, const VkImageSubresourceRange* pRanges); +typedef void (VKAPI_PTR *PFN_vkCmdClearAttachments)(VkCommandBuffer commandBuffer, uint32_t attachmentCount, const VkClearAttachment* pAttachments, uint32_t rectCount, const VkClearRect* pRects); +typedef void (VKAPI_PTR *PFN_vkCmdResolveImage)(VkCommandBuffer commandBuffer, VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout, uint32_t regionCount, const VkImageResolve* pRegions); +typedef void (VKAPI_PTR *PFN_vkCmdSetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); +typedef void (VKAPI_PTR *PFN_vkCmdResetEvent)(VkCommandBuffer commandBuffer, VkEvent event, VkPipelineStageFlags stageMask); +typedef void (VKAPI_PTR *PFN_vkCmdWaitEvents)(VkCommandBuffer commandBuffer, uint32_t eventCount, const VkEvent* pEvents, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); +typedef void (VKAPI_PTR *PFN_vkCmdPipelineBarrier)(VkCommandBuffer commandBuffer, VkPipelineStageFlags srcStageMask, VkPipelineStageFlags dstStageMask, VkDependencyFlags dependencyFlags, uint32_t memoryBarrierCount, const VkMemoryBarrier* pMemoryBarriers, uint32_t bufferMemoryBarrierCount, const VkBufferMemoryBarrier* pBufferMemoryBarriers, uint32_t imageMemoryBarrierCount, const VkImageMemoryBarrier* pImageMemoryBarriers); +typedef void (VKAPI_PTR *PFN_vkCmdBeginQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags); +typedef void (VKAPI_PTR *PFN_vkCmdEndQuery)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query); +typedef void (VKAPI_PTR *PFN_vkCmdResetQueryPool)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); +typedef void (VKAPI_PTR *PFN_vkCmdWriteTimestamp)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkQueryPool queryPool, uint32_t query); +typedef void (VKAPI_PTR *PFN_vkCmdCopyQueryPoolResults)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount, VkBuffer dstBuffer, VkDeviceSize dstOffset, VkDeviceSize stride, VkQueryResultFlags flags); +typedef void (VKAPI_PTR *PFN_vkCmdPushConstants)(VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues); +typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderPass)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, VkSubpassContents contents); +typedef void (VKAPI_PTR *PFN_vkCmdNextSubpass)(VkCommandBuffer commandBuffer, VkSubpassContents contents); +typedef void (VKAPI_PTR *PFN_vkCmdEndRenderPass)(VkCommandBuffer commandBuffer); +typedef void (VKAPI_PTR *PFN_vkCmdExecuteCommands)(VkCommandBuffer commandBuffer, uint32_t commandBufferCount, const VkCommandBuffer* pCommandBuffers); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance( + const VkInstanceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkInstance* pInstance); + +VKAPI_ATTR void VKAPI_CALL vkDestroyInstance( + VkInstance instance, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDevices( + VkInstance instance, + uint32_t* pPhysicalDeviceCount, + VkPhysicalDevice* pPhysicalDevices); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures* pFeatures); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties* pFormatProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags, + VkImageFormatProperties* pImageFormatProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties* pProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties( + VkPhysicalDevice physicalDevice, + uint32_t* pQueueFamilyPropertyCount, + VkQueueFamilyProperties* pQueueFamilyProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties* pMemoryProperties); + +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr( + VkInstance instance, + const char* pName); + +VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetDeviceProcAddr( + VkDevice device, + const char* pName); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDevice( + VkPhysicalDevice physicalDevice, + const VkDeviceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDevice* pDevice); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDevice( + VkDevice device, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceExtensionProperties( + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceExtensionProperties( + VkPhysicalDevice physicalDevice, + const char* pLayerName, + uint32_t* pPropertyCount, + VkExtensionProperties* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceLayerProperties( + uint32_t* pPropertyCount, + VkLayerProperties* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateDeviceLayerProperties( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkLayerProperties* pProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue( + VkDevice device, + uint32_t queueFamilyIndex, + uint32_t queueIndex, + VkQueue* pQueue); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueSubmit( + VkQueue queue, + uint32_t submitCount, + const VkSubmitInfo* pSubmits, + VkFence fence); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueWaitIdle( + VkQueue queue); + +VKAPI_ATTR VkResult VKAPI_CALL vkDeviceWaitIdle( + VkDevice device); + +VKAPI_ATTR VkResult VKAPI_CALL vkAllocateMemory( + VkDevice device, + const VkMemoryAllocateInfo* pAllocateInfo, + const VkAllocationCallbacks* pAllocator, + VkDeviceMemory* pMemory); + +VKAPI_ATTR void VKAPI_CALL vkFreeMemory( + VkDevice device, + VkDeviceMemory memory, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkMapMemory( + VkDevice device, + VkDeviceMemory memory, + VkDeviceSize offset, + VkDeviceSize size, + VkMemoryMapFlags flags, + void** ppData); + +VKAPI_ATTR void VKAPI_CALL vkUnmapMemory( + VkDevice device, + VkDeviceMemory memory); + +VKAPI_ATTR VkResult VKAPI_CALL vkFlushMappedMemoryRanges( + VkDevice device, + uint32_t memoryRangeCount, + const VkMappedMemoryRange* pMemoryRanges); + +VKAPI_ATTR VkResult VKAPI_CALL vkInvalidateMappedMemoryRanges( + VkDevice device, + uint32_t memoryRangeCount, + const VkMappedMemoryRange* pMemoryRanges); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceMemoryCommitment( + VkDevice device, + VkDeviceMemory memory, + VkDeviceSize* pCommittedMemoryInBytes); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory( + VkDevice device, + VkBuffer buffer, + VkDeviceMemory memory, + VkDeviceSize memoryOffset); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory( + VkDevice device, + VkImage image, + VkDeviceMemory memory, + VkDeviceSize memoryOffset); + +VKAPI_ATTR void VKAPI_CALL vkGetBufferMemoryRequirements( + VkDevice device, + VkBuffer buffer, + VkMemoryRequirements* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetImageMemoryRequirements( + VkDevice device, + VkImage image, + VkMemoryRequirements* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements( + VkDevice device, + VkImage image, + uint32_t* pSparseMemoryRequirementCount, + VkSparseImageMemoryRequirements* pSparseMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkSampleCountFlagBits samples, + VkImageUsageFlags usage, + VkImageTiling tiling, + uint32_t* pPropertyCount, + VkSparseImageFormatProperties* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueBindSparse( + VkQueue queue, + uint32_t bindInfoCount, + const VkBindSparseInfo* pBindInfo, + VkFence fence); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateFence( + VkDevice device, + const VkFenceCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence); + +VKAPI_ATTR void VKAPI_CALL vkDestroyFence( + VkDevice device, + VkFence fence, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceStatus( + VkDevice device, + VkFence fence); + +VKAPI_ATTR VkResult VKAPI_CALL vkWaitForFences( + VkDevice device, + uint32_t fenceCount, + const VkFence* pFences, + VkBool32 waitAll, + uint64_t timeout); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSemaphore( + VkDevice device, + const VkSemaphoreCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSemaphore* pSemaphore); + +VKAPI_ATTR void VKAPI_CALL vkDestroySemaphore( + VkDevice device, + VkSemaphore semaphore, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateEvent( + VkDevice device, + const VkEventCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkEvent* pEvent); + +VKAPI_ATTR void VKAPI_CALL vkDestroyEvent( + VkDevice device, + VkEvent event, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetEventStatus( + VkDevice device, + VkEvent event); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetEvent( + VkDevice device, + VkEvent event); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetEvent( + VkDevice device, + VkEvent event); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateQueryPool( + VkDevice device, + const VkQueryPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkQueryPool* pQueryPool); + +VKAPI_ATTR void VKAPI_CALL vkDestroyQueryPool( + VkDevice device, + VkQueryPool queryPool, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetQueryPoolResults( + VkDevice device, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + size_t dataSize, + void* pData, + VkDeviceSize stride, + VkQueryResultFlags flags); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateBuffer( + VkDevice device, + const VkBufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBuffer* pBuffer); + +VKAPI_ATTR void VKAPI_CALL vkDestroyBuffer( + VkDevice device, + VkBuffer buffer, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferView( + VkDevice device, + const VkBufferViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkBufferView* pView); + +VKAPI_ATTR void VKAPI_CALL vkDestroyBufferView( + VkDevice device, + VkBufferView bufferView, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateImage( + VkDevice device, + const VkImageCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImage* pImage); + +VKAPI_ATTR void VKAPI_CALL vkDestroyImage( + VkDevice device, + VkImage image, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout( + VkDevice device, + VkImage image, + const VkImageSubresource* pSubresource, + VkSubresourceLayout* pLayout); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateImageView( + VkDevice device, + const VkImageViewCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkImageView* pView); + +VKAPI_ATTR void VKAPI_CALL vkDestroyImageView( + VkDevice device, + VkImageView imageView, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateShaderModule( + VkDevice device, + const VkShaderModuleCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkShaderModule* pShaderModule); + +VKAPI_ATTR void VKAPI_CALL vkDestroyShaderModule( + VkDevice device, + VkShaderModule shaderModule, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineCache( + VkDevice device, + const VkPipelineCacheCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPipelineCache* pPipelineCache); + +VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineCache( + VkDevice device, + VkPipelineCache pipelineCache, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineCacheData( + VkDevice device, + VkPipelineCache pipelineCache, + size_t* pDataSize, + void* pData); + +VKAPI_ATTR VkResult VKAPI_CALL vkMergePipelineCaches( + VkDevice device, + VkPipelineCache dstCache, + uint32_t srcCacheCount, + const VkPipelineCache* pSrcCaches); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateGraphicsPipelines( + VkDevice device, + VkPipelineCache pipelineCache, + uint32_t createInfoCount, + const VkGraphicsPipelineCreateInfo* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateComputePipelines( + VkDevice device, + VkPipelineCache pipelineCache, + uint32_t createInfoCount, + const VkComputePipelineCreateInfo* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +VKAPI_ATTR void VKAPI_CALL vkDestroyPipeline( + VkDevice device, + VkPipeline pipeline, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreatePipelineLayout( + VkDevice device, + const VkPipelineLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkPipelineLayout* pPipelineLayout); + +VKAPI_ATTR void VKAPI_CALL vkDestroyPipelineLayout( + VkDevice device, + VkPipelineLayout pipelineLayout, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSampler( + VkDevice device, + const VkSamplerCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSampler* pSampler); + +VKAPI_ATTR void VKAPI_CALL vkDestroySampler( + VkDevice device, + VkSampler sampler, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorSetLayout( + VkDevice device, + const VkDescriptorSetLayoutCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDescriptorSetLayout* pSetLayout); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorSetLayout( + VkDevice device, + VkDescriptorSetLayout descriptorSetLayout, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorPool( + VkDevice device, + const VkDescriptorPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDescriptorPool* pDescriptorPool); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorPool( + VkDevice device, + VkDescriptorPool descriptorPool, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetDescriptorPool( + VkDevice device, + VkDescriptorPool descriptorPool, + VkDescriptorPoolResetFlags flags); + +VKAPI_ATTR VkResult VKAPI_CALL vkAllocateDescriptorSets( + VkDevice device, + const VkDescriptorSetAllocateInfo* pAllocateInfo, + VkDescriptorSet* pDescriptorSets); + +VKAPI_ATTR VkResult VKAPI_CALL vkFreeDescriptorSets( + VkDevice device, + VkDescriptorPool descriptorPool, + uint32_t descriptorSetCount, + const VkDescriptorSet* pDescriptorSets); + +VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSets( + VkDevice device, + uint32_t descriptorWriteCount, + const VkWriteDescriptorSet* pDescriptorWrites, + uint32_t descriptorCopyCount, + const VkCopyDescriptorSet* pDescriptorCopies); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateFramebuffer( + VkDevice device, + const VkFramebufferCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkFramebuffer* pFramebuffer); + +VKAPI_ATTR void VKAPI_CALL vkDestroyFramebuffer( + VkDevice device, + VkFramebuffer framebuffer, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass( + VkDevice device, + const VkRenderPassCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkRenderPass* pRenderPass); + +VKAPI_ATTR void VKAPI_CALL vkDestroyRenderPass( + VkDevice device, + VkRenderPass renderPass, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkGetRenderAreaGranularity( + VkDevice device, + VkRenderPass renderPass, + VkExtent2D* pGranularity); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateCommandPool( + VkDevice device, + const VkCommandPoolCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkCommandPool* pCommandPool); + +VKAPI_ATTR void VKAPI_CALL vkDestroyCommandPool( + VkDevice device, + VkCommandPool commandPool, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandPool( + VkDevice device, + VkCommandPool commandPool, + VkCommandPoolResetFlags flags); + +VKAPI_ATTR VkResult VKAPI_CALL vkAllocateCommandBuffers( + VkDevice device, + const VkCommandBufferAllocateInfo* pAllocateInfo, + VkCommandBuffer* pCommandBuffers); + +VKAPI_ATTR void VKAPI_CALL vkFreeCommandBuffers( + VkDevice device, + VkCommandPool commandPool, + uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers); + +VKAPI_ATTR VkResult VKAPI_CALL vkBeginCommandBuffer( + VkCommandBuffer commandBuffer, + const VkCommandBufferBeginInfo* pBeginInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkEndCommandBuffer( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR VkResult VKAPI_CALL vkResetCommandBuffer( + VkCommandBuffer commandBuffer, + VkCommandBufferResetFlags flags); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindPipeline( + VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipeline pipeline); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetViewport( + VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkViewport* pViewports); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetScissor( + VkCommandBuffer commandBuffer, + uint32_t firstScissor, + uint32_t scissorCount, + const VkRect2D* pScissors); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetLineWidth( + VkCommandBuffer commandBuffer, + float lineWidth); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBias( + VkCommandBuffer commandBuffer, + float depthBiasConstantFactor, + float depthBiasClamp, + float depthBiasSlopeFactor); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetBlendConstants( + VkCommandBuffer commandBuffer, + const float blendConstants[4]); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDepthBounds( + VkCommandBuffer commandBuffer, + float minDepthBounds, + float maxDepthBounds); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilCompareMask( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t compareMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilWriteMask( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t writeMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetStencilReference( + VkCommandBuffer commandBuffer, + VkStencilFaceFlags faceMask, + uint32_t reference); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindDescriptorSets( + VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipelineLayout layout, + uint32_t firstSet, + uint32_t descriptorSetCount, + const VkDescriptorSet* pDescriptorSets, + uint32_t dynamicOffsetCount, + const uint32_t* pDynamicOffsets); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindIndexBuffer( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkIndexType indexType); + +VKAPI_ATTR void VKAPI_CALL vkCmdBindVertexBuffers( + VkCommandBuffer commandBuffer, + uint32_t firstBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets); + +VKAPI_ATTR void VKAPI_CALL vkCmdDraw( + VkCommandBuffer commandBuffer, + uint32_t vertexCount, + uint32_t instanceCount, + uint32_t firstVertex, + uint32_t firstInstance); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexed( + VkCommandBuffer commandBuffer, + uint32_t indexCount, + uint32_t instanceCount, + uint32_t firstIndex, + int32_t vertexOffset, + uint32_t firstInstance); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t drawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t drawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDispatch( + VkCommandBuffer commandBuffer, + uint32_t groupCountX, + uint32_t groupCountY, + uint32_t groupCountZ); + +VKAPI_ATTR void VKAPI_CALL vkCmdDispatchIndirect( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyBuffer( + VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferCopy* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageCopy* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdBlitImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageBlit* pRegions, + VkFilter filter); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyBufferToImage( + VkCommandBuffer commandBuffer, + VkBuffer srcBuffer, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkBufferImageCopy* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyImageToBuffer( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferImageCopy* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdUpdateBuffer( + VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize dataSize, + const void* pData); + +VKAPI_ATTR void VKAPI_CALL vkCmdFillBuffer( + VkCommandBuffer commandBuffer, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize size, + uint32_t data); + +VKAPI_ATTR void VKAPI_CALL vkCmdClearColorImage( + VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearColorValue* pColor, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges); + +VKAPI_ATTR void VKAPI_CALL vkCmdClearDepthStencilImage( + VkCommandBuffer commandBuffer, + VkImage image, + VkImageLayout imageLayout, + const VkClearDepthStencilValue* pDepthStencil, + uint32_t rangeCount, + const VkImageSubresourceRange* pRanges); + +VKAPI_ATTR void VKAPI_CALL vkCmdClearAttachments( + VkCommandBuffer commandBuffer, + uint32_t attachmentCount, + const VkClearAttachment* pAttachments, + uint32_t rectCount, + const VkClearRect* pRects); + +VKAPI_ATTR void VKAPI_CALL vkCmdResolveImage( + VkCommandBuffer commandBuffer, + VkImage srcImage, + VkImageLayout srcImageLayout, + VkImage dstImage, + VkImageLayout dstImageLayout, + uint32_t regionCount, + const VkImageResolve* pRegions); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetEvent( + VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags stageMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdResetEvent( + VkCommandBuffer commandBuffer, + VkEvent event, + VkPipelineStageFlags stageMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdWaitEvents( + VkCommandBuffer commandBuffer, + uint32_t eventCount, + const VkEvent* pEvents, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers); + +VKAPI_ATTR void VKAPI_CALL vkCmdPipelineBarrier( + VkCommandBuffer commandBuffer, + VkPipelineStageFlags srcStageMask, + VkPipelineStageFlags dstStageMask, + VkDependencyFlags dependencyFlags, + uint32_t memoryBarrierCount, + const VkMemoryBarrier* pMemoryBarriers, + uint32_t bufferMemoryBarrierCount, + const VkBufferMemoryBarrier* pBufferMemoryBarriers, + uint32_t imageMemoryBarrierCount, + const VkImageMemoryBarrier* pImageMemoryBarriers); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginQuery( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + VkQueryControlFlags flags); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndQuery( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query); + +VKAPI_ATTR void VKAPI_CALL vkCmdResetQueryPool( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount); + +VKAPI_ATTR void VKAPI_CALL vkCmdWriteTimestamp( + VkCommandBuffer commandBuffer, + VkPipelineStageFlagBits pipelineStage, + VkQueryPool queryPool, + uint32_t query); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyQueryPoolResults( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + VkDeviceSize stride, + VkQueryResultFlags flags); + +VKAPI_ATTR void VKAPI_CALL vkCmdPushConstants( + VkCommandBuffer commandBuffer, + VkPipelineLayout layout, + VkShaderStageFlags stageFlags, + uint32_t offset, + uint32_t size, + const void* pValues); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass( + VkCommandBuffer commandBuffer, + const VkRenderPassBeginInfo* pRenderPassBegin, + VkSubpassContents contents); + +VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass( + VkCommandBuffer commandBuffer, + VkSubpassContents contents); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderPass( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR void VKAPI_CALL vkCmdExecuteCommands( + VkCommandBuffer commandBuffer, + uint32_t commandBufferCount, + const VkCommandBuffer* pCommandBuffers); +#endif + + +#define VK_VERSION_1_1 1 +// Vulkan 1.1 version number +#define VK_API_VERSION_1_1 VK_MAKE_VERSION(1, 1, 0)// Patch version should always be set to 0 + +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSamplerYcbcrConversion) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDescriptorUpdateTemplate) +#define VK_MAX_DEVICE_GROUP_SIZE 32 +#define VK_LUID_SIZE 8 +#define VK_QUEUE_FAMILY_EXTERNAL (~0U-1) + +typedef enum VkPointClippingBehavior { + VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES = 0, + VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY = 1, + VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES_KHR = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES, + VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY_KHR = VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY, + VK_POINT_CLIPPING_BEHAVIOR_BEGIN_RANGE = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES, + VK_POINT_CLIPPING_BEHAVIOR_END_RANGE = VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY, + VK_POINT_CLIPPING_BEHAVIOR_RANGE_SIZE = (VK_POINT_CLIPPING_BEHAVIOR_USER_CLIP_PLANES_ONLY - VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES + 1), + VK_POINT_CLIPPING_BEHAVIOR_MAX_ENUM = 0x7FFFFFFF +} VkPointClippingBehavior; + +typedef enum VkTessellationDomainOrigin { + VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT = 0, + VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT = 1, + VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT, + VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT_KHR = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT, + VK_TESSELLATION_DOMAIN_ORIGIN_BEGIN_RANGE = VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT, + VK_TESSELLATION_DOMAIN_ORIGIN_END_RANGE = VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT, + VK_TESSELLATION_DOMAIN_ORIGIN_RANGE_SIZE = (VK_TESSELLATION_DOMAIN_ORIGIN_LOWER_LEFT - VK_TESSELLATION_DOMAIN_ORIGIN_UPPER_LEFT + 1), + VK_TESSELLATION_DOMAIN_ORIGIN_MAX_ENUM = 0x7FFFFFFF +} VkTessellationDomainOrigin; + +typedef enum VkSamplerYcbcrModelConversion { + VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY = 0, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY = 1, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709 = 2, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601 = 3, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 = 4, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020_KHR = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_BEGIN_RANGE = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_END_RANGE = VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020, + VK_SAMPLER_YCBCR_MODEL_CONVERSION_RANGE_SIZE = (VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020 - VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY + 1), + VK_SAMPLER_YCBCR_MODEL_CONVERSION_MAX_ENUM = 0x7FFFFFFF +} VkSamplerYcbcrModelConversion; + +typedef enum VkSamplerYcbcrRange { + VK_SAMPLER_YCBCR_RANGE_ITU_FULL = 0, + VK_SAMPLER_YCBCR_RANGE_ITU_NARROW = 1, + VK_SAMPLER_YCBCR_RANGE_ITU_FULL_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_FULL, + VK_SAMPLER_YCBCR_RANGE_ITU_NARROW_KHR = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW, + VK_SAMPLER_YCBCR_RANGE_BEGIN_RANGE = VK_SAMPLER_YCBCR_RANGE_ITU_FULL, + VK_SAMPLER_YCBCR_RANGE_END_RANGE = VK_SAMPLER_YCBCR_RANGE_ITU_NARROW, + VK_SAMPLER_YCBCR_RANGE_RANGE_SIZE = (VK_SAMPLER_YCBCR_RANGE_ITU_NARROW - VK_SAMPLER_YCBCR_RANGE_ITU_FULL + 1), + VK_SAMPLER_YCBCR_RANGE_MAX_ENUM = 0x7FFFFFFF +} VkSamplerYcbcrRange; + +typedef enum VkChromaLocation { + VK_CHROMA_LOCATION_COSITED_EVEN = 0, + VK_CHROMA_LOCATION_MIDPOINT = 1, + VK_CHROMA_LOCATION_COSITED_EVEN_KHR = VK_CHROMA_LOCATION_COSITED_EVEN, + VK_CHROMA_LOCATION_MIDPOINT_KHR = VK_CHROMA_LOCATION_MIDPOINT, + VK_CHROMA_LOCATION_BEGIN_RANGE = VK_CHROMA_LOCATION_COSITED_EVEN, + VK_CHROMA_LOCATION_END_RANGE = VK_CHROMA_LOCATION_MIDPOINT, + VK_CHROMA_LOCATION_RANGE_SIZE = (VK_CHROMA_LOCATION_MIDPOINT - VK_CHROMA_LOCATION_COSITED_EVEN + 1), + VK_CHROMA_LOCATION_MAX_ENUM = 0x7FFFFFFF +} VkChromaLocation; + +typedef enum VkDescriptorUpdateTemplateType { + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET = 0, + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_PUSH_DESCRIPTORS_KHR = 1, + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_BEGIN_RANGE = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_END_RANGE = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET, + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_RANGE_SIZE = (VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET - VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET + 1), + VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkDescriptorUpdateTemplateType; + +typedef enum VkSubgroupFeatureFlagBits { + VK_SUBGROUP_FEATURE_BASIC_BIT = 0x00000001, + VK_SUBGROUP_FEATURE_VOTE_BIT = 0x00000002, + VK_SUBGROUP_FEATURE_ARITHMETIC_BIT = 0x00000004, + VK_SUBGROUP_FEATURE_BALLOT_BIT = 0x00000008, + VK_SUBGROUP_FEATURE_SHUFFLE_BIT = 0x00000010, + VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT = 0x00000020, + VK_SUBGROUP_FEATURE_CLUSTERED_BIT = 0x00000040, + VK_SUBGROUP_FEATURE_QUAD_BIT = 0x00000080, + VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV = 0x00000100, + VK_SUBGROUP_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSubgroupFeatureFlagBits; +typedef VkFlags VkSubgroupFeatureFlags; + +typedef enum VkPeerMemoryFeatureFlagBits { + VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT = 0x00000001, + VK_PEER_MEMORY_FEATURE_COPY_DST_BIT = 0x00000002, + VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT = 0x00000004, + VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT = 0x00000008, + VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT_KHR = VK_PEER_MEMORY_FEATURE_COPY_SRC_BIT, + VK_PEER_MEMORY_FEATURE_COPY_DST_BIT_KHR = VK_PEER_MEMORY_FEATURE_COPY_DST_BIT, + VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT_KHR = VK_PEER_MEMORY_FEATURE_GENERIC_SRC_BIT, + VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT_KHR = VK_PEER_MEMORY_FEATURE_GENERIC_DST_BIT, + VK_PEER_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkPeerMemoryFeatureFlagBits; +typedef VkFlags VkPeerMemoryFeatureFlags; + +typedef enum VkMemoryAllocateFlagBits { + VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT = 0x00000001, + VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT_KHR = VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT, + VK_MEMORY_ALLOCATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkMemoryAllocateFlagBits; +typedef VkFlags VkMemoryAllocateFlags; +typedef VkFlags VkCommandPoolTrimFlags; +typedef VkFlags VkDescriptorUpdateTemplateCreateFlags; + +typedef enum VkExternalMemoryHandleTypeFlagBits { + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT = 0x00000001, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT = 0x00000002, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 0x00000004, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT = 0x00000008, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT = 0x00000010, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT = 0x00000020, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT = 0x00000040, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT = 0x00000200, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID = 0x00000400, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT = 0x00000080, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_MAPPED_FOREIGN_MEMORY_BIT_EXT = 0x00000100, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_BIT, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_TEXTURE_KMT_BIT, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_HEAP_BIT, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D12_RESOURCE_BIT, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkExternalMemoryHandleTypeFlagBits; +typedef VkFlags VkExternalMemoryHandleTypeFlags; + +typedef enum VkExternalMemoryFeatureFlagBits { + VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT = 0x00000001, + VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT = 0x00000002, + VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT = 0x00000004, + VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_KHR = VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT, + VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_KHR = VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT, + VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_KHR = VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT, + VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkExternalMemoryFeatureFlagBits; +typedef VkFlags VkExternalMemoryFeatureFlags; + +typedef enum VkExternalFenceHandleTypeFlagBits { + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT = 0x00000001, + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT = 0x00000002, + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 0x00000004, + VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT = 0x00000008, + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_FD_BIT, + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT, + VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR = VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, + VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT_KHR = VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT, + VK_EXTERNAL_FENCE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkExternalFenceHandleTypeFlagBits; +typedef VkFlags VkExternalFenceHandleTypeFlags; + +typedef enum VkExternalFenceFeatureFlagBits { + VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT = 0x00000001, + VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT = 0x00000002, + VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT_KHR = VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT, + VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT_KHR = VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT, + VK_EXTERNAL_FENCE_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkExternalFenceFeatureFlagBits; +typedef VkFlags VkExternalFenceFeatureFlags; + +typedef enum VkFenceImportFlagBits { + VK_FENCE_IMPORT_TEMPORARY_BIT = 0x00000001, + VK_FENCE_IMPORT_TEMPORARY_BIT_KHR = VK_FENCE_IMPORT_TEMPORARY_BIT, + VK_FENCE_IMPORT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkFenceImportFlagBits; +typedef VkFlags VkFenceImportFlags; + +typedef enum VkSemaphoreImportFlagBits { + VK_SEMAPHORE_IMPORT_TEMPORARY_BIT = 0x00000001, + VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR = VK_SEMAPHORE_IMPORT_TEMPORARY_BIT, + VK_SEMAPHORE_IMPORT_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkSemaphoreImportFlagBits; +typedef VkFlags VkSemaphoreImportFlags; + +typedef enum VkExternalSemaphoreHandleTypeFlagBits { + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT = 0x00000001, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT = 0x00000002, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT = 0x00000004, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT = 0x00000008, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT = 0x00000010, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_D3D12_FENCE_BIT, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT_KHR = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, + VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkExternalSemaphoreHandleTypeFlagBits; +typedef VkFlags VkExternalSemaphoreHandleTypeFlags; + +typedef enum VkExternalSemaphoreFeatureFlagBits { + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT = 0x00000001, + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT = 0x00000002, + VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR = VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT, + VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR = VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT, + VK_EXTERNAL_SEMAPHORE_FEATURE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VkExternalSemaphoreFeatureFlagBits; +typedef VkFlags VkExternalSemaphoreFeatureFlags; +typedef struct VkPhysicalDeviceSubgroupProperties { + VkStructureType sType; + void* pNext; + uint32_t subgroupSize; + VkShaderStageFlags supportedStages; + VkSubgroupFeatureFlags supportedOperations; + VkBool32 quadOperationsInAllStages; +} VkPhysicalDeviceSubgroupProperties; + +typedef struct VkBindBufferMemoryInfo { + VkStructureType sType; + const void* pNext; + VkBuffer buffer; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; +} VkBindBufferMemoryInfo; + +typedef struct VkBindImageMemoryInfo { + VkStructureType sType; + const void* pNext; + VkImage image; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; +} VkBindImageMemoryInfo; + +typedef struct VkPhysicalDevice16BitStorageFeatures { + VkStructureType sType; + void* pNext; + VkBool32 storageBuffer16BitAccess; + VkBool32 uniformAndStorageBuffer16BitAccess; + VkBool32 storagePushConstant16; + VkBool32 storageInputOutput16; +} VkPhysicalDevice16BitStorageFeatures; + +typedef struct VkMemoryDedicatedRequirements { + VkStructureType sType; + void* pNext; + VkBool32 prefersDedicatedAllocation; + VkBool32 requiresDedicatedAllocation; +} VkMemoryDedicatedRequirements; + +typedef struct VkMemoryDedicatedAllocateInfo { + VkStructureType sType; + const void* pNext; + VkImage image; + VkBuffer buffer; +} VkMemoryDedicatedAllocateInfo; + +typedef struct VkMemoryAllocateFlagsInfo { + VkStructureType sType; + const void* pNext; + VkMemoryAllocateFlags flags; + uint32_t deviceMask; +} VkMemoryAllocateFlagsInfo; + +typedef struct VkDeviceGroupRenderPassBeginInfo { + VkStructureType sType; + const void* pNext; + uint32_t deviceMask; + uint32_t deviceRenderAreaCount; + const VkRect2D* pDeviceRenderAreas; +} VkDeviceGroupRenderPassBeginInfo; + +typedef struct VkDeviceGroupCommandBufferBeginInfo { + VkStructureType sType; + const void* pNext; + uint32_t deviceMask; +} VkDeviceGroupCommandBufferBeginInfo; + +typedef struct VkDeviceGroupSubmitInfo { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreCount; + const uint32_t* pWaitSemaphoreDeviceIndices; + uint32_t commandBufferCount; + const uint32_t* pCommandBufferDeviceMasks; + uint32_t signalSemaphoreCount; + const uint32_t* pSignalSemaphoreDeviceIndices; +} VkDeviceGroupSubmitInfo; + +typedef struct VkDeviceGroupBindSparseInfo { + VkStructureType sType; + const void* pNext; + uint32_t resourceDeviceIndex; + uint32_t memoryDeviceIndex; +} VkDeviceGroupBindSparseInfo; + +typedef struct VkBindBufferMemoryDeviceGroupInfo { + VkStructureType sType; + const void* pNext; + uint32_t deviceIndexCount; + const uint32_t* pDeviceIndices; +} VkBindBufferMemoryDeviceGroupInfo; + +typedef struct VkBindImageMemoryDeviceGroupInfo { + VkStructureType sType; + const void* pNext; + uint32_t deviceIndexCount; + const uint32_t* pDeviceIndices; + uint32_t splitInstanceBindRegionCount; + const VkRect2D* pSplitInstanceBindRegions; +} VkBindImageMemoryDeviceGroupInfo; + +typedef struct VkPhysicalDeviceGroupProperties { + VkStructureType sType; + void* pNext; + uint32_t physicalDeviceCount; + VkPhysicalDevice physicalDevices[VK_MAX_DEVICE_GROUP_SIZE]; + VkBool32 subsetAllocation; +} VkPhysicalDeviceGroupProperties; + +typedef struct VkDeviceGroupDeviceCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t physicalDeviceCount; + const VkPhysicalDevice* pPhysicalDevices; +} VkDeviceGroupDeviceCreateInfo; + +typedef struct VkBufferMemoryRequirementsInfo2 { + VkStructureType sType; + const void* pNext; + VkBuffer buffer; +} VkBufferMemoryRequirementsInfo2; + +typedef struct VkImageMemoryRequirementsInfo2 { + VkStructureType sType; + const void* pNext; + VkImage image; +} VkImageMemoryRequirementsInfo2; + +typedef struct VkImageSparseMemoryRequirementsInfo2 { + VkStructureType sType; + const void* pNext; + VkImage image; +} VkImageSparseMemoryRequirementsInfo2; + +typedef struct VkMemoryRequirements2 { + VkStructureType sType; + void* pNext; + VkMemoryRequirements memoryRequirements; +} VkMemoryRequirements2; + +typedef VkMemoryRequirements2 VkMemoryRequirements2KHR; + +typedef struct VkSparseImageMemoryRequirements2 { + VkStructureType sType; + void* pNext; + VkSparseImageMemoryRequirements memoryRequirements; +} VkSparseImageMemoryRequirements2; + +typedef struct VkPhysicalDeviceFeatures2 { + VkStructureType sType; + void* pNext; + VkPhysicalDeviceFeatures features; +} VkPhysicalDeviceFeatures2; + +typedef struct VkPhysicalDeviceProperties2 { + VkStructureType sType; + void* pNext; + VkPhysicalDeviceProperties properties; +} VkPhysicalDeviceProperties2; + +typedef struct VkFormatProperties2 { + VkStructureType sType; + void* pNext; + VkFormatProperties formatProperties; +} VkFormatProperties2; + +typedef struct VkImageFormatProperties2 { + VkStructureType sType; + void* pNext; + VkImageFormatProperties imageFormatProperties; +} VkImageFormatProperties2; + +typedef struct VkPhysicalDeviceImageFormatInfo2 { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkImageType type; + VkImageTiling tiling; + VkImageUsageFlags usage; + VkImageCreateFlags flags; +} VkPhysicalDeviceImageFormatInfo2; + +typedef struct VkQueueFamilyProperties2 { + VkStructureType sType; + void* pNext; + VkQueueFamilyProperties queueFamilyProperties; +} VkQueueFamilyProperties2; + +typedef struct VkPhysicalDeviceMemoryProperties2 { + VkStructureType sType; + void* pNext; + VkPhysicalDeviceMemoryProperties memoryProperties; +} VkPhysicalDeviceMemoryProperties2; + +typedef struct VkSparseImageFormatProperties2 { + VkStructureType sType; + void* pNext; + VkSparseImageFormatProperties properties; +} VkSparseImageFormatProperties2; + +typedef struct VkPhysicalDeviceSparseImageFormatInfo2 { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkImageType type; + VkSampleCountFlagBits samples; + VkImageUsageFlags usage; + VkImageTiling tiling; +} VkPhysicalDeviceSparseImageFormatInfo2; + +typedef struct VkPhysicalDevicePointClippingProperties { + VkStructureType sType; + void* pNext; + VkPointClippingBehavior pointClippingBehavior; +} VkPhysicalDevicePointClippingProperties; + +typedef struct VkInputAttachmentAspectReference { + uint32_t subpass; + uint32_t inputAttachmentIndex; + VkImageAspectFlags aspectMask; +} VkInputAttachmentAspectReference; + +typedef struct VkRenderPassInputAttachmentAspectCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t aspectReferenceCount; + const VkInputAttachmentAspectReference* pAspectReferences; +} VkRenderPassInputAttachmentAspectCreateInfo; + +typedef struct VkImageViewUsageCreateInfo { + VkStructureType sType; + const void* pNext; + VkImageUsageFlags usage; +} VkImageViewUsageCreateInfo; + +typedef struct VkPipelineTessellationDomainOriginStateCreateInfo { + VkStructureType sType; + const void* pNext; + VkTessellationDomainOrigin domainOrigin; +} VkPipelineTessellationDomainOriginStateCreateInfo; + +typedef struct VkRenderPassMultiviewCreateInfo { + VkStructureType sType; + const void* pNext; + uint32_t subpassCount; + const uint32_t* pViewMasks; + uint32_t dependencyCount; + const int32_t* pViewOffsets; + uint32_t correlationMaskCount; + const uint32_t* pCorrelationMasks; +} VkRenderPassMultiviewCreateInfo; + +typedef struct VkPhysicalDeviceMultiviewFeatures { + VkStructureType sType; + void* pNext; + VkBool32 multiview; + VkBool32 multiviewGeometryShader; + VkBool32 multiviewTessellationShader; +} VkPhysicalDeviceMultiviewFeatures; + +typedef struct VkPhysicalDeviceMultiviewProperties { + VkStructureType sType; + void* pNext; + uint32_t maxMultiviewViewCount; + uint32_t maxMultiviewInstanceIndex; +} VkPhysicalDeviceMultiviewProperties; + +typedef struct VkPhysicalDeviceVariablePointersFeatures { + VkStructureType sType; + void* pNext; + VkBool32 variablePointersStorageBuffer; + VkBool32 variablePointers; +} VkPhysicalDeviceVariablePointersFeatures; + +typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointerFeatures; + +typedef struct VkPhysicalDeviceProtectedMemoryFeatures { + VkStructureType sType; + void* pNext; + VkBool32 protectedMemory; +} VkPhysicalDeviceProtectedMemoryFeatures; + +typedef struct VkPhysicalDeviceProtectedMemoryProperties { + VkStructureType sType; + void* pNext; + VkBool32 protectedNoFault; +} VkPhysicalDeviceProtectedMemoryProperties; + +typedef struct VkDeviceQueueInfo2 { + VkStructureType sType; + const void* pNext; + VkDeviceQueueCreateFlags flags; + uint32_t queueFamilyIndex; + uint32_t queueIndex; +} VkDeviceQueueInfo2; + +typedef struct VkProtectedSubmitInfo { + VkStructureType sType; + const void* pNext; + VkBool32 protectedSubmit; +} VkProtectedSubmitInfo; + +typedef struct VkSamplerYcbcrConversionCreateInfo { + VkStructureType sType; + const void* pNext; + VkFormat format; + VkSamplerYcbcrModelConversion ycbcrModel; + VkSamplerYcbcrRange ycbcrRange; + VkComponentMapping components; + VkChromaLocation xChromaOffset; + VkChromaLocation yChromaOffset; + VkFilter chromaFilter; + VkBool32 forceExplicitReconstruction; +} VkSamplerYcbcrConversionCreateInfo; + +typedef struct VkSamplerYcbcrConversionInfo { + VkStructureType sType; + const void* pNext; + VkSamplerYcbcrConversion conversion; +} VkSamplerYcbcrConversionInfo; + +typedef struct VkBindImagePlaneMemoryInfo { + VkStructureType sType; + const void* pNext; + VkImageAspectFlagBits planeAspect; +} VkBindImagePlaneMemoryInfo; + +typedef struct VkImagePlaneMemoryRequirementsInfo { + VkStructureType sType; + const void* pNext; + VkImageAspectFlagBits planeAspect; +} VkImagePlaneMemoryRequirementsInfo; + +typedef struct VkPhysicalDeviceSamplerYcbcrConversionFeatures { + VkStructureType sType; + void* pNext; + VkBool32 samplerYcbcrConversion; +} VkPhysicalDeviceSamplerYcbcrConversionFeatures; + +typedef struct VkSamplerYcbcrConversionImageFormatProperties { + VkStructureType sType; + void* pNext; + uint32_t combinedImageSamplerDescriptorCount; +} VkSamplerYcbcrConversionImageFormatProperties; + +typedef struct VkDescriptorUpdateTemplateEntry { + uint32_t dstBinding; + uint32_t dstArrayElement; + uint32_t descriptorCount; + VkDescriptorType descriptorType; + size_t offset; + size_t stride; +} VkDescriptorUpdateTemplateEntry; + +typedef struct VkDescriptorUpdateTemplateCreateInfo { + VkStructureType sType; + const void* pNext; + VkDescriptorUpdateTemplateCreateFlags flags; + uint32_t descriptorUpdateEntryCount; + const VkDescriptorUpdateTemplateEntry* pDescriptorUpdateEntries; + VkDescriptorUpdateTemplateType templateType; + VkDescriptorSetLayout descriptorSetLayout; + VkPipelineBindPoint pipelineBindPoint; + VkPipelineLayout pipelineLayout; + uint32_t set; +} VkDescriptorUpdateTemplateCreateInfo; + +typedef struct VkExternalMemoryProperties { + VkExternalMemoryFeatureFlags externalMemoryFeatures; + VkExternalMemoryHandleTypeFlags exportFromImportedHandleTypes; + VkExternalMemoryHandleTypeFlags compatibleHandleTypes; +} VkExternalMemoryProperties; + +typedef struct VkPhysicalDeviceExternalImageFormatInfo { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagBits handleType; +} VkPhysicalDeviceExternalImageFormatInfo; + +typedef struct VkExternalImageFormatProperties { + VkStructureType sType; + void* pNext; + VkExternalMemoryProperties externalMemoryProperties; +} VkExternalImageFormatProperties; + +typedef struct VkPhysicalDeviceExternalBufferInfo { + VkStructureType sType; + const void* pNext; + VkBufferCreateFlags flags; + VkBufferUsageFlags usage; + VkExternalMemoryHandleTypeFlagBits handleType; +} VkPhysicalDeviceExternalBufferInfo; + +typedef struct VkExternalBufferProperties { + VkStructureType sType; + void* pNext; + VkExternalMemoryProperties externalMemoryProperties; +} VkExternalBufferProperties; + +typedef struct VkPhysicalDeviceIDProperties { + VkStructureType sType; + void* pNext; + uint8_t deviceUUID[VK_UUID_SIZE]; + uint8_t driverUUID[VK_UUID_SIZE]; + uint8_t deviceLUID[VK_LUID_SIZE]; + uint32_t deviceNodeMask; + VkBool32 deviceLUIDValid; +} VkPhysicalDeviceIDProperties; + +typedef struct VkExternalMemoryImageCreateInfo { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlags handleTypes; +} VkExternalMemoryImageCreateInfo; + +typedef struct VkExternalMemoryBufferCreateInfo { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlags handleTypes; +} VkExternalMemoryBufferCreateInfo; + +typedef struct VkExportMemoryAllocateInfo { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlags handleTypes; +} VkExportMemoryAllocateInfo; + +typedef struct VkPhysicalDeviceExternalFenceInfo { + VkStructureType sType; + const void* pNext; + VkExternalFenceHandleTypeFlagBits handleType; +} VkPhysicalDeviceExternalFenceInfo; + +typedef struct VkExternalFenceProperties { + VkStructureType sType; + void* pNext; + VkExternalFenceHandleTypeFlags exportFromImportedHandleTypes; + VkExternalFenceHandleTypeFlags compatibleHandleTypes; + VkExternalFenceFeatureFlags externalFenceFeatures; +} VkExternalFenceProperties; + +typedef struct VkExportFenceCreateInfo { + VkStructureType sType; + const void* pNext; + VkExternalFenceHandleTypeFlags handleTypes; +} VkExportFenceCreateInfo; + +typedef struct VkExportSemaphoreCreateInfo { + VkStructureType sType; + const void* pNext; + VkExternalSemaphoreHandleTypeFlags handleTypes; +} VkExportSemaphoreCreateInfo; + +typedef struct VkPhysicalDeviceExternalSemaphoreInfo { + VkStructureType sType; + const void* pNext; + VkExternalSemaphoreHandleTypeFlagBits handleType; +} VkPhysicalDeviceExternalSemaphoreInfo; + +typedef struct VkExternalSemaphoreProperties { + VkStructureType sType; + void* pNext; + VkExternalSemaphoreHandleTypeFlags exportFromImportedHandleTypes; + VkExternalSemaphoreHandleTypeFlags compatibleHandleTypes; + VkExternalSemaphoreFeatureFlags externalSemaphoreFeatures; +} VkExternalSemaphoreProperties; + +typedef struct VkPhysicalDeviceMaintenance3Properties { + VkStructureType sType; + void* pNext; + uint32_t maxPerSetDescriptors; + VkDeviceSize maxMemoryAllocationSize; +} VkPhysicalDeviceMaintenance3Properties; + +typedef struct VkDescriptorSetLayoutSupport { + VkStructureType sType; + void* pNext; + VkBool32 supported; +} VkDescriptorSetLayoutSupport; + +typedef struct VkPhysicalDeviceShaderDrawParametersFeatures { + VkStructureType sType; + void* pNext; + VkBool32 shaderDrawParameters; +} VkPhysicalDeviceShaderDrawParametersFeatures; + +typedef VkPhysicalDeviceShaderDrawParametersFeatures VkPhysicalDeviceShaderDrawParameterFeatures; + +typedef VkResult (VKAPI_PTR *PFN_vkEnumerateInstanceVersion)(uint32_t* pApiVersion); +typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos); +typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); +typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeatures)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); +typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMask)(VkCommandBuffer commandBuffer, uint32_t deviceMask); +typedef void (VKAPI_PTR *PFN_vkCmdDispatchBase)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); +typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDeviceGroups)(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); +typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements2)(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetBufferMemoryRequirements2)(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetImageSparseMemoryRequirements2)(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties); +typedef void (VKAPI_PTR *PFN_vkTrimCommandPool)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); +typedef void (VKAPI_PTR *PFN_vkGetDeviceQueue2)(VkDevice device, const VkDeviceQueueInfo2* pQueueInfo, VkQueue* pQueue); +typedef VkResult (VKAPI_PTR *PFN_vkCreateSamplerYcbcrConversion)(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); +typedef void (VKAPI_PTR *PFN_vkDestroySamplerYcbcrConversion)(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorUpdateTemplate)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate); +typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorUpdateTemplate)(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSetWithTemplate)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalBufferProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalFenceProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalSemaphoreProperties)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties); +typedef void (VKAPI_PTR *PFN_vkGetDescriptorSetLayoutSupport)(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkEnumerateInstanceVersion( + uint32_t* pApiVersion); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2( + VkDevice device, + uint32_t bindInfoCount, + const VkBindBufferMemoryInfo* pBindInfos); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2( + VkDevice device, + uint32_t bindInfoCount, + const VkBindImageMemoryInfo* pBindInfos); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeatures( + VkDevice device, + uint32_t heapIndex, + uint32_t localDeviceIndex, + uint32_t remoteDeviceIndex, + VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMask( + VkCommandBuffer commandBuffer, + uint32_t deviceMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBase( + VkCommandBuffer commandBuffer, + uint32_t baseGroupX, + uint32_t baseGroupY, + uint32_t baseGroupZ, + uint32_t groupCountX, + uint32_t groupCountY, + uint32_t groupCountZ); + +VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroups( + VkInstance instance, + uint32_t* pPhysicalDeviceGroupCount, + VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetImageMemoryRequirements2( + VkDevice device, + const VkImageMemoryRequirementsInfo2* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetBufferMemoryRequirements2( + VkDevice device, + const VkBufferMemoryRequirementsInfo2* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements2( + VkDevice device, + const VkImageSparseMemoryRequirementsInfo2* pInfo, + uint32_t* pSparseMemoryRequirementCount, + VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties2* pProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties2* pFormatProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, + VkImageFormatProperties2* pImageFormatProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2( + VkPhysicalDevice physicalDevice, + uint32_t* pQueueFamilyPropertyCount, + VkQueueFamilyProperties2* pQueueFamilyProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties2* pMemoryProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, + uint32_t* pPropertyCount, + VkSparseImageFormatProperties2* pProperties); + +VKAPI_ATTR void VKAPI_CALL vkTrimCommandPool( + VkDevice device, + VkCommandPool commandPool, + VkCommandPoolTrimFlags flags); + +VKAPI_ATTR void VKAPI_CALL vkGetDeviceQueue2( + VkDevice device, + const VkDeviceQueueInfo2* pQueueInfo, + VkQueue* pQueue); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSamplerYcbcrConversion( + VkDevice device, + const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSamplerYcbcrConversion* pYcbcrConversion); + +VKAPI_ATTR void VKAPI_CALL vkDestroySamplerYcbcrConversion( + VkDevice device, + VkSamplerYcbcrConversion ycbcrConversion, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorUpdateTemplate( + VkDevice device, + const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorUpdateTemplate( + VkDevice device, + VkDescriptorUpdateTemplate descriptorUpdateTemplate, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSetWithTemplate( + VkDevice device, + VkDescriptorSet descriptorSet, + VkDescriptorUpdateTemplate descriptorUpdateTemplate, + const void* pData); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalBufferProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, + VkExternalBufferProperties* pExternalBufferProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalFenceProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, + VkExternalFenceProperties* pExternalFenceProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalSemaphoreProperties( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, + VkExternalSemaphoreProperties* pExternalSemaphoreProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetDescriptorSetLayoutSupport( + VkDevice device, + const VkDescriptorSetLayoutCreateInfo* pCreateInfo, + VkDescriptorSetLayoutSupport* pSupport); +#endif + + +#define VK_KHR_surface 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSurfaceKHR) +#define VK_KHR_SURFACE_SPEC_VERSION 25 +#define VK_KHR_SURFACE_EXTENSION_NAME "VK_KHR_surface" + +typedef enum VkColorSpaceKHR { + VK_COLOR_SPACE_SRGB_NONLINEAR_KHR = 0, + VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT = 1000104001, + VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT = 1000104002, + VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT = 1000104003, + VK_COLOR_SPACE_DCI_P3_NONLINEAR_EXT = 1000104004, + VK_COLOR_SPACE_BT709_LINEAR_EXT = 1000104005, + VK_COLOR_SPACE_BT709_NONLINEAR_EXT = 1000104006, + VK_COLOR_SPACE_BT2020_LINEAR_EXT = 1000104007, + VK_COLOR_SPACE_HDR10_ST2084_EXT = 1000104008, + VK_COLOR_SPACE_DOLBYVISION_EXT = 1000104009, + VK_COLOR_SPACE_HDR10_HLG_EXT = 1000104010, + VK_COLOR_SPACE_ADOBERGB_LINEAR_EXT = 1000104011, + VK_COLOR_SPACE_ADOBERGB_NONLINEAR_EXT = 1000104012, + VK_COLOR_SPACE_PASS_THROUGH_EXT = 1000104013, + VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT = 1000104014, + VK_COLOR_SPACE_DISPLAY_NATIVE_AMD = 1000213000, + VK_COLORSPACE_SRGB_NONLINEAR_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_DCI_P3_LINEAR_EXT = VK_COLOR_SPACE_DISPLAY_P3_LINEAR_EXT, + VK_COLOR_SPACE_BEGIN_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_END_RANGE_KHR = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, + VK_COLOR_SPACE_RANGE_SIZE_KHR = (VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - VK_COLOR_SPACE_SRGB_NONLINEAR_KHR + 1), + VK_COLOR_SPACE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkColorSpaceKHR; + +typedef enum VkPresentModeKHR { + VK_PRESENT_MODE_IMMEDIATE_KHR = 0, + VK_PRESENT_MODE_MAILBOX_KHR = 1, + VK_PRESENT_MODE_FIFO_KHR = 2, + VK_PRESENT_MODE_FIFO_RELAXED_KHR = 3, + VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR = 1000111000, + VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR = 1000111001, + VK_PRESENT_MODE_BEGIN_RANGE_KHR = VK_PRESENT_MODE_IMMEDIATE_KHR, + VK_PRESENT_MODE_END_RANGE_KHR = VK_PRESENT_MODE_FIFO_RELAXED_KHR, + VK_PRESENT_MODE_RANGE_SIZE_KHR = (VK_PRESENT_MODE_FIFO_RELAXED_KHR - VK_PRESENT_MODE_IMMEDIATE_KHR + 1), + VK_PRESENT_MODE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPresentModeKHR; + +typedef enum VkSurfaceTransformFlagBitsKHR { + VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR = 0x00000001, + VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR = 0x00000002, + VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR = 0x00000004, + VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR = 0x00000008, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_BIT_KHR = 0x00000010, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_90_BIT_KHR = 0x00000020, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_180_BIT_KHR = 0x00000040, + VK_SURFACE_TRANSFORM_HORIZONTAL_MIRROR_ROTATE_270_BIT_KHR = 0x00000080, + VK_SURFACE_TRANSFORM_INHERIT_BIT_KHR = 0x00000100, + VK_SURFACE_TRANSFORM_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSurfaceTransformFlagBitsKHR; +typedef VkFlags VkSurfaceTransformFlagsKHR; + +typedef enum VkCompositeAlphaFlagBitsKHR { + VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, + VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR = 0x00000002, + VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR = 0x00000004, + VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR = 0x00000008, + VK_COMPOSITE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkCompositeAlphaFlagBitsKHR; +typedef VkFlags VkCompositeAlphaFlagsKHR; +typedef struct VkSurfaceCapabilitiesKHR { + uint32_t minImageCount; + uint32_t maxImageCount; + VkExtent2D currentExtent; + VkExtent2D minImageExtent; + VkExtent2D maxImageExtent; + uint32_t maxImageArrayLayers; + VkSurfaceTransformFlagsKHR supportedTransforms; + VkSurfaceTransformFlagBitsKHR currentTransform; + VkCompositeAlphaFlagsKHR supportedCompositeAlpha; + VkImageUsageFlags supportedUsageFlags; +} VkSurfaceCapabilitiesKHR; + +typedef struct VkSurfaceFormatKHR { + VkFormat format; + VkColorSpaceKHR colorSpace; +} VkSurfaceFormatKHR; + +typedef void (VKAPI_PTR *PFN_vkDestroySurfaceKHR)(VkInstance instance, VkSurfaceKHR surface, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, VkSurfaceKHR surface, VkBool32* pSupported); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceFormatsKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pSurfaceFormatCount, VkSurfaceFormatKHR* pSurfaceFormats); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkDestroySurfaceKHR( + VkInstance instance, + VkSurfaceKHR surface, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + VkSurfaceKHR surface, + VkBool32* pSupported); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilitiesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilitiesKHR* pSurfaceCapabilities); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormatsKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + uint32_t* pSurfaceFormatCount, + VkSurfaceFormatKHR* pSurfaceFormats); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + uint32_t* pPresentModeCount, + VkPresentModeKHR* pPresentModes); +#endif + + +#define VK_KHR_swapchain 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSwapchainKHR) +#define VK_KHR_SWAPCHAIN_SPEC_VERSION 70 +#define VK_KHR_SWAPCHAIN_EXTENSION_NAME "VK_KHR_swapchain" + +typedef enum VkSwapchainCreateFlagBitsKHR { + VK_SWAPCHAIN_CREATE_SPLIT_INSTANCE_BIND_REGIONS_BIT_KHR = 0x00000001, + VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR = 0x00000002, + VK_SWAPCHAIN_CREATE_MUTABLE_FORMAT_BIT_KHR = 0x00000004, + VK_SWAPCHAIN_CREATE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSwapchainCreateFlagBitsKHR; +typedef VkFlags VkSwapchainCreateFlagsKHR; + +typedef enum VkDeviceGroupPresentModeFlagBitsKHR { + VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_BIT_KHR = 0x00000001, + VK_DEVICE_GROUP_PRESENT_MODE_REMOTE_BIT_KHR = 0x00000002, + VK_DEVICE_GROUP_PRESENT_MODE_SUM_BIT_KHR = 0x00000004, + VK_DEVICE_GROUP_PRESENT_MODE_LOCAL_MULTI_DEVICE_BIT_KHR = 0x00000008, + VK_DEVICE_GROUP_PRESENT_MODE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkDeviceGroupPresentModeFlagBitsKHR; +typedef VkFlags VkDeviceGroupPresentModeFlagsKHR; +typedef struct VkSwapchainCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkSwapchainCreateFlagsKHR flags; + VkSurfaceKHR surface; + uint32_t minImageCount; + VkFormat imageFormat; + VkColorSpaceKHR imageColorSpace; + VkExtent2D imageExtent; + uint32_t imageArrayLayers; + VkImageUsageFlags imageUsage; + VkSharingMode imageSharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; + VkSurfaceTransformFlagBitsKHR preTransform; + VkCompositeAlphaFlagBitsKHR compositeAlpha; + VkPresentModeKHR presentMode; + VkBool32 clipped; + VkSwapchainKHR oldSwapchain; +} VkSwapchainCreateInfoKHR; + +typedef struct VkPresentInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreCount; + const VkSemaphore* pWaitSemaphores; + uint32_t swapchainCount; + const VkSwapchainKHR* pSwapchains; + const uint32_t* pImageIndices; + VkResult* pResults; +} VkPresentInfoKHR; + +typedef struct VkImageSwapchainCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkSwapchainKHR swapchain; +} VkImageSwapchainCreateInfoKHR; + +typedef struct VkBindImageMemorySwapchainInfoKHR { + VkStructureType sType; + const void* pNext; + VkSwapchainKHR swapchain; + uint32_t imageIndex; +} VkBindImageMemorySwapchainInfoKHR; + +typedef struct VkAcquireNextImageInfoKHR { + VkStructureType sType; + const void* pNext; + VkSwapchainKHR swapchain; + uint64_t timeout; + VkSemaphore semaphore; + VkFence fence; + uint32_t deviceMask; +} VkAcquireNextImageInfoKHR; + +typedef struct VkDeviceGroupPresentCapabilitiesKHR { + VkStructureType sType; + const void* pNext; + uint32_t presentMask[VK_MAX_DEVICE_GROUP_SIZE]; + VkDeviceGroupPresentModeFlagsKHR modes; +} VkDeviceGroupPresentCapabilitiesKHR; + +typedef struct VkDeviceGroupPresentInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t swapchainCount; + const uint32_t* pDeviceMasks; + VkDeviceGroupPresentModeFlagBitsKHR mode; +} VkDeviceGroupPresentInfoKHR; + +typedef struct VkDeviceGroupSwapchainCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkDeviceGroupPresentModeFlagsKHR modes; +} VkDeviceGroupSwapchainCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateSwapchainKHR)(VkDevice device, const VkSwapchainCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchain); +typedef void (VKAPI_PTR *PFN_vkDestroySwapchainKHR)(VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainImagesKHR)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pSwapchainImageCount, VkImage* pSwapchainImages); +typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImageKHR)(VkDevice device, VkSwapchainKHR swapchain, uint64_t timeout, VkSemaphore semaphore, VkFence fence, uint32_t* pImageIndex); +typedef VkResult (VKAPI_PTR *PFN_vkQueuePresentKHR)(VkQueue queue, const VkPresentInfoKHR* pPresentInfo); +typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupPresentCapabilitiesKHR)(VkDevice device, VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities); +typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModesKHR)(VkDevice device, VkSurfaceKHR surface, VkDeviceGroupPresentModeFlagsKHR* pModes); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDevicePresentRectanglesKHR)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, uint32_t* pRectCount, VkRect2D* pRects); +typedef VkResult (VKAPI_PTR *PFN_vkAcquireNextImage2KHR)(VkDevice device, const VkAcquireNextImageInfoKHR* pAcquireInfo, uint32_t* pImageIndex); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSwapchainKHR( + VkDevice device, + const VkSwapchainCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSwapchainKHR* pSwapchain); + +VKAPI_ATTR void VKAPI_CALL vkDestroySwapchainKHR( + VkDevice device, + VkSwapchainKHR swapchain, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainImagesKHR( + VkDevice device, + VkSwapchainKHR swapchain, + uint32_t* pSwapchainImageCount, + VkImage* pSwapchainImages); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImageKHR( + VkDevice device, + VkSwapchainKHR swapchain, + uint64_t timeout, + VkSemaphore semaphore, + VkFence fence, + uint32_t* pImageIndex); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueuePresentKHR( + VkQueue queue, + const VkPresentInfoKHR* pPresentInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupPresentCapabilitiesKHR( + VkDevice device, + VkDeviceGroupPresentCapabilitiesKHR* pDeviceGroupPresentCapabilities); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModesKHR( + VkDevice device, + VkSurfaceKHR surface, + VkDeviceGroupPresentModeFlagsKHR* pModes); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDevicePresentRectanglesKHR( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + uint32_t* pRectCount, + VkRect2D* pRects); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireNextImage2KHR( + VkDevice device, + const VkAcquireNextImageInfoKHR* pAcquireInfo, + uint32_t* pImageIndex); +#endif + + +#define VK_KHR_display 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayKHR) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDisplayModeKHR) +#define VK_KHR_DISPLAY_SPEC_VERSION 23 +#define VK_KHR_DISPLAY_EXTENSION_NAME "VK_KHR_display" + +typedef enum VkDisplayPlaneAlphaFlagBitsKHR { + VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR = 0x00000001, + VK_DISPLAY_PLANE_ALPHA_GLOBAL_BIT_KHR = 0x00000002, + VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_BIT_KHR = 0x00000004, + VK_DISPLAY_PLANE_ALPHA_PER_PIXEL_PREMULTIPLIED_BIT_KHR = 0x00000008, + VK_DISPLAY_PLANE_ALPHA_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkDisplayPlaneAlphaFlagBitsKHR; +typedef VkFlags VkDisplayPlaneAlphaFlagsKHR; +typedef VkFlags VkDisplayModeCreateFlagsKHR; +typedef VkFlags VkDisplaySurfaceCreateFlagsKHR; +typedef struct VkDisplayPropertiesKHR { + VkDisplayKHR display; + const char* displayName; + VkExtent2D physicalDimensions; + VkExtent2D physicalResolution; + VkSurfaceTransformFlagsKHR supportedTransforms; + VkBool32 planeReorderPossible; + VkBool32 persistentContent; +} VkDisplayPropertiesKHR; + +typedef struct VkDisplayModeParametersKHR { + VkExtent2D visibleRegion; + uint32_t refreshRate; +} VkDisplayModeParametersKHR; + +typedef struct VkDisplayModePropertiesKHR { + VkDisplayModeKHR displayMode; + VkDisplayModeParametersKHR parameters; +} VkDisplayModePropertiesKHR; + +typedef struct VkDisplayModeCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkDisplayModeCreateFlagsKHR flags; + VkDisplayModeParametersKHR parameters; +} VkDisplayModeCreateInfoKHR; + +typedef struct VkDisplayPlaneCapabilitiesKHR { + VkDisplayPlaneAlphaFlagsKHR supportedAlpha; + VkOffset2D minSrcPosition; + VkOffset2D maxSrcPosition; + VkExtent2D minSrcExtent; + VkExtent2D maxSrcExtent; + VkOffset2D minDstPosition; + VkOffset2D maxDstPosition; + VkExtent2D minDstExtent; + VkExtent2D maxDstExtent; +} VkDisplayPlaneCapabilitiesKHR; + +typedef struct VkDisplayPlanePropertiesKHR { + VkDisplayKHR currentDisplay; + uint32_t currentStackIndex; +} VkDisplayPlanePropertiesKHR; + +typedef struct VkDisplaySurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkDisplaySurfaceCreateFlagsKHR flags; + VkDisplayModeKHR displayMode; + uint32_t planeIndex; + uint32_t planeStackIndex; + VkSurfaceTransformFlagBitsKHR transform; + float globalAlpha; + VkDisplayPlaneAlphaFlagBitsKHR alphaMode; + VkExtent2D imageExtent; +} VkDisplaySurfaceCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPropertiesKHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPlanePropertiesKHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlanePropertiesKHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneSupportedDisplaysKHR)(VkPhysicalDevice physicalDevice, uint32_t planeIndex, uint32_t* pDisplayCount, VkDisplayKHR* pDisplays); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayModePropertiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t* pPropertyCount, VkDisplayModePropertiesKHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayModeKHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, const VkDisplayModeCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDisplayModeKHR* pMode); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneCapabilitiesKHR)(VkPhysicalDevice physicalDevice, VkDisplayModeKHR mode, uint32_t planeIndex, VkDisplayPlaneCapabilitiesKHR* pCapabilities); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDisplayPlaneSurfaceKHR)(VkInstance instance, const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPropertiesKHR( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkDisplayPropertiesKHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPlanePropertiesKHR( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkDisplayPlanePropertiesKHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneSupportedDisplaysKHR( + VkPhysicalDevice physicalDevice, + uint32_t planeIndex, + uint32_t* pDisplayCount, + VkDisplayKHR* pDisplays); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayModePropertiesKHR( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display, + uint32_t* pPropertyCount, + VkDisplayModePropertiesKHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayModeKHR( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display, + const VkDisplayModeCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDisplayModeKHR* pMode); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneCapabilitiesKHR( + VkPhysicalDevice physicalDevice, + VkDisplayModeKHR mode, + uint32_t planeIndex, + VkDisplayPlaneCapabilitiesKHR* pCapabilities); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDisplayPlaneSurfaceKHR( + VkInstance instance, + const VkDisplaySurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + + +#define VK_KHR_display_swapchain 1 +#define VK_KHR_DISPLAY_SWAPCHAIN_SPEC_VERSION 10 +#define VK_KHR_DISPLAY_SWAPCHAIN_EXTENSION_NAME "VK_KHR_display_swapchain" +typedef struct VkDisplayPresentInfoKHR { + VkStructureType sType; + const void* pNext; + VkRect2D srcRect; + VkRect2D dstRect; + VkBool32 persistent; +} VkDisplayPresentInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateSharedSwapchainsKHR)(VkDevice device, uint32_t swapchainCount, const VkSwapchainCreateInfoKHR* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkSwapchainKHR* pSwapchains); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSharedSwapchainsKHR( + VkDevice device, + uint32_t swapchainCount, + const VkSwapchainCreateInfoKHR* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkSwapchainKHR* pSwapchains); +#endif + + +#define VK_KHR_sampler_mirror_clamp_to_edge 1 +#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_SPEC_VERSION 3 +#define VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME "VK_KHR_sampler_mirror_clamp_to_edge" + + +#define VK_KHR_multiview 1 +#define VK_KHR_MULTIVIEW_SPEC_VERSION 1 +#define VK_KHR_MULTIVIEW_EXTENSION_NAME "VK_KHR_multiview" +typedef VkRenderPassMultiviewCreateInfo VkRenderPassMultiviewCreateInfoKHR; + +typedef VkPhysicalDeviceMultiviewFeatures VkPhysicalDeviceMultiviewFeaturesKHR; + +typedef VkPhysicalDeviceMultiviewProperties VkPhysicalDeviceMultiviewPropertiesKHR; + + + +#define VK_KHR_get_physical_device_properties2 1 +#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_SPEC_VERSION 2 +#define VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_physical_device_properties2" +typedef VkPhysicalDeviceFeatures2 VkPhysicalDeviceFeatures2KHR; + +typedef VkPhysicalDeviceProperties2 VkPhysicalDeviceProperties2KHR; + +typedef VkFormatProperties2 VkFormatProperties2KHR; + +typedef VkImageFormatProperties2 VkImageFormatProperties2KHR; + +typedef VkPhysicalDeviceImageFormatInfo2 VkPhysicalDeviceImageFormatInfo2KHR; + +typedef VkQueueFamilyProperties2 VkQueueFamilyProperties2KHR; + +typedef VkPhysicalDeviceMemoryProperties2 VkPhysicalDeviceMemoryProperties2KHR; + +typedef VkSparseImageFormatProperties2 VkSparseImageFormatProperties2KHR; + +typedef VkPhysicalDeviceSparseImageFormatInfo2 VkPhysicalDeviceSparseImageFormatInfo2KHR; + +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFeatures2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceFeatures2* pFeatures); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties2* pProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceFormatProperties2KHR)(VkPhysicalDevice physicalDevice, VkFormat format, VkFormatProperties2* pFormatProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, VkImageFormatProperties2* pImageFormatProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceQueueFamilyProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties2* pQueueFamilyProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMemoryProperties2KHR)(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties2* pMemoryProperties); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceSparseImageFormatProperties2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, uint32_t* pPropertyCount, VkSparseImageFormatProperties2* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFeatures2KHR( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceFeatures2* pFeatures); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceProperties2KHR( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceProperties2* pProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceFormatProperties2KHR( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkFormatProperties2* pFormatProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceImageFormatProperties2KHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo, + VkImageFormatProperties2* pImageFormatProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceQueueFamilyProperties2KHR( + VkPhysicalDevice physicalDevice, + uint32_t* pQueueFamilyPropertyCount, + VkQueueFamilyProperties2* pQueueFamilyProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMemoryProperties2KHR( + VkPhysicalDevice physicalDevice, + VkPhysicalDeviceMemoryProperties2* pMemoryProperties); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceSparseImageFormatProperties2KHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSparseImageFormatInfo2* pFormatInfo, + uint32_t* pPropertyCount, + VkSparseImageFormatProperties2* pProperties); +#endif + + +#define VK_KHR_device_group 1 +#define VK_KHR_DEVICE_GROUP_SPEC_VERSION 4 +#define VK_KHR_DEVICE_GROUP_EXTENSION_NAME "VK_KHR_device_group" +typedef VkPeerMemoryFeatureFlags VkPeerMemoryFeatureFlagsKHR; + +typedef VkPeerMemoryFeatureFlagBits VkPeerMemoryFeatureFlagBitsKHR; + +typedef VkMemoryAllocateFlags VkMemoryAllocateFlagsKHR; + +typedef VkMemoryAllocateFlagBits VkMemoryAllocateFlagBitsKHR; + +typedef VkMemoryAllocateFlagsInfo VkMemoryAllocateFlagsInfoKHR; + +typedef VkDeviceGroupRenderPassBeginInfo VkDeviceGroupRenderPassBeginInfoKHR; + +typedef VkDeviceGroupCommandBufferBeginInfo VkDeviceGroupCommandBufferBeginInfoKHR; + +typedef VkDeviceGroupSubmitInfo VkDeviceGroupSubmitInfoKHR; + +typedef VkDeviceGroupBindSparseInfo VkDeviceGroupBindSparseInfoKHR; + +typedef VkBindBufferMemoryDeviceGroupInfo VkBindBufferMemoryDeviceGroupInfoKHR; + +typedef VkBindImageMemoryDeviceGroupInfo VkBindImageMemoryDeviceGroupInfoKHR; + +typedef void (VKAPI_PTR *PFN_vkGetDeviceGroupPeerMemoryFeaturesKHR)(VkDevice device, uint32_t heapIndex, uint32_t localDeviceIndex, uint32_t remoteDeviceIndex, VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); +typedef void (VKAPI_PTR *PFN_vkCmdSetDeviceMaskKHR)(VkCommandBuffer commandBuffer, uint32_t deviceMask); +typedef void (VKAPI_PTR *PFN_vkCmdDispatchBaseKHR)(VkCommandBuffer commandBuffer, uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ, uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetDeviceGroupPeerMemoryFeaturesKHR( + VkDevice device, + uint32_t heapIndex, + uint32_t localDeviceIndex, + uint32_t remoteDeviceIndex, + VkPeerMemoryFeatureFlags* pPeerMemoryFeatures); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetDeviceMaskKHR( + VkCommandBuffer commandBuffer, + uint32_t deviceMask); + +VKAPI_ATTR void VKAPI_CALL vkCmdDispatchBaseKHR( + VkCommandBuffer commandBuffer, + uint32_t baseGroupX, + uint32_t baseGroupY, + uint32_t baseGroupZ, + uint32_t groupCountX, + uint32_t groupCountY, + uint32_t groupCountZ); +#endif + + +#define VK_KHR_shader_draw_parameters 1 +#define VK_KHR_SHADER_DRAW_PARAMETERS_SPEC_VERSION 1 +#define VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME "VK_KHR_shader_draw_parameters" + + +#define VK_KHR_maintenance1 1 +#define VK_KHR_MAINTENANCE1_SPEC_VERSION 2 +#define VK_KHR_MAINTENANCE1_EXTENSION_NAME "VK_KHR_maintenance1" +typedef VkCommandPoolTrimFlags VkCommandPoolTrimFlagsKHR; + +typedef void (VKAPI_PTR *PFN_vkTrimCommandPoolKHR)(VkDevice device, VkCommandPool commandPool, VkCommandPoolTrimFlags flags); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkTrimCommandPoolKHR( + VkDevice device, + VkCommandPool commandPool, + VkCommandPoolTrimFlags flags); +#endif + + +#define VK_KHR_device_group_creation 1 +#define VK_KHR_DEVICE_GROUP_CREATION_SPEC_VERSION 1 +#define VK_KHR_DEVICE_GROUP_CREATION_EXTENSION_NAME "VK_KHR_device_group_creation" +#define VK_MAX_DEVICE_GROUP_SIZE_KHR VK_MAX_DEVICE_GROUP_SIZE +typedef VkPhysicalDeviceGroupProperties VkPhysicalDeviceGroupPropertiesKHR; + +typedef VkDeviceGroupDeviceCreateInfo VkDeviceGroupDeviceCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkEnumeratePhysicalDeviceGroupsKHR)(VkInstance instance, uint32_t* pPhysicalDeviceGroupCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkEnumeratePhysicalDeviceGroupsKHR( + VkInstance instance, + uint32_t* pPhysicalDeviceGroupCount, + VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProperties); +#endif + + +#define VK_KHR_external_memory_capabilities 1 +#define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_memory_capabilities" +#define VK_LUID_SIZE_KHR VK_LUID_SIZE +typedef VkExternalMemoryHandleTypeFlags VkExternalMemoryHandleTypeFlagsKHR; + +typedef VkExternalMemoryHandleTypeFlagBits VkExternalMemoryHandleTypeFlagBitsKHR; + +typedef VkExternalMemoryFeatureFlags VkExternalMemoryFeatureFlagsKHR; + +typedef VkExternalMemoryFeatureFlagBits VkExternalMemoryFeatureFlagBitsKHR; + +typedef VkExternalMemoryProperties VkExternalMemoryPropertiesKHR; + +typedef VkPhysicalDeviceExternalImageFormatInfo VkPhysicalDeviceExternalImageFormatInfoKHR; + +typedef VkExternalImageFormatProperties VkExternalImageFormatPropertiesKHR; + +typedef VkPhysicalDeviceExternalBufferInfo VkPhysicalDeviceExternalBufferInfoKHR; + +typedef VkExternalBufferProperties VkExternalBufferPropertiesKHR; + +typedef VkPhysicalDeviceIDProperties VkPhysicalDeviceIDPropertiesKHR; + +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalBufferPropertiesKHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, VkExternalBufferProperties* pExternalBufferProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalBufferPropertiesKHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo, + VkExternalBufferProperties* pExternalBufferProperties); +#endif + + +#define VK_KHR_external_memory 1 +#define VK_KHR_EXTERNAL_MEMORY_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME "VK_KHR_external_memory" +#define VK_QUEUE_FAMILY_EXTERNAL_KHR VK_QUEUE_FAMILY_EXTERNAL +typedef VkExternalMemoryImageCreateInfo VkExternalMemoryImageCreateInfoKHR; + +typedef VkExternalMemoryBufferCreateInfo VkExternalMemoryBufferCreateInfoKHR; + +typedef VkExportMemoryAllocateInfo VkExportMemoryAllocateInfoKHR; + + + +#define VK_KHR_external_memory_fd 1 +#define VK_KHR_EXTERNAL_MEMORY_FD_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME "VK_KHR_external_memory_fd" +typedef struct VkImportMemoryFdInfoKHR { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagBits handleType; + int fd; +} VkImportMemoryFdInfoKHR; + +typedef struct VkMemoryFdPropertiesKHR { + VkStructureType sType; + void* pNext; + uint32_t memoryTypeBits; +} VkMemoryFdPropertiesKHR; + +typedef struct VkMemoryGetFdInfoKHR { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; + VkExternalMemoryHandleTypeFlagBits handleType; +} VkMemoryGetFdInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdKHR)(VkDevice device, const VkMemoryGetFdInfoKHR* pGetFdInfo, int* pFd); +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryFdPropertiesKHR)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, int fd, VkMemoryFdPropertiesKHR* pMemoryFdProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdKHR( + VkDevice device, + const VkMemoryGetFdInfoKHR* pGetFdInfo, + int* pFd); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFdPropertiesKHR( + VkDevice device, + VkExternalMemoryHandleTypeFlagBits handleType, + int fd, + VkMemoryFdPropertiesKHR* pMemoryFdProperties); +#endif + + +#define VK_KHR_external_semaphore_capabilities 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_semaphore_capabilities" +typedef VkExternalSemaphoreHandleTypeFlags VkExternalSemaphoreHandleTypeFlagsKHR; + +typedef VkExternalSemaphoreHandleTypeFlagBits VkExternalSemaphoreHandleTypeFlagBitsKHR; + +typedef VkExternalSemaphoreFeatureFlags VkExternalSemaphoreFeatureFlagsKHR; + +typedef VkExternalSemaphoreFeatureFlagBits VkExternalSemaphoreFeatureFlagBitsKHR; + +typedef VkPhysicalDeviceExternalSemaphoreInfo VkPhysicalDeviceExternalSemaphoreInfoKHR; + +typedef VkExternalSemaphoreProperties VkExternalSemaphorePropertiesKHR; + +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalSemaphorePropertiesKHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, + VkExternalSemaphoreProperties* pExternalSemaphoreProperties); +#endif + + +#define VK_KHR_external_semaphore 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_KHR_external_semaphore" +typedef VkSemaphoreImportFlags VkSemaphoreImportFlagsKHR; + +typedef VkSemaphoreImportFlagBits VkSemaphoreImportFlagBitsKHR; + +typedef VkExportSemaphoreCreateInfo VkExportSemaphoreCreateInfoKHR; + + + +#define VK_KHR_external_semaphore_fd 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_FD_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME "VK_KHR_external_semaphore_fd" +typedef struct VkImportSemaphoreFdInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkSemaphoreImportFlags flags; + VkExternalSemaphoreHandleTypeFlagBits handleType; + int fd; +} VkImportSemaphoreFdInfoKHR; + +typedef struct VkSemaphoreGetFdInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkExternalSemaphoreHandleTypeFlagBits handleType; +} VkSemaphoreGetFdInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreFdKHR)(VkDevice device, const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo); +typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreFdKHR)(VkDevice device, const VkSemaphoreGetFdInfoKHR* pGetFdInfo, int* pFd); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreFdKHR( + VkDevice device, + const VkImportSemaphoreFdInfoKHR* pImportSemaphoreFdInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreFdKHR( + VkDevice device, + const VkSemaphoreGetFdInfoKHR* pGetFdInfo, + int* pFd); +#endif + + +#define VK_KHR_push_descriptor 1 +#define VK_KHR_PUSH_DESCRIPTOR_SPEC_VERSION 2 +#define VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME "VK_KHR_push_descriptor" +typedef struct VkPhysicalDevicePushDescriptorPropertiesKHR { + VkStructureType sType; + void* pNext; + uint32_t maxPushDescriptors; +} VkPhysicalDevicePushDescriptorPropertiesKHR; + +typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetKHR)(VkCommandBuffer commandBuffer, VkPipelineBindPoint pipelineBindPoint, VkPipelineLayout layout, uint32_t set, uint32_t descriptorWriteCount, const VkWriteDescriptorSet* pDescriptorWrites); +typedef void (VKAPI_PTR *PFN_vkCmdPushDescriptorSetWithTemplateKHR)(VkCommandBuffer commandBuffer, VkDescriptorUpdateTemplate descriptorUpdateTemplate, VkPipelineLayout layout, uint32_t set, const void* pData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetKHR( + VkCommandBuffer commandBuffer, + VkPipelineBindPoint pipelineBindPoint, + VkPipelineLayout layout, + uint32_t set, + uint32_t descriptorWriteCount, + const VkWriteDescriptorSet* pDescriptorWrites); + +VKAPI_ATTR void VKAPI_CALL vkCmdPushDescriptorSetWithTemplateKHR( + VkCommandBuffer commandBuffer, + VkDescriptorUpdateTemplate descriptorUpdateTemplate, + VkPipelineLayout layout, + uint32_t set, + const void* pData); +#endif + + +#define VK_KHR_shader_float16_int8 1 +#define VK_KHR_SHADER_FLOAT16_INT8_SPEC_VERSION 1 +#define VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME "VK_KHR_shader_float16_int8" +typedef struct VkPhysicalDeviceShaderFloat16Int8FeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderFloat16; + VkBool32 shaderInt8; +} VkPhysicalDeviceShaderFloat16Int8FeaturesKHR; + +typedef VkPhysicalDeviceShaderFloat16Int8FeaturesKHR VkPhysicalDeviceFloat16Int8FeaturesKHR; + + + +#define VK_KHR_16bit_storage 1 +#define VK_KHR_16BIT_STORAGE_SPEC_VERSION 1 +#define VK_KHR_16BIT_STORAGE_EXTENSION_NAME "VK_KHR_16bit_storage" +typedef VkPhysicalDevice16BitStorageFeatures VkPhysicalDevice16BitStorageFeaturesKHR; + + + +#define VK_KHR_incremental_present 1 +#define VK_KHR_INCREMENTAL_PRESENT_SPEC_VERSION 1 +#define VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME "VK_KHR_incremental_present" +typedef struct VkRectLayerKHR { + VkOffset2D offset; + VkExtent2D extent; + uint32_t layer; +} VkRectLayerKHR; + +typedef struct VkPresentRegionKHR { + uint32_t rectangleCount; + const VkRectLayerKHR* pRectangles; +} VkPresentRegionKHR; + +typedef struct VkPresentRegionsKHR { + VkStructureType sType; + const void* pNext; + uint32_t swapchainCount; + const VkPresentRegionKHR* pRegions; +} VkPresentRegionsKHR; + + + +#define VK_KHR_descriptor_update_template 1 +typedef VkDescriptorUpdateTemplate VkDescriptorUpdateTemplateKHR; + +#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_SPEC_VERSION 1 +#define VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME "VK_KHR_descriptor_update_template" +typedef VkDescriptorUpdateTemplateType VkDescriptorUpdateTemplateTypeKHR; + +typedef VkDescriptorUpdateTemplateCreateFlags VkDescriptorUpdateTemplateCreateFlagsKHR; + +typedef VkDescriptorUpdateTemplateEntry VkDescriptorUpdateTemplateEntryKHR; + +typedef VkDescriptorUpdateTemplateCreateInfo VkDescriptorUpdateTemplateCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateDescriptorUpdateTemplateKHR)(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate); +typedef void (VKAPI_PTR *PFN_vkDestroyDescriptorUpdateTemplateKHR)(VkDevice device, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkUpdateDescriptorSetWithTemplateKHR)(VkDevice device, VkDescriptorSet descriptorSet, VkDescriptorUpdateTemplate descriptorUpdateTemplate, const void* pData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorUpdateTemplateKHR( + VkDevice device, + const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDescriptorUpdateTemplateKHR( + VkDevice device, + VkDescriptorUpdateTemplate descriptorUpdateTemplate, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkUpdateDescriptorSetWithTemplateKHR( + VkDevice device, + VkDescriptorSet descriptorSet, + VkDescriptorUpdateTemplate descriptorUpdateTemplate, + const void* pData); +#endif + + +#define VK_KHR_imageless_framebuffer 1 +#define VK_KHR_IMAGELESS_FRAMEBUFFER_SPEC_VERSION 1 +#define VK_KHR_IMAGELESS_FRAMEBUFFER_EXTENSION_NAME "VK_KHR_imageless_framebuffer" +typedef struct VkPhysicalDeviceImagelessFramebufferFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 imagelessFramebuffer; +} VkPhysicalDeviceImagelessFramebufferFeaturesKHR; + +typedef struct VkFramebufferAttachmentImageInfoKHR { + VkStructureType sType; + const void* pNext; + VkImageCreateFlags flags; + VkImageUsageFlags usage; + uint32_t width; + uint32_t height; + uint32_t layerCount; + uint32_t viewFormatCount; + const VkFormat* pViewFormats; +} VkFramebufferAttachmentImageInfoKHR; + +typedef struct VkFramebufferAttachmentsCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t attachmentImageInfoCount; + const VkFramebufferAttachmentImageInfoKHR* pAttachmentImageInfos; +} VkFramebufferAttachmentsCreateInfoKHR; + +typedef struct VkRenderPassAttachmentBeginInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t attachmentCount; + const VkImageView* pAttachments; +} VkRenderPassAttachmentBeginInfoKHR; + + + +#define VK_KHR_create_renderpass2 1 +#define VK_KHR_CREATE_RENDERPASS_2_SPEC_VERSION 1 +#define VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME "VK_KHR_create_renderpass2" +typedef struct VkAttachmentDescription2KHR { + VkStructureType sType; + const void* pNext; + VkAttachmentDescriptionFlags flags; + VkFormat format; + VkSampleCountFlagBits samples; + VkAttachmentLoadOp loadOp; + VkAttachmentStoreOp storeOp; + VkAttachmentLoadOp stencilLoadOp; + VkAttachmentStoreOp stencilStoreOp; + VkImageLayout initialLayout; + VkImageLayout finalLayout; +} VkAttachmentDescription2KHR; + +typedef struct VkAttachmentReference2KHR { + VkStructureType sType; + const void* pNext; + uint32_t attachment; + VkImageLayout layout; + VkImageAspectFlags aspectMask; +} VkAttachmentReference2KHR; + +typedef struct VkSubpassDescription2KHR { + VkStructureType sType; + const void* pNext; + VkSubpassDescriptionFlags flags; + VkPipelineBindPoint pipelineBindPoint; + uint32_t viewMask; + uint32_t inputAttachmentCount; + const VkAttachmentReference2KHR* pInputAttachments; + uint32_t colorAttachmentCount; + const VkAttachmentReference2KHR* pColorAttachments; + const VkAttachmentReference2KHR* pResolveAttachments; + const VkAttachmentReference2KHR* pDepthStencilAttachment; + uint32_t preserveAttachmentCount; + const uint32_t* pPreserveAttachments; +} VkSubpassDescription2KHR; + +typedef struct VkSubpassDependency2KHR { + VkStructureType sType; + const void* pNext; + uint32_t srcSubpass; + uint32_t dstSubpass; + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkDependencyFlags dependencyFlags; + int32_t viewOffset; +} VkSubpassDependency2KHR; + +typedef struct VkRenderPassCreateInfo2KHR { + VkStructureType sType; + const void* pNext; + VkRenderPassCreateFlags flags; + uint32_t attachmentCount; + const VkAttachmentDescription2KHR* pAttachments; + uint32_t subpassCount; + const VkSubpassDescription2KHR* pSubpasses; + uint32_t dependencyCount; + const VkSubpassDependency2KHR* pDependencies; + uint32_t correlatedViewMaskCount; + const uint32_t* pCorrelatedViewMasks; +} VkRenderPassCreateInfo2KHR; + +typedef struct VkSubpassBeginInfoKHR { + VkStructureType sType; + const void* pNext; + VkSubpassContents contents; +} VkSubpassBeginInfoKHR; + +typedef struct VkSubpassEndInfoKHR { + VkStructureType sType; + const void* pNext; +} VkSubpassEndInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateRenderPass2KHR)(VkDevice device, const VkRenderPassCreateInfo2KHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkRenderPass* pRenderPass); +typedef void (VKAPI_PTR *PFN_vkCmdBeginRenderPass2KHR)(VkCommandBuffer commandBuffer, const VkRenderPassBeginInfo* pRenderPassBegin, const VkSubpassBeginInfoKHR* pSubpassBeginInfo); +typedef void (VKAPI_PTR *PFN_vkCmdNextSubpass2KHR)(VkCommandBuffer commandBuffer, const VkSubpassBeginInfoKHR* pSubpassBeginInfo, const VkSubpassEndInfoKHR* pSubpassEndInfo); +typedef void (VKAPI_PTR *PFN_vkCmdEndRenderPass2KHR)(VkCommandBuffer commandBuffer, const VkSubpassEndInfoKHR* pSubpassEndInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass2KHR( + VkDevice device, + const VkRenderPassCreateInfo2KHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkRenderPass* pRenderPass); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginRenderPass2KHR( + VkCommandBuffer commandBuffer, + const VkRenderPassBeginInfo* pRenderPassBegin, + const VkSubpassBeginInfoKHR* pSubpassBeginInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdNextSubpass2KHR( + VkCommandBuffer commandBuffer, + const VkSubpassBeginInfoKHR* pSubpassBeginInfo, + const VkSubpassEndInfoKHR* pSubpassEndInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndRenderPass2KHR( + VkCommandBuffer commandBuffer, + const VkSubpassEndInfoKHR* pSubpassEndInfo); +#endif + + +#define VK_KHR_shared_presentable_image 1 +#define VK_KHR_SHARED_PRESENTABLE_IMAGE_SPEC_VERSION 1 +#define VK_KHR_SHARED_PRESENTABLE_IMAGE_EXTENSION_NAME "VK_KHR_shared_presentable_image" +typedef struct VkSharedPresentSurfaceCapabilitiesKHR { + VkStructureType sType; + void* pNext; + VkImageUsageFlags sharedPresentSupportedUsageFlags; +} VkSharedPresentSurfaceCapabilitiesKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainStatusKHR)(VkDevice device, VkSwapchainKHR swapchain); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainStatusKHR( + VkDevice device, + VkSwapchainKHR swapchain); +#endif + + +#define VK_KHR_external_fence_capabilities 1 +#define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_FENCE_CAPABILITIES_EXTENSION_NAME "VK_KHR_external_fence_capabilities" +typedef VkExternalFenceHandleTypeFlags VkExternalFenceHandleTypeFlagsKHR; + +typedef VkExternalFenceHandleTypeFlagBits VkExternalFenceHandleTypeFlagBitsKHR; + +typedef VkExternalFenceFeatureFlags VkExternalFenceFeatureFlagsKHR; + +typedef VkExternalFenceFeatureFlagBits VkExternalFenceFeatureFlagBitsKHR; + +typedef VkPhysicalDeviceExternalFenceInfo VkPhysicalDeviceExternalFenceInfoKHR; + +typedef VkExternalFenceProperties VkExternalFencePropertiesKHR; + +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalFencePropertiesKHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceExternalFencePropertiesKHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, + VkExternalFenceProperties* pExternalFenceProperties); +#endif + + +#define VK_KHR_external_fence 1 +#define VK_KHR_EXTERNAL_FENCE_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_FENCE_EXTENSION_NAME "VK_KHR_external_fence" +typedef VkFenceImportFlags VkFenceImportFlagsKHR; + +typedef VkFenceImportFlagBits VkFenceImportFlagBitsKHR; + +typedef VkExportFenceCreateInfo VkExportFenceCreateInfoKHR; + + + +#define VK_KHR_external_fence_fd 1 +#define VK_KHR_EXTERNAL_FENCE_FD_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_FENCE_FD_EXTENSION_NAME "VK_KHR_external_fence_fd" +typedef struct VkImportFenceFdInfoKHR { + VkStructureType sType; + const void* pNext; + VkFence fence; + VkFenceImportFlags flags; + VkExternalFenceHandleTypeFlagBits handleType; + int fd; +} VkImportFenceFdInfoKHR; + +typedef struct VkFenceGetFdInfoKHR { + VkStructureType sType; + const void* pNext; + VkFence fence; + VkExternalFenceHandleTypeFlagBits handleType; +} VkFenceGetFdInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkImportFenceFdKHR)(VkDevice device, const VkImportFenceFdInfoKHR* pImportFenceFdInfo); +typedef VkResult (VKAPI_PTR *PFN_vkGetFenceFdKHR)(VkDevice device, const VkFenceGetFdInfoKHR* pGetFdInfo, int* pFd); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkImportFenceFdKHR( + VkDevice device, + const VkImportFenceFdInfoKHR* pImportFenceFdInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceFdKHR( + VkDevice device, + const VkFenceGetFdInfoKHR* pGetFdInfo, + int* pFd); +#endif + + +#define VK_KHR_maintenance2 1 +#define VK_KHR_MAINTENANCE2_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE2_EXTENSION_NAME "VK_KHR_maintenance2" +typedef VkPointClippingBehavior VkPointClippingBehaviorKHR; + +typedef VkTessellationDomainOrigin VkTessellationDomainOriginKHR; + +typedef VkPhysicalDevicePointClippingProperties VkPhysicalDevicePointClippingPropertiesKHR; + +typedef VkRenderPassInputAttachmentAspectCreateInfo VkRenderPassInputAttachmentAspectCreateInfoKHR; + +typedef VkInputAttachmentAspectReference VkInputAttachmentAspectReferenceKHR; + +typedef VkImageViewUsageCreateInfo VkImageViewUsageCreateInfoKHR; + +typedef VkPipelineTessellationDomainOriginStateCreateInfo VkPipelineTessellationDomainOriginStateCreateInfoKHR; + + + +#define VK_KHR_get_surface_capabilities2 1 +#define VK_KHR_GET_SURFACE_CAPABILITIES_2_SPEC_VERSION 1 +#define VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME "VK_KHR_get_surface_capabilities2" +typedef struct VkPhysicalDeviceSurfaceInfo2KHR { + VkStructureType sType; + const void* pNext; + VkSurfaceKHR surface; +} VkPhysicalDeviceSurfaceInfo2KHR; + +typedef struct VkSurfaceCapabilities2KHR { + VkStructureType sType; + void* pNext; + VkSurfaceCapabilitiesKHR surfaceCapabilities; +} VkSurfaceCapabilities2KHR; + +typedef struct VkSurfaceFormat2KHR { + VkStructureType sType; + void* pNext; + VkSurfaceFormatKHR surfaceFormat; +} VkSurfaceFormat2KHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkSurfaceCapabilities2KHR* pSurfaceCapabilities); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceFormats2KHR)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pSurfaceFormatCount, VkSurfaceFormat2KHR* pSurfaceFormats); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2KHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, + VkSurfaceCapabilities2KHR* pSurfaceCapabilities); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceFormats2KHR( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, + uint32_t* pSurfaceFormatCount, + VkSurfaceFormat2KHR* pSurfaceFormats); +#endif + + +#define VK_KHR_variable_pointers 1 +#define VK_KHR_VARIABLE_POINTERS_SPEC_VERSION 1 +#define VK_KHR_VARIABLE_POINTERS_EXTENSION_NAME "VK_KHR_variable_pointers" +typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointerFeaturesKHR; + +typedef VkPhysicalDeviceVariablePointersFeatures VkPhysicalDeviceVariablePointersFeaturesKHR; + + + +#define VK_KHR_get_display_properties2 1 +#define VK_KHR_GET_DISPLAY_PROPERTIES_2_SPEC_VERSION 1 +#define VK_KHR_GET_DISPLAY_PROPERTIES_2_EXTENSION_NAME "VK_KHR_get_display_properties2" +typedef struct VkDisplayProperties2KHR { + VkStructureType sType; + void* pNext; + VkDisplayPropertiesKHR displayProperties; +} VkDisplayProperties2KHR; + +typedef struct VkDisplayPlaneProperties2KHR { + VkStructureType sType; + void* pNext; + VkDisplayPlanePropertiesKHR displayPlaneProperties; +} VkDisplayPlaneProperties2KHR; + +typedef struct VkDisplayModeProperties2KHR { + VkStructureType sType; + void* pNext; + VkDisplayModePropertiesKHR displayModeProperties; +} VkDisplayModeProperties2KHR; + +typedef struct VkDisplayPlaneInfo2KHR { + VkStructureType sType; + const void* pNext; + VkDisplayModeKHR mode; + uint32_t planeIndex; +} VkDisplayPlaneInfo2KHR; + +typedef struct VkDisplayPlaneCapabilities2KHR { + VkStructureType sType; + void* pNext; + VkDisplayPlaneCapabilitiesKHR capabilities; +} VkDisplayPlaneCapabilities2KHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayProperties2KHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceDisplayPlaneProperties2KHR)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkDisplayPlaneProperties2KHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayModeProperties2KHR)(VkPhysicalDevice physicalDevice, VkDisplayKHR display, uint32_t* pPropertyCount, VkDisplayModeProperties2KHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetDisplayPlaneCapabilities2KHR)(VkPhysicalDevice physicalDevice, const VkDisplayPlaneInfo2KHR* pDisplayPlaneInfo, VkDisplayPlaneCapabilities2KHR* pCapabilities); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayProperties2KHR( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkDisplayProperties2KHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceDisplayPlaneProperties2KHR( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkDisplayPlaneProperties2KHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayModeProperties2KHR( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display, + uint32_t* pPropertyCount, + VkDisplayModeProperties2KHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDisplayPlaneCapabilities2KHR( + VkPhysicalDevice physicalDevice, + const VkDisplayPlaneInfo2KHR* pDisplayPlaneInfo, + VkDisplayPlaneCapabilities2KHR* pCapabilities); +#endif + + +#define VK_KHR_dedicated_allocation 1 +#define VK_KHR_DEDICATED_ALLOCATION_SPEC_VERSION 3 +#define VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_KHR_dedicated_allocation" +typedef VkMemoryDedicatedRequirements VkMemoryDedicatedRequirementsKHR; + +typedef VkMemoryDedicatedAllocateInfo VkMemoryDedicatedAllocateInfoKHR; + + + +#define VK_KHR_storage_buffer_storage_class 1 +#define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_SPEC_VERSION 1 +#define VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME "VK_KHR_storage_buffer_storage_class" + + +#define VK_KHR_relaxed_block_layout 1 +#define VK_KHR_RELAXED_BLOCK_LAYOUT_SPEC_VERSION 1 +#define VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME "VK_KHR_relaxed_block_layout" + + +#define VK_KHR_get_memory_requirements2 1 +#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_SPEC_VERSION 1 +#define VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME "VK_KHR_get_memory_requirements2" +typedef VkBufferMemoryRequirementsInfo2 VkBufferMemoryRequirementsInfo2KHR; + +typedef VkImageMemoryRequirementsInfo2 VkImageMemoryRequirementsInfo2KHR; + +typedef VkImageSparseMemoryRequirementsInfo2 VkImageSparseMemoryRequirementsInfo2KHR; + +typedef VkSparseImageMemoryRequirements2 VkSparseImageMemoryRequirements2KHR; + +typedef void (VKAPI_PTR *PFN_vkGetImageMemoryRequirements2KHR)(VkDevice device, const VkImageMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetBufferMemoryRequirements2KHR)(VkDevice device, const VkBufferMemoryRequirementsInfo2* pInfo, VkMemoryRequirements2* pMemoryRequirements); +typedef void (VKAPI_PTR *PFN_vkGetImageSparseMemoryRequirements2KHR)(VkDevice device, const VkImageSparseMemoryRequirementsInfo2* pInfo, uint32_t* pSparseMemoryRequirementCount, VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetImageMemoryRequirements2KHR( + VkDevice device, + const VkImageMemoryRequirementsInfo2* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetBufferMemoryRequirements2KHR( + VkDevice device, + const VkBufferMemoryRequirementsInfo2* pInfo, + VkMemoryRequirements2* pMemoryRequirements); + +VKAPI_ATTR void VKAPI_CALL vkGetImageSparseMemoryRequirements2KHR( + VkDevice device, + const VkImageSparseMemoryRequirementsInfo2* pInfo, + uint32_t* pSparseMemoryRequirementCount, + VkSparseImageMemoryRequirements2* pSparseMemoryRequirements); +#endif + + +#define VK_KHR_image_format_list 1 +#define VK_KHR_IMAGE_FORMAT_LIST_SPEC_VERSION 1 +#define VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME "VK_KHR_image_format_list" +typedef struct VkImageFormatListCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t viewFormatCount; + const VkFormat* pViewFormats; +} VkImageFormatListCreateInfoKHR; + + + +#define VK_KHR_sampler_ycbcr_conversion 1 +typedef VkSamplerYcbcrConversion VkSamplerYcbcrConversionKHR; + +#define VK_KHR_SAMPLER_YCBCR_CONVERSION_SPEC_VERSION 14 +#define VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME "VK_KHR_sampler_ycbcr_conversion" +typedef VkSamplerYcbcrModelConversion VkSamplerYcbcrModelConversionKHR; + +typedef VkSamplerYcbcrRange VkSamplerYcbcrRangeKHR; + +typedef VkChromaLocation VkChromaLocationKHR; + +typedef VkSamplerYcbcrConversionCreateInfo VkSamplerYcbcrConversionCreateInfoKHR; + +typedef VkSamplerYcbcrConversionInfo VkSamplerYcbcrConversionInfoKHR; + +typedef VkBindImagePlaneMemoryInfo VkBindImagePlaneMemoryInfoKHR; + +typedef VkImagePlaneMemoryRequirementsInfo VkImagePlaneMemoryRequirementsInfoKHR; + +typedef VkPhysicalDeviceSamplerYcbcrConversionFeatures VkPhysicalDeviceSamplerYcbcrConversionFeaturesKHR; + +typedef VkSamplerYcbcrConversionImageFormatProperties VkSamplerYcbcrConversionImageFormatPropertiesKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateSamplerYcbcrConversionKHR)(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion); +typedef void (VKAPI_PTR *PFN_vkDestroySamplerYcbcrConversionKHR)(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateSamplerYcbcrConversionKHR( + VkDevice device, + const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSamplerYcbcrConversion* pYcbcrConversion); + +VKAPI_ATTR void VKAPI_CALL vkDestroySamplerYcbcrConversionKHR( + VkDevice device, + VkSamplerYcbcrConversion ycbcrConversion, + const VkAllocationCallbacks* pAllocator); +#endif + + +#define VK_KHR_bind_memory2 1 +#define VK_KHR_BIND_MEMORY_2_SPEC_VERSION 1 +#define VK_KHR_BIND_MEMORY_2_EXTENSION_NAME "VK_KHR_bind_memory2" +typedef VkBindBufferMemoryInfo VkBindBufferMemoryInfoKHR; + +typedef VkBindImageMemoryInfo VkBindImageMemoryInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkBindBufferMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindBufferMemoryInfo* pBindInfos); +typedef VkResult (VKAPI_PTR *PFN_vkBindImageMemory2KHR)(VkDevice device, uint32_t bindInfoCount, const VkBindImageMemoryInfo* pBindInfos); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkBindBufferMemory2KHR( + VkDevice device, + uint32_t bindInfoCount, + const VkBindBufferMemoryInfo* pBindInfos); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindImageMemory2KHR( + VkDevice device, + uint32_t bindInfoCount, + const VkBindImageMemoryInfo* pBindInfos); +#endif + + +#define VK_KHR_maintenance3 1 +#define VK_KHR_MAINTENANCE3_SPEC_VERSION 1 +#define VK_KHR_MAINTENANCE3_EXTENSION_NAME "VK_KHR_maintenance3" +typedef VkPhysicalDeviceMaintenance3Properties VkPhysicalDeviceMaintenance3PropertiesKHR; + +typedef VkDescriptorSetLayoutSupport VkDescriptorSetLayoutSupportKHR; + +typedef void (VKAPI_PTR *PFN_vkGetDescriptorSetLayoutSupportKHR)(VkDevice device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo, VkDescriptorSetLayoutSupport* pSupport); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkGetDescriptorSetLayoutSupportKHR( + VkDevice device, + const VkDescriptorSetLayoutCreateInfo* pCreateInfo, + VkDescriptorSetLayoutSupport* pSupport); +#endif + + +#define VK_KHR_draw_indirect_count 1 +#define VK_KHR_DRAW_INDIRECT_COUNT_SPEC_VERSION 1 +#define VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_KHR_draw_indirect_count" +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountKHR)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountKHR)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountKHR( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountKHR( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); +#endif + + +#define VK_KHR_shader_subgroup_extended_types 1 +#define VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_SPEC_VERSION 1 +#define VK_KHR_SHADER_SUBGROUP_EXTENDED_TYPES_EXTENSION_NAME "VK_KHR_shader_subgroup_extended_types" +typedef struct VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderSubgroupExtendedTypes; +} VkPhysicalDeviceShaderSubgroupExtendedTypesFeaturesKHR; + + + +#define VK_KHR_8bit_storage 1 +#define VK_KHR_8BIT_STORAGE_SPEC_VERSION 1 +#define VK_KHR_8BIT_STORAGE_EXTENSION_NAME "VK_KHR_8bit_storage" +typedef struct VkPhysicalDevice8BitStorageFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 storageBuffer8BitAccess; + VkBool32 uniformAndStorageBuffer8BitAccess; + VkBool32 storagePushConstant8; +} VkPhysicalDevice8BitStorageFeaturesKHR; + + + +#define VK_KHR_shader_atomic_int64 1 +#define VK_KHR_SHADER_ATOMIC_INT64_SPEC_VERSION 1 +#define VK_KHR_SHADER_ATOMIC_INT64_EXTENSION_NAME "VK_KHR_shader_atomic_int64" +typedef struct VkPhysicalDeviceShaderAtomicInt64FeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderBufferInt64Atomics; + VkBool32 shaderSharedInt64Atomics; +} VkPhysicalDeviceShaderAtomicInt64FeaturesKHR; + + + +#define VK_KHR_shader_clock 1 +#define VK_KHR_SHADER_CLOCK_SPEC_VERSION 1 +#define VK_KHR_SHADER_CLOCK_EXTENSION_NAME "VK_KHR_shader_clock" +typedef struct VkPhysicalDeviceShaderClockFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 shaderSubgroupClock; + VkBool32 shaderDeviceClock; +} VkPhysicalDeviceShaderClockFeaturesKHR; + + + +#define VK_KHR_driver_properties 1 +#define VK_MAX_DRIVER_NAME_SIZE_KHR 256 +#define VK_MAX_DRIVER_INFO_SIZE_KHR 256 +#define VK_KHR_DRIVER_PROPERTIES_SPEC_VERSION 1 +#define VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME "VK_KHR_driver_properties" + +typedef enum VkDriverIdKHR { + VK_DRIVER_ID_AMD_PROPRIETARY_KHR = 1, + VK_DRIVER_ID_AMD_OPEN_SOURCE_KHR = 2, + VK_DRIVER_ID_MESA_RADV_KHR = 3, + VK_DRIVER_ID_NVIDIA_PROPRIETARY_KHR = 4, + VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS_KHR = 5, + VK_DRIVER_ID_INTEL_OPEN_SOURCE_MESA_KHR = 6, + VK_DRIVER_ID_IMAGINATION_PROPRIETARY_KHR = 7, + VK_DRIVER_ID_QUALCOMM_PROPRIETARY_KHR = 8, + VK_DRIVER_ID_ARM_PROPRIETARY_KHR = 9, + VK_DRIVER_ID_GOOGLE_SWIFTSHADER_KHR = 10, + VK_DRIVER_ID_GGP_PROPRIETARY_KHR = 11, + VK_DRIVER_ID_BROADCOM_PROPRIETARY_KHR = 12, + VK_DRIVER_ID_BEGIN_RANGE_KHR = VK_DRIVER_ID_AMD_PROPRIETARY_KHR, + VK_DRIVER_ID_END_RANGE_KHR = VK_DRIVER_ID_BROADCOM_PROPRIETARY_KHR, + VK_DRIVER_ID_RANGE_SIZE_KHR = (VK_DRIVER_ID_BROADCOM_PROPRIETARY_KHR - VK_DRIVER_ID_AMD_PROPRIETARY_KHR + 1), + VK_DRIVER_ID_MAX_ENUM_KHR = 0x7FFFFFFF +} VkDriverIdKHR; +typedef struct VkConformanceVersionKHR { + uint8_t major; + uint8_t minor; + uint8_t subminor; + uint8_t patch; +} VkConformanceVersionKHR; + +typedef struct VkPhysicalDeviceDriverPropertiesKHR { + VkStructureType sType; + void* pNext; + VkDriverIdKHR driverID; + char driverName[VK_MAX_DRIVER_NAME_SIZE_KHR]; + char driverInfo[VK_MAX_DRIVER_INFO_SIZE_KHR]; + VkConformanceVersionKHR conformanceVersion; +} VkPhysicalDeviceDriverPropertiesKHR; + + + +#define VK_KHR_shader_float_controls 1 +#define VK_KHR_SHADER_FLOAT_CONTROLS_SPEC_VERSION 4 +#define VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME "VK_KHR_shader_float_controls" + +typedef enum VkShaderFloatControlsIndependenceKHR { + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR = 0, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_ALL_KHR = 1, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR = 2, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_BEGIN_RANGE_KHR = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_END_RANGE_KHR = VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR, + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_RANGE_SIZE_KHR = (VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_NONE_KHR - VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_32_BIT_ONLY_KHR + 1), + VK_SHADER_FLOAT_CONTROLS_INDEPENDENCE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkShaderFloatControlsIndependenceKHR; +typedef struct VkPhysicalDeviceFloatControlsPropertiesKHR { + VkStructureType sType; + void* pNext; + VkShaderFloatControlsIndependenceKHR denormBehaviorIndependence; + VkShaderFloatControlsIndependenceKHR roundingModeIndependence; + VkBool32 shaderSignedZeroInfNanPreserveFloat16; + VkBool32 shaderSignedZeroInfNanPreserveFloat32; + VkBool32 shaderSignedZeroInfNanPreserveFloat64; + VkBool32 shaderDenormPreserveFloat16; + VkBool32 shaderDenormPreserveFloat32; + VkBool32 shaderDenormPreserveFloat64; + VkBool32 shaderDenormFlushToZeroFloat16; + VkBool32 shaderDenormFlushToZeroFloat32; + VkBool32 shaderDenormFlushToZeroFloat64; + VkBool32 shaderRoundingModeRTEFloat16; + VkBool32 shaderRoundingModeRTEFloat32; + VkBool32 shaderRoundingModeRTEFloat64; + VkBool32 shaderRoundingModeRTZFloat16; + VkBool32 shaderRoundingModeRTZFloat32; + VkBool32 shaderRoundingModeRTZFloat64; +} VkPhysicalDeviceFloatControlsPropertiesKHR; + + + +#define VK_KHR_depth_stencil_resolve 1 +#define VK_KHR_DEPTH_STENCIL_RESOLVE_SPEC_VERSION 1 +#define VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME "VK_KHR_depth_stencil_resolve" + +typedef enum VkResolveModeFlagBitsKHR { + VK_RESOLVE_MODE_NONE_KHR = 0, + VK_RESOLVE_MODE_SAMPLE_ZERO_BIT_KHR = 0x00000001, + VK_RESOLVE_MODE_AVERAGE_BIT_KHR = 0x00000002, + VK_RESOLVE_MODE_MIN_BIT_KHR = 0x00000004, + VK_RESOLVE_MODE_MAX_BIT_KHR = 0x00000008, + VK_RESOLVE_MODE_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkResolveModeFlagBitsKHR; +typedef VkFlags VkResolveModeFlagsKHR; +typedef struct VkSubpassDescriptionDepthStencilResolveKHR { + VkStructureType sType; + const void* pNext; + VkResolveModeFlagBitsKHR depthResolveMode; + VkResolveModeFlagBitsKHR stencilResolveMode; + const VkAttachmentReference2KHR* pDepthStencilResolveAttachment; +} VkSubpassDescriptionDepthStencilResolveKHR; + +typedef struct VkPhysicalDeviceDepthStencilResolvePropertiesKHR { + VkStructureType sType; + void* pNext; + VkResolveModeFlagsKHR supportedDepthResolveModes; + VkResolveModeFlagsKHR supportedStencilResolveModes; + VkBool32 independentResolveNone; + VkBool32 independentResolve; +} VkPhysicalDeviceDepthStencilResolvePropertiesKHR; + + + +#define VK_KHR_swapchain_mutable_format 1 +#define VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_SPEC_VERSION 1 +#define VK_KHR_SWAPCHAIN_MUTABLE_FORMAT_EXTENSION_NAME "VK_KHR_swapchain_mutable_format" + + +#define VK_KHR_timeline_semaphore 1 +#define VK_KHR_TIMELINE_SEMAPHORE_SPEC_VERSION 2 +#define VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME "VK_KHR_timeline_semaphore" + +typedef enum VkSemaphoreTypeKHR { + VK_SEMAPHORE_TYPE_BINARY_KHR = 0, + VK_SEMAPHORE_TYPE_TIMELINE_KHR = 1, + VK_SEMAPHORE_TYPE_BEGIN_RANGE_KHR = VK_SEMAPHORE_TYPE_BINARY_KHR, + VK_SEMAPHORE_TYPE_END_RANGE_KHR = VK_SEMAPHORE_TYPE_TIMELINE_KHR, + VK_SEMAPHORE_TYPE_RANGE_SIZE_KHR = (VK_SEMAPHORE_TYPE_TIMELINE_KHR - VK_SEMAPHORE_TYPE_BINARY_KHR + 1), + VK_SEMAPHORE_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSemaphoreTypeKHR; + +typedef enum VkSemaphoreWaitFlagBitsKHR { + VK_SEMAPHORE_WAIT_ANY_BIT_KHR = 0x00000001, + VK_SEMAPHORE_WAIT_FLAG_BITS_MAX_ENUM_KHR = 0x7FFFFFFF +} VkSemaphoreWaitFlagBitsKHR; +typedef VkFlags VkSemaphoreWaitFlagsKHR; +typedef struct VkPhysicalDeviceTimelineSemaphoreFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 timelineSemaphore; +} VkPhysicalDeviceTimelineSemaphoreFeaturesKHR; + +typedef struct VkPhysicalDeviceTimelineSemaphorePropertiesKHR { + VkStructureType sType; + void* pNext; + uint64_t maxTimelineSemaphoreValueDifference; +} VkPhysicalDeviceTimelineSemaphorePropertiesKHR; + +typedef struct VkSemaphoreTypeCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphoreTypeKHR semaphoreType; + uint64_t initialValue; +} VkSemaphoreTypeCreateInfoKHR; + +typedef struct VkTimelineSemaphoreSubmitInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreValueCount; + const uint64_t* pWaitSemaphoreValues; + uint32_t signalSemaphoreValueCount; + const uint64_t* pSignalSemaphoreValues; +} VkTimelineSemaphoreSubmitInfoKHR; + +typedef struct VkSemaphoreWaitInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphoreWaitFlagsKHR flags; + uint32_t semaphoreCount; + const VkSemaphore* pSemaphores; + const uint64_t* pValues; +} VkSemaphoreWaitInfoKHR; + +typedef struct VkSemaphoreSignalInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + uint64_t value; +} VkSemaphoreSignalInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreCounterValueKHR)(VkDevice device, VkSemaphore semaphore, uint64_t* pValue); +typedef VkResult (VKAPI_PTR *PFN_vkWaitSemaphoresKHR)(VkDevice device, const VkSemaphoreWaitInfoKHR* pWaitInfo, uint64_t timeout); +typedef VkResult (VKAPI_PTR *PFN_vkSignalSemaphoreKHR)(VkDevice device, const VkSemaphoreSignalInfoKHR* pSignalInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreCounterValueKHR( + VkDevice device, + VkSemaphore semaphore, + uint64_t* pValue); + +VKAPI_ATTR VkResult VKAPI_CALL vkWaitSemaphoresKHR( + VkDevice device, + const VkSemaphoreWaitInfoKHR* pWaitInfo, + uint64_t timeout); + +VKAPI_ATTR VkResult VKAPI_CALL vkSignalSemaphoreKHR( + VkDevice device, + const VkSemaphoreSignalInfoKHR* pSignalInfo); +#endif + + +#define VK_KHR_vulkan_memory_model 1 +#define VK_KHR_VULKAN_MEMORY_MODEL_SPEC_VERSION 3 +#define VK_KHR_VULKAN_MEMORY_MODEL_EXTENSION_NAME "VK_KHR_vulkan_memory_model" +typedef struct VkPhysicalDeviceVulkanMemoryModelFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 vulkanMemoryModel; + VkBool32 vulkanMemoryModelDeviceScope; + VkBool32 vulkanMemoryModelAvailabilityVisibilityChains; +} VkPhysicalDeviceVulkanMemoryModelFeaturesKHR; + + + +#define VK_KHR_spirv_1_4 1 +#define VK_KHR_SPIRV_1_4_SPEC_VERSION 1 +#define VK_KHR_SPIRV_1_4_EXTENSION_NAME "VK_KHR_spirv_1_4" + + +#define VK_KHR_surface_protected_capabilities 1 +#define VK_KHR_SURFACE_PROTECTED_CAPABILITIES_SPEC_VERSION 1 +#define VK_KHR_SURFACE_PROTECTED_CAPABILITIES_EXTENSION_NAME "VK_KHR_surface_protected_capabilities" +typedef struct VkSurfaceProtectedCapabilitiesKHR { + VkStructureType sType; + const void* pNext; + VkBool32 supportsProtected; +} VkSurfaceProtectedCapabilitiesKHR; + + + +#define VK_KHR_uniform_buffer_standard_layout 1 +#define VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_SPEC_VERSION 1 +#define VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME "VK_KHR_uniform_buffer_standard_layout" +typedef struct VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 uniformBufferStandardLayout; +} VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR; + + + +#define VK_KHR_pipeline_executable_properties 1 +#define VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_SPEC_VERSION 1 +#define VK_KHR_PIPELINE_EXECUTABLE_PROPERTIES_EXTENSION_NAME "VK_KHR_pipeline_executable_properties" + +typedef enum VkPipelineExecutableStatisticFormatKHR { + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_BOOL32_KHR = 0, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_INT64_KHR = 1, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_UINT64_KHR = 2, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR = 3, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_BEGIN_RANGE_KHR = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_BOOL32_KHR, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_END_RANGE_KHR = VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR, + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_RANGE_SIZE_KHR = (VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_FLOAT64_KHR - VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_BOOL32_KHR + 1), + VK_PIPELINE_EXECUTABLE_STATISTIC_FORMAT_MAX_ENUM_KHR = 0x7FFFFFFF +} VkPipelineExecutableStatisticFormatKHR; +typedef struct VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR { + VkStructureType sType; + void* pNext; + VkBool32 pipelineExecutableInfo; +} VkPhysicalDevicePipelineExecutablePropertiesFeaturesKHR; + +typedef struct VkPipelineInfoKHR { + VkStructureType sType; + const void* pNext; + VkPipeline pipeline; +} VkPipelineInfoKHR; + +typedef struct VkPipelineExecutablePropertiesKHR { + VkStructureType sType; + void* pNext; + VkShaderStageFlags stages; + char name[VK_MAX_DESCRIPTION_SIZE]; + char description[VK_MAX_DESCRIPTION_SIZE]; + uint32_t subgroupSize; +} VkPipelineExecutablePropertiesKHR; + +typedef struct VkPipelineExecutableInfoKHR { + VkStructureType sType; + const void* pNext; + VkPipeline pipeline; + uint32_t executableIndex; +} VkPipelineExecutableInfoKHR; + +typedef union VkPipelineExecutableStatisticValueKHR { + VkBool32 b32; + int64_t i64; + uint64_t u64; + double f64; +} VkPipelineExecutableStatisticValueKHR; + +typedef struct VkPipelineExecutableStatisticKHR { + VkStructureType sType; + void* pNext; + char name[VK_MAX_DESCRIPTION_SIZE]; + char description[VK_MAX_DESCRIPTION_SIZE]; + VkPipelineExecutableStatisticFormatKHR format; + VkPipelineExecutableStatisticValueKHR value; +} VkPipelineExecutableStatisticKHR; + +typedef struct VkPipelineExecutableInternalRepresentationKHR { + VkStructureType sType; + void* pNext; + char name[VK_MAX_DESCRIPTION_SIZE]; + char description[VK_MAX_DESCRIPTION_SIZE]; + VkBool32 isText; + size_t dataSize; + void* pData; +} VkPipelineExecutableInternalRepresentationKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineExecutablePropertiesKHR)(VkDevice device, const VkPipelineInfoKHR* pPipelineInfo, uint32_t* pExecutableCount, VkPipelineExecutablePropertiesKHR* pProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineExecutableStatisticsKHR)(VkDevice device, const VkPipelineExecutableInfoKHR* pExecutableInfo, uint32_t* pStatisticCount, VkPipelineExecutableStatisticKHR* pStatistics); +typedef VkResult (VKAPI_PTR *PFN_vkGetPipelineExecutableInternalRepresentationsKHR)(VkDevice device, const VkPipelineExecutableInfoKHR* pExecutableInfo, uint32_t* pInternalRepresentationCount, VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineExecutablePropertiesKHR( + VkDevice device, + const VkPipelineInfoKHR* pPipelineInfo, + uint32_t* pExecutableCount, + VkPipelineExecutablePropertiesKHR* pProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineExecutableStatisticsKHR( + VkDevice device, + const VkPipelineExecutableInfoKHR* pExecutableInfo, + uint32_t* pStatisticCount, + VkPipelineExecutableStatisticKHR* pStatistics); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPipelineExecutableInternalRepresentationsKHR( + VkDevice device, + const VkPipelineExecutableInfoKHR* pExecutableInfo, + uint32_t* pInternalRepresentationCount, + VkPipelineExecutableInternalRepresentationKHR* pInternalRepresentations); +#endif + + +#define VK_EXT_debug_report 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugReportCallbackEXT) +#define VK_EXT_DEBUG_REPORT_SPEC_VERSION 9 +#define VK_EXT_DEBUG_REPORT_EXTENSION_NAME "VK_EXT_debug_report" + +typedef enum VkDebugReportObjectTypeEXT { + VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT = 0, + VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT = 1, + VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT = 2, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT = 3, + VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT = 4, + VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT = 5, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT = 6, + VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT = 7, + VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT = 8, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT = 9, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT = 10, + VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT = 11, + VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT = 12, + VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT = 13, + VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT = 14, + VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT = 15, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT = 16, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT = 17, + VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT = 18, + VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT = 19, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT = 20, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT = 21, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT = 22, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT = 23, + VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT = 24, + VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT = 25, + VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT = 26, + VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT = 27, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT = 28, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT = 29, + VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT = 30, + VK_DEBUG_REPORT_OBJECT_TYPE_OBJECT_TABLE_NVX_EXT = 31, + VK_DEBUG_REPORT_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NVX_EXT = 32, + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT = 33, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT = 1000156000, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT = 1000085000, + VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT = 1000165000, + VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_KHR_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_BEGIN_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_END_RANGE_EXT = VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT, + VK_DEBUG_REPORT_OBJECT_TYPE_RANGE_SIZE_EXT = (VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT - VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT + 1), + VK_DEBUG_REPORT_OBJECT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugReportObjectTypeEXT; + +typedef enum VkDebugReportFlagBitsEXT { + VK_DEBUG_REPORT_INFORMATION_BIT_EXT = 0x00000001, + VK_DEBUG_REPORT_WARNING_BIT_EXT = 0x00000002, + VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT = 0x00000004, + VK_DEBUG_REPORT_ERROR_BIT_EXT = 0x00000008, + VK_DEBUG_REPORT_DEBUG_BIT_EXT = 0x00000010, + VK_DEBUG_REPORT_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugReportFlagBitsEXT; +typedef VkFlags VkDebugReportFlagsEXT; +typedef VkBool32 (VKAPI_PTR *PFN_vkDebugReportCallbackEXT)( + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage, + void* pUserData); + +typedef struct VkDebugReportCallbackCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportFlagsEXT flags; + PFN_vkDebugReportCallbackEXT pfnCallback; + void* pUserData; +} VkDebugReportCallbackCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugReportCallbackEXT)(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugReportCallbackEXT* pCallback); +typedef void (VKAPI_PTR *PFN_vkDestroyDebugReportCallbackEXT)(VkInstance instance, VkDebugReportCallbackEXT callback, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkDebugReportMessageEXT)(VkInstance instance, VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, size_t location, int32_t messageCode, const char* pLayerPrefix, const char* pMessage); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugReportCallbackEXT( + VkInstance instance, + const VkDebugReportCallbackCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugReportCallbackEXT* pCallback); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDebugReportCallbackEXT( + VkInstance instance, + VkDebugReportCallbackEXT callback, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkDebugReportMessageEXT( + VkInstance instance, + VkDebugReportFlagsEXT flags, + VkDebugReportObjectTypeEXT objectType, + uint64_t object, + size_t location, + int32_t messageCode, + const char* pLayerPrefix, + const char* pMessage); +#endif + + +#define VK_NV_glsl_shader 1 +#define VK_NV_GLSL_SHADER_SPEC_VERSION 1 +#define VK_NV_GLSL_SHADER_EXTENSION_NAME "VK_NV_glsl_shader" + + +#define VK_EXT_depth_range_unrestricted 1 +#define VK_EXT_DEPTH_RANGE_UNRESTRICTED_SPEC_VERSION 1 +#define VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME "VK_EXT_depth_range_unrestricted" + + +#define VK_IMG_filter_cubic 1 +#define VK_IMG_FILTER_CUBIC_SPEC_VERSION 1 +#define VK_IMG_FILTER_CUBIC_EXTENSION_NAME "VK_IMG_filter_cubic" + + +#define VK_AMD_rasterization_order 1 +#define VK_AMD_RASTERIZATION_ORDER_SPEC_VERSION 1 +#define VK_AMD_RASTERIZATION_ORDER_EXTENSION_NAME "VK_AMD_rasterization_order" + +typedef enum VkRasterizationOrderAMD { + VK_RASTERIZATION_ORDER_STRICT_AMD = 0, + VK_RASTERIZATION_ORDER_RELAXED_AMD = 1, + VK_RASTERIZATION_ORDER_BEGIN_RANGE_AMD = VK_RASTERIZATION_ORDER_STRICT_AMD, + VK_RASTERIZATION_ORDER_END_RANGE_AMD = VK_RASTERIZATION_ORDER_RELAXED_AMD, + VK_RASTERIZATION_ORDER_RANGE_SIZE_AMD = (VK_RASTERIZATION_ORDER_RELAXED_AMD - VK_RASTERIZATION_ORDER_STRICT_AMD + 1), + VK_RASTERIZATION_ORDER_MAX_ENUM_AMD = 0x7FFFFFFF +} VkRasterizationOrderAMD; +typedef struct VkPipelineRasterizationStateRasterizationOrderAMD { + VkStructureType sType; + const void* pNext; + VkRasterizationOrderAMD rasterizationOrder; +} VkPipelineRasterizationStateRasterizationOrderAMD; + + + +#define VK_AMD_shader_trinary_minmax 1 +#define VK_AMD_SHADER_TRINARY_MINMAX_SPEC_VERSION 1 +#define VK_AMD_SHADER_TRINARY_MINMAX_EXTENSION_NAME "VK_AMD_shader_trinary_minmax" + + +#define VK_AMD_shader_explicit_vertex_parameter 1 +#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_SPEC_VERSION 1 +#define VK_AMD_SHADER_EXPLICIT_VERTEX_PARAMETER_EXTENSION_NAME "VK_AMD_shader_explicit_vertex_parameter" + + +#define VK_EXT_debug_marker 1 +#define VK_EXT_DEBUG_MARKER_SPEC_VERSION 4 +#define VK_EXT_DEBUG_MARKER_EXTENSION_NAME "VK_EXT_debug_marker" +typedef struct VkDebugMarkerObjectNameInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportObjectTypeEXT objectType; + uint64_t object; + const char* pObjectName; +} VkDebugMarkerObjectNameInfoEXT; + +typedef struct VkDebugMarkerObjectTagInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugReportObjectTypeEXT objectType; + uint64_t object; + uint64_t tagName; + size_t tagSize; + const void* pTag; +} VkDebugMarkerObjectTagInfoEXT; + +typedef struct VkDebugMarkerMarkerInfoEXT { + VkStructureType sType; + const void* pNext; + const char* pMarkerName; + float color[4]; +} VkDebugMarkerMarkerInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectTagEXT)(VkDevice device, const VkDebugMarkerObjectTagInfoEXT* pTagInfo); +typedef VkResult (VKAPI_PTR *PFN_vkDebugMarkerSetObjectNameEXT)(VkDevice device, const VkDebugMarkerObjectNameInfoEXT* pNameInfo); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerBeginEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerEndEXT)(VkCommandBuffer commandBuffer); +typedef void (VKAPI_PTR *PFN_vkCmdDebugMarkerInsertEXT)(VkCommandBuffer commandBuffer, const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectTagEXT( + VkDevice device, + const VkDebugMarkerObjectTagInfoEXT* pTagInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkDebugMarkerSetObjectNameEXT( + VkDevice device, + const VkDebugMarkerObjectNameInfoEXT* pNameInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerBeginEXT( + VkCommandBuffer commandBuffer, + const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerEndEXT( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR void VKAPI_CALL vkCmdDebugMarkerInsertEXT( + VkCommandBuffer commandBuffer, + const VkDebugMarkerMarkerInfoEXT* pMarkerInfo); +#endif + + +#define VK_AMD_gcn_shader 1 +#define VK_AMD_GCN_SHADER_SPEC_VERSION 1 +#define VK_AMD_GCN_SHADER_EXTENSION_NAME "VK_AMD_gcn_shader" + + +#define VK_NV_dedicated_allocation 1 +#define VK_NV_DEDICATED_ALLOCATION_SPEC_VERSION 1 +#define VK_NV_DEDICATED_ALLOCATION_EXTENSION_NAME "VK_NV_dedicated_allocation" +typedef struct VkDedicatedAllocationImageCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 dedicatedAllocation; +} VkDedicatedAllocationImageCreateInfoNV; + +typedef struct VkDedicatedAllocationBufferCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 dedicatedAllocation; +} VkDedicatedAllocationBufferCreateInfoNV; + +typedef struct VkDedicatedAllocationMemoryAllocateInfoNV { + VkStructureType sType; + const void* pNext; + VkImage image; + VkBuffer buffer; +} VkDedicatedAllocationMemoryAllocateInfoNV; + + + +#define VK_EXT_transform_feedback 1 +#define VK_EXT_TRANSFORM_FEEDBACK_SPEC_VERSION 1 +#define VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME "VK_EXT_transform_feedback" +typedef VkFlags VkPipelineRasterizationStateStreamCreateFlagsEXT; +typedef struct VkPhysicalDeviceTransformFeedbackFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 transformFeedback; + VkBool32 geometryStreams; +} VkPhysicalDeviceTransformFeedbackFeaturesEXT; + +typedef struct VkPhysicalDeviceTransformFeedbackPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxTransformFeedbackStreams; + uint32_t maxTransformFeedbackBuffers; + VkDeviceSize maxTransformFeedbackBufferSize; + uint32_t maxTransformFeedbackStreamDataSize; + uint32_t maxTransformFeedbackBufferDataSize; + uint32_t maxTransformFeedbackBufferDataStride; + VkBool32 transformFeedbackQueries; + VkBool32 transformFeedbackStreamsLinesTriangles; + VkBool32 transformFeedbackRasterizationStreamSelect; + VkBool32 transformFeedbackDraw; +} VkPhysicalDeviceTransformFeedbackPropertiesEXT; + +typedef struct VkPipelineRasterizationStateStreamCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPipelineRasterizationStateStreamCreateFlagsEXT flags; + uint32_t rasterizationStream; +} VkPipelineRasterizationStateStreamCreateInfoEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdBindTransformFeedbackBuffersEXT)(VkCommandBuffer commandBuffer, uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets, const VkDeviceSize* pSizes); +typedef void (VKAPI_PTR *PFN_vkCmdBeginTransformFeedbackEXT)(VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer, uint32_t counterBufferCount, const VkBuffer* pCounterBuffers, const VkDeviceSize* pCounterBufferOffsets); +typedef void (VKAPI_PTR *PFN_vkCmdEndTransformFeedbackEXT)(VkCommandBuffer commandBuffer, uint32_t firstCounterBuffer, uint32_t counterBufferCount, const VkBuffer* pCounterBuffers, const VkDeviceSize* pCounterBufferOffsets); +typedef void (VKAPI_PTR *PFN_vkCmdBeginQueryIndexedEXT)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, VkQueryControlFlags flags, uint32_t index); +typedef void (VKAPI_PTR *PFN_vkCmdEndQueryIndexedEXT)(VkCommandBuffer commandBuffer, VkQueryPool queryPool, uint32_t query, uint32_t index); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectByteCountEXT)(VkCommandBuffer commandBuffer, uint32_t instanceCount, uint32_t firstInstance, VkBuffer counterBuffer, VkDeviceSize counterBufferOffset, uint32_t counterOffset, uint32_t vertexStride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdBindTransformFeedbackBuffersEXT( + VkCommandBuffer commandBuffer, + uint32_t firstBinding, + uint32_t bindingCount, + const VkBuffer* pBuffers, + const VkDeviceSize* pOffsets, + const VkDeviceSize* pSizes); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginTransformFeedbackEXT( + VkCommandBuffer commandBuffer, + uint32_t firstCounterBuffer, + uint32_t counterBufferCount, + const VkBuffer* pCounterBuffers, + const VkDeviceSize* pCounterBufferOffsets); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndTransformFeedbackEXT( + VkCommandBuffer commandBuffer, + uint32_t firstCounterBuffer, + uint32_t counterBufferCount, + const VkBuffer* pCounterBuffers, + const VkDeviceSize* pCounterBufferOffsets); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginQueryIndexedEXT( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + VkQueryControlFlags flags, + uint32_t index); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndQueryIndexedEXT( + VkCommandBuffer commandBuffer, + VkQueryPool queryPool, + uint32_t query, + uint32_t index); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectByteCountEXT( + VkCommandBuffer commandBuffer, + uint32_t instanceCount, + uint32_t firstInstance, + VkBuffer counterBuffer, + VkDeviceSize counterBufferOffset, + uint32_t counterOffset, + uint32_t vertexStride); +#endif + + +#define VK_NVX_image_view_handle 1 +#define VK_NVX_IMAGE_VIEW_HANDLE_SPEC_VERSION 1 +#define VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME "VK_NVX_image_view_handle" +typedef struct VkImageViewHandleInfoNVX { + VkStructureType sType; + const void* pNext; + VkImageView imageView; + VkDescriptorType descriptorType; + VkSampler sampler; +} VkImageViewHandleInfoNVX; + +typedef uint32_t (VKAPI_PTR *PFN_vkGetImageViewHandleNVX)(VkDevice device, const VkImageViewHandleInfoNVX* pInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR uint32_t VKAPI_CALL vkGetImageViewHandleNVX( + VkDevice device, + const VkImageViewHandleInfoNVX* pInfo); +#endif + + +#define VK_AMD_draw_indirect_count 1 +#define VK_AMD_DRAW_INDIRECT_COUNT_SPEC_VERSION 2 +#define VK_AMD_DRAW_INDIRECT_COUNT_EXTENSION_NAME "VK_AMD_draw_indirect_count" +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawIndexedIndirectCountAMD)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndirectCountAMD( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawIndexedIndirectCountAMD( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); +#endif + + +#define VK_AMD_negative_viewport_height 1 +#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_SPEC_VERSION 1 +#define VK_AMD_NEGATIVE_VIEWPORT_HEIGHT_EXTENSION_NAME "VK_AMD_negative_viewport_height" + + +#define VK_AMD_gpu_shader_half_float 1 +#define VK_AMD_GPU_SHADER_HALF_FLOAT_SPEC_VERSION 2 +#define VK_AMD_GPU_SHADER_HALF_FLOAT_EXTENSION_NAME "VK_AMD_gpu_shader_half_float" + + +#define VK_AMD_shader_ballot 1 +#define VK_AMD_SHADER_BALLOT_SPEC_VERSION 1 +#define VK_AMD_SHADER_BALLOT_EXTENSION_NAME "VK_AMD_shader_ballot" + + +#define VK_AMD_texture_gather_bias_lod 1 +#define VK_AMD_TEXTURE_GATHER_BIAS_LOD_SPEC_VERSION 1 +#define VK_AMD_TEXTURE_GATHER_BIAS_LOD_EXTENSION_NAME "VK_AMD_texture_gather_bias_lod" +typedef struct VkTextureLODGatherFormatPropertiesAMD { + VkStructureType sType; + void* pNext; + VkBool32 supportsTextureGatherLODBiasAMD; +} VkTextureLODGatherFormatPropertiesAMD; + + + +#define VK_AMD_shader_info 1 +#define VK_AMD_SHADER_INFO_SPEC_VERSION 1 +#define VK_AMD_SHADER_INFO_EXTENSION_NAME "VK_AMD_shader_info" + +typedef enum VkShaderInfoTypeAMD { + VK_SHADER_INFO_TYPE_STATISTICS_AMD = 0, + VK_SHADER_INFO_TYPE_BINARY_AMD = 1, + VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD = 2, + VK_SHADER_INFO_TYPE_BEGIN_RANGE_AMD = VK_SHADER_INFO_TYPE_STATISTICS_AMD, + VK_SHADER_INFO_TYPE_END_RANGE_AMD = VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD, + VK_SHADER_INFO_TYPE_RANGE_SIZE_AMD = (VK_SHADER_INFO_TYPE_DISASSEMBLY_AMD - VK_SHADER_INFO_TYPE_STATISTICS_AMD + 1), + VK_SHADER_INFO_TYPE_MAX_ENUM_AMD = 0x7FFFFFFF +} VkShaderInfoTypeAMD; +typedef struct VkShaderResourceUsageAMD { + uint32_t numUsedVgprs; + uint32_t numUsedSgprs; + uint32_t ldsSizePerLocalWorkGroup; + size_t ldsUsageSizeInBytes; + size_t scratchMemUsageInBytes; +} VkShaderResourceUsageAMD; + +typedef struct VkShaderStatisticsInfoAMD { + VkShaderStageFlags shaderStageMask; + VkShaderResourceUsageAMD resourceUsage; + uint32_t numPhysicalVgprs; + uint32_t numPhysicalSgprs; + uint32_t numAvailableVgprs; + uint32_t numAvailableSgprs; + uint32_t computeWorkGroupSize[3]; +} VkShaderStatisticsInfoAMD; + +typedef VkResult (VKAPI_PTR *PFN_vkGetShaderInfoAMD)(VkDevice device, VkPipeline pipeline, VkShaderStageFlagBits shaderStage, VkShaderInfoTypeAMD infoType, size_t* pInfoSize, void* pInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetShaderInfoAMD( + VkDevice device, + VkPipeline pipeline, + VkShaderStageFlagBits shaderStage, + VkShaderInfoTypeAMD infoType, + size_t* pInfoSize, + void* pInfo); +#endif + + +#define VK_AMD_shader_image_load_store_lod 1 +#define VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_SPEC_VERSION 1 +#define VK_AMD_SHADER_IMAGE_LOAD_STORE_LOD_EXTENSION_NAME "VK_AMD_shader_image_load_store_lod" + + +#define VK_NV_corner_sampled_image 1 +#define VK_NV_CORNER_SAMPLED_IMAGE_SPEC_VERSION 2 +#define VK_NV_CORNER_SAMPLED_IMAGE_EXTENSION_NAME "VK_NV_corner_sampled_image" +typedef struct VkPhysicalDeviceCornerSampledImageFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 cornerSampledImage; +} VkPhysicalDeviceCornerSampledImageFeaturesNV; + + + +#define VK_IMG_format_pvrtc 1 +#define VK_IMG_FORMAT_PVRTC_SPEC_VERSION 1 +#define VK_IMG_FORMAT_PVRTC_EXTENSION_NAME "VK_IMG_format_pvrtc" + + +#define VK_NV_external_memory_capabilities 1 +#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME "VK_NV_external_memory_capabilities" + +typedef enum VkExternalMemoryHandleTypeFlagBitsNV { + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT_NV = 0x00000001, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT_NV = 0x00000002, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_BIT_NV = 0x00000004, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_D3D11_IMAGE_KMT_BIT_NV = 0x00000008, + VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkExternalMemoryHandleTypeFlagBitsNV; +typedef VkFlags VkExternalMemoryHandleTypeFlagsNV; + +typedef enum VkExternalMemoryFeatureFlagBitsNV { + VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT_NV = 0x00000001, + VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT_NV = 0x00000002, + VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT_NV = 0x00000004, + VK_EXTERNAL_MEMORY_FEATURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkExternalMemoryFeatureFlagBitsNV; +typedef VkFlags VkExternalMemoryFeatureFlagsNV; +typedef struct VkExternalImageFormatPropertiesNV { + VkImageFormatProperties imageFormatProperties; + VkExternalMemoryFeatureFlagsNV externalMemoryFeatures; + VkExternalMemoryHandleTypeFlagsNV exportFromImportedHandleTypes; + VkExternalMemoryHandleTypeFlagsNV compatibleHandleTypes; +} VkExternalImageFormatPropertiesNV; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceExternalImageFormatPropertiesNV)(VkPhysicalDevice physicalDevice, VkFormat format, VkImageType type, VkImageTiling tiling, VkImageUsageFlags usage, VkImageCreateFlags flags, VkExternalMemoryHandleTypeFlagsNV externalHandleType, VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceExternalImageFormatPropertiesNV( + VkPhysicalDevice physicalDevice, + VkFormat format, + VkImageType type, + VkImageTiling tiling, + VkImageUsageFlags usage, + VkImageCreateFlags flags, + VkExternalMemoryHandleTypeFlagsNV externalHandleType, + VkExternalImageFormatPropertiesNV* pExternalImageFormatProperties); +#endif + + +#define VK_NV_external_memory 1 +#define VK_NV_EXTERNAL_MEMORY_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_EXTENSION_NAME "VK_NV_external_memory" +typedef struct VkExternalMemoryImageCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleTypes; +} VkExternalMemoryImageCreateInfoNV; + +typedef struct VkExportMemoryAllocateInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleTypes; +} VkExportMemoryAllocateInfoNV; + + + +#define VK_EXT_validation_flags 1 +#define VK_EXT_VALIDATION_FLAGS_SPEC_VERSION 2 +#define VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME "VK_EXT_validation_flags" + +typedef enum VkValidationCheckEXT { + VK_VALIDATION_CHECK_ALL_EXT = 0, + VK_VALIDATION_CHECK_SHADERS_EXT = 1, + VK_VALIDATION_CHECK_BEGIN_RANGE_EXT = VK_VALIDATION_CHECK_ALL_EXT, + VK_VALIDATION_CHECK_END_RANGE_EXT = VK_VALIDATION_CHECK_SHADERS_EXT, + VK_VALIDATION_CHECK_RANGE_SIZE_EXT = (VK_VALIDATION_CHECK_SHADERS_EXT - VK_VALIDATION_CHECK_ALL_EXT + 1), + VK_VALIDATION_CHECK_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationCheckEXT; +typedef struct VkValidationFlagsEXT { + VkStructureType sType; + const void* pNext; + uint32_t disabledValidationCheckCount; + const VkValidationCheckEXT* pDisabledValidationChecks; +} VkValidationFlagsEXT; + + + +#define VK_EXT_shader_subgroup_ballot 1 +#define VK_EXT_SHADER_SUBGROUP_BALLOT_SPEC_VERSION 1 +#define VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME "VK_EXT_shader_subgroup_ballot" + + +#define VK_EXT_shader_subgroup_vote 1 +#define VK_EXT_SHADER_SUBGROUP_VOTE_SPEC_VERSION 1 +#define VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME "VK_EXT_shader_subgroup_vote" + + +#define VK_EXT_texture_compression_astc_hdr 1 +#define VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_SPEC_VERSION 1 +#define VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_EXTENSION_NAME "VK_EXT_texture_compression_astc_hdr" +typedef struct VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT { + VkStructureType sType; + const void* pNext; + VkBool32 textureCompressionASTC_HDR; +} VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT; + + + +#define VK_EXT_astc_decode_mode 1 +#define VK_EXT_ASTC_DECODE_MODE_SPEC_VERSION 1 +#define VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME "VK_EXT_astc_decode_mode" +typedef struct VkImageViewASTCDecodeModeEXT { + VkStructureType sType; + const void* pNext; + VkFormat decodeMode; +} VkImageViewASTCDecodeModeEXT; + +typedef struct VkPhysicalDeviceASTCDecodeFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 decodeModeSharedExponent; +} VkPhysicalDeviceASTCDecodeFeaturesEXT; + + + +#define VK_EXT_conditional_rendering 1 +#define VK_EXT_CONDITIONAL_RENDERING_SPEC_VERSION 2 +#define VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME "VK_EXT_conditional_rendering" + +typedef enum VkConditionalRenderingFlagBitsEXT { + VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT = 0x00000001, + VK_CONDITIONAL_RENDERING_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkConditionalRenderingFlagBitsEXT; +typedef VkFlags VkConditionalRenderingFlagsEXT; +typedef struct VkConditionalRenderingBeginInfoEXT { + VkStructureType sType; + const void* pNext; + VkBuffer buffer; + VkDeviceSize offset; + VkConditionalRenderingFlagsEXT flags; +} VkConditionalRenderingBeginInfoEXT; + +typedef struct VkPhysicalDeviceConditionalRenderingFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 conditionalRendering; + VkBool32 inheritedConditionalRendering; +} VkPhysicalDeviceConditionalRenderingFeaturesEXT; + +typedef struct VkCommandBufferInheritanceConditionalRenderingInfoEXT { + VkStructureType sType; + const void* pNext; + VkBool32 conditionalRenderingEnable; +} VkCommandBufferInheritanceConditionalRenderingInfoEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdBeginConditionalRenderingEXT)(VkCommandBuffer commandBuffer, const VkConditionalRenderingBeginInfoEXT* pConditionalRenderingBegin); +typedef void (VKAPI_PTR *PFN_vkCmdEndConditionalRenderingEXT)(VkCommandBuffer commandBuffer); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdBeginConditionalRenderingEXT( + VkCommandBuffer commandBuffer, + const VkConditionalRenderingBeginInfoEXT* pConditionalRenderingBegin); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndConditionalRenderingEXT( + VkCommandBuffer commandBuffer); +#endif + + +#define VK_NVX_device_generated_commands 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkObjectTableNVX) +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkIndirectCommandsLayoutNVX) +#define VK_NVX_DEVICE_GENERATED_COMMANDS_SPEC_VERSION 3 +#define VK_NVX_DEVICE_GENERATED_COMMANDS_EXTENSION_NAME "VK_NVX_device_generated_commands" + +typedef enum VkIndirectCommandsTokenTypeNVX { + VK_INDIRECT_COMMANDS_TOKEN_TYPE_PIPELINE_NVX = 0, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DESCRIPTOR_SET_NVX = 1, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_INDEX_BUFFER_NVX = 2, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_VERTEX_BUFFER_NVX = 3, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_PUSH_CONSTANT_NVX = 4, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_INDEXED_NVX = 5, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DRAW_NVX = 6, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NVX = 7, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_BEGIN_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_TYPE_PIPELINE_NVX, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_END_RANGE_NVX = VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NVX, + VK_INDIRECT_COMMANDS_TOKEN_TYPE_RANGE_SIZE_NVX = (VK_INDIRECT_COMMANDS_TOKEN_TYPE_DISPATCH_NVX - VK_INDIRECT_COMMANDS_TOKEN_TYPE_PIPELINE_NVX + 1), + VK_INDIRECT_COMMANDS_TOKEN_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF +} VkIndirectCommandsTokenTypeNVX; + +typedef enum VkObjectEntryTypeNVX { + VK_OBJECT_ENTRY_TYPE_DESCRIPTOR_SET_NVX = 0, + VK_OBJECT_ENTRY_TYPE_PIPELINE_NVX = 1, + VK_OBJECT_ENTRY_TYPE_INDEX_BUFFER_NVX = 2, + VK_OBJECT_ENTRY_TYPE_VERTEX_BUFFER_NVX = 3, + VK_OBJECT_ENTRY_TYPE_PUSH_CONSTANT_NVX = 4, + VK_OBJECT_ENTRY_TYPE_BEGIN_RANGE_NVX = VK_OBJECT_ENTRY_TYPE_DESCRIPTOR_SET_NVX, + VK_OBJECT_ENTRY_TYPE_END_RANGE_NVX = VK_OBJECT_ENTRY_TYPE_PUSH_CONSTANT_NVX, + VK_OBJECT_ENTRY_TYPE_RANGE_SIZE_NVX = (VK_OBJECT_ENTRY_TYPE_PUSH_CONSTANT_NVX - VK_OBJECT_ENTRY_TYPE_DESCRIPTOR_SET_NVX + 1), + VK_OBJECT_ENTRY_TYPE_MAX_ENUM_NVX = 0x7FFFFFFF +} VkObjectEntryTypeNVX; + +typedef enum VkIndirectCommandsLayoutUsageFlagBitsNVX { + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_UNORDERED_SEQUENCES_BIT_NVX = 0x00000001, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_SPARSE_SEQUENCES_BIT_NVX = 0x00000002, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_EMPTY_EXECUTIONS_BIT_NVX = 0x00000004, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_INDEXED_SEQUENCES_BIT_NVX = 0x00000008, + VK_INDIRECT_COMMANDS_LAYOUT_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF +} VkIndirectCommandsLayoutUsageFlagBitsNVX; +typedef VkFlags VkIndirectCommandsLayoutUsageFlagsNVX; + +typedef enum VkObjectEntryUsageFlagBitsNVX { + VK_OBJECT_ENTRY_USAGE_GRAPHICS_BIT_NVX = 0x00000001, + VK_OBJECT_ENTRY_USAGE_COMPUTE_BIT_NVX = 0x00000002, + VK_OBJECT_ENTRY_USAGE_FLAG_BITS_MAX_ENUM_NVX = 0x7FFFFFFF +} VkObjectEntryUsageFlagBitsNVX; +typedef VkFlags VkObjectEntryUsageFlagsNVX; +typedef struct VkDeviceGeneratedCommandsFeaturesNVX { + VkStructureType sType; + const void* pNext; + VkBool32 computeBindingPointSupport; +} VkDeviceGeneratedCommandsFeaturesNVX; + +typedef struct VkDeviceGeneratedCommandsLimitsNVX { + VkStructureType sType; + const void* pNext; + uint32_t maxIndirectCommandsLayoutTokenCount; + uint32_t maxObjectEntryCounts; + uint32_t minSequenceCountBufferOffsetAlignment; + uint32_t minSequenceIndexBufferOffsetAlignment; + uint32_t minCommandsTokenBufferOffsetAlignment; +} VkDeviceGeneratedCommandsLimitsNVX; + +typedef struct VkIndirectCommandsTokenNVX { + VkIndirectCommandsTokenTypeNVX tokenType; + VkBuffer buffer; + VkDeviceSize offset; +} VkIndirectCommandsTokenNVX; + +typedef struct VkIndirectCommandsLayoutTokenNVX { + VkIndirectCommandsTokenTypeNVX tokenType; + uint32_t bindingUnit; + uint32_t dynamicCount; + uint32_t divisor; +} VkIndirectCommandsLayoutTokenNVX; + +typedef struct VkIndirectCommandsLayoutCreateInfoNVX { + VkStructureType sType; + const void* pNext; + VkPipelineBindPoint pipelineBindPoint; + VkIndirectCommandsLayoutUsageFlagsNVX flags; + uint32_t tokenCount; + const VkIndirectCommandsLayoutTokenNVX* pTokens; +} VkIndirectCommandsLayoutCreateInfoNVX; + +typedef struct VkCmdProcessCommandsInfoNVX { + VkStructureType sType; + const void* pNext; + VkObjectTableNVX objectTable; + VkIndirectCommandsLayoutNVX indirectCommandsLayout; + uint32_t indirectCommandsTokenCount; + const VkIndirectCommandsTokenNVX* pIndirectCommandsTokens; + uint32_t maxSequencesCount; + VkCommandBuffer targetCommandBuffer; + VkBuffer sequencesCountBuffer; + VkDeviceSize sequencesCountOffset; + VkBuffer sequencesIndexBuffer; + VkDeviceSize sequencesIndexOffset; +} VkCmdProcessCommandsInfoNVX; + +typedef struct VkCmdReserveSpaceForCommandsInfoNVX { + VkStructureType sType; + const void* pNext; + VkObjectTableNVX objectTable; + VkIndirectCommandsLayoutNVX indirectCommandsLayout; + uint32_t maxSequencesCount; +} VkCmdReserveSpaceForCommandsInfoNVX; + +typedef struct VkObjectTableCreateInfoNVX { + VkStructureType sType; + const void* pNext; + uint32_t objectCount; + const VkObjectEntryTypeNVX* pObjectEntryTypes; + const uint32_t* pObjectEntryCounts; + const VkObjectEntryUsageFlagsNVX* pObjectEntryUsageFlags; + uint32_t maxUniformBuffersPerDescriptor; + uint32_t maxStorageBuffersPerDescriptor; + uint32_t maxStorageImagesPerDescriptor; + uint32_t maxSampledImagesPerDescriptor; + uint32_t maxPipelineLayouts; +} VkObjectTableCreateInfoNVX; + +typedef struct VkObjectTableEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; +} VkObjectTableEntryNVX; + +typedef struct VkObjectTablePipelineEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipeline pipeline; +} VkObjectTablePipelineEntryNVX; + +typedef struct VkObjectTableDescriptorSetEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipelineLayout pipelineLayout; + VkDescriptorSet descriptorSet; +} VkObjectTableDescriptorSetEntryNVX; + +typedef struct VkObjectTableVertexBufferEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkBuffer buffer; +} VkObjectTableVertexBufferEntryNVX; + +typedef struct VkObjectTableIndexBufferEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkBuffer buffer; + VkIndexType indexType; +} VkObjectTableIndexBufferEntryNVX; + +typedef struct VkObjectTablePushConstantEntryNVX { + VkObjectEntryTypeNVX type; + VkObjectEntryUsageFlagsNVX flags; + VkPipelineLayout pipelineLayout; + VkShaderStageFlags stageFlags; +} VkObjectTablePushConstantEntryNVX; + +typedef void (VKAPI_PTR *PFN_vkCmdProcessCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); +typedef void (VKAPI_PTR *PFN_vkCmdReserveSpaceForCommandsNVX)(VkCommandBuffer commandBuffer, const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCreateIndirectCommandsLayoutNVX)(VkDevice device, const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); +typedef void (VKAPI_PTR *PFN_vkDestroyIndirectCommandsLayoutNVX)(VkDevice device, VkIndirectCommandsLayoutNVX indirectCommandsLayout, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkCreateObjectTableNVX)(VkDevice device, const VkObjectTableCreateInfoNVX* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkObjectTableNVX* pObjectTable); +typedef void (VKAPI_PTR *PFN_vkDestroyObjectTableNVX)(VkDevice device, VkObjectTableNVX objectTable, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkRegisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectTableEntryNVX* const* ppObjectTableEntries, const uint32_t* pObjectIndices); +typedef VkResult (VKAPI_PTR *PFN_vkUnregisterObjectsNVX)(VkDevice device, VkObjectTableNVX objectTable, uint32_t objectCount, const VkObjectEntryTypeNVX* pObjectEntryTypes, const uint32_t* pObjectIndices); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX)(VkPhysicalDevice physicalDevice, VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, VkDeviceGeneratedCommandsLimitsNVX* pLimits); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdProcessCommandsNVX( + VkCommandBuffer commandBuffer, + const VkCmdProcessCommandsInfoNVX* pProcessCommandsInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdReserveSpaceForCommandsNVX( + VkCommandBuffer commandBuffer, + const VkCmdReserveSpaceForCommandsInfoNVX* pReserveSpaceInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateIndirectCommandsLayoutNVX( + VkDevice device, + const VkIndirectCommandsLayoutCreateInfoNVX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkIndirectCommandsLayoutNVX* pIndirectCommandsLayout); + +VKAPI_ATTR void VKAPI_CALL vkDestroyIndirectCommandsLayoutNVX( + VkDevice device, + VkIndirectCommandsLayoutNVX indirectCommandsLayout, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateObjectTableNVX( + VkDevice device, + const VkObjectTableCreateInfoNVX* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkObjectTableNVX* pObjectTable); + +VKAPI_ATTR void VKAPI_CALL vkDestroyObjectTableNVX( + VkDevice device, + VkObjectTableNVX objectTable, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkRegisterObjectsNVX( + VkDevice device, + VkObjectTableNVX objectTable, + uint32_t objectCount, + const VkObjectTableEntryNVX* const* ppObjectTableEntries, + const uint32_t* pObjectIndices); + +VKAPI_ATTR VkResult VKAPI_CALL vkUnregisterObjectsNVX( + VkDevice device, + VkObjectTableNVX objectTable, + uint32_t objectCount, + const VkObjectEntryTypeNVX* pObjectEntryTypes, + const uint32_t* pObjectIndices); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX( + VkPhysicalDevice physicalDevice, + VkDeviceGeneratedCommandsFeaturesNVX* pFeatures, + VkDeviceGeneratedCommandsLimitsNVX* pLimits); +#endif + + +#define VK_NV_clip_space_w_scaling 1 +#define VK_NV_CLIP_SPACE_W_SCALING_SPEC_VERSION 1 +#define VK_NV_CLIP_SPACE_W_SCALING_EXTENSION_NAME "VK_NV_clip_space_w_scaling" +typedef struct VkViewportWScalingNV { + float xcoeff; + float ycoeff; +} VkViewportWScalingNV; + +typedef struct VkPipelineViewportWScalingStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 viewportWScalingEnable; + uint32_t viewportCount; + const VkViewportWScalingNV* pViewportWScalings; +} VkPipelineViewportWScalingStateCreateInfoNV; + +typedef void (VKAPI_PTR *PFN_vkCmdSetViewportWScalingNV)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkViewportWScalingNV* pViewportWScalings); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportWScalingNV( + VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkViewportWScalingNV* pViewportWScalings); +#endif + + +#define VK_EXT_direct_mode_display 1 +#define VK_EXT_DIRECT_MODE_DISPLAY_SPEC_VERSION 1 +#define VK_EXT_DIRECT_MODE_DISPLAY_EXTENSION_NAME "VK_EXT_direct_mode_display" +typedef VkResult (VKAPI_PTR *PFN_vkReleaseDisplayEXT)(VkPhysicalDevice physicalDevice, VkDisplayKHR display); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkReleaseDisplayEXT( + VkPhysicalDevice physicalDevice, + VkDisplayKHR display); +#endif + + +#define VK_EXT_display_surface_counter 1 +#define VK_EXT_DISPLAY_SURFACE_COUNTER_SPEC_VERSION 1 +#define VK_EXT_DISPLAY_SURFACE_COUNTER_EXTENSION_NAME "VK_EXT_display_surface_counter" + +typedef enum VkSurfaceCounterFlagBitsEXT { + VK_SURFACE_COUNTER_VBLANK_EXT = 0x00000001, + VK_SURFACE_COUNTER_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkSurfaceCounterFlagBitsEXT; +typedef VkFlags VkSurfaceCounterFlagsEXT; +typedef struct VkSurfaceCapabilities2EXT { + VkStructureType sType; + void* pNext; + uint32_t minImageCount; + uint32_t maxImageCount; + VkExtent2D currentExtent; + VkExtent2D minImageExtent; + VkExtent2D maxImageExtent; + uint32_t maxImageArrayLayers; + VkSurfaceTransformFlagsKHR supportedTransforms; + VkSurfaceTransformFlagBitsKHR currentTransform; + VkCompositeAlphaFlagsKHR supportedCompositeAlpha; + VkImageUsageFlags supportedUsageFlags; + VkSurfaceCounterFlagsEXT supportedSurfaceCounters; +} VkSurfaceCapabilities2EXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfaceCapabilities2EXT)(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface, VkSurfaceCapabilities2EXT* pSurfaceCapabilities); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfaceCapabilities2EXT( + VkPhysicalDevice physicalDevice, + VkSurfaceKHR surface, + VkSurfaceCapabilities2EXT* pSurfaceCapabilities); +#endif + + +#define VK_EXT_display_control 1 +#define VK_EXT_DISPLAY_CONTROL_SPEC_VERSION 1 +#define VK_EXT_DISPLAY_CONTROL_EXTENSION_NAME "VK_EXT_display_control" + +typedef enum VkDisplayPowerStateEXT { + VK_DISPLAY_POWER_STATE_OFF_EXT = 0, + VK_DISPLAY_POWER_STATE_SUSPEND_EXT = 1, + VK_DISPLAY_POWER_STATE_ON_EXT = 2, + VK_DISPLAY_POWER_STATE_BEGIN_RANGE_EXT = VK_DISPLAY_POWER_STATE_OFF_EXT, + VK_DISPLAY_POWER_STATE_END_RANGE_EXT = VK_DISPLAY_POWER_STATE_ON_EXT, + VK_DISPLAY_POWER_STATE_RANGE_SIZE_EXT = (VK_DISPLAY_POWER_STATE_ON_EXT - VK_DISPLAY_POWER_STATE_OFF_EXT + 1), + VK_DISPLAY_POWER_STATE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDisplayPowerStateEXT; + +typedef enum VkDeviceEventTypeEXT { + VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT = 0, + VK_DEVICE_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT, + VK_DEVICE_EVENT_TYPE_END_RANGE_EXT = VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT, + VK_DEVICE_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT - VK_DEVICE_EVENT_TYPE_DISPLAY_HOTPLUG_EXT + 1), + VK_DEVICE_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDeviceEventTypeEXT; + +typedef enum VkDisplayEventTypeEXT { + VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT = 0, + VK_DISPLAY_EVENT_TYPE_BEGIN_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT, + VK_DISPLAY_EVENT_TYPE_END_RANGE_EXT = VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT, + VK_DISPLAY_EVENT_TYPE_RANGE_SIZE_EXT = (VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT - VK_DISPLAY_EVENT_TYPE_FIRST_PIXEL_OUT_EXT + 1), + VK_DISPLAY_EVENT_TYPE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDisplayEventTypeEXT; +typedef struct VkDisplayPowerInfoEXT { + VkStructureType sType; + const void* pNext; + VkDisplayPowerStateEXT powerState; +} VkDisplayPowerInfoEXT; + +typedef struct VkDeviceEventInfoEXT { + VkStructureType sType; + const void* pNext; + VkDeviceEventTypeEXT deviceEvent; +} VkDeviceEventInfoEXT; + +typedef struct VkDisplayEventInfoEXT { + VkStructureType sType; + const void* pNext; + VkDisplayEventTypeEXT displayEvent; +} VkDisplayEventInfoEXT; + +typedef struct VkSwapchainCounterCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkSurfaceCounterFlagsEXT surfaceCounters; +} VkSwapchainCounterCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkDisplayPowerControlEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayPowerInfoEXT* pDisplayPowerInfo); +typedef VkResult (VKAPI_PTR *PFN_vkRegisterDeviceEventEXT)(VkDevice device, const VkDeviceEventInfoEXT* pDeviceEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); +typedef VkResult (VKAPI_PTR *PFN_vkRegisterDisplayEventEXT)(VkDevice device, VkDisplayKHR display, const VkDisplayEventInfoEXT* pDisplayEventInfo, const VkAllocationCallbacks* pAllocator, VkFence* pFence); +typedef VkResult (VKAPI_PTR *PFN_vkGetSwapchainCounterEXT)(VkDevice device, VkSwapchainKHR swapchain, VkSurfaceCounterFlagBitsEXT counter, uint64_t* pCounterValue); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkDisplayPowerControlEXT( + VkDevice device, + VkDisplayKHR display, + const VkDisplayPowerInfoEXT* pDisplayPowerInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDeviceEventEXT( + VkDevice device, + const VkDeviceEventInfoEXT* pDeviceEventInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence); + +VKAPI_ATTR VkResult VKAPI_CALL vkRegisterDisplayEventEXT( + VkDevice device, + VkDisplayKHR display, + const VkDisplayEventInfoEXT* pDisplayEventInfo, + const VkAllocationCallbacks* pAllocator, + VkFence* pFence); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSwapchainCounterEXT( + VkDevice device, + VkSwapchainKHR swapchain, + VkSurfaceCounterFlagBitsEXT counter, + uint64_t* pCounterValue); +#endif + + +#define VK_GOOGLE_display_timing 1 +#define VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION 1 +#define VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME "VK_GOOGLE_display_timing" +typedef struct VkRefreshCycleDurationGOOGLE { + uint64_t refreshDuration; +} VkRefreshCycleDurationGOOGLE; + +typedef struct VkPastPresentationTimingGOOGLE { + uint32_t presentID; + uint64_t desiredPresentTime; + uint64_t actualPresentTime; + uint64_t earliestPresentTime; + uint64_t presentMargin; +} VkPastPresentationTimingGOOGLE; + +typedef struct VkPresentTimeGOOGLE { + uint32_t presentID; + uint64_t desiredPresentTime; +} VkPresentTimeGOOGLE; + +typedef struct VkPresentTimesInfoGOOGLE { + VkStructureType sType; + const void* pNext; + uint32_t swapchainCount; + const VkPresentTimeGOOGLE* pTimes; +} VkPresentTimesInfoGOOGLE; + +typedef VkResult (VKAPI_PTR *PFN_vkGetRefreshCycleDurationGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties); +typedef VkResult (VKAPI_PTR *PFN_vkGetPastPresentationTimingGOOGLE)(VkDevice device, VkSwapchainKHR swapchain, uint32_t* pPresentationTimingCount, VkPastPresentationTimingGOOGLE* pPresentationTimings); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetRefreshCycleDurationGOOGLE( + VkDevice device, + VkSwapchainKHR swapchain, + VkRefreshCycleDurationGOOGLE* pDisplayTimingProperties); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPastPresentationTimingGOOGLE( + VkDevice device, + VkSwapchainKHR swapchain, + uint32_t* pPresentationTimingCount, + VkPastPresentationTimingGOOGLE* pPresentationTimings); +#endif + + +#define VK_NV_sample_mask_override_coverage 1 +#define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_SPEC_VERSION 1 +#define VK_NV_SAMPLE_MASK_OVERRIDE_COVERAGE_EXTENSION_NAME "VK_NV_sample_mask_override_coverage" + + +#define VK_NV_geometry_shader_passthrough 1 +#define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_SPEC_VERSION 1 +#define VK_NV_GEOMETRY_SHADER_PASSTHROUGH_EXTENSION_NAME "VK_NV_geometry_shader_passthrough" + + +#define VK_NV_viewport_array2 1 +#define VK_NV_VIEWPORT_ARRAY2_SPEC_VERSION 1 +#define VK_NV_VIEWPORT_ARRAY2_EXTENSION_NAME "VK_NV_viewport_array2" + + +#define VK_NVX_multiview_per_view_attributes 1 +#define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_SPEC_VERSION 1 +#define VK_NVX_MULTIVIEW_PER_VIEW_ATTRIBUTES_EXTENSION_NAME "VK_NVX_multiview_per_view_attributes" +typedef struct VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX { + VkStructureType sType; + void* pNext; + VkBool32 perViewPositionAllComponents; +} VkPhysicalDeviceMultiviewPerViewAttributesPropertiesNVX; + + + +#define VK_NV_viewport_swizzle 1 +#define VK_NV_VIEWPORT_SWIZZLE_SPEC_VERSION 1 +#define VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME "VK_NV_viewport_swizzle" + +typedef enum VkViewportCoordinateSwizzleNV { + VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV = 0, + VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_X_NV = 1, + VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Y_NV = 2, + VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Y_NV = 3, + VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_Z_NV = 4, + VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_Z_NV = 5, + VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_W_NV = 6, + VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV = 7, + VK_VIEWPORT_COORDINATE_SWIZZLE_BEGIN_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV, + VK_VIEWPORT_COORDINATE_SWIZZLE_END_RANGE_NV = VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV, + VK_VIEWPORT_COORDINATE_SWIZZLE_RANGE_SIZE_NV = (VK_VIEWPORT_COORDINATE_SWIZZLE_NEGATIVE_W_NV - VK_VIEWPORT_COORDINATE_SWIZZLE_POSITIVE_X_NV + 1), + VK_VIEWPORT_COORDINATE_SWIZZLE_MAX_ENUM_NV = 0x7FFFFFFF +} VkViewportCoordinateSwizzleNV; +typedef VkFlags VkPipelineViewportSwizzleStateCreateFlagsNV; +typedef struct VkViewportSwizzleNV { + VkViewportCoordinateSwizzleNV x; + VkViewportCoordinateSwizzleNV y; + VkViewportCoordinateSwizzleNV z; + VkViewportCoordinateSwizzleNV w; +} VkViewportSwizzleNV; + +typedef struct VkPipelineViewportSwizzleStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineViewportSwizzleStateCreateFlagsNV flags; + uint32_t viewportCount; + const VkViewportSwizzleNV* pViewportSwizzles; +} VkPipelineViewportSwizzleStateCreateInfoNV; + + + +#define VK_EXT_discard_rectangles 1 +#define VK_EXT_DISCARD_RECTANGLES_SPEC_VERSION 1 +#define VK_EXT_DISCARD_RECTANGLES_EXTENSION_NAME "VK_EXT_discard_rectangles" + +typedef enum VkDiscardRectangleModeEXT { + VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT = 0, + VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT = 1, + VK_DISCARD_RECTANGLE_MODE_BEGIN_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT, + VK_DISCARD_RECTANGLE_MODE_END_RANGE_EXT = VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT, + VK_DISCARD_RECTANGLE_MODE_RANGE_SIZE_EXT = (VK_DISCARD_RECTANGLE_MODE_EXCLUSIVE_EXT - VK_DISCARD_RECTANGLE_MODE_INCLUSIVE_EXT + 1), + VK_DISCARD_RECTANGLE_MODE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDiscardRectangleModeEXT; +typedef VkFlags VkPipelineDiscardRectangleStateCreateFlagsEXT; +typedef struct VkPhysicalDeviceDiscardRectanglePropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxDiscardRectangles; +} VkPhysicalDeviceDiscardRectanglePropertiesEXT; + +typedef struct VkPipelineDiscardRectangleStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPipelineDiscardRectangleStateCreateFlagsEXT flags; + VkDiscardRectangleModeEXT discardRectangleMode; + uint32_t discardRectangleCount; + const VkRect2D* pDiscardRectangles; +} VkPipelineDiscardRectangleStateCreateInfoEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdSetDiscardRectangleEXT)(VkCommandBuffer commandBuffer, uint32_t firstDiscardRectangle, uint32_t discardRectangleCount, const VkRect2D* pDiscardRectangles); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetDiscardRectangleEXT( + VkCommandBuffer commandBuffer, + uint32_t firstDiscardRectangle, + uint32_t discardRectangleCount, + const VkRect2D* pDiscardRectangles); +#endif + + +#define VK_EXT_conservative_rasterization 1 +#define VK_EXT_CONSERVATIVE_RASTERIZATION_SPEC_VERSION 1 +#define VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME "VK_EXT_conservative_rasterization" + +typedef enum VkConservativeRasterizationModeEXT { + VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT = 0, + VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT = 1, + VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT = 2, + VK_CONSERVATIVE_RASTERIZATION_MODE_BEGIN_RANGE_EXT = VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT, + VK_CONSERVATIVE_RASTERIZATION_MODE_END_RANGE_EXT = VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT, + VK_CONSERVATIVE_RASTERIZATION_MODE_RANGE_SIZE_EXT = (VK_CONSERVATIVE_RASTERIZATION_MODE_UNDERESTIMATE_EXT - VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT + 1), + VK_CONSERVATIVE_RASTERIZATION_MODE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkConservativeRasterizationModeEXT; +typedef VkFlags VkPipelineRasterizationConservativeStateCreateFlagsEXT; +typedef struct VkPhysicalDeviceConservativeRasterizationPropertiesEXT { + VkStructureType sType; + void* pNext; + float primitiveOverestimationSize; + float maxExtraPrimitiveOverestimationSize; + float extraPrimitiveOverestimationSizeGranularity; + VkBool32 primitiveUnderestimation; + VkBool32 conservativePointAndLineRasterization; + VkBool32 degenerateTrianglesRasterized; + VkBool32 degenerateLinesRasterized; + VkBool32 fullyCoveredFragmentShaderInputVariable; + VkBool32 conservativeRasterizationPostDepthCoverage; +} VkPhysicalDeviceConservativeRasterizationPropertiesEXT; + +typedef struct VkPipelineRasterizationConservativeStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPipelineRasterizationConservativeStateCreateFlagsEXT flags; + VkConservativeRasterizationModeEXT conservativeRasterizationMode; + float extraPrimitiveOverestimationSize; +} VkPipelineRasterizationConservativeStateCreateInfoEXT; + + + +#define VK_EXT_depth_clip_enable 1 +#define VK_EXT_DEPTH_CLIP_ENABLE_SPEC_VERSION 1 +#define VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME "VK_EXT_depth_clip_enable" +typedef VkFlags VkPipelineRasterizationDepthClipStateCreateFlagsEXT; +typedef struct VkPhysicalDeviceDepthClipEnableFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 depthClipEnable; +} VkPhysicalDeviceDepthClipEnableFeaturesEXT; + +typedef struct VkPipelineRasterizationDepthClipStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPipelineRasterizationDepthClipStateCreateFlagsEXT flags; + VkBool32 depthClipEnable; +} VkPipelineRasterizationDepthClipStateCreateInfoEXT; + + + +#define VK_EXT_swapchain_colorspace 1 +#define VK_EXT_SWAPCHAIN_COLOR_SPACE_SPEC_VERSION 4 +#define VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME "VK_EXT_swapchain_colorspace" + + +#define VK_EXT_hdr_metadata 1 +#define VK_EXT_HDR_METADATA_SPEC_VERSION 2 +#define VK_EXT_HDR_METADATA_EXTENSION_NAME "VK_EXT_hdr_metadata" +typedef struct VkXYColorEXT { + float x; + float y; +} VkXYColorEXT; + +typedef struct VkHdrMetadataEXT { + VkStructureType sType; + const void* pNext; + VkXYColorEXT displayPrimaryRed; + VkXYColorEXT displayPrimaryGreen; + VkXYColorEXT displayPrimaryBlue; + VkXYColorEXT whitePoint; + float maxLuminance; + float minLuminance; + float maxContentLightLevel; + float maxFrameAverageLightLevel; +} VkHdrMetadataEXT; + +typedef void (VKAPI_PTR *PFN_vkSetHdrMetadataEXT)(VkDevice device, uint32_t swapchainCount, const VkSwapchainKHR* pSwapchains, const VkHdrMetadataEXT* pMetadata); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkSetHdrMetadataEXT( + VkDevice device, + uint32_t swapchainCount, + const VkSwapchainKHR* pSwapchains, + const VkHdrMetadataEXT* pMetadata); +#endif + + +#define VK_EXT_external_memory_dma_buf 1 +#define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_SPEC_VERSION 1 +#define VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME "VK_EXT_external_memory_dma_buf" + + +#define VK_EXT_queue_family_foreign 1 +#define VK_EXT_QUEUE_FAMILY_FOREIGN_SPEC_VERSION 1 +#define VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME "VK_EXT_queue_family_foreign" +#define VK_QUEUE_FAMILY_FOREIGN_EXT (~0U-2) + + +#define VK_EXT_debug_utils 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDebugUtilsMessengerEXT) +#define VK_EXT_DEBUG_UTILS_SPEC_VERSION 1 +#define VK_EXT_DEBUG_UTILS_EXTENSION_NAME "VK_EXT_debug_utils" +typedef VkFlags VkDebugUtilsMessengerCallbackDataFlagsEXT; +typedef VkFlags VkDebugUtilsMessengerCreateFlagsEXT; + +typedef enum VkDebugUtilsMessageSeverityFlagBitsEXT { + VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT = 0x00000001, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT = 0x00000010, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT = 0x00000100, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT = 0x00001000, + VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugUtilsMessageSeverityFlagBitsEXT; +typedef VkFlags VkDebugUtilsMessageSeverityFlagsEXT; + +typedef enum VkDebugUtilsMessageTypeFlagBitsEXT { + VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT = 0x00000001, + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT = 0x00000002, + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT = 0x00000004, + VK_DEBUG_UTILS_MESSAGE_TYPE_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDebugUtilsMessageTypeFlagBitsEXT; +typedef VkFlags VkDebugUtilsMessageTypeFlagsEXT; +typedef struct VkDebugUtilsObjectNameInfoEXT { + VkStructureType sType; + const void* pNext; + VkObjectType objectType; + uint64_t objectHandle; + const char* pObjectName; +} VkDebugUtilsObjectNameInfoEXT; + +typedef struct VkDebugUtilsObjectTagInfoEXT { + VkStructureType sType; + const void* pNext; + VkObjectType objectType; + uint64_t objectHandle; + uint64_t tagName; + size_t tagSize; + const void* pTag; +} VkDebugUtilsObjectTagInfoEXT; + +typedef struct VkDebugUtilsLabelEXT { + VkStructureType sType; + const void* pNext; + const char* pLabelName; + float color[4]; +} VkDebugUtilsLabelEXT; + +typedef struct VkDebugUtilsMessengerCallbackDataEXT { + VkStructureType sType; + const void* pNext; + VkDebugUtilsMessengerCallbackDataFlagsEXT flags; + const char* pMessageIdName; + int32_t messageIdNumber; + const char* pMessage; + uint32_t queueLabelCount; + const VkDebugUtilsLabelEXT* pQueueLabels; + uint32_t cmdBufLabelCount; + const VkDebugUtilsLabelEXT* pCmdBufLabels; + uint32_t objectCount; + const VkDebugUtilsObjectNameInfoEXT* pObjects; +} VkDebugUtilsMessengerCallbackDataEXT; + +typedef VkBool32 (VKAPI_PTR *PFN_vkDebugUtilsMessengerCallbackEXT)( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, + void* pUserData); + +typedef struct VkDebugUtilsMessengerCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDebugUtilsMessengerCreateFlagsEXT flags; + VkDebugUtilsMessageSeverityFlagsEXT messageSeverity; + VkDebugUtilsMessageTypeFlagsEXT messageType; + PFN_vkDebugUtilsMessengerCallbackEXT pfnUserCallback; + void* pUserData; +} VkDebugUtilsMessengerCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkSetDebugUtilsObjectNameEXT)(VkDevice device, const VkDebugUtilsObjectNameInfoEXT* pNameInfo); +typedef VkResult (VKAPI_PTR *PFN_vkSetDebugUtilsObjectTagEXT)(VkDevice device, const VkDebugUtilsObjectTagInfoEXT* pTagInfo); +typedef void (VKAPI_PTR *PFN_vkQueueBeginDebugUtilsLabelEXT)(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo); +typedef void (VKAPI_PTR *PFN_vkQueueEndDebugUtilsLabelEXT)(VkQueue queue); +typedef void (VKAPI_PTR *PFN_vkQueueInsertDebugUtilsLabelEXT)(VkQueue queue, const VkDebugUtilsLabelEXT* pLabelInfo); +typedef void (VKAPI_PTR *PFN_vkCmdBeginDebugUtilsLabelEXT)(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo); +typedef void (VKAPI_PTR *PFN_vkCmdEndDebugUtilsLabelEXT)(VkCommandBuffer commandBuffer); +typedef void (VKAPI_PTR *PFN_vkCmdInsertDebugUtilsLabelEXT)(VkCommandBuffer commandBuffer, const VkDebugUtilsLabelEXT* pLabelInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCreateDebugUtilsMessengerEXT)(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pMessenger); +typedef void (VKAPI_PTR *PFN_vkDestroyDebugUtilsMessengerEXT)(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkSubmitDebugUtilsMessageEXT)(VkInstance instance, VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectNameEXT( + VkDevice device, + const VkDebugUtilsObjectNameInfoEXT* pNameInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetDebugUtilsObjectTagEXT( + VkDevice device, + const VkDebugUtilsObjectTagInfoEXT* pTagInfo); + +VKAPI_ATTR void VKAPI_CALL vkQueueBeginDebugUtilsLabelEXT( + VkQueue queue, + const VkDebugUtilsLabelEXT* pLabelInfo); + +VKAPI_ATTR void VKAPI_CALL vkQueueEndDebugUtilsLabelEXT( + VkQueue queue); + +VKAPI_ATTR void VKAPI_CALL vkQueueInsertDebugUtilsLabelEXT( + VkQueue queue, + const VkDebugUtilsLabelEXT* pLabelInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdBeginDebugUtilsLabelEXT( + VkCommandBuffer commandBuffer, + const VkDebugUtilsLabelEXT* pLabelInfo); + +VKAPI_ATTR void VKAPI_CALL vkCmdEndDebugUtilsLabelEXT( + VkCommandBuffer commandBuffer); + +VKAPI_ATTR void VKAPI_CALL vkCmdInsertDebugUtilsLabelEXT( + VkCommandBuffer commandBuffer, + const VkDebugUtilsLabelEXT* pLabelInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT( + VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkDebugUtilsMessengerEXT* pMessenger); + +VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT( + VkInstance instance, + VkDebugUtilsMessengerEXT messenger, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkSubmitDebugUtilsMessageEXT( + VkInstance instance, + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageTypes, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData); +#endif + + +#define VK_EXT_sampler_filter_minmax 1 +#define VK_EXT_SAMPLER_FILTER_MINMAX_SPEC_VERSION 2 +#define VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME "VK_EXT_sampler_filter_minmax" + +typedef enum VkSamplerReductionModeEXT { + VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT = 0, + VK_SAMPLER_REDUCTION_MODE_MIN_EXT = 1, + VK_SAMPLER_REDUCTION_MODE_MAX_EXT = 2, + VK_SAMPLER_REDUCTION_MODE_BEGIN_RANGE_EXT = VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT, + VK_SAMPLER_REDUCTION_MODE_END_RANGE_EXT = VK_SAMPLER_REDUCTION_MODE_MAX_EXT, + VK_SAMPLER_REDUCTION_MODE_RANGE_SIZE_EXT = (VK_SAMPLER_REDUCTION_MODE_MAX_EXT - VK_SAMPLER_REDUCTION_MODE_WEIGHTED_AVERAGE_EXT + 1), + VK_SAMPLER_REDUCTION_MODE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkSamplerReductionModeEXT; +typedef struct VkSamplerReductionModeCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkSamplerReductionModeEXT reductionMode; +} VkSamplerReductionModeCreateInfoEXT; + +typedef struct VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT { + VkStructureType sType; + void* pNext; + VkBool32 filterMinmaxSingleComponentFormats; + VkBool32 filterMinmaxImageComponentMapping; +} VkPhysicalDeviceSamplerFilterMinmaxPropertiesEXT; + + + +#define VK_AMD_gpu_shader_int16 1 +#define VK_AMD_GPU_SHADER_INT16_SPEC_VERSION 2 +#define VK_AMD_GPU_SHADER_INT16_EXTENSION_NAME "VK_AMD_gpu_shader_int16" + + +#define VK_AMD_mixed_attachment_samples 1 +#define VK_AMD_MIXED_ATTACHMENT_SAMPLES_SPEC_VERSION 1 +#define VK_AMD_MIXED_ATTACHMENT_SAMPLES_EXTENSION_NAME "VK_AMD_mixed_attachment_samples" + + +#define VK_AMD_shader_fragment_mask 1 +#define VK_AMD_SHADER_FRAGMENT_MASK_SPEC_VERSION 1 +#define VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME "VK_AMD_shader_fragment_mask" + + +#define VK_EXT_inline_uniform_block 1 +#define VK_EXT_INLINE_UNIFORM_BLOCK_SPEC_VERSION 1 +#define VK_EXT_INLINE_UNIFORM_BLOCK_EXTENSION_NAME "VK_EXT_inline_uniform_block" +typedef struct VkPhysicalDeviceInlineUniformBlockFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 inlineUniformBlock; + VkBool32 descriptorBindingInlineUniformBlockUpdateAfterBind; +} VkPhysicalDeviceInlineUniformBlockFeaturesEXT; + +typedef struct VkPhysicalDeviceInlineUniformBlockPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxInlineUniformBlockSize; + uint32_t maxPerStageDescriptorInlineUniformBlocks; + uint32_t maxPerStageDescriptorUpdateAfterBindInlineUniformBlocks; + uint32_t maxDescriptorSetInlineUniformBlocks; + uint32_t maxDescriptorSetUpdateAfterBindInlineUniformBlocks; +} VkPhysicalDeviceInlineUniformBlockPropertiesEXT; + +typedef struct VkWriteDescriptorSetInlineUniformBlockEXT { + VkStructureType sType; + const void* pNext; + uint32_t dataSize; + const void* pData; +} VkWriteDescriptorSetInlineUniformBlockEXT; + +typedef struct VkDescriptorPoolInlineUniformBlockCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t maxInlineUniformBlockBindings; +} VkDescriptorPoolInlineUniformBlockCreateInfoEXT; + + + +#define VK_EXT_shader_stencil_export 1 +#define VK_EXT_SHADER_STENCIL_EXPORT_SPEC_VERSION 1 +#define VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME "VK_EXT_shader_stencil_export" + + +#define VK_EXT_sample_locations 1 +#define VK_EXT_SAMPLE_LOCATIONS_SPEC_VERSION 1 +#define VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME "VK_EXT_sample_locations" +typedef struct VkSampleLocationEXT { + float x; + float y; +} VkSampleLocationEXT; + +typedef struct VkSampleLocationsInfoEXT { + VkStructureType sType; + const void* pNext; + VkSampleCountFlagBits sampleLocationsPerPixel; + VkExtent2D sampleLocationGridSize; + uint32_t sampleLocationsCount; + const VkSampleLocationEXT* pSampleLocations; +} VkSampleLocationsInfoEXT; + +typedef struct VkAttachmentSampleLocationsEXT { + uint32_t attachmentIndex; + VkSampleLocationsInfoEXT sampleLocationsInfo; +} VkAttachmentSampleLocationsEXT; + +typedef struct VkSubpassSampleLocationsEXT { + uint32_t subpassIndex; + VkSampleLocationsInfoEXT sampleLocationsInfo; +} VkSubpassSampleLocationsEXT; + +typedef struct VkRenderPassSampleLocationsBeginInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t attachmentInitialSampleLocationsCount; + const VkAttachmentSampleLocationsEXT* pAttachmentInitialSampleLocations; + uint32_t postSubpassSampleLocationsCount; + const VkSubpassSampleLocationsEXT* pPostSubpassSampleLocations; +} VkRenderPassSampleLocationsBeginInfoEXT; + +typedef struct VkPipelineSampleLocationsStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkBool32 sampleLocationsEnable; + VkSampleLocationsInfoEXT sampleLocationsInfo; +} VkPipelineSampleLocationsStateCreateInfoEXT; + +typedef struct VkPhysicalDeviceSampleLocationsPropertiesEXT { + VkStructureType sType; + void* pNext; + VkSampleCountFlags sampleLocationSampleCounts; + VkExtent2D maxSampleLocationGridSize; + float sampleLocationCoordinateRange[2]; + uint32_t sampleLocationSubPixelBits; + VkBool32 variableSampleLocations; +} VkPhysicalDeviceSampleLocationsPropertiesEXT; + +typedef struct VkMultisamplePropertiesEXT { + VkStructureType sType; + void* pNext; + VkExtent2D maxSampleLocationGridSize; +} VkMultisamplePropertiesEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdSetSampleLocationsEXT)(VkCommandBuffer commandBuffer, const VkSampleLocationsInfoEXT* pSampleLocationsInfo); +typedef void (VKAPI_PTR *PFN_vkGetPhysicalDeviceMultisamplePropertiesEXT)(VkPhysicalDevice physicalDevice, VkSampleCountFlagBits samples, VkMultisamplePropertiesEXT* pMultisampleProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetSampleLocationsEXT( + VkCommandBuffer commandBuffer, + const VkSampleLocationsInfoEXT* pSampleLocationsInfo); + +VKAPI_ATTR void VKAPI_CALL vkGetPhysicalDeviceMultisamplePropertiesEXT( + VkPhysicalDevice physicalDevice, + VkSampleCountFlagBits samples, + VkMultisamplePropertiesEXT* pMultisampleProperties); +#endif + + +#define VK_EXT_blend_operation_advanced 1 +#define VK_EXT_BLEND_OPERATION_ADVANCED_SPEC_VERSION 2 +#define VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME "VK_EXT_blend_operation_advanced" + +typedef enum VkBlendOverlapEXT { + VK_BLEND_OVERLAP_UNCORRELATED_EXT = 0, + VK_BLEND_OVERLAP_DISJOINT_EXT = 1, + VK_BLEND_OVERLAP_CONJOINT_EXT = 2, + VK_BLEND_OVERLAP_BEGIN_RANGE_EXT = VK_BLEND_OVERLAP_UNCORRELATED_EXT, + VK_BLEND_OVERLAP_END_RANGE_EXT = VK_BLEND_OVERLAP_CONJOINT_EXT, + VK_BLEND_OVERLAP_RANGE_SIZE_EXT = (VK_BLEND_OVERLAP_CONJOINT_EXT - VK_BLEND_OVERLAP_UNCORRELATED_EXT + 1), + VK_BLEND_OVERLAP_MAX_ENUM_EXT = 0x7FFFFFFF +} VkBlendOverlapEXT; +typedef struct VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 advancedBlendCoherentOperations; +} VkPhysicalDeviceBlendOperationAdvancedFeaturesEXT; + +typedef struct VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t advancedBlendMaxColorAttachments; + VkBool32 advancedBlendIndependentBlend; + VkBool32 advancedBlendNonPremultipliedSrcColor; + VkBool32 advancedBlendNonPremultipliedDstColor; + VkBool32 advancedBlendCorrelatedOverlap; + VkBool32 advancedBlendAllOperations; +} VkPhysicalDeviceBlendOperationAdvancedPropertiesEXT; + +typedef struct VkPipelineColorBlendAdvancedStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkBool32 srcPremultiplied; + VkBool32 dstPremultiplied; + VkBlendOverlapEXT blendOverlap; +} VkPipelineColorBlendAdvancedStateCreateInfoEXT; + + + +#define VK_NV_fragment_coverage_to_color 1 +#define VK_NV_FRAGMENT_COVERAGE_TO_COLOR_SPEC_VERSION 1 +#define VK_NV_FRAGMENT_COVERAGE_TO_COLOR_EXTENSION_NAME "VK_NV_fragment_coverage_to_color" +typedef VkFlags VkPipelineCoverageToColorStateCreateFlagsNV; +typedef struct VkPipelineCoverageToColorStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineCoverageToColorStateCreateFlagsNV flags; + VkBool32 coverageToColorEnable; + uint32_t coverageToColorLocation; +} VkPipelineCoverageToColorStateCreateInfoNV; + + + +#define VK_NV_framebuffer_mixed_samples 1 +#define VK_NV_FRAMEBUFFER_MIXED_SAMPLES_SPEC_VERSION 1 +#define VK_NV_FRAMEBUFFER_MIXED_SAMPLES_EXTENSION_NAME "VK_NV_framebuffer_mixed_samples" + +typedef enum VkCoverageModulationModeNV { + VK_COVERAGE_MODULATION_MODE_NONE_NV = 0, + VK_COVERAGE_MODULATION_MODE_RGB_NV = 1, + VK_COVERAGE_MODULATION_MODE_ALPHA_NV = 2, + VK_COVERAGE_MODULATION_MODE_RGBA_NV = 3, + VK_COVERAGE_MODULATION_MODE_BEGIN_RANGE_NV = VK_COVERAGE_MODULATION_MODE_NONE_NV, + VK_COVERAGE_MODULATION_MODE_END_RANGE_NV = VK_COVERAGE_MODULATION_MODE_RGBA_NV, + VK_COVERAGE_MODULATION_MODE_RANGE_SIZE_NV = (VK_COVERAGE_MODULATION_MODE_RGBA_NV - VK_COVERAGE_MODULATION_MODE_NONE_NV + 1), + VK_COVERAGE_MODULATION_MODE_MAX_ENUM_NV = 0x7FFFFFFF +} VkCoverageModulationModeNV; +typedef VkFlags VkPipelineCoverageModulationStateCreateFlagsNV; +typedef struct VkPipelineCoverageModulationStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineCoverageModulationStateCreateFlagsNV flags; + VkCoverageModulationModeNV coverageModulationMode; + VkBool32 coverageModulationTableEnable; + uint32_t coverageModulationTableCount; + const float* pCoverageModulationTable; +} VkPipelineCoverageModulationStateCreateInfoNV; + + + +#define VK_NV_fill_rectangle 1 +#define VK_NV_FILL_RECTANGLE_SPEC_VERSION 1 +#define VK_NV_FILL_RECTANGLE_EXTENSION_NAME "VK_NV_fill_rectangle" + + +#define VK_NV_shader_sm_builtins 1 +#define VK_NV_SHADER_SM_BUILTINS_SPEC_VERSION 1 +#define VK_NV_SHADER_SM_BUILTINS_EXTENSION_NAME "VK_NV_shader_sm_builtins" +typedef struct VkPhysicalDeviceShaderSMBuiltinsPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t shaderSMCount; + uint32_t shaderWarpsPerSM; +} VkPhysicalDeviceShaderSMBuiltinsPropertiesNV; + +typedef struct VkPhysicalDeviceShaderSMBuiltinsFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 shaderSMBuiltins; +} VkPhysicalDeviceShaderSMBuiltinsFeaturesNV; + + + +#define VK_EXT_post_depth_coverage 1 +#define VK_EXT_POST_DEPTH_COVERAGE_SPEC_VERSION 1 +#define VK_EXT_POST_DEPTH_COVERAGE_EXTENSION_NAME "VK_EXT_post_depth_coverage" + + +#define VK_EXT_image_drm_format_modifier 1 +#define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_SPEC_VERSION 1 +#define VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME "VK_EXT_image_drm_format_modifier" +typedef struct VkDrmFormatModifierPropertiesEXT { + uint64_t drmFormatModifier; + uint32_t drmFormatModifierPlaneCount; + VkFormatFeatureFlags drmFormatModifierTilingFeatures; +} VkDrmFormatModifierPropertiesEXT; + +typedef struct VkDrmFormatModifierPropertiesListEXT { + VkStructureType sType; + void* pNext; + uint32_t drmFormatModifierCount; + VkDrmFormatModifierPropertiesEXT* pDrmFormatModifierProperties; +} VkDrmFormatModifierPropertiesListEXT; + +typedef struct VkPhysicalDeviceImageDrmFormatModifierInfoEXT { + VkStructureType sType; + const void* pNext; + uint64_t drmFormatModifier; + VkSharingMode sharingMode; + uint32_t queueFamilyIndexCount; + const uint32_t* pQueueFamilyIndices; +} VkPhysicalDeviceImageDrmFormatModifierInfoEXT; + +typedef struct VkImageDrmFormatModifierListCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t drmFormatModifierCount; + const uint64_t* pDrmFormatModifiers; +} VkImageDrmFormatModifierListCreateInfoEXT; + +typedef struct VkImageDrmFormatModifierExplicitCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint64_t drmFormatModifier; + uint32_t drmFormatModifierPlaneCount; + const VkSubresourceLayout* pPlaneLayouts; +} VkImageDrmFormatModifierExplicitCreateInfoEXT; + +typedef struct VkImageDrmFormatModifierPropertiesEXT { + VkStructureType sType; + void* pNext; + uint64_t drmFormatModifier; +} VkImageDrmFormatModifierPropertiesEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetImageDrmFormatModifierPropertiesEXT)(VkDevice device, VkImage image, VkImageDrmFormatModifierPropertiesEXT* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetImageDrmFormatModifierPropertiesEXT( + VkDevice device, + VkImage image, + VkImageDrmFormatModifierPropertiesEXT* pProperties); +#endif + + +#define VK_EXT_validation_cache 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkValidationCacheEXT) +#define VK_EXT_VALIDATION_CACHE_SPEC_VERSION 1 +#define VK_EXT_VALIDATION_CACHE_EXTENSION_NAME "VK_EXT_validation_cache" + +typedef enum VkValidationCacheHeaderVersionEXT { + VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT = 1, + VK_VALIDATION_CACHE_HEADER_VERSION_BEGIN_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT, + VK_VALIDATION_CACHE_HEADER_VERSION_END_RANGE_EXT = VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT, + VK_VALIDATION_CACHE_HEADER_VERSION_RANGE_SIZE_EXT = (VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT - VK_VALIDATION_CACHE_HEADER_VERSION_ONE_EXT + 1), + VK_VALIDATION_CACHE_HEADER_VERSION_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationCacheHeaderVersionEXT; +typedef VkFlags VkValidationCacheCreateFlagsEXT; +typedef struct VkValidationCacheCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkValidationCacheCreateFlagsEXT flags; + size_t initialDataSize; + const void* pInitialData; +} VkValidationCacheCreateInfoEXT; + +typedef struct VkShaderModuleValidationCacheCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkValidationCacheEXT validationCache; +} VkShaderModuleValidationCacheCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateValidationCacheEXT)(VkDevice device, const VkValidationCacheCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkValidationCacheEXT* pValidationCache); +typedef void (VKAPI_PTR *PFN_vkDestroyValidationCacheEXT)(VkDevice device, VkValidationCacheEXT validationCache, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkMergeValidationCachesEXT)(VkDevice device, VkValidationCacheEXT dstCache, uint32_t srcCacheCount, const VkValidationCacheEXT* pSrcCaches); +typedef VkResult (VKAPI_PTR *PFN_vkGetValidationCacheDataEXT)(VkDevice device, VkValidationCacheEXT validationCache, size_t* pDataSize, void* pData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateValidationCacheEXT( + VkDevice device, + const VkValidationCacheCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkValidationCacheEXT* pValidationCache); + +VKAPI_ATTR void VKAPI_CALL vkDestroyValidationCacheEXT( + VkDevice device, + VkValidationCacheEXT validationCache, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkMergeValidationCachesEXT( + VkDevice device, + VkValidationCacheEXT dstCache, + uint32_t srcCacheCount, + const VkValidationCacheEXT* pSrcCaches); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetValidationCacheDataEXT( + VkDevice device, + VkValidationCacheEXT validationCache, + size_t* pDataSize, + void* pData); +#endif + + +#define VK_EXT_descriptor_indexing 1 +#define VK_EXT_DESCRIPTOR_INDEXING_SPEC_VERSION 2 +#define VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME "VK_EXT_descriptor_indexing" + +typedef enum VkDescriptorBindingFlagBitsEXT { + VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT = 0x00000001, + VK_DESCRIPTOR_BINDING_UPDATE_UNUSED_WHILE_PENDING_BIT_EXT = 0x00000002, + VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT = 0x00000004, + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT_EXT = 0x00000008, + VK_DESCRIPTOR_BINDING_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkDescriptorBindingFlagBitsEXT; +typedef VkFlags VkDescriptorBindingFlagsEXT; +typedef struct VkDescriptorSetLayoutBindingFlagsCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t bindingCount; + const VkDescriptorBindingFlagsEXT* pBindingFlags; +} VkDescriptorSetLayoutBindingFlagsCreateInfoEXT; + +typedef struct VkPhysicalDeviceDescriptorIndexingFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 shaderInputAttachmentArrayDynamicIndexing; + VkBool32 shaderUniformTexelBufferArrayDynamicIndexing; + VkBool32 shaderStorageTexelBufferArrayDynamicIndexing; + VkBool32 shaderUniformBufferArrayNonUniformIndexing; + VkBool32 shaderSampledImageArrayNonUniformIndexing; + VkBool32 shaderStorageBufferArrayNonUniformIndexing; + VkBool32 shaderStorageImageArrayNonUniformIndexing; + VkBool32 shaderInputAttachmentArrayNonUniformIndexing; + VkBool32 shaderUniformTexelBufferArrayNonUniformIndexing; + VkBool32 shaderStorageTexelBufferArrayNonUniformIndexing; + VkBool32 descriptorBindingUniformBufferUpdateAfterBind; + VkBool32 descriptorBindingSampledImageUpdateAfterBind; + VkBool32 descriptorBindingStorageImageUpdateAfterBind; + VkBool32 descriptorBindingStorageBufferUpdateAfterBind; + VkBool32 descriptorBindingUniformTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingStorageTexelBufferUpdateAfterBind; + VkBool32 descriptorBindingUpdateUnusedWhilePending; + VkBool32 descriptorBindingPartiallyBound; + VkBool32 descriptorBindingVariableDescriptorCount; + VkBool32 runtimeDescriptorArray; +} VkPhysicalDeviceDescriptorIndexingFeaturesEXT; + +typedef struct VkPhysicalDeviceDescriptorIndexingPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxUpdateAfterBindDescriptorsInAllPools; + VkBool32 shaderUniformBufferArrayNonUniformIndexingNative; + VkBool32 shaderSampledImageArrayNonUniformIndexingNative; + VkBool32 shaderStorageBufferArrayNonUniformIndexingNative; + VkBool32 shaderStorageImageArrayNonUniformIndexingNative; + VkBool32 shaderInputAttachmentArrayNonUniformIndexingNative; + VkBool32 robustBufferAccessUpdateAfterBind; + VkBool32 quadDivergentImplicitLod; + uint32_t maxPerStageDescriptorUpdateAfterBindSamplers; + uint32_t maxPerStageDescriptorUpdateAfterBindUniformBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageBuffers; + uint32_t maxPerStageDescriptorUpdateAfterBindSampledImages; + uint32_t maxPerStageDescriptorUpdateAfterBindStorageImages; + uint32_t maxPerStageDescriptorUpdateAfterBindInputAttachments; + uint32_t maxPerStageUpdateAfterBindResources; + uint32_t maxDescriptorSetUpdateAfterBindSamplers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffers; + uint32_t maxDescriptorSetUpdateAfterBindUniformBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffers; + uint32_t maxDescriptorSetUpdateAfterBindStorageBuffersDynamic; + uint32_t maxDescriptorSetUpdateAfterBindSampledImages; + uint32_t maxDescriptorSetUpdateAfterBindStorageImages; + uint32_t maxDescriptorSetUpdateAfterBindInputAttachments; +} VkPhysicalDeviceDescriptorIndexingPropertiesEXT; + +typedef struct VkDescriptorSetVariableDescriptorCountAllocateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t descriptorSetCount; + const uint32_t* pDescriptorCounts; +} VkDescriptorSetVariableDescriptorCountAllocateInfoEXT; + +typedef struct VkDescriptorSetVariableDescriptorCountLayoutSupportEXT { + VkStructureType sType; + void* pNext; + uint32_t maxVariableDescriptorCount; +} VkDescriptorSetVariableDescriptorCountLayoutSupportEXT; + + + +#define VK_EXT_shader_viewport_index_layer 1 +#define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_SPEC_VERSION 1 +#define VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME "VK_EXT_shader_viewport_index_layer" + + +#define VK_NV_shading_rate_image 1 +#define VK_NV_SHADING_RATE_IMAGE_SPEC_VERSION 3 +#define VK_NV_SHADING_RATE_IMAGE_EXTENSION_NAME "VK_NV_shading_rate_image" + +typedef enum VkShadingRatePaletteEntryNV { + VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV = 0, + VK_SHADING_RATE_PALETTE_ENTRY_16_INVOCATIONS_PER_PIXEL_NV = 1, + VK_SHADING_RATE_PALETTE_ENTRY_8_INVOCATIONS_PER_PIXEL_NV = 2, + VK_SHADING_RATE_PALETTE_ENTRY_4_INVOCATIONS_PER_PIXEL_NV = 3, + VK_SHADING_RATE_PALETTE_ENTRY_2_INVOCATIONS_PER_PIXEL_NV = 4, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_PIXEL_NV = 5, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X1_PIXELS_NV = 6, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_1X2_PIXELS_NV = 7, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X2_PIXELS_NV = 8, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X2_PIXELS_NV = 9, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_2X4_PIXELS_NV = 10, + VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV = 11, + VK_SHADING_RATE_PALETTE_ENTRY_BEGIN_RANGE_NV = VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV, + VK_SHADING_RATE_PALETTE_ENTRY_END_RANGE_NV = VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV, + VK_SHADING_RATE_PALETTE_ENTRY_RANGE_SIZE_NV = (VK_SHADING_RATE_PALETTE_ENTRY_1_INVOCATION_PER_4X4_PIXELS_NV - VK_SHADING_RATE_PALETTE_ENTRY_NO_INVOCATIONS_NV + 1), + VK_SHADING_RATE_PALETTE_ENTRY_MAX_ENUM_NV = 0x7FFFFFFF +} VkShadingRatePaletteEntryNV; + +typedef enum VkCoarseSampleOrderTypeNV { + VK_COARSE_SAMPLE_ORDER_TYPE_DEFAULT_NV = 0, + VK_COARSE_SAMPLE_ORDER_TYPE_CUSTOM_NV = 1, + VK_COARSE_SAMPLE_ORDER_TYPE_PIXEL_MAJOR_NV = 2, + VK_COARSE_SAMPLE_ORDER_TYPE_SAMPLE_MAJOR_NV = 3, + VK_COARSE_SAMPLE_ORDER_TYPE_BEGIN_RANGE_NV = VK_COARSE_SAMPLE_ORDER_TYPE_DEFAULT_NV, + VK_COARSE_SAMPLE_ORDER_TYPE_END_RANGE_NV = VK_COARSE_SAMPLE_ORDER_TYPE_SAMPLE_MAJOR_NV, + VK_COARSE_SAMPLE_ORDER_TYPE_RANGE_SIZE_NV = (VK_COARSE_SAMPLE_ORDER_TYPE_SAMPLE_MAJOR_NV - VK_COARSE_SAMPLE_ORDER_TYPE_DEFAULT_NV + 1), + VK_COARSE_SAMPLE_ORDER_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkCoarseSampleOrderTypeNV; +typedef struct VkShadingRatePaletteNV { + uint32_t shadingRatePaletteEntryCount; + const VkShadingRatePaletteEntryNV* pShadingRatePaletteEntries; +} VkShadingRatePaletteNV; + +typedef struct VkPipelineViewportShadingRateImageStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 shadingRateImageEnable; + uint32_t viewportCount; + const VkShadingRatePaletteNV* pShadingRatePalettes; +} VkPipelineViewportShadingRateImageStateCreateInfoNV; + +typedef struct VkPhysicalDeviceShadingRateImageFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 shadingRateImage; + VkBool32 shadingRateCoarseSampleOrder; +} VkPhysicalDeviceShadingRateImageFeaturesNV; + +typedef struct VkPhysicalDeviceShadingRateImagePropertiesNV { + VkStructureType sType; + void* pNext; + VkExtent2D shadingRateTexelSize; + uint32_t shadingRatePaletteSize; + uint32_t shadingRateMaxCoarseSamples; +} VkPhysicalDeviceShadingRateImagePropertiesNV; + +typedef struct VkCoarseSampleLocationNV { + uint32_t pixelX; + uint32_t pixelY; + uint32_t sample; +} VkCoarseSampleLocationNV; + +typedef struct VkCoarseSampleOrderCustomNV { + VkShadingRatePaletteEntryNV shadingRate; + uint32_t sampleCount; + uint32_t sampleLocationCount; + const VkCoarseSampleLocationNV* pSampleLocations; +} VkCoarseSampleOrderCustomNV; + +typedef struct VkPipelineViewportCoarseSampleOrderStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkCoarseSampleOrderTypeNV sampleOrderType; + uint32_t customSampleOrderCount; + const VkCoarseSampleOrderCustomNV* pCustomSampleOrders; +} VkPipelineViewportCoarseSampleOrderStateCreateInfoNV; + +typedef void (VKAPI_PTR *PFN_vkCmdBindShadingRateImageNV)(VkCommandBuffer commandBuffer, VkImageView imageView, VkImageLayout imageLayout); +typedef void (VKAPI_PTR *PFN_vkCmdSetViewportShadingRatePaletteNV)(VkCommandBuffer commandBuffer, uint32_t firstViewport, uint32_t viewportCount, const VkShadingRatePaletteNV* pShadingRatePalettes); +typedef void (VKAPI_PTR *PFN_vkCmdSetCoarseSampleOrderNV)(VkCommandBuffer commandBuffer, VkCoarseSampleOrderTypeNV sampleOrderType, uint32_t customSampleOrderCount, const VkCoarseSampleOrderCustomNV* pCustomSampleOrders); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdBindShadingRateImageNV( + VkCommandBuffer commandBuffer, + VkImageView imageView, + VkImageLayout imageLayout); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetViewportShadingRatePaletteNV( + VkCommandBuffer commandBuffer, + uint32_t firstViewport, + uint32_t viewportCount, + const VkShadingRatePaletteNV* pShadingRatePalettes); + +VKAPI_ATTR void VKAPI_CALL vkCmdSetCoarseSampleOrderNV( + VkCommandBuffer commandBuffer, + VkCoarseSampleOrderTypeNV sampleOrderType, + uint32_t customSampleOrderCount, + const VkCoarseSampleOrderCustomNV* pCustomSampleOrders); +#endif + + +#define VK_NV_ray_tracing 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkAccelerationStructureNV) +#define VK_NV_RAY_TRACING_SPEC_VERSION 3 +#define VK_NV_RAY_TRACING_EXTENSION_NAME "VK_NV_ray_tracing" +#define VK_SHADER_UNUSED_NV (~0U) + +typedef enum VkAccelerationStructureTypeNV { + VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV = 0, + VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV = 1, + VK_ACCELERATION_STRUCTURE_TYPE_BEGIN_RANGE_NV = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV, + VK_ACCELERATION_STRUCTURE_TYPE_END_RANGE_NV = VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV, + VK_ACCELERATION_STRUCTURE_TYPE_RANGE_SIZE_NV = (VK_ACCELERATION_STRUCTURE_TYPE_BOTTOM_LEVEL_NV - VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_NV + 1), + VK_ACCELERATION_STRUCTURE_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkAccelerationStructureTypeNV; + +typedef enum VkRayTracingShaderGroupTypeNV { + VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV = 0, + VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_NV = 1, + VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV = 2, + VK_RAY_TRACING_SHADER_GROUP_TYPE_BEGIN_RANGE_NV = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV, + VK_RAY_TRACING_SHADER_GROUP_TYPE_END_RANGE_NV = VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV, + VK_RAY_TRACING_SHADER_GROUP_TYPE_RANGE_SIZE_NV = (VK_RAY_TRACING_SHADER_GROUP_TYPE_PROCEDURAL_HIT_GROUP_NV - VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_NV + 1), + VK_RAY_TRACING_SHADER_GROUP_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkRayTracingShaderGroupTypeNV; + +typedef enum VkGeometryTypeNV { + VK_GEOMETRY_TYPE_TRIANGLES_NV = 0, + VK_GEOMETRY_TYPE_AABBS_NV = 1, + VK_GEOMETRY_TYPE_BEGIN_RANGE_NV = VK_GEOMETRY_TYPE_TRIANGLES_NV, + VK_GEOMETRY_TYPE_END_RANGE_NV = VK_GEOMETRY_TYPE_AABBS_NV, + VK_GEOMETRY_TYPE_RANGE_SIZE_NV = (VK_GEOMETRY_TYPE_AABBS_NV - VK_GEOMETRY_TYPE_TRIANGLES_NV + 1), + VK_GEOMETRY_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkGeometryTypeNV; + +typedef enum VkCopyAccelerationStructureModeNV { + VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_NV = 0, + VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV = 1, + VK_COPY_ACCELERATION_STRUCTURE_MODE_BEGIN_RANGE_NV = VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_NV, + VK_COPY_ACCELERATION_STRUCTURE_MODE_END_RANGE_NV = VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV, + VK_COPY_ACCELERATION_STRUCTURE_MODE_RANGE_SIZE_NV = (VK_COPY_ACCELERATION_STRUCTURE_MODE_COMPACT_NV - VK_COPY_ACCELERATION_STRUCTURE_MODE_CLONE_NV + 1), + VK_COPY_ACCELERATION_STRUCTURE_MODE_MAX_ENUM_NV = 0x7FFFFFFF +} VkCopyAccelerationStructureModeNV; + +typedef enum VkAccelerationStructureMemoryRequirementsTypeNV { + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV = 0, + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BUILD_SCRATCH_NV = 1, + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV = 2, + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_BEGIN_RANGE_NV = VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV, + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_END_RANGE_NV = VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV, + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_RANGE_SIZE_NV = (VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_UPDATE_SCRATCH_NV - VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_OBJECT_NV + 1), + VK_ACCELERATION_STRUCTURE_MEMORY_REQUIREMENTS_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkAccelerationStructureMemoryRequirementsTypeNV; + +typedef enum VkGeometryFlagBitsNV { + VK_GEOMETRY_OPAQUE_BIT_NV = 0x00000001, + VK_GEOMETRY_NO_DUPLICATE_ANY_HIT_INVOCATION_BIT_NV = 0x00000002, + VK_GEOMETRY_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkGeometryFlagBitsNV; +typedef VkFlags VkGeometryFlagsNV; + +typedef enum VkGeometryInstanceFlagBitsNV { + VK_GEOMETRY_INSTANCE_TRIANGLE_CULL_DISABLE_BIT_NV = 0x00000001, + VK_GEOMETRY_INSTANCE_TRIANGLE_FRONT_COUNTERCLOCKWISE_BIT_NV = 0x00000002, + VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_NV = 0x00000004, + VK_GEOMETRY_INSTANCE_FORCE_NO_OPAQUE_BIT_NV = 0x00000008, + VK_GEOMETRY_INSTANCE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkGeometryInstanceFlagBitsNV; +typedef VkFlags VkGeometryInstanceFlagsNV; + +typedef enum VkBuildAccelerationStructureFlagBitsNV { + VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_UPDATE_BIT_NV = 0x00000001, + VK_BUILD_ACCELERATION_STRUCTURE_ALLOW_COMPACTION_BIT_NV = 0x00000002, + VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_NV = 0x00000004, + VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_BUILD_BIT_NV = 0x00000008, + VK_BUILD_ACCELERATION_STRUCTURE_LOW_MEMORY_BIT_NV = 0x00000010, + VK_BUILD_ACCELERATION_STRUCTURE_FLAG_BITS_MAX_ENUM_NV = 0x7FFFFFFF +} VkBuildAccelerationStructureFlagBitsNV; +typedef VkFlags VkBuildAccelerationStructureFlagsNV; +typedef struct VkRayTracingShaderGroupCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkRayTracingShaderGroupTypeNV type; + uint32_t generalShader; + uint32_t closestHitShader; + uint32_t anyHitShader; + uint32_t intersectionShader; +} VkRayTracingShaderGroupCreateInfoNV; + +typedef struct VkRayTracingPipelineCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineCreateFlags flags; + uint32_t stageCount; + const VkPipelineShaderStageCreateInfo* pStages; + uint32_t groupCount; + const VkRayTracingShaderGroupCreateInfoNV* pGroups; + uint32_t maxRecursionDepth; + VkPipelineLayout layout; + VkPipeline basePipelineHandle; + int32_t basePipelineIndex; +} VkRayTracingPipelineCreateInfoNV; + +typedef struct VkGeometryTrianglesNV { + VkStructureType sType; + const void* pNext; + VkBuffer vertexData; + VkDeviceSize vertexOffset; + uint32_t vertexCount; + VkDeviceSize vertexStride; + VkFormat vertexFormat; + VkBuffer indexData; + VkDeviceSize indexOffset; + uint32_t indexCount; + VkIndexType indexType; + VkBuffer transformData; + VkDeviceSize transformOffset; +} VkGeometryTrianglesNV; + +typedef struct VkGeometryAABBNV { + VkStructureType sType; + const void* pNext; + VkBuffer aabbData; + uint32_t numAABBs; + uint32_t stride; + VkDeviceSize offset; +} VkGeometryAABBNV; + +typedef struct VkGeometryDataNV { + VkGeometryTrianglesNV triangles; + VkGeometryAABBNV aabbs; +} VkGeometryDataNV; + +typedef struct VkGeometryNV { + VkStructureType sType; + const void* pNext; + VkGeometryTypeNV geometryType; + VkGeometryDataNV geometry; + VkGeometryFlagsNV flags; +} VkGeometryNV; + +typedef struct VkAccelerationStructureInfoNV { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureTypeNV type; + VkBuildAccelerationStructureFlagsNV flags; + uint32_t instanceCount; + uint32_t geometryCount; + const VkGeometryNV* pGeometries; +} VkAccelerationStructureInfoNV; + +typedef struct VkAccelerationStructureCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkDeviceSize compactedSize; + VkAccelerationStructureInfoNV info; +} VkAccelerationStructureCreateInfoNV; + +typedef struct VkBindAccelerationStructureMemoryInfoNV { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureNV accelerationStructure; + VkDeviceMemory memory; + VkDeviceSize memoryOffset; + uint32_t deviceIndexCount; + const uint32_t* pDeviceIndices; +} VkBindAccelerationStructureMemoryInfoNV; + +typedef struct VkWriteDescriptorSetAccelerationStructureNV { + VkStructureType sType; + const void* pNext; + uint32_t accelerationStructureCount; + const VkAccelerationStructureNV* pAccelerationStructures; +} VkWriteDescriptorSetAccelerationStructureNV; + +typedef struct VkAccelerationStructureMemoryRequirementsInfoNV { + VkStructureType sType; + const void* pNext; + VkAccelerationStructureMemoryRequirementsTypeNV type; + VkAccelerationStructureNV accelerationStructure; +} VkAccelerationStructureMemoryRequirementsInfoNV; + +typedef struct VkPhysicalDeviceRayTracingPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t shaderGroupHandleSize; + uint32_t maxRecursionDepth; + uint32_t maxShaderGroupStride; + uint32_t shaderGroupBaseAlignment; + uint64_t maxGeometryCount; + uint64_t maxInstanceCount; + uint64_t maxTriangleCount; + uint32_t maxDescriptorSetAccelerationStructures; +} VkPhysicalDeviceRayTracingPropertiesNV; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateAccelerationStructureNV)(VkDevice device, const VkAccelerationStructureCreateInfoNV* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkAccelerationStructureNV* pAccelerationStructure); +typedef void (VKAPI_PTR *PFN_vkDestroyAccelerationStructureNV)(VkDevice device, VkAccelerationStructureNV accelerationStructure, const VkAllocationCallbacks* pAllocator); +typedef void (VKAPI_PTR *PFN_vkGetAccelerationStructureMemoryRequirementsNV)(VkDevice device, const VkAccelerationStructureMemoryRequirementsInfoNV* pInfo, VkMemoryRequirements2KHR* pMemoryRequirements); +typedef VkResult (VKAPI_PTR *PFN_vkBindAccelerationStructureMemoryNV)(VkDevice device, uint32_t bindInfoCount, const VkBindAccelerationStructureMemoryInfoNV* pBindInfos); +typedef void (VKAPI_PTR *PFN_vkCmdBuildAccelerationStructureNV)(VkCommandBuffer commandBuffer, const VkAccelerationStructureInfoNV* pInfo, VkBuffer instanceData, VkDeviceSize instanceOffset, VkBool32 update, VkAccelerationStructureNV dst, VkAccelerationStructureNV src, VkBuffer scratch, VkDeviceSize scratchOffset); +typedef void (VKAPI_PTR *PFN_vkCmdCopyAccelerationStructureNV)(VkCommandBuffer commandBuffer, VkAccelerationStructureNV dst, VkAccelerationStructureNV src, VkCopyAccelerationStructureModeNV mode); +typedef void (VKAPI_PTR *PFN_vkCmdTraceRaysNV)(VkCommandBuffer commandBuffer, VkBuffer raygenShaderBindingTableBuffer, VkDeviceSize raygenShaderBindingOffset, VkBuffer missShaderBindingTableBuffer, VkDeviceSize missShaderBindingOffset, VkDeviceSize missShaderBindingStride, VkBuffer hitShaderBindingTableBuffer, VkDeviceSize hitShaderBindingOffset, VkDeviceSize hitShaderBindingStride, VkBuffer callableShaderBindingTableBuffer, VkDeviceSize callableShaderBindingOffset, VkDeviceSize callableShaderBindingStride, uint32_t width, uint32_t height, uint32_t depth); +typedef VkResult (VKAPI_PTR *PFN_vkCreateRayTracingPipelinesNV)(VkDevice device, VkPipelineCache pipelineCache, uint32_t createInfoCount, const VkRayTracingPipelineCreateInfoNV* pCreateInfos, const VkAllocationCallbacks* pAllocator, VkPipeline* pPipelines); +typedef VkResult (VKAPI_PTR *PFN_vkGetRayTracingShaderGroupHandlesNV)(VkDevice device, VkPipeline pipeline, uint32_t firstGroup, uint32_t groupCount, size_t dataSize, void* pData); +typedef VkResult (VKAPI_PTR *PFN_vkGetAccelerationStructureHandleNV)(VkDevice device, VkAccelerationStructureNV accelerationStructure, size_t dataSize, void* pData); +typedef void (VKAPI_PTR *PFN_vkCmdWriteAccelerationStructuresPropertiesNV)(VkCommandBuffer commandBuffer, uint32_t accelerationStructureCount, const VkAccelerationStructureNV* pAccelerationStructures, VkQueryType queryType, VkQueryPool queryPool, uint32_t firstQuery); +typedef VkResult (VKAPI_PTR *PFN_vkCompileDeferredNV)(VkDevice device, VkPipeline pipeline, uint32_t shader); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateAccelerationStructureNV( + VkDevice device, + const VkAccelerationStructureCreateInfoNV* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkAccelerationStructureNV* pAccelerationStructure); + +VKAPI_ATTR void VKAPI_CALL vkDestroyAccelerationStructureNV( + VkDevice device, + VkAccelerationStructureNV accelerationStructure, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR void VKAPI_CALL vkGetAccelerationStructureMemoryRequirementsNV( + VkDevice device, + const VkAccelerationStructureMemoryRequirementsInfoNV* pInfo, + VkMemoryRequirements2KHR* pMemoryRequirements); + +VKAPI_ATTR VkResult VKAPI_CALL vkBindAccelerationStructureMemoryNV( + VkDevice device, + uint32_t bindInfoCount, + const VkBindAccelerationStructureMemoryInfoNV* pBindInfos); + +VKAPI_ATTR void VKAPI_CALL vkCmdBuildAccelerationStructureNV( + VkCommandBuffer commandBuffer, + const VkAccelerationStructureInfoNV* pInfo, + VkBuffer instanceData, + VkDeviceSize instanceOffset, + VkBool32 update, + VkAccelerationStructureNV dst, + VkAccelerationStructureNV src, + VkBuffer scratch, + VkDeviceSize scratchOffset); + +VKAPI_ATTR void VKAPI_CALL vkCmdCopyAccelerationStructureNV( + VkCommandBuffer commandBuffer, + VkAccelerationStructureNV dst, + VkAccelerationStructureNV src, + VkCopyAccelerationStructureModeNV mode); + +VKAPI_ATTR void VKAPI_CALL vkCmdTraceRaysNV( + VkCommandBuffer commandBuffer, + VkBuffer raygenShaderBindingTableBuffer, + VkDeviceSize raygenShaderBindingOffset, + VkBuffer missShaderBindingTableBuffer, + VkDeviceSize missShaderBindingOffset, + VkDeviceSize missShaderBindingStride, + VkBuffer hitShaderBindingTableBuffer, + VkDeviceSize hitShaderBindingOffset, + VkDeviceSize hitShaderBindingStride, + VkBuffer callableShaderBindingTableBuffer, + VkDeviceSize callableShaderBindingOffset, + VkDeviceSize callableShaderBindingStride, + uint32_t width, + uint32_t height, + uint32_t depth); + +VKAPI_ATTR VkResult VKAPI_CALL vkCreateRayTracingPipelinesNV( + VkDevice device, + VkPipelineCache pipelineCache, + uint32_t createInfoCount, + const VkRayTracingPipelineCreateInfoNV* pCreateInfos, + const VkAllocationCallbacks* pAllocator, + VkPipeline* pPipelines); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetRayTracingShaderGroupHandlesNV( + VkDevice device, + VkPipeline pipeline, + uint32_t firstGroup, + uint32_t groupCount, + size_t dataSize, + void* pData); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetAccelerationStructureHandleNV( + VkDevice device, + VkAccelerationStructureNV accelerationStructure, + size_t dataSize, + void* pData); + +VKAPI_ATTR void VKAPI_CALL vkCmdWriteAccelerationStructuresPropertiesNV( + VkCommandBuffer commandBuffer, + uint32_t accelerationStructureCount, + const VkAccelerationStructureNV* pAccelerationStructures, + VkQueryType queryType, + VkQueryPool queryPool, + uint32_t firstQuery); + +VKAPI_ATTR VkResult VKAPI_CALL vkCompileDeferredNV( + VkDevice device, + VkPipeline pipeline, + uint32_t shader); +#endif + + +#define VK_NV_representative_fragment_test 1 +#define VK_NV_REPRESENTATIVE_FRAGMENT_TEST_SPEC_VERSION 2 +#define VK_NV_REPRESENTATIVE_FRAGMENT_TEST_EXTENSION_NAME "VK_NV_representative_fragment_test" +typedef struct VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 representativeFragmentTest; +} VkPhysicalDeviceRepresentativeFragmentTestFeaturesNV; + +typedef struct VkPipelineRepresentativeFragmentTestStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkBool32 representativeFragmentTestEnable; +} VkPipelineRepresentativeFragmentTestStateCreateInfoNV; + + + +#define VK_EXT_filter_cubic 1 +#define VK_EXT_FILTER_CUBIC_SPEC_VERSION 2 +#define VK_EXT_FILTER_CUBIC_EXTENSION_NAME "VK_EXT_filter_cubic" +typedef struct VkPhysicalDeviceImageViewImageFormatInfoEXT { + VkStructureType sType; + void* pNext; + VkImageViewType imageViewType; +} VkPhysicalDeviceImageViewImageFormatInfoEXT; + +typedef struct VkFilterCubicImageViewImageFormatPropertiesEXT { + VkStructureType sType; + void* pNext; + VkBool32 filterCubic; + VkBool32 filterCubicMinmax ; +} VkFilterCubicImageViewImageFormatPropertiesEXT; + + + +#define VK_EXT_global_priority 1 +#define VK_EXT_GLOBAL_PRIORITY_SPEC_VERSION 2 +#define VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME "VK_EXT_global_priority" + +typedef enum VkQueueGlobalPriorityEXT { + VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT = 128, + VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT = 256, + VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT = 512, + VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT = 1024, + VK_QUEUE_GLOBAL_PRIORITY_BEGIN_RANGE_EXT = VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT, + VK_QUEUE_GLOBAL_PRIORITY_END_RANGE_EXT = VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT, + VK_QUEUE_GLOBAL_PRIORITY_RANGE_SIZE_EXT = (VK_QUEUE_GLOBAL_PRIORITY_REALTIME_EXT - VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT + 1), + VK_QUEUE_GLOBAL_PRIORITY_MAX_ENUM_EXT = 0x7FFFFFFF +} VkQueueGlobalPriorityEXT; +typedef struct VkDeviceQueueGlobalPriorityCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkQueueGlobalPriorityEXT globalPriority; +} VkDeviceQueueGlobalPriorityCreateInfoEXT; + + + +#define VK_EXT_external_memory_host 1 +#define VK_EXT_EXTERNAL_MEMORY_HOST_SPEC_VERSION 1 +#define VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME "VK_EXT_external_memory_host" +typedef struct VkImportMemoryHostPointerInfoEXT { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagBits handleType; + void* pHostPointer; +} VkImportMemoryHostPointerInfoEXT; + +typedef struct VkMemoryHostPointerPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t memoryTypeBits; +} VkMemoryHostPointerPropertiesEXT; + +typedef struct VkPhysicalDeviceExternalMemoryHostPropertiesEXT { + VkStructureType sType; + void* pNext; + VkDeviceSize minImportedHostPointerAlignment; +} VkPhysicalDeviceExternalMemoryHostPropertiesEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryHostPointerPropertiesEXT)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, const void* pHostPointer, VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryHostPointerPropertiesEXT( + VkDevice device, + VkExternalMemoryHandleTypeFlagBits handleType, + const void* pHostPointer, + VkMemoryHostPointerPropertiesEXT* pMemoryHostPointerProperties); +#endif + + +#define VK_AMD_buffer_marker 1 +#define VK_AMD_BUFFER_MARKER_SPEC_VERSION 1 +#define VK_AMD_BUFFER_MARKER_EXTENSION_NAME "VK_AMD_buffer_marker" +typedef void (VKAPI_PTR *PFN_vkCmdWriteBufferMarkerAMD)(VkCommandBuffer commandBuffer, VkPipelineStageFlagBits pipelineStage, VkBuffer dstBuffer, VkDeviceSize dstOffset, uint32_t marker); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdWriteBufferMarkerAMD( + VkCommandBuffer commandBuffer, + VkPipelineStageFlagBits pipelineStage, + VkBuffer dstBuffer, + VkDeviceSize dstOffset, + uint32_t marker); +#endif + + +#define VK_AMD_pipeline_compiler_control 1 +#define VK_AMD_PIPELINE_COMPILER_CONTROL_SPEC_VERSION 1 +#define VK_AMD_PIPELINE_COMPILER_CONTROL_EXTENSION_NAME "VK_AMD_pipeline_compiler_control" + +typedef enum VkPipelineCompilerControlFlagBitsAMD { + VK_PIPELINE_COMPILER_CONTROL_FLAG_BITS_MAX_ENUM_AMD = 0x7FFFFFFF +} VkPipelineCompilerControlFlagBitsAMD; +typedef VkFlags VkPipelineCompilerControlFlagsAMD; +typedef struct VkPipelineCompilerControlCreateInfoAMD { + VkStructureType sType; + const void* pNext; + VkPipelineCompilerControlFlagsAMD compilerControlFlags; +} VkPipelineCompilerControlCreateInfoAMD; + + + +#define VK_EXT_calibrated_timestamps 1 +#define VK_EXT_CALIBRATED_TIMESTAMPS_SPEC_VERSION 1 +#define VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME "VK_EXT_calibrated_timestamps" + +typedef enum VkTimeDomainEXT { + VK_TIME_DOMAIN_DEVICE_EXT = 0, + VK_TIME_DOMAIN_CLOCK_MONOTONIC_EXT = 1, + VK_TIME_DOMAIN_CLOCK_MONOTONIC_RAW_EXT = 2, + VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT = 3, + VK_TIME_DOMAIN_BEGIN_RANGE_EXT = VK_TIME_DOMAIN_DEVICE_EXT, + VK_TIME_DOMAIN_END_RANGE_EXT = VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT, + VK_TIME_DOMAIN_RANGE_SIZE_EXT = (VK_TIME_DOMAIN_QUERY_PERFORMANCE_COUNTER_EXT - VK_TIME_DOMAIN_DEVICE_EXT + 1), + VK_TIME_DOMAIN_MAX_ENUM_EXT = 0x7FFFFFFF +} VkTimeDomainEXT; +typedef struct VkCalibratedTimestampInfoEXT { + VkStructureType sType; + const void* pNext; + VkTimeDomainEXT timeDomain; +} VkCalibratedTimestampInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceCalibrateableTimeDomainsEXT)(VkPhysicalDevice physicalDevice, uint32_t* pTimeDomainCount, VkTimeDomainEXT* pTimeDomains); +typedef VkResult (VKAPI_PTR *PFN_vkGetCalibratedTimestampsEXT)(VkDevice device, uint32_t timestampCount, const VkCalibratedTimestampInfoEXT* pTimestampInfos, uint64_t* pTimestamps, uint64_t* pMaxDeviation); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceCalibrateableTimeDomainsEXT( + VkPhysicalDevice physicalDevice, + uint32_t* pTimeDomainCount, + VkTimeDomainEXT* pTimeDomains); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetCalibratedTimestampsEXT( + VkDevice device, + uint32_t timestampCount, + const VkCalibratedTimestampInfoEXT* pTimestampInfos, + uint64_t* pTimestamps, + uint64_t* pMaxDeviation); +#endif + + +#define VK_AMD_shader_core_properties 1 +#define VK_AMD_SHADER_CORE_PROPERTIES_SPEC_VERSION 2 +#define VK_AMD_SHADER_CORE_PROPERTIES_EXTENSION_NAME "VK_AMD_shader_core_properties" +typedef struct VkPhysicalDeviceShaderCorePropertiesAMD { + VkStructureType sType; + void* pNext; + uint32_t shaderEngineCount; + uint32_t shaderArraysPerEngineCount; + uint32_t computeUnitsPerShaderArray; + uint32_t simdPerComputeUnit; + uint32_t wavefrontsPerSimd; + uint32_t wavefrontSize; + uint32_t sgprsPerSimd; + uint32_t minSgprAllocation; + uint32_t maxSgprAllocation; + uint32_t sgprAllocationGranularity; + uint32_t vgprsPerSimd; + uint32_t minVgprAllocation; + uint32_t maxVgprAllocation; + uint32_t vgprAllocationGranularity; +} VkPhysicalDeviceShaderCorePropertiesAMD; + + + +#define VK_AMD_memory_overallocation_behavior 1 +#define VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_SPEC_VERSION 1 +#define VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_EXTENSION_NAME "VK_AMD_memory_overallocation_behavior" + +typedef enum VkMemoryOverallocationBehaviorAMD { + VK_MEMORY_OVERALLOCATION_BEHAVIOR_DEFAULT_AMD = 0, + VK_MEMORY_OVERALLOCATION_BEHAVIOR_ALLOWED_AMD = 1, + VK_MEMORY_OVERALLOCATION_BEHAVIOR_DISALLOWED_AMD = 2, + VK_MEMORY_OVERALLOCATION_BEHAVIOR_BEGIN_RANGE_AMD = VK_MEMORY_OVERALLOCATION_BEHAVIOR_DEFAULT_AMD, + VK_MEMORY_OVERALLOCATION_BEHAVIOR_END_RANGE_AMD = VK_MEMORY_OVERALLOCATION_BEHAVIOR_DISALLOWED_AMD, + VK_MEMORY_OVERALLOCATION_BEHAVIOR_RANGE_SIZE_AMD = (VK_MEMORY_OVERALLOCATION_BEHAVIOR_DISALLOWED_AMD - VK_MEMORY_OVERALLOCATION_BEHAVIOR_DEFAULT_AMD + 1), + VK_MEMORY_OVERALLOCATION_BEHAVIOR_MAX_ENUM_AMD = 0x7FFFFFFF +} VkMemoryOverallocationBehaviorAMD; +typedef struct VkDeviceMemoryOverallocationCreateInfoAMD { + VkStructureType sType; + const void* pNext; + VkMemoryOverallocationBehaviorAMD overallocationBehavior; +} VkDeviceMemoryOverallocationCreateInfoAMD; + + + +#define VK_EXT_vertex_attribute_divisor 1 +#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION 3 +#define VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME "VK_EXT_vertex_attribute_divisor" +typedef struct VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t maxVertexAttribDivisor; +} VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT; + +typedef struct VkVertexInputBindingDivisorDescriptionEXT { + uint32_t binding; + uint32_t divisor; +} VkVertexInputBindingDivisorDescriptionEXT; + +typedef struct VkPipelineVertexInputDivisorStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + uint32_t vertexBindingDivisorCount; + const VkVertexInputBindingDivisorDescriptionEXT* pVertexBindingDivisors; +} VkPipelineVertexInputDivisorStateCreateInfoEXT; + +typedef struct VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 vertexAttributeInstanceRateDivisor; + VkBool32 vertexAttributeInstanceRateZeroDivisor; +} VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT; + + + +#define VK_EXT_pipeline_creation_feedback 1 +#define VK_EXT_PIPELINE_CREATION_FEEDBACK_SPEC_VERSION 1 +#define VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME "VK_EXT_pipeline_creation_feedback" + +typedef enum VkPipelineCreationFeedbackFlagBitsEXT { + VK_PIPELINE_CREATION_FEEDBACK_VALID_BIT_EXT = 0x00000001, + VK_PIPELINE_CREATION_FEEDBACK_APPLICATION_PIPELINE_CACHE_HIT_BIT_EXT = 0x00000002, + VK_PIPELINE_CREATION_FEEDBACK_BASE_PIPELINE_ACCELERATION_BIT_EXT = 0x00000004, + VK_PIPELINE_CREATION_FEEDBACK_FLAG_BITS_MAX_ENUM_EXT = 0x7FFFFFFF +} VkPipelineCreationFeedbackFlagBitsEXT; +typedef VkFlags VkPipelineCreationFeedbackFlagsEXT; +typedef struct VkPipelineCreationFeedbackEXT { + VkPipelineCreationFeedbackFlagsEXT flags; + uint64_t duration; +} VkPipelineCreationFeedbackEXT; + +typedef struct VkPipelineCreationFeedbackCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkPipelineCreationFeedbackEXT* pPipelineCreationFeedback; + uint32_t pipelineStageCreationFeedbackCount; + VkPipelineCreationFeedbackEXT* pPipelineStageCreationFeedbacks; +} VkPipelineCreationFeedbackCreateInfoEXT; + + + +#define VK_NV_shader_subgroup_partitioned 1 +#define VK_NV_SHADER_SUBGROUP_PARTITIONED_SPEC_VERSION 1 +#define VK_NV_SHADER_SUBGROUP_PARTITIONED_EXTENSION_NAME "VK_NV_shader_subgroup_partitioned" + + +#define VK_NV_compute_shader_derivatives 1 +#define VK_NV_COMPUTE_SHADER_DERIVATIVES_SPEC_VERSION 1 +#define VK_NV_COMPUTE_SHADER_DERIVATIVES_EXTENSION_NAME "VK_NV_compute_shader_derivatives" +typedef struct VkPhysicalDeviceComputeShaderDerivativesFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 computeDerivativeGroupQuads; + VkBool32 computeDerivativeGroupLinear; +} VkPhysicalDeviceComputeShaderDerivativesFeaturesNV; + + + +#define VK_NV_mesh_shader 1 +#define VK_NV_MESH_SHADER_SPEC_VERSION 1 +#define VK_NV_MESH_SHADER_EXTENSION_NAME "VK_NV_mesh_shader" +typedef struct VkPhysicalDeviceMeshShaderFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 taskShader; + VkBool32 meshShader; +} VkPhysicalDeviceMeshShaderFeaturesNV; + +typedef struct VkPhysicalDeviceMeshShaderPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t maxDrawMeshTasksCount; + uint32_t maxTaskWorkGroupInvocations; + uint32_t maxTaskWorkGroupSize[3]; + uint32_t maxTaskTotalMemorySize; + uint32_t maxTaskOutputCount; + uint32_t maxMeshWorkGroupInvocations; + uint32_t maxMeshWorkGroupSize[3]; + uint32_t maxMeshTotalMemorySize; + uint32_t maxMeshOutputVertices; + uint32_t maxMeshOutputPrimitives; + uint32_t maxMeshMultiviewViewCount; + uint32_t meshOutputPerVertexGranularity; + uint32_t meshOutputPerPrimitiveGranularity; +} VkPhysicalDeviceMeshShaderPropertiesNV; + +typedef struct VkDrawMeshTasksIndirectCommandNV { + uint32_t taskCount; + uint32_t firstTask; +} VkDrawMeshTasksIndirectCommandNV; + +typedef void (VKAPI_PTR *PFN_vkCmdDrawMeshTasksNV)(VkCommandBuffer commandBuffer, uint32_t taskCount, uint32_t firstTask); +typedef void (VKAPI_PTR *PFN_vkCmdDrawMeshTasksIndirectNV)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount, uint32_t stride); +typedef void (VKAPI_PTR *PFN_vkCmdDrawMeshTasksIndirectCountNV)(VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset, VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdDrawMeshTasksNV( + VkCommandBuffer commandBuffer, + uint32_t taskCount, + uint32_t firstTask); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawMeshTasksIndirectNV( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + uint32_t drawCount, + uint32_t stride); + +VKAPI_ATTR void VKAPI_CALL vkCmdDrawMeshTasksIndirectCountNV( + VkCommandBuffer commandBuffer, + VkBuffer buffer, + VkDeviceSize offset, + VkBuffer countBuffer, + VkDeviceSize countBufferOffset, + uint32_t maxDrawCount, + uint32_t stride); +#endif + + +#define VK_NV_fragment_shader_barycentric 1 +#define VK_NV_FRAGMENT_SHADER_BARYCENTRIC_SPEC_VERSION 1 +#define VK_NV_FRAGMENT_SHADER_BARYCENTRIC_EXTENSION_NAME "VK_NV_fragment_shader_barycentric" +typedef struct VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 fragmentShaderBarycentric; +} VkPhysicalDeviceFragmentShaderBarycentricFeaturesNV; + + + +#define VK_NV_shader_image_footprint 1 +#define VK_NV_SHADER_IMAGE_FOOTPRINT_SPEC_VERSION 2 +#define VK_NV_SHADER_IMAGE_FOOTPRINT_EXTENSION_NAME "VK_NV_shader_image_footprint" +typedef struct VkPhysicalDeviceShaderImageFootprintFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 imageFootprint; +} VkPhysicalDeviceShaderImageFootprintFeaturesNV; + + + +#define VK_NV_scissor_exclusive 1 +#define VK_NV_SCISSOR_EXCLUSIVE_SPEC_VERSION 1 +#define VK_NV_SCISSOR_EXCLUSIVE_EXTENSION_NAME "VK_NV_scissor_exclusive" +typedef struct VkPipelineViewportExclusiveScissorStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + uint32_t exclusiveScissorCount; + const VkRect2D* pExclusiveScissors; +} VkPipelineViewportExclusiveScissorStateCreateInfoNV; + +typedef struct VkPhysicalDeviceExclusiveScissorFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 exclusiveScissor; +} VkPhysicalDeviceExclusiveScissorFeaturesNV; + +typedef void (VKAPI_PTR *PFN_vkCmdSetExclusiveScissorNV)(VkCommandBuffer commandBuffer, uint32_t firstExclusiveScissor, uint32_t exclusiveScissorCount, const VkRect2D* pExclusiveScissors); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetExclusiveScissorNV( + VkCommandBuffer commandBuffer, + uint32_t firstExclusiveScissor, + uint32_t exclusiveScissorCount, + const VkRect2D* pExclusiveScissors); +#endif + + +#define VK_NV_device_diagnostic_checkpoints 1 +#define VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_SPEC_VERSION 2 +#define VK_NV_DEVICE_DIAGNOSTIC_CHECKPOINTS_EXTENSION_NAME "VK_NV_device_diagnostic_checkpoints" +typedef struct VkQueueFamilyCheckpointPropertiesNV { + VkStructureType sType; + void* pNext; + VkPipelineStageFlags checkpointExecutionStageMask; +} VkQueueFamilyCheckpointPropertiesNV; + +typedef struct VkCheckpointDataNV { + VkStructureType sType; + void* pNext; + VkPipelineStageFlagBits stage; + void* pCheckpointMarker; +} VkCheckpointDataNV; + +typedef void (VKAPI_PTR *PFN_vkCmdSetCheckpointNV)(VkCommandBuffer commandBuffer, const void* pCheckpointMarker); +typedef void (VKAPI_PTR *PFN_vkGetQueueCheckpointDataNV)(VkQueue queue, uint32_t* pCheckpointDataCount, VkCheckpointDataNV* pCheckpointData); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetCheckpointNV( + VkCommandBuffer commandBuffer, + const void* pCheckpointMarker); + +VKAPI_ATTR void VKAPI_CALL vkGetQueueCheckpointDataNV( + VkQueue queue, + uint32_t* pCheckpointDataCount, + VkCheckpointDataNV* pCheckpointData); +#endif + + +#define VK_INTEL_shader_integer_functions2 1 +#define VK_INTEL_SHADER_INTEGER_FUNCTIONS_2_SPEC_VERSION 1 +#define VK_INTEL_SHADER_INTEGER_FUNCTIONS_2_EXTENSION_NAME "VK_INTEL_shader_integer_functions2" +typedef struct VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL { + VkStructureType sType; + void* pNext; + VkBool32 shaderIntegerFunctions2; +} VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL; + + + +#define VK_INTEL_performance_query 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkPerformanceConfigurationINTEL) +#define VK_INTEL_PERFORMANCE_QUERY_SPEC_VERSION 1 +#define VK_INTEL_PERFORMANCE_QUERY_EXTENSION_NAME "VK_INTEL_performance_query" + +typedef enum VkPerformanceConfigurationTypeINTEL { + VK_PERFORMANCE_CONFIGURATION_TYPE_COMMAND_QUEUE_METRICS_DISCOVERY_ACTIVATED_INTEL = 0, + VK_PERFORMANCE_CONFIGURATION_TYPE_BEGIN_RANGE_INTEL = VK_PERFORMANCE_CONFIGURATION_TYPE_COMMAND_QUEUE_METRICS_DISCOVERY_ACTIVATED_INTEL, + VK_PERFORMANCE_CONFIGURATION_TYPE_END_RANGE_INTEL = VK_PERFORMANCE_CONFIGURATION_TYPE_COMMAND_QUEUE_METRICS_DISCOVERY_ACTIVATED_INTEL, + VK_PERFORMANCE_CONFIGURATION_TYPE_RANGE_SIZE_INTEL = (VK_PERFORMANCE_CONFIGURATION_TYPE_COMMAND_QUEUE_METRICS_DISCOVERY_ACTIVATED_INTEL - VK_PERFORMANCE_CONFIGURATION_TYPE_COMMAND_QUEUE_METRICS_DISCOVERY_ACTIVATED_INTEL + 1), + VK_PERFORMANCE_CONFIGURATION_TYPE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkPerformanceConfigurationTypeINTEL; + +typedef enum VkQueryPoolSamplingModeINTEL { + VK_QUERY_POOL_SAMPLING_MODE_MANUAL_INTEL = 0, + VK_QUERY_POOL_SAMPLING_MODE_BEGIN_RANGE_INTEL = VK_QUERY_POOL_SAMPLING_MODE_MANUAL_INTEL, + VK_QUERY_POOL_SAMPLING_MODE_END_RANGE_INTEL = VK_QUERY_POOL_SAMPLING_MODE_MANUAL_INTEL, + VK_QUERY_POOL_SAMPLING_MODE_RANGE_SIZE_INTEL = (VK_QUERY_POOL_SAMPLING_MODE_MANUAL_INTEL - VK_QUERY_POOL_SAMPLING_MODE_MANUAL_INTEL + 1), + VK_QUERY_POOL_SAMPLING_MODE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkQueryPoolSamplingModeINTEL; + +typedef enum VkPerformanceOverrideTypeINTEL { + VK_PERFORMANCE_OVERRIDE_TYPE_NULL_HARDWARE_INTEL = 0, + VK_PERFORMANCE_OVERRIDE_TYPE_FLUSH_GPU_CACHES_INTEL = 1, + VK_PERFORMANCE_OVERRIDE_TYPE_BEGIN_RANGE_INTEL = VK_PERFORMANCE_OVERRIDE_TYPE_NULL_HARDWARE_INTEL, + VK_PERFORMANCE_OVERRIDE_TYPE_END_RANGE_INTEL = VK_PERFORMANCE_OVERRIDE_TYPE_FLUSH_GPU_CACHES_INTEL, + VK_PERFORMANCE_OVERRIDE_TYPE_RANGE_SIZE_INTEL = (VK_PERFORMANCE_OVERRIDE_TYPE_FLUSH_GPU_CACHES_INTEL - VK_PERFORMANCE_OVERRIDE_TYPE_NULL_HARDWARE_INTEL + 1), + VK_PERFORMANCE_OVERRIDE_TYPE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkPerformanceOverrideTypeINTEL; + +typedef enum VkPerformanceParameterTypeINTEL { + VK_PERFORMANCE_PARAMETER_TYPE_HW_COUNTERS_SUPPORTED_INTEL = 0, + VK_PERFORMANCE_PARAMETER_TYPE_STREAM_MARKER_VALID_BITS_INTEL = 1, + VK_PERFORMANCE_PARAMETER_TYPE_BEGIN_RANGE_INTEL = VK_PERFORMANCE_PARAMETER_TYPE_HW_COUNTERS_SUPPORTED_INTEL, + VK_PERFORMANCE_PARAMETER_TYPE_END_RANGE_INTEL = VK_PERFORMANCE_PARAMETER_TYPE_STREAM_MARKER_VALID_BITS_INTEL, + VK_PERFORMANCE_PARAMETER_TYPE_RANGE_SIZE_INTEL = (VK_PERFORMANCE_PARAMETER_TYPE_STREAM_MARKER_VALID_BITS_INTEL - VK_PERFORMANCE_PARAMETER_TYPE_HW_COUNTERS_SUPPORTED_INTEL + 1), + VK_PERFORMANCE_PARAMETER_TYPE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkPerformanceParameterTypeINTEL; + +typedef enum VkPerformanceValueTypeINTEL { + VK_PERFORMANCE_VALUE_TYPE_UINT32_INTEL = 0, + VK_PERFORMANCE_VALUE_TYPE_UINT64_INTEL = 1, + VK_PERFORMANCE_VALUE_TYPE_FLOAT_INTEL = 2, + VK_PERFORMANCE_VALUE_TYPE_BOOL_INTEL = 3, + VK_PERFORMANCE_VALUE_TYPE_STRING_INTEL = 4, + VK_PERFORMANCE_VALUE_TYPE_BEGIN_RANGE_INTEL = VK_PERFORMANCE_VALUE_TYPE_UINT32_INTEL, + VK_PERFORMANCE_VALUE_TYPE_END_RANGE_INTEL = VK_PERFORMANCE_VALUE_TYPE_STRING_INTEL, + VK_PERFORMANCE_VALUE_TYPE_RANGE_SIZE_INTEL = (VK_PERFORMANCE_VALUE_TYPE_STRING_INTEL - VK_PERFORMANCE_VALUE_TYPE_UINT32_INTEL + 1), + VK_PERFORMANCE_VALUE_TYPE_MAX_ENUM_INTEL = 0x7FFFFFFF +} VkPerformanceValueTypeINTEL; +typedef union VkPerformanceValueDataINTEL { + uint32_t value32; + uint64_t value64; + float valueFloat; + VkBool32 valueBool; + const char* valueString; +} VkPerformanceValueDataINTEL; + +typedef struct VkPerformanceValueINTEL { + VkPerformanceValueTypeINTEL type; + VkPerformanceValueDataINTEL data; +} VkPerformanceValueINTEL; + +typedef struct VkInitializePerformanceApiInfoINTEL { + VkStructureType sType; + const void* pNext; + void* pUserData; +} VkInitializePerformanceApiInfoINTEL; + +typedef struct VkQueryPoolCreateInfoINTEL { + VkStructureType sType; + const void* pNext; + VkQueryPoolSamplingModeINTEL performanceCountersSampling; +} VkQueryPoolCreateInfoINTEL; + +typedef struct VkPerformanceMarkerInfoINTEL { + VkStructureType sType; + const void* pNext; + uint64_t marker; +} VkPerformanceMarkerInfoINTEL; + +typedef struct VkPerformanceStreamMarkerInfoINTEL { + VkStructureType sType; + const void* pNext; + uint32_t marker; +} VkPerformanceStreamMarkerInfoINTEL; + +typedef struct VkPerformanceOverrideInfoINTEL { + VkStructureType sType; + const void* pNext; + VkPerformanceOverrideTypeINTEL type; + VkBool32 enable; + uint64_t parameter; +} VkPerformanceOverrideInfoINTEL; + +typedef struct VkPerformanceConfigurationAcquireInfoINTEL { + VkStructureType sType; + const void* pNext; + VkPerformanceConfigurationTypeINTEL type; +} VkPerformanceConfigurationAcquireInfoINTEL; + +typedef VkResult (VKAPI_PTR *PFN_vkInitializePerformanceApiINTEL)(VkDevice device, const VkInitializePerformanceApiInfoINTEL* pInitializeInfo); +typedef void (VKAPI_PTR *PFN_vkUninitializePerformanceApiINTEL)(VkDevice device); +typedef VkResult (VKAPI_PTR *PFN_vkCmdSetPerformanceMarkerINTEL)(VkCommandBuffer commandBuffer, const VkPerformanceMarkerInfoINTEL* pMarkerInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCmdSetPerformanceStreamMarkerINTEL)(VkCommandBuffer commandBuffer, const VkPerformanceStreamMarkerInfoINTEL* pMarkerInfo); +typedef VkResult (VKAPI_PTR *PFN_vkCmdSetPerformanceOverrideINTEL)(VkCommandBuffer commandBuffer, const VkPerformanceOverrideInfoINTEL* pOverrideInfo); +typedef VkResult (VKAPI_PTR *PFN_vkAcquirePerformanceConfigurationINTEL)(VkDevice device, const VkPerformanceConfigurationAcquireInfoINTEL* pAcquireInfo, VkPerformanceConfigurationINTEL* pConfiguration); +typedef VkResult (VKAPI_PTR *PFN_vkReleasePerformanceConfigurationINTEL)(VkDevice device, VkPerformanceConfigurationINTEL configuration); +typedef VkResult (VKAPI_PTR *PFN_vkQueueSetPerformanceConfigurationINTEL)(VkQueue queue, VkPerformanceConfigurationINTEL configuration); +typedef VkResult (VKAPI_PTR *PFN_vkGetPerformanceParameterINTEL)(VkDevice device, VkPerformanceParameterTypeINTEL parameter, VkPerformanceValueINTEL* pValue); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkInitializePerformanceApiINTEL( + VkDevice device, + const VkInitializePerformanceApiInfoINTEL* pInitializeInfo); + +VKAPI_ATTR void VKAPI_CALL vkUninitializePerformanceApiINTEL( + VkDevice device); + +VKAPI_ATTR VkResult VKAPI_CALL vkCmdSetPerformanceMarkerINTEL( + VkCommandBuffer commandBuffer, + const VkPerformanceMarkerInfoINTEL* pMarkerInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCmdSetPerformanceStreamMarkerINTEL( + VkCommandBuffer commandBuffer, + const VkPerformanceStreamMarkerInfoINTEL* pMarkerInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkCmdSetPerformanceOverrideINTEL( + VkCommandBuffer commandBuffer, + const VkPerformanceOverrideInfoINTEL* pOverrideInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquirePerformanceConfigurationINTEL( + VkDevice device, + const VkPerformanceConfigurationAcquireInfoINTEL* pAcquireInfo, + VkPerformanceConfigurationINTEL* pConfiguration); + +VKAPI_ATTR VkResult VKAPI_CALL vkReleasePerformanceConfigurationINTEL( + VkDevice device, + VkPerformanceConfigurationINTEL configuration); + +VKAPI_ATTR VkResult VKAPI_CALL vkQueueSetPerformanceConfigurationINTEL( + VkQueue queue, + VkPerformanceConfigurationINTEL configuration); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetPerformanceParameterINTEL( + VkDevice device, + VkPerformanceParameterTypeINTEL parameter, + VkPerformanceValueINTEL* pValue); +#endif + + +#define VK_EXT_pci_bus_info 1 +#define VK_EXT_PCI_BUS_INFO_SPEC_VERSION 2 +#define VK_EXT_PCI_BUS_INFO_EXTENSION_NAME "VK_EXT_pci_bus_info" +typedef struct VkPhysicalDevicePCIBusInfoPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t pciDomain; + uint32_t pciBus; + uint32_t pciDevice; + uint32_t pciFunction; +} VkPhysicalDevicePCIBusInfoPropertiesEXT; + + + +#define VK_AMD_display_native_hdr 1 +#define VK_AMD_DISPLAY_NATIVE_HDR_SPEC_VERSION 1 +#define VK_AMD_DISPLAY_NATIVE_HDR_EXTENSION_NAME "VK_AMD_display_native_hdr" +typedef struct VkDisplayNativeHdrSurfaceCapabilitiesAMD { + VkStructureType sType; + void* pNext; + VkBool32 localDimmingSupport; +} VkDisplayNativeHdrSurfaceCapabilitiesAMD; + +typedef struct VkSwapchainDisplayNativeHdrCreateInfoAMD { + VkStructureType sType; + const void* pNext; + VkBool32 localDimmingEnable; +} VkSwapchainDisplayNativeHdrCreateInfoAMD; + +typedef void (VKAPI_PTR *PFN_vkSetLocalDimmingAMD)(VkDevice device, VkSwapchainKHR swapChain, VkBool32 localDimmingEnable); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkSetLocalDimmingAMD( + VkDevice device, + VkSwapchainKHR swapChain, + VkBool32 localDimmingEnable); +#endif + + +#define VK_EXT_fragment_density_map 1 +#define VK_EXT_FRAGMENT_DENSITY_MAP_SPEC_VERSION 1 +#define VK_EXT_FRAGMENT_DENSITY_MAP_EXTENSION_NAME "VK_EXT_fragment_density_map" +typedef struct VkPhysicalDeviceFragmentDensityMapFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 fragmentDensityMap; + VkBool32 fragmentDensityMapDynamic; + VkBool32 fragmentDensityMapNonSubsampledImages; +} VkPhysicalDeviceFragmentDensityMapFeaturesEXT; + +typedef struct VkPhysicalDeviceFragmentDensityMapPropertiesEXT { + VkStructureType sType; + void* pNext; + VkExtent2D minFragmentDensityTexelSize; + VkExtent2D maxFragmentDensityTexelSize; + VkBool32 fragmentDensityInvocations; +} VkPhysicalDeviceFragmentDensityMapPropertiesEXT; + +typedef struct VkRenderPassFragmentDensityMapCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkAttachmentReference fragmentDensityMapAttachment; +} VkRenderPassFragmentDensityMapCreateInfoEXT; + + + +#define VK_EXT_scalar_block_layout 1 +#define VK_EXT_SCALAR_BLOCK_LAYOUT_SPEC_VERSION 1 +#define VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME "VK_EXT_scalar_block_layout" +typedef struct VkPhysicalDeviceScalarBlockLayoutFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 scalarBlockLayout; +} VkPhysicalDeviceScalarBlockLayoutFeaturesEXT; + + + +#define VK_GOOGLE_hlsl_functionality1 1 +#define VK_GOOGLE_HLSL_FUNCTIONALITY1_SPEC_VERSION 1 +#define VK_GOOGLE_HLSL_FUNCTIONALITY1_EXTENSION_NAME "VK_GOOGLE_hlsl_functionality1" + + +#define VK_GOOGLE_decorate_string 1 +#define VK_GOOGLE_DECORATE_STRING_SPEC_VERSION 1 +#define VK_GOOGLE_DECORATE_STRING_EXTENSION_NAME "VK_GOOGLE_decorate_string" + + +#define VK_EXT_subgroup_size_control 1 +#define VK_EXT_SUBGROUP_SIZE_CONTROL_SPEC_VERSION 2 +#define VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME "VK_EXT_subgroup_size_control" +typedef struct VkPhysicalDeviceSubgroupSizeControlFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 subgroupSizeControl; + VkBool32 computeFullSubgroups; +} VkPhysicalDeviceSubgroupSizeControlFeaturesEXT; + +typedef struct VkPhysicalDeviceSubgroupSizeControlPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t minSubgroupSize; + uint32_t maxSubgroupSize; + uint32_t maxComputeWorkgroupSubgroups; + VkShaderStageFlags requiredSubgroupSizeStages; +} VkPhysicalDeviceSubgroupSizeControlPropertiesEXT; + +typedef struct VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT { + VkStructureType sType; + void* pNext; + uint32_t requiredSubgroupSize; +} VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT; + + + +#define VK_AMD_shader_core_properties2 1 +#define VK_AMD_SHADER_CORE_PROPERTIES_2_SPEC_VERSION 1 +#define VK_AMD_SHADER_CORE_PROPERTIES_2_EXTENSION_NAME "VK_AMD_shader_core_properties2" + +typedef enum VkShaderCorePropertiesFlagBitsAMD { + VK_SHADER_CORE_PROPERTIES_FLAG_BITS_MAX_ENUM_AMD = 0x7FFFFFFF +} VkShaderCorePropertiesFlagBitsAMD; +typedef VkFlags VkShaderCorePropertiesFlagsAMD; +typedef struct VkPhysicalDeviceShaderCoreProperties2AMD { + VkStructureType sType; + void* pNext; + VkShaderCorePropertiesFlagsAMD shaderCoreFeatures; + uint32_t activeComputeUnitCount; +} VkPhysicalDeviceShaderCoreProperties2AMD; + + + +#define VK_AMD_device_coherent_memory 1 +#define VK_AMD_DEVICE_COHERENT_MEMORY_SPEC_VERSION 1 +#define VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME "VK_AMD_device_coherent_memory" +typedef struct VkPhysicalDeviceCoherentMemoryFeaturesAMD { + VkStructureType sType; + void* pNext; + VkBool32 deviceCoherentMemory; +} VkPhysicalDeviceCoherentMemoryFeaturesAMD; + + + +#define VK_EXT_memory_budget 1 +#define VK_EXT_MEMORY_BUDGET_SPEC_VERSION 1 +#define VK_EXT_MEMORY_BUDGET_EXTENSION_NAME "VK_EXT_memory_budget" +typedef struct VkPhysicalDeviceMemoryBudgetPropertiesEXT { + VkStructureType sType; + void* pNext; + VkDeviceSize heapBudget[VK_MAX_MEMORY_HEAPS]; + VkDeviceSize heapUsage[VK_MAX_MEMORY_HEAPS]; +} VkPhysicalDeviceMemoryBudgetPropertiesEXT; + + + +#define VK_EXT_memory_priority 1 +#define VK_EXT_MEMORY_PRIORITY_SPEC_VERSION 1 +#define VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME "VK_EXT_memory_priority" +typedef struct VkPhysicalDeviceMemoryPriorityFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 memoryPriority; +} VkPhysicalDeviceMemoryPriorityFeaturesEXT; + +typedef struct VkMemoryPriorityAllocateInfoEXT { + VkStructureType sType; + const void* pNext; + float priority; +} VkMemoryPriorityAllocateInfoEXT; + + + +#define VK_NV_dedicated_allocation_image_aliasing 1 +#define VK_NV_DEDICATED_ALLOCATION_IMAGE_ALIASING_SPEC_VERSION 1 +#define VK_NV_DEDICATED_ALLOCATION_IMAGE_ALIASING_EXTENSION_NAME "VK_NV_dedicated_allocation_image_aliasing" +typedef struct VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 dedicatedAllocationImageAliasing; +} VkPhysicalDeviceDedicatedAllocationImageAliasingFeaturesNV; + + + +#define VK_EXT_buffer_device_address 1 +typedef uint64_t VkDeviceAddress; +#define VK_EXT_BUFFER_DEVICE_ADDRESS_SPEC_VERSION 2 +#define VK_EXT_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME "VK_EXT_buffer_device_address" +typedef struct VkPhysicalDeviceBufferDeviceAddressFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 bufferDeviceAddress; + VkBool32 bufferDeviceAddressCaptureReplay; + VkBool32 bufferDeviceAddressMultiDevice; +} VkPhysicalDeviceBufferDeviceAddressFeaturesEXT; + +typedef VkPhysicalDeviceBufferDeviceAddressFeaturesEXT VkPhysicalDeviceBufferAddressFeaturesEXT; + +typedef struct VkBufferDeviceAddressInfoEXT { + VkStructureType sType; + const void* pNext; + VkBuffer buffer; +} VkBufferDeviceAddressInfoEXT; + +typedef struct VkBufferDeviceAddressCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkDeviceAddress deviceAddress; +} VkBufferDeviceAddressCreateInfoEXT; + +typedef VkDeviceAddress (VKAPI_PTR *PFN_vkGetBufferDeviceAddressEXT)(VkDevice device, const VkBufferDeviceAddressInfoEXT* pInfo); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkDeviceAddress VKAPI_CALL vkGetBufferDeviceAddressEXT( + VkDevice device, + const VkBufferDeviceAddressInfoEXT* pInfo); +#endif + + +#define VK_EXT_separate_stencil_usage 1 +#define VK_EXT_SEPARATE_STENCIL_USAGE_SPEC_VERSION 1 +#define VK_EXT_SEPARATE_STENCIL_USAGE_EXTENSION_NAME "VK_EXT_separate_stencil_usage" +typedef struct VkImageStencilUsageCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkImageUsageFlags stencilUsage; +} VkImageStencilUsageCreateInfoEXT; + + + +#define VK_EXT_validation_features 1 +#define VK_EXT_VALIDATION_FEATURES_SPEC_VERSION 2 +#define VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME "VK_EXT_validation_features" + +typedef enum VkValidationFeatureEnableEXT { + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT = 0, + VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT = 1, + VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT = 2, + VK_VALIDATION_FEATURE_ENABLE_BEGIN_RANGE_EXT = VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT, + VK_VALIDATION_FEATURE_ENABLE_END_RANGE_EXT = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT, + VK_VALIDATION_FEATURE_ENABLE_RANGE_SIZE_EXT = (VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT - VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT + 1), + VK_VALIDATION_FEATURE_ENABLE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationFeatureEnableEXT; + +typedef enum VkValidationFeatureDisableEXT { + VK_VALIDATION_FEATURE_DISABLE_ALL_EXT = 0, + VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT = 1, + VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT = 2, + VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT = 3, + VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT = 4, + VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT = 5, + VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT = 6, + VK_VALIDATION_FEATURE_DISABLE_BEGIN_RANGE_EXT = VK_VALIDATION_FEATURE_DISABLE_ALL_EXT, + VK_VALIDATION_FEATURE_DISABLE_END_RANGE_EXT = VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT, + VK_VALIDATION_FEATURE_DISABLE_RANGE_SIZE_EXT = (VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT - VK_VALIDATION_FEATURE_DISABLE_ALL_EXT + 1), + VK_VALIDATION_FEATURE_DISABLE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkValidationFeatureDisableEXT; +typedef struct VkValidationFeaturesEXT { + VkStructureType sType; + const void* pNext; + uint32_t enabledValidationFeatureCount; + const VkValidationFeatureEnableEXT* pEnabledValidationFeatures; + uint32_t disabledValidationFeatureCount; + const VkValidationFeatureDisableEXT* pDisabledValidationFeatures; +} VkValidationFeaturesEXT; + + + +#define VK_NV_cooperative_matrix 1 +#define VK_NV_COOPERATIVE_MATRIX_SPEC_VERSION 1 +#define VK_NV_COOPERATIVE_MATRIX_EXTENSION_NAME "VK_NV_cooperative_matrix" + +typedef enum VkComponentTypeNV { + VK_COMPONENT_TYPE_FLOAT16_NV = 0, + VK_COMPONENT_TYPE_FLOAT32_NV = 1, + VK_COMPONENT_TYPE_FLOAT64_NV = 2, + VK_COMPONENT_TYPE_SINT8_NV = 3, + VK_COMPONENT_TYPE_SINT16_NV = 4, + VK_COMPONENT_TYPE_SINT32_NV = 5, + VK_COMPONENT_TYPE_SINT64_NV = 6, + VK_COMPONENT_TYPE_UINT8_NV = 7, + VK_COMPONENT_TYPE_UINT16_NV = 8, + VK_COMPONENT_TYPE_UINT32_NV = 9, + VK_COMPONENT_TYPE_UINT64_NV = 10, + VK_COMPONENT_TYPE_BEGIN_RANGE_NV = VK_COMPONENT_TYPE_FLOAT16_NV, + VK_COMPONENT_TYPE_END_RANGE_NV = VK_COMPONENT_TYPE_UINT64_NV, + VK_COMPONENT_TYPE_RANGE_SIZE_NV = (VK_COMPONENT_TYPE_UINT64_NV - VK_COMPONENT_TYPE_FLOAT16_NV + 1), + VK_COMPONENT_TYPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkComponentTypeNV; + +typedef enum VkScopeNV { + VK_SCOPE_DEVICE_NV = 1, + VK_SCOPE_WORKGROUP_NV = 2, + VK_SCOPE_SUBGROUP_NV = 3, + VK_SCOPE_QUEUE_FAMILY_NV = 5, + VK_SCOPE_BEGIN_RANGE_NV = VK_SCOPE_DEVICE_NV, + VK_SCOPE_END_RANGE_NV = VK_SCOPE_QUEUE_FAMILY_NV, + VK_SCOPE_RANGE_SIZE_NV = (VK_SCOPE_QUEUE_FAMILY_NV - VK_SCOPE_DEVICE_NV + 1), + VK_SCOPE_MAX_ENUM_NV = 0x7FFFFFFF +} VkScopeNV; +typedef struct VkCooperativeMatrixPropertiesNV { + VkStructureType sType; + void* pNext; + uint32_t MSize; + uint32_t NSize; + uint32_t KSize; + VkComponentTypeNV AType; + VkComponentTypeNV BType; + VkComponentTypeNV CType; + VkComponentTypeNV DType; + VkScopeNV scope; +} VkCooperativeMatrixPropertiesNV; + +typedef struct VkPhysicalDeviceCooperativeMatrixFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 cooperativeMatrix; + VkBool32 cooperativeMatrixRobustBufferAccess; +} VkPhysicalDeviceCooperativeMatrixFeaturesNV; + +typedef struct VkPhysicalDeviceCooperativeMatrixPropertiesNV { + VkStructureType sType; + void* pNext; + VkShaderStageFlags cooperativeMatrixSupportedStages; +} VkPhysicalDeviceCooperativeMatrixPropertiesNV; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceCooperativeMatrixPropertiesNV)(VkPhysicalDevice physicalDevice, uint32_t* pPropertyCount, VkCooperativeMatrixPropertiesNV* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceCooperativeMatrixPropertiesNV( + VkPhysicalDevice physicalDevice, + uint32_t* pPropertyCount, + VkCooperativeMatrixPropertiesNV* pProperties); +#endif + + +#define VK_NV_coverage_reduction_mode 1 +#define VK_NV_COVERAGE_REDUCTION_MODE_SPEC_VERSION 1 +#define VK_NV_COVERAGE_REDUCTION_MODE_EXTENSION_NAME "VK_NV_coverage_reduction_mode" + +typedef enum VkCoverageReductionModeNV { + VK_COVERAGE_REDUCTION_MODE_MERGE_NV = 0, + VK_COVERAGE_REDUCTION_MODE_TRUNCATE_NV = 1, + VK_COVERAGE_REDUCTION_MODE_BEGIN_RANGE_NV = VK_COVERAGE_REDUCTION_MODE_MERGE_NV, + VK_COVERAGE_REDUCTION_MODE_END_RANGE_NV = VK_COVERAGE_REDUCTION_MODE_TRUNCATE_NV, + VK_COVERAGE_REDUCTION_MODE_RANGE_SIZE_NV = (VK_COVERAGE_REDUCTION_MODE_TRUNCATE_NV - VK_COVERAGE_REDUCTION_MODE_MERGE_NV + 1), + VK_COVERAGE_REDUCTION_MODE_MAX_ENUM_NV = 0x7FFFFFFF +} VkCoverageReductionModeNV; +typedef VkFlags VkPipelineCoverageReductionStateCreateFlagsNV; +typedef struct VkPhysicalDeviceCoverageReductionModeFeaturesNV { + VkStructureType sType; + void* pNext; + VkBool32 coverageReductionMode; +} VkPhysicalDeviceCoverageReductionModeFeaturesNV; + +typedef struct VkPipelineCoverageReductionStateCreateInfoNV { + VkStructureType sType; + const void* pNext; + VkPipelineCoverageReductionStateCreateFlagsNV flags; + VkCoverageReductionModeNV coverageReductionMode; +} VkPipelineCoverageReductionStateCreateInfoNV; + +typedef struct VkFramebufferMixedSamplesCombinationNV { + VkStructureType sType; + void* pNext; + VkCoverageReductionModeNV coverageReductionMode; + VkSampleCountFlagBits rasterizationSamples; + VkSampleCountFlags depthStencilSamples; + VkSampleCountFlags colorSamples; +} VkFramebufferMixedSamplesCombinationNV; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV)(VkPhysicalDevice physicalDevice, uint32_t* pCombinationCount, VkFramebufferMixedSamplesCombinationNV* pCombinations); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSupportedFramebufferMixedSamplesCombinationsNV( + VkPhysicalDevice physicalDevice, + uint32_t* pCombinationCount, + VkFramebufferMixedSamplesCombinationNV* pCombinations); +#endif + + +#define VK_EXT_fragment_shader_interlock 1 +#define VK_EXT_FRAGMENT_SHADER_INTERLOCK_SPEC_VERSION 1 +#define VK_EXT_FRAGMENT_SHADER_INTERLOCK_EXTENSION_NAME "VK_EXT_fragment_shader_interlock" +typedef struct VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 fragmentShaderSampleInterlock; + VkBool32 fragmentShaderPixelInterlock; + VkBool32 fragmentShaderShadingRateInterlock; +} VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT; + + + +#define VK_EXT_ycbcr_image_arrays 1 +#define VK_EXT_YCBCR_IMAGE_ARRAYS_SPEC_VERSION 1 +#define VK_EXT_YCBCR_IMAGE_ARRAYS_EXTENSION_NAME "VK_EXT_ycbcr_image_arrays" +typedef struct VkPhysicalDeviceYcbcrImageArraysFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 ycbcrImageArrays; +} VkPhysicalDeviceYcbcrImageArraysFeaturesEXT; + + + +#define VK_EXT_headless_surface 1 +#define VK_EXT_HEADLESS_SURFACE_SPEC_VERSION 1 +#define VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME "VK_EXT_headless_surface" +typedef VkFlags VkHeadlessSurfaceCreateFlagsEXT; +typedef struct VkHeadlessSurfaceCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkHeadlessSurfaceCreateFlagsEXT flags; +} VkHeadlessSurfaceCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateHeadlessSurfaceEXT)(VkInstance instance, const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateHeadlessSurfaceEXT( + VkInstance instance, + const VkHeadlessSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + + +#define VK_EXT_line_rasterization 1 +#define VK_EXT_LINE_RASTERIZATION_SPEC_VERSION 1 +#define VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME "VK_EXT_line_rasterization" + +typedef enum VkLineRasterizationModeEXT { + VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT = 0, + VK_LINE_RASTERIZATION_MODE_RECTANGULAR_EXT = 1, + VK_LINE_RASTERIZATION_MODE_BRESENHAM_EXT = 2, + VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT = 3, + VK_LINE_RASTERIZATION_MODE_BEGIN_RANGE_EXT = VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT, + VK_LINE_RASTERIZATION_MODE_END_RANGE_EXT = VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT, + VK_LINE_RASTERIZATION_MODE_RANGE_SIZE_EXT = (VK_LINE_RASTERIZATION_MODE_RECTANGULAR_SMOOTH_EXT - VK_LINE_RASTERIZATION_MODE_DEFAULT_EXT + 1), + VK_LINE_RASTERIZATION_MODE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkLineRasterizationModeEXT; +typedef struct VkPhysicalDeviceLineRasterizationFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 rectangularLines; + VkBool32 bresenhamLines; + VkBool32 smoothLines; + VkBool32 stippledRectangularLines; + VkBool32 stippledBresenhamLines; + VkBool32 stippledSmoothLines; +} VkPhysicalDeviceLineRasterizationFeaturesEXT; + +typedef struct VkPhysicalDeviceLineRasterizationPropertiesEXT { + VkStructureType sType; + void* pNext; + uint32_t lineSubPixelPrecisionBits; +} VkPhysicalDeviceLineRasterizationPropertiesEXT; + +typedef struct VkPipelineRasterizationLineStateCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkLineRasterizationModeEXT lineRasterizationMode; + VkBool32 stippledLineEnable; + uint32_t lineStippleFactor; + uint16_t lineStipplePattern; +} VkPipelineRasterizationLineStateCreateInfoEXT; + +typedef void (VKAPI_PTR *PFN_vkCmdSetLineStippleEXT)(VkCommandBuffer commandBuffer, uint32_t lineStippleFactor, uint16_t lineStipplePattern); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkCmdSetLineStippleEXT( + VkCommandBuffer commandBuffer, + uint32_t lineStippleFactor, + uint16_t lineStipplePattern); +#endif + + +#define VK_EXT_host_query_reset 1 +#define VK_EXT_HOST_QUERY_RESET_SPEC_VERSION 1 +#define VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME "VK_EXT_host_query_reset" +typedef struct VkPhysicalDeviceHostQueryResetFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 hostQueryReset; +} VkPhysicalDeviceHostQueryResetFeaturesEXT; + +typedef void (VKAPI_PTR *PFN_vkResetQueryPoolEXT)(VkDevice device, VkQueryPool queryPool, uint32_t firstQuery, uint32_t queryCount); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR void VKAPI_CALL vkResetQueryPoolEXT( + VkDevice device, + VkQueryPool queryPool, + uint32_t firstQuery, + uint32_t queryCount); +#endif + + +#define VK_EXT_index_type_uint8 1 +#define VK_EXT_INDEX_TYPE_UINT8_SPEC_VERSION 1 +#define VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME "VK_EXT_index_type_uint8" +typedef struct VkPhysicalDeviceIndexTypeUint8FeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 indexTypeUint8; +} VkPhysicalDeviceIndexTypeUint8FeaturesEXT; + + + +#define VK_EXT_shader_demote_to_helper_invocation 1 +#define VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_SPEC_VERSION 1 +#define VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME "VK_EXT_shader_demote_to_helper_invocation" +typedef struct VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 shaderDemoteToHelperInvocation; +} VkPhysicalDeviceShaderDemoteToHelperInvocationFeaturesEXT; + + + +#define VK_EXT_texel_buffer_alignment 1 +#define VK_EXT_TEXEL_BUFFER_ALIGNMENT_SPEC_VERSION 1 +#define VK_EXT_TEXEL_BUFFER_ALIGNMENT_EXTENSION_NAME "VK_EXT_texel_buffer_alignment" +typedef struct VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT { + VkStructureType sType; + void* pNext; + VkBool32 texelBufferAlignment; +} VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT; + +typedef struct VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT { + VkStructureType sType; + void* pNext; + VkDeviceSize storageTexelBufferOffsetAlignmentBytes; + VkBool32 storageTexelBufferOffsetSingleTexelAlignment; + VkDeviceSize uniformTexelBufferOffsetAlignmentBytes; + VkBool32 uniformTexelBufferOffsetSingleTexelAlignment; +} VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT; + + + +#define VK_GOOGLE_user_type 1 +#define VK_GOOGLE_USER_TYPE_SPEC_VERSION 1 +#define VK_GOOGLE_USER_TYPE_EXTENSION_NAME "VK_GOOGLE_user_type" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_fuchsia.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_fuchsia.h new file mode 100644 index 00000000000..81ebe55d313 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_fuchsia.h @@ -0,0 +1,57 @@ +#ifndef VULKAN_FUCHSIA_H_ +#define VULKAN_FUCHSIA_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_FUCHSIA_imagepipe_surface 1 +#define VK_FUCHSIA_IMAGEPIPE_SURFACE_SPEC_VERSION 1 +#define VK_FUCHSIA_IMAGEPIPE_SURFACE_EXTENSION_NAME "VK_FUCHSIA_imagepipe_surface" +typedef VkFlags VkImagePipeSurfaceCreateFlagsFUCHSIA; +typedef struct VkImagePipeSurfaceCreateInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkImagePipeSurfaceCreateFlagsFUCHSIA flags; + zx_handle_t imagePipeHandle; +} VkImagePipeSurfaceCreateInfoFUCHSIA; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateImagePipeSurfaceFUCHSIA)(VkInstance instance, const VkImagePipeSurfaceCreateInfoFUCHSIA* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateImagePipeSurfaceFUCHSIA( + VkInstance instance, + const VkImagePipeSurfaceCreateInfoFUCHSIA* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_fuchsia_extras.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_fuchsia_extras.h new file mode 100644 index 00000000000..46bdc596229 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_fuchsia_extras.h @@ -0,0 +1,232 @@ +#ifndef VULKAN_FUCHSIA_EXTRAS_H_ +#define VULKAN_FUCHSIA_EXTRAS_H_ 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +// IMPORTANT: +// +// The following declarations are not part of the upstream Khronos headers yet +// but the Fuchsia platform currently relies on them to implement various +// extensions required by its compositor and graphics libraries. + + +////////////////////////////////////////////////////////////////////////// +/// +/// The following will be part of vulkan_core.h +/// + +#define VK_STRUCTURE_TYPE_BUFFER_COLLECTION_CREATE_INFO_FUCHSIA ((VkStructureType)1001004000) +#define VK_STRUCTURE_TYPE_FUCHSIA_IMAGE_FORMAT_FUCHSIA ((VkStructureType)1001004001) +#define VK_STRUCTURE_TYPE_IMPORT_MEMORY_BUFFER_COLLECTION_FUCHSIA ((VkStructureType)1001004004) +#define VK_STRUCTURE_TYPE_BUFFER_COLLECTION_IMAGE_CREATE_INFO_FUCHSIA ((VkStructureType)1001004005) +#define VK_STRUCTURE_TYPE_BUFFER_COLLECTION_PROPERTIES_FUCHSIA ((VkStructureType)1001004006) +#define VK_STRUCTURE_TYPE_BUFFER_CONSTRAINTS_INFO_FUCHSIA ((VkStructureType)1001004007) +#define VK_STRUCTURE_TYPE_BUFFER_COLLECTION_BUFFER_CREATE_INFO_FUCHSIA ((VkStructureType)1001004008) +#define VK_STRUCTURE_TYPE_TEMP_IMPORT_MEMORY_ZIRCON_HANDLE_INFO_FUCHSIA ((VkStructureType)1001005000) +#define VK_STRUCTURE_TYPE_TEMP_MEMORY_ZIRCON_HANDLE_PROPERTIES_FUCHSIA ((VkStructureType)1001005001) +#define VK_STRUCTURE_TYPE_TEMP_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA ((VkStructureType)1001005002) +#define VK_STRUCTURE_TYPE_TEMP_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA ((VkStructureType)1001006000) +#define VK_STRUCTURE_TYPE_TEMP_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA ((VkStructureType)1001006001) + +#define VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA ((VkObjectType)1001004002) + +#define VK_EXTERNAL_MEMORY_HANDLE_TYPE_TEMP_ZIRCON_VMO_BIT_FUCHSIA ((VkExternalMemoryHandleTypeFlagBits)0x00100000) +#define VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA ((VkExternalSemaphoreHandleTypeFlagBits) 0x00100000) + +#define VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA_EXT ((VkDebugReportObjectTypeEXT) 1001004003) + + +////////////////////////////////////////////////////////////////////////// +/// +/// The following will be part of vulkan_fuchsia.h +/// + +#define VK_FUCHSIA_buffer_collection 1 +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBufferCollectionFUCHSIA) + +#define VK_FUCHSIA_BUFFER_COLLECTION_SPEC_VERSION 1 +#define VK_FUCHSIA_BUFFER_COLLECTION_EXTENSION_NAME "VK_FUCHSIA_buffer_collection" + +typedef struct VkBufferCollectionCreateInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + zx_handle_t collectionToken; +} VkBufferCollectionCreateInfoFUCHSIA; + +typedef struct VkFuchsiaImageFormatFUCHSIA { + VkStructureType sType; + const void* pNext; + const void* imageFormat; + uint32_t imageFormatSize; +} VkFuchsiaImageFormatFUCHSIA; + +typedef struct VkImportMemoryBufferCollectionFUCHSIA { + VkStructureType sType; + const void* pNext; + VkBufferCollectionFUCHSIA collection; + uint32_t index; +} VkImportMemoryBufferCollectionFUCHSIA; + +typedef struct VkBufferCollectionImageCreateInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkBufferCollectionFUCHSIA collection; + uint32_t index; +} VkBufferCollectionImageCreateInfoFUCHSIA; + +typedef struct VkBufferConstraintsInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + const VkBufferCreateInfo* pBufferCreateInfo; + VkFormatFeatureFlags requiredFormatFeatures; + uint32_t minCount; +} VkBufferConstraintsInfoFUCHSIA; + +typedef struct VkBufferCollectionBufferCreateInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkBufferCollectionFUCHSIA collection; + uint32_t index; +} VkBufferCollectionBufferCreateInfoFUCHSIA; + +typedef struct VkBufferCollectionPropertiesFUCHSIA { + VkStructureType sType; + void* pNext; + uint32_t memoryTypeBits; + uint32_t count; +} VkBufferCollectionPropertiesFUCHSIA; + + +typedef VkResult (VKAPI_PTR *PFN_vkCreateBufferCollectionFUCHSIA)(VkDevice device, const VkBufferCollectionCreateInfoFUCHSIA* pImportInfo, const VkAllocationCallbacks* pAllocator, VkBufferCollectionFUCHSIA* pCollection); +typedef VkResult (VKAPI_PTR *PFN_vkSetBufferCollectionConstraintsFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkImageCreateInfo* pImageInfo); +typedef VkResult (VKAPI_PTR *PFN_vkSetBufferCollectionBufferConstraintsFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo); +typedef void (VKAPI_PTR *PFN_vkDestroyBufferCollectionFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, const VkAllocationCallbacks* pAllocator); +typedef VkResult (VKAPI_PTR *PFN_vkGetBufferCollectionPropertiesFUCHSIA)(VkDevice device, VkBufferCollectionFUCHSIA collection, VkBufferCollectionPropertiesFUCHSIA* pProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateBufferCollectionFUCHSIA( + VkDevice device, + const VkBufferCollectionCreateInfoFUCHSIA* pImportInfo, + const VkAllocationCallbacks* pAllocator, + VkBufferCollectionFUCHSIA* pCollection); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetBufferCollectionConstraintsFUCHSIA( + VkDevice device, + VkBufferCollectionFUCHSIA collection, + const VkImageCreateInfo* pImageInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkSetBufferCollectionBufferConstraintsFUCHSIA( + VkDevice device, + VkBufferCollectionFUCHSIA collection, + const VkBufferConstraintsInfoFUCHSIA* pBufferConstraintsInfo); + +VKAPI_ATTR void VKAPI_CALL vkDestroyBufferCollectionFUCHSIA( + VkDevice device, + VkBufferCollectionFUCHSIA collection, + const VkAllocationCallbacks* pAllocator); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetBufferCollectionPropertiesFUCHSIA( + VkDevice device, + VkBufferCollectionFUCHSIA collection, + VkBufferCollectionPropertiesFUCHSIA* pProperties); +#endif + +#define VK_FUCHSIA_external_memory 1 +#define VK_FUCHSIA_EXTERNAL_MEMORY_SPEC_VERSION 1 +#define VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME "VK_FUCHSIA_external_memory" + +typedef struct VkImportMemoryZirconHandleInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagBits handleType; + zx_handle_t handle; +} VkImportMemoryZirconHandleInfoFUCHSIA; + +typedef struct VkMemoryZirconHandlePropertiesFUCHSIA { + VkStructureType sType; + void* pNext; + uint32_t memoryTypeBits; +} VkMemoryZirconHandlePropertiesFUCHSIA; + +typedef struct VkMemoryGetZirconHandleInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; + VkExternalMemoryHandleTypeFlagBits handleType; +} VkMemoryGetZirconHandleInfoFUCHSIA; + + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryZirconHandleFUCHSIA)(VkDevice device, const VkMemoryGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, zx_handle_t* pZirconHandle); +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryZirconHandlePropertiesFUCHSIA)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, zx_handle_t ZirconHandle, VkMemoryZirconHandlePropertiesFUCHSIA* pMemoryZirconHandleProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryZirconHandleFUCHSIA( + VkDevice device, + const VkMemoryGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, + zx_handle_t* pZirconHandle); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryZirconHandlePropertiesFUCHSIA( + VkDevice device, + VkExternalMemoryHandleTypeFlagBits handleType, + zx_handle_t ZirconHandle, + VkMemoryZirconHandlePropertiesFUCHSIA* pMemoryZirconHandleProperties); +#endif + +#define VK_FUCHSIA_external_semaphore 1 +#define VK_FUCHSIA_EXTERNAL_SEMAPHORE_SPEC_VERSION 1 +#define VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME "VK_FUCHSIA_external_semaphore" + +typedef struct VkImportSemaphoreZirconHandleInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkSemaphoreImportFlags flags; + VkExternalSemaphoreHandleTypeFlagBits handleType; + zx_handle_t handle; +} VkImportSemaphoreZirconHandleInfoFUCHSIA; + +typedef struct VkSemaphoreGetZirconHandleInfoFUCHSIA { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkExternalSemaphoreHandleTypeFlagBits handleType; +} VkSemaphoreGetZirconHandleInfoFUCHSIA; + + +typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreZirconHandleFUCHSIA)(VkDevice device, const VkImportSemaphoreZirconHandleInfoFUCHSIA* pImportSemaphoreZirconHandleInfo); +typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreZirconHandleFUCHSIA)(VkDevice device, const VkSemaphoreGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, zx_handle_t* pZirconHandle); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreZirconHandleFUCHSIA( + VkDevice device, + const VkImportSemaphoreZirconHandleInfoFUCHSIA* pImportSemaphoreZirconHandleInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreZirconHandleFUCHSIA( + VkDevice device, + const VkSemaphoreGetZirconHandleInfoFUCHSIA* pGetZirconHandleInfo, + zx_handle_t* pZirconHandle); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // VULKAN_FUCHSIA_EXTRAS_H_ diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_ggp.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_ggp.h new file mode 100644 index 00000000000..fd306131c32 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_ggp.h @@ -0,0 +1,68 @@ +#ifndef VULKAN_GGP_H_ +#define VULKAN_GGP_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_GGP_stream_descriptor_surface 1 +#define VK_GGP_STREAM_DESCRIPTOR_SURFACE_SPEC_VERSION 1 +#define VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME "VK_GGP_stream_descriptor_surface" +typedef VkFlags VkStreamDescriptorSurfaceCreateFlagsGGP; +typedef struct VkStreamDescriptorSurfaceCreateInfoGGP { + VkStructureType sType; + const void* pNext; + VkStreamDescriptorSurfaceCreateFlagsGGP flags; + GgpStreamDescriptor streamDescriptor; +} VkStreamDescriptorSurfaceCreateInfoGGP; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateStreamDescriptorSurfaceGGP)(VkInstance instance, const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateStreamDescriptorSurfaceGGP( + VkInstance instance, + const VkStreamDescriptorSurfaceCreateInfoGGP* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + + +#define VK_GGP_frame_token 1 +#define VK_GGP_FRAME_TOKEN_SPEC_VERSION 1 +#define VK_GGP_FRAME_TOKEN_EXTENSION_NAME "VK_GGP_frame_token" +typedef struct VkPresentFrameTokenGGP { + VkStructureType sType; + const void* pNext; + GgpFrameToken frameToken; +} VkPresentFrameTokenGGP; + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_ios.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_ios.h new file mode 100644 index 00000000000..72ef1a8a825 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_ios.h @@ -0,0 +1,57 @@ +#ifndef VULKAN_IOS_H_ +#define VULKAN_IOS_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_MVK_ios_surface 1 +#define VK_MVK_IOS_SURFACE_SPEC_VERSION 2 +#define VK_MVK_IOS_SURFACE_EXTENSION_NAME "VK_MVK_ios_surface" +typedef VkFlags VkIOSSurfaceCreateFlagsMVK; +typedef struct VkIOSSurfaceCreateInfoMVK { + VkStructureType sType; + const void* pNext; + VkIOSSurfaceCreateFlagsMVK flags; + const void* pView; +} VkIOSSurfaceCreateInfoMVK; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateIOSSurfaceMVK)(VkInstance instance, const VkIOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateIOSSurfaceMVK( + VkInstance instance, + const VkIOSSurfaceCreateInfoMVK* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_macos.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_macos.h new file mode 100644 index 00000000000..e6e5deaa366 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_macos.h @@ -0,0 +1,57 @@ +#ifndef VULKAN_MACOS_H_ +#define VULKAN_MACOS_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_MVK_macos_surface 1 +#define VK_MVK_MACOS_SURFACE_SPEC_VERSION 2 +#define VK_MVK_MACOS_SURFACE_EXTENSION_NAME "VK_MVK_macos_surface" +typedef VkFlags VkMacOSSurfaceCreateFlagsMVK; +typedef struct VkMacOSSurfaceCreateInfoMVK { + VkStructureType sType; + const void* pNext; + VkMacOSSurfaceCreateFlagsMVK flags; + const void* pView; +} VkMacOSSurfaceCreateInfoMVK; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateMacOSSurfaceMVK)(VkInstance instance, const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateMacOSSurfaceMVK( + VkInstance instance, + const VkMacOSSurfaceCreateInfoMVK* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_metal.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_metal.h new file mode 100644 index 00000000000..3dec68c7713 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_metal.h @@ -0,0 +1,64 @@ +#ifndef VULKAN_METAL_H_ +#define VULKAN_METAL_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_EXT_metal_surface 1 + +#ifdef __OBJC__ +@class CAMetalLayer; +#else +typedef void CAMetalLayer; +#endif + +#define VK_EXT_METAL_SURFACE_SPEC_VERSION 1 +#define VK_EXT_METAL_SURFACE_EXTENSION_NAME "VK_EXT_metal_surface" +typedef VkFlags VkMetalSurfaceCreateFlagsEXT; +typedef struct VkMetalSurfaceCreateInfoEXT { + VkStructureType sType; + const void* pNext; + VkMetalSurfaceCreateFlagsEXT flags; + const CAMetalLayer* pLayer; +} VkMetalSurfaceCreateInfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateMetalSurfaceEXT)(VkInstance instance, const VkMetalSurfaceCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateMetalSurfaceEXT( + VkInstance instance, + const VkMetalSurfaceCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_vi.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_vi.h new file mode 100644 index 00000000000..6fb66f9dd28 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_vi.h @@ -0,0 +1,57 @@ +#ifndef VULKAN_VI_H_ +#define VULKAN_VI_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_NN_vi_surface 1 +#define VK_NN_VI_SURFACE_SPEC_VERSION 1 +#define VK_NN_VI_SURFACE_EXTENSION_NAME "VK_NN_vi_surface" +typedef VkFlags VkViSurfaceCreateFlagsNN; +typedef struct VkViSurfaceCreateInfoNN { + VkStructureType sType; + const void* pNext; + VkViSurfaceCreateFlagsNN flags; + void* window; +} VkViSurfaceCreateInfoNN; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateViSurfaceNN)(VkInstance instance, const VkViSurfaceCreateInfoNN* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateViSurfaceNN( + VkInstance instance, + const VkViSurfaceCreateInfoNN* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_wayland.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_wayland.h new file mode 100644 index 00000000000..599d05b24a5 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_wayland.h @@ -0,0 +1,64 @@ +#ifndef VULKAN_WAYLAND_H_ +#define VULKAN_WAYLAND_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_KHR_wayland_surface 1 +#define VK_KHR_WAYLAND_SURFACE_SPEC_VERSION 6 +#define VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME "VK_KHR_wayland_surface" +typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; +typedef struct VkWaylandSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkWaylandSurfaceCreateFlagsKHR flags; + struct wl_display* display; + struct wl_surface* surface; +} VkWaylandSurfaceCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateWaylandSurfaceKHR)(VkInstance instance, const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, struct wl_display* display); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateWaylandSurfaceKHR( + VkInstance instance, + const VkWaylandSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWaylandPresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + struct wl_display* display); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_win32.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_win32.h new file mode 100644 index 00000000000..20a1dc0e588 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_win32.h @@ -0,0 +1,328 @@ +#ifndef VULKAN_WIN32_H_ +#define VULKAN_WIN32_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_KHR_win32_surface 1 +#define VK_KHR_WIN32_SURFACE_SPEC_VERSION 6 +#define VK_KHR_WIN32_SURFACE_EXTENSION_NAME "VK_KHR_win32_surface" +typedef VkFlags VkWin32SurfaceCreateFlagsKHR; +typedef struct VkWin32SurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkWin32SurfaceCreateFlagsKHR flags; + HINSTANCE hinstance; + HWND hwnd; +} VkWin32SurfaceCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateWin32SurfaceKHR)(VkInstance instance, const VkWin32SurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateWin32SurfaceKHR( + VkInstance instance, + const VkWin32SurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceWin32PresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex); +#endif + + +#define VK_KHR_external_memory_win32 1 +#define VK_KHR_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_KHR_external_memory_win32" +typedef struct VkImportMemoryWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagBits handleType; + HANDLE handle; + LPCWSTR name; +} VkImportMemoryWin32HandleInfoKHR; + +typedef struct VkExportMemoryWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + const SECURITY_ATTRIBUTES* pAttributes; + DWORD dwAccess; + LPCWSTR name; +} VkExportMemoryWin32HandleInfoKHR; + +typedef struct VkMemoryWin32HandlePropertiesKHR { + VkStructureType sType; + void* pNext; + uint32_t memoryTypeBits; +} VkMemoryWin32HandlePropertiesKHR; + +typedef struct VkMemoryGetWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; + VkExternalMemoryHandleTypeFlagBits handleType; +} VkMemoryGetWin32HandleInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleKHR)(VkDevice device, const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle); +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandlePropertiesKHR)(VkDevice device, VkExternalMemoryHandleTypeFlagBits handleType, HANDLE handle, VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleKHR( + VkDevice device, + const VkMemoryGetWin32HandleInfoKHR* pGetWin32HandleInfo, + HANDLE* pHandle); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandlePropertiesKHR( + VkDevice device, + VkExternalMemoryHandleTypeFlagBits handleType, + HANDLE handle, + VkMemoryWin32HandlePropertiesKHR* pMemoryWin32HandleProperties); +#endif + + +#define VK_KHR_win32_keyed_mutex 1 +#define VK_KHR_WIN32_KEYED_MUTEX_SPEC_VERSION 1 +#define VK_KHR_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_KHR_win32_keyed_mutex" +typedef struct VkWin32KeyedMutexAcquireReleaseInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t acquireCount; + const VkDeviceMemory* pAcquireSyncs; + const uint64_t* pAcquireKeys; + const uint32_t* pAcquireTimeouts; + uint32_t releaseCount; + const VkDeviceMemory* pReleaseSyncs; + const uint64_t* pReleaseKeys; +} VkWin32KeyedMutexAcquireReleaseInfoKHR; + + + +#define VK_KHR_external_semaphore_win32 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME "VK_KHR_external_semaphore_win32" +typedef struct VkImportSemaphoreWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkSemaphoreImportFlags flags; + VkExternalSemaphoreHandleTypeFlagBits handleType; + HANDLE handle; + LPCWSTR name; +} VkImportSemaphoreWin32HandleInfoKHR; + +typedef struct VkExportSemaphoreWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + const SECURITY_ATTRIBUTES* pAttributes; + DWORD dwAccess; + LPCWSTR name; +} VkExportSemaphoreWin32HandleInfoKHR; + +typedef struct VkD3D12FenceSubmitInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t waitSemaphoreValuesCount; + const uint64_t* pWaitSemaphoreValues; + uint32_t signalSemaphoreValuesCount; + const uint64_t* pSignalSemaphoreValues; +} VkD3D12FenceSubmitInfoKHR; + +typedef struct VkSemaphoreGetWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkExternalSemaphoreHandleTypeFlagBits handleType; +} VkSemaphoreGetWin32HandleInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkImportSemaphoreWin32HandleKHR)(VkDevice device, const VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo); +typedef VkResult (VKAPI_PTR *PFN_vkGetSemaphoreWin32HandleKHR)(VkDevice device, const VkSemaphoreGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkImportSemaphoreWin32HandleKHR( + VkDevice device, + const VkImportSemaphoreWin32HandleInfoKHR* pImportSemaphoreWin32HandleInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreWin32HandleKHR( + VkDevice device, + const VkSemaphoreGetWin32HandleInfoKHR* pGetWin32HandleInfo, + HANDLE* pHandle); +#endif + + +#define VK_KHR_external_fence_win32 1 +#define VK_KHR_EXTERNAL_FENCE_WIN32_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME "VK_KHR_external_fence_win32" +typedef struct VkImportFenceWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkFence fence; + VkFenceImportFlags flags; + VkExternalFenceHandleTypeFlagBits handleType; + HANDLE handle; + LPCWSTR name; +} VkImportFenceWin32HandleInfoKHR; + +typedef struct VkExportFenceWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + const SECURITY_ATTRIBUTES* pAttributes; + DWORD dwAccess; + LPCWSTR name; +} VkExportFenceWin32HandleInfoKHR; + +typedef struct VkFenceGetWin32HandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkFence fence; + VkExternalFenceHandleTypeFlagBits handleType; +} VkFenceGetWin32HandleInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkImportFenceWin32HandleKHR)(VkDevice device, const VkImportFenceWin32HandleInfoKHR* pImportFenceWin32HandleInfo); +typedef VkResult (VKAPI_PTR *PFN_vkGetFenceWin32HandleKHR)(VkDevice device, const VkFenceGetWin32HandleInfoKHR* pGetWin32HandleInfo, HANDLE* pHandle); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkImportFenceWin32HandleKHR( + VkDevice device, + const VkImportFenceWin32HandleInfoKHR* pImportFenceWin32HandleInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetFenceWin32HandleKHR( + VkDevice device, + const VkFenceGetWin32HandleInfoKHR* pGetWin32HandleInfo, + HANDLE* pHandle); +#endif + + +#define VK_NV_external_memory_win32 1 +#define VK_NV_EXTERNAL_MEMORY_WIN32_SPEC_VERSION 1 +#define VK_NV_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME "VK_NV_external_memory_win32" +typedef struct VkImportMemoryWin32HandleInfoNV { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagsNV handleType; + HANDLE handle; +} VkImportMemoryWin32HandleInfoNV; + +typedef struct VkExportMemoryWin32HandleInfoNV { + VkStructureType sType; + const void* pNext; + const SECURITY_ATTRIBUTES* pAttributes; + DWORD dwAccess; +} VkExportMemoryWin32HandleInfoNV; + +typedef VkResult (VKAPI_PTR *PFN_vkGetMemoryWin32HandleNV)(VkDevice device, VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagsNV handleType, HANDLE* pHandle); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryWin32HandleNV( + VkDevice device, + VkDeviceMemory memory, + VkExternalMemoryHandleTypeFlagsNV handleType, + HANDLE* pHandle); +#endif + + +#define VK_NV_win32_keyed_mutex 1 +#define VK_NV_WIN32_KEYED_MUTEX_SPEC_VERSION 2 +#define VK_NV_WIN32_KEYED_MUTEX_EXTENSION_NAME "VK_NV_win32_keyed_mutex" +typedef struct VkWin32KeyedMutexAcquireReleaseInfoNV { + VkStructureType sType; + const void* pNext; + uint32_t acquireCount; + const VkDeviceMemory* pAcquireSyncs; + const uint64_t* pAcquireKeys; + const uint32_t* pAcquireTimeoutMilliseconds; + uint32_t releaseCount; + const VkDeviceMemory* pReleaseSyncs; + const uint64_t* pReleaseKeys; +} VkWin32KeyedMutexAcquireReleaseInfoNV; + + + +#define VK_EXT_full_screen_exclusive 1 +#define VK_EXT_FULL_SCREEN_EXCLUSIVE_SPEC_VERSION 4 +#define VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME "VK_EXT_full_screen_exclusive" + +typedef enum VkFullScreenExclusiveEXT { + VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT = 0, + VK_FULL_SCREEN_EXCLUSIVE_ALLOWED_EXT = 1, + VK_FULL_SCREEN_EXCLUSIVE_DISALLOWED_EXT = 2, + VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT = 3, + VK_FULL_SCREEN_EXCLUSIVE_BEGIN_RANGE_EXT = VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT, + VK_FULL_SCREEN_EXCLUSIVE_END_RANGE_EXT = VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT, + VK_FULL_SCREEN_EXCLUSIVE_RANGE_SIZE_EXT = (VK_FULL_SCREEN_EXCLUSIVE_APPLICATION_CONTROLLED_EXT - VK_FULL_SCREEN_EXCLUSIVE_DEFAULT_EXT + 1), + VK_FULL_SCREEN_EXCLUSIVE_MAX_ENUM_EXT = 0x7FFFFFFF +} VkFullScreenExclusiveEXT; +typedef struct VkSurfaceFullScreenExclusiveInfoEXT { + VkStructureType sType; + void* pNext; + VkFullScreenExclusiveEXT fullScreenExclusive; +} VkSurfaceFullScreenExclusiveInfoEXT; + +typedef struct VkSurfaceCapabilitiesFullScreenExclusiveEXT { + VkStructureType sType; + void* pNext; + VkBool32 fullScreenExclusiveSupported; +} VkSurfaceCapabilitiesFullScreenExclusiveEXT; + +typedef struct VkSurfaceFullScreenExclusiveWin32InfoEXT { + VkStructureType sType; + const void* pNext; + HMONITOR hmonitor; +} VkSurfaceFullScreenExclusiveWin32InfoEXT; + +typedef VkResult (VKAPI_PTR *PFN_vkGetPhysicalDeviceSurfacePresentModes2EXT)(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, uint32_t* pPresentModeCount, VkPresentModeKHR* pPresentModes); +typedef VkResult (VKAPI_PTR *PFN_vkAcquireFullScreenExclusiveModeEXT)(VkDevice device, VkSwapchainKHR swapchain); +typedef VkResult (VKAPI_PTR *PFN_vkReleaseFullScreenExclusiveModeEXT)(VkDevice device, VkSwapchainKHR swapchain); +typedef VkResult (VKAPI_PTR *PFN_vkGetDeviceGroupSurfacePresentModes2EXT)(VkDevice device, const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, VkDeviceGroupPresentModeFlagsKHR* pModes); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetPhysicalDeviceSurfacePresentModes2EXT( + VkPhysicalDevice physicalDevice, + const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, + uint32_t* pPresentModeCount, + VkPresentModeKHR* pPresentModes); + +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireFullScreenExclusiveModeEXT( + VkDevice device, + VkSwapchainKHR swapchain); + +VKAPI_ATTR VkResult VKAPI_CALL vkReleaseFullScreenExclusiveModeEXT( + VkDevice device, + VkSwapchainKHR swapchain); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetDeviceGroupSurfacePresentModes2EXT( + VkDevice device, + const VkPhysicalDeviceSurfaceInfo2KHR* pSurfaceInfo, + VkDeviceGroupPresentModeFlagsKHR* pModes); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_xcb.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_xcb.h new file mode 100644 index 00000000000..4cc0bc0cec5 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_xcb.h @@ -0,0 +1,65 @@ +#ifndef VULKAN_XCB_H_ +#define VULKAN_XCB_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_KHR_xcb_surface 1 +#define VK_KHR_XCB_SURFACE_SPEC_VERSION 6 +#define VK_KHR_XCB_SURFACE_EXTENSION_NAME "VK_KHR_xcb_surface" +typedef VkFlags VkXcbSurfaceCreateFlagsKHR; +typedef struct VkXcbSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkXcbSurfaceCreateFlagsKHR flags; + xcb_connection_t* connection; + xcb_window_t window; +} VkXcbSurfaceCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateXcbSurfaceKHR)(VkInstance instance, const VkXcbSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, xcb_connection_t* connection, xcb_visualid_t visual_id); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR( + VkInstance instance, + const VkXcbSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXcbPresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + xcb_connection_t* connection, + xcb_visualid_t visual_id); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_xlib.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_xlib.h new file mode 100644 index 00000000000..ee2b48accb0 --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_xlib.h @@ -0,0 +1,65 @@ +#ifndef VULKAN_XLIB_H_ +#define VULKAN_XLIB_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_KHR_xlib_surface 1 +#define VK_KHR_XLIB_SURFACE_SPEC_VERSION 6 +#define VK_KHR_XLIB_SURFACE_EXTENSION_NAME "VK_KHR_xlib_surface" +typedef VkFlags VkXlibSurfaceCreateFlagsKHR; +typedef struct VkXlibSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + VkXlibSurfaceCreateFlagsKHR flags; + Display* dpy; + Window window; +} VkXlibSurfaceCreateInfoKHR; + +typedef VkResult (VKAPI_PTR *PFN_vkCreateXlibSurfaceKHR)(VkInstance instance, const VkXlibSurfaceCreateInfoKHR* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSurfaceKHR* pSurface); +typedef VkBool32 (VKAPI_PTR *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice physicalDevice, uint32_t queueFamilyIndex, Display* dpy, VisualID visualID); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkCreateXlibSurfaceKHR( + VkInstance instance, + const VkXlibSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL vkGetPhysicalDeviceXlibPresentationSupportKHR( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex, + Display* dpy, + VisualID visualID); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/third_party/dawn/third_party/khronos/vulkan/vulkan_xlib_xrandr.h b/third_party/dawn/third_party/khronos/vulkan/vulkan_xlib_xrandr.h new file mode 100644 index 00000000000..08c4fd729cd --- /dev/null +++ b/third_party/dawn/third_party/khronos/vulkan/vulkan_xlib_xrandr.h @@ -0,0 +1,55 @@ +#ifndef VULKAN_XLIB_XRANDR_H_ +#define VULKAN_XLIB_XRANDR_H_ 1 + +/* +** Copyright (c) 2015-2019 The Khronos Group Inc. +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +/* +** This header is generated from the Khronos Vulkan XML API Registry. +** +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define VK_EXT_acquire_xlib_display 1 +#define VK_EXT_ACQUIRE_XLIB_DISPLAY_SPEC_VERSION 1 +#define VK_EXT_ACQUIRE_XLIB_DISPLAY_EXTENSION_NAME "VK_EXT_acquire_xlib_display" +typedef VkResult (VKAPI_PTR *PFN_vkAcquireXlibDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, VkDisplayKHR display); +typedef VkResult (VKAPI_PTR *PFN_vkGetRandROutputDisplayEXT)(VkPhysicalDevice physicalDevice, Display* dpy, RROutput rrOutput, VkDisplayKHR* pDisplay); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkAcquireXlibDisplayEXT( + VkPhysicalDevice physicalDevice, + Display* dpy, + VkDisplayKHR display); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetRandROutputDisplayEXT( + VkPhysicalDevice physicalDevice, + Display* dpy, + RROutput rrOutput, + VkDisplayKHR* pDisplay); +#endif + +#ifdef __cplusplus +} +#endif + +#endif