aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2021-06-21 14:47:38 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2021-06-21 14:47:38 +0000
commitbba7ff3cfe1b19faa1d8273d4f0f03febea9c156 (patch)
treebdf409c89e96d95b1a90258eb449dfaf7c450239
parent82e4066d660069b8f2bb6905a1223a0273098536 (diff)
parent0706e0aded758a72a088051fecabdd0412d1e454 (diff)
downloadamber-android12-mainline-extservices-release.tar.gz
Change-Id: I6fef64bc4a25890142cc7a37f34d1ebd155b4611
-rw-r--r--.gitignore9
-rw-r--r--Android.bp77
-rw-r--r--CMakeLists.txt36
-rw-r--r--DEPS95
-rw-r--r--Doxyfile2
-rw-r--r--METADATA3
-rw-r--r--README.md54
-rw-r--r--android_gradle/.gitignore15
-rwxr-xr-xandroid_gradle/amber.sh38
-rw-r--r--android_gradle/app/build.gradle49
-rw-r--r--android_gradle/app/jniLibs/.gitignore2
-rw-r--r--android_gradle/app/src/androidTest/AndroidManifest.xml8
-rw-r--r--android_gradle/app/src/androidTest/java/com/google/amber/AmberLauncher.java67
-rw-r--r--android_gradle/app/src/main/AndroidManifest.xml14
-rw-r--r--android_gradle/app/src/main/java/com/google/amber/Amber.java23
-rw-r--r--android_gradle/build.gradle28
-rw-r--r--android_gradle/gradle.properties20
-rw-r--r--android_gradle/gradle/wrapper/gradle-wrapper.jarbin0 -> 54329 bytes
-rw-r--r--android_gradle/gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xandroid_gradle/gradlew172
-rw-r--r--android_gradle/gradlew.bat84
-rw-r--r--android_gradle/settings.gradle2
-rw-r--r--android_test/jni/Application.mk3
-rw-r--r--android_test/test.cc2
-rw-r--r--docs/amber_script.md522
-rw-r--r--docs/debugger.md192
-rw-r--r--docs/engines.md6
-rw-r--r--framebuffer.pngbin0 -> 3912 bytes
-rw-r--r--include/amber/amber.h28
-rw-r--r--include/amber/result.h23
-rw-r--r--include/amber/shader_info.h4
-rwxr-xr-x[-rw-r--r--]kokoro/android/build.sh45
-rwxr-xr-x[-rw-r--r--]kokoro/check-format/build.sh0
-rwxr-xr-xkokoro/license-check/build-docker.sh21
-rwxr-xr-xkokoro/license-check/build.sh29
-rw-r--r--kokoro/license-check/presubmit.cfg15
-rwxr-xr-x[-rw-r--r--]kokoro/linux-clang-debug/build.sh4
-rwxr-xr-x[-rw-r--r--]kokoro/linux-clang-release/build.sh5
-rwxr-xr-x[-rw-r--r--]kokoro/linux-gcc-debug-dawn/build.sh2
-rwxr-xr-x[-rw-r--r--]kokoro/linux-gcc-debug/build.sh4
-rwxr-xr-x[-rw-r--r--]kokoro/linux-gcc-release-clspv/build.sh4
-rwxr-xr-x[-rw-r--r--]kokoro/linux-gcc-release/build.sh5
-rwxr-xr-x[-rw-r--r--]kokoro/macos-clang-debug/build.sh4
-rwxr-xr-x[-rw-r--r--]kokoro/macos-clang-release/build.sh4
-rwxr-xr-x[-rw-r--r--]kokoro/ndk-build/build.sh41
-rwxr-xr-xkokoro/scripts/linux/build-docker.sh92
-rwxr-xr-x[-rw-r--r--]kokoro/scripts/linux/build.sh93
-rwxr-xr-xkokoro/scripts/linux/build_dawn-docker.sh86
-rwxr-xr-x[-rw-r--r--]kokoro/scripts/linux/build_dawn.sh113
-rwxr-xr-x[-rw-r--r--]kokoro/scripts/macos/build.sh27
-rw-r--r--kokoro/scripts/windows/build.bat4
-rw-r--r--license-checker.cfg43
-rw-r--r--samples/amber.cc237
-rw-r--r--samples/android_main.cc57
-rw-r--r--samples/config_helper.cc3
-rw-r--r--samples/config_helper.h2
-rw-r--r--samples/config_helper_dawn.cc1
-rw-r--r--samples/config_helper_dawn.h1
-rw-r--r--samples/config_helper_vulkan.cc408
-rw-r--r--samples/config_helper_vulkan.h21
-rw-r--r--samples/image_diff.cc69
-rw-r--r--samples/png.cc29
-rw-r--r--samples/png.h8
-rw-r--r--samples/ppm.cc8
-rw-r--r--samples/timestamp.cc2
-rw-r--r--setup_debug_local.env14
-rw-r--r--src/CMakeLists.txt47
-rw-r--r--src/amber.cc63
-rw-r--r--src/amberscript/parser.cc2248
-rw-r--r--src/amberscript/parser.h17
-rw-r--r--src/amberscript/parser_attach_test.cc24
-rw-r--r--src/amberscript/parser_bind_test.cc2012
-rw-r--r--src/amberscript/parser_buffer_test.cc419
-rw-r--r--src/amberscript/parser_clear_color_test.cc2
-rw-r--r--src/amberscript/parser_clear_depth_test.cc134
-rw-r--r--src/amberscript/parser_clear_stencil_test.cc137
-rw-r--r--src/amberscript/parser_clear_test.cc2
-rw-r--r--src/amberscript/parser_compile_options_test.cc6
-rw-r--r--src/amberscript/parser_debug_test.cc296
-rw-r--r--src/amberscript/parser_depth_test.cc557
-rw-r--r--src/amberscript/parser_device_feature_test.cc31
-rw-r--r--src/amberscript/parser_expect_test.cc235
-rw-r--r--src/amberscript/parser_extension_test.cc6
-rw-r--r--src/amberscript/parser_framebuffer_test.cc11
-rw-r--r--src/amberscript/parser_image_test.cc369
-rw-r--r--src/amberscript/parser_pipeline_set_test.cc19
-rw-r--r--src/amberscript/parser_pipeline_test.cc125
-rw-r--r--src/amberscript/parser_run_test.cc719
-rw-r--r--src/amberscript/parser_sampler_test.cc195
-rw-r--r--src/amberscript/parser_set_test.cc2
-rw-r--r--src/amberscript/parser_shader_opt_test.cc5
-rw-r--r--src/amberscript/parser_shader_test.cc139
-rw-r--r--src/amberscript/parser_stencil_test.cc578
-rw-r--r--src/amberscript/parser_subgroup_size_control_test.cc489
-rw-r--r--src/amberscript/parser_test.cc2
-rw-r--r--src/buffer.cc40
-rw-r--r--src/buffer.h73
-rw-r--r--src/buffer_test.cc238
-rw-r--r--src/cast_hash.h2
-rw-r--r--src/clspv_helper.cc295
-rw-r--r--src/clspv_helper.h3
-rw-r--r--src/command.cc21
-rw-r--r--src/command.h162
-rw-r--r--src/command_data.h6
-rw-r--r--src/dawn/engine_dawn.cc14
-rw-r--r--src/dawn/engine_dawn.h6
-rw-r--r--src/debug.cc136
-rw-r--r--src/debug.h156
-rw-r--r--src/descriptor_set_and_binding_parser.cc24
-rw-r--r--src/descriptor_set_and_binding_parser.h7
-rw-r--r--src/descriptor_set_and_binding_parser_test.cc24
-rw-r--r--src/dxc_helper.cc130
-rw-r--r--src/dxc_helper.h8
-rw-r--r--src/engine.cc2
-rw-r--r--src/engine.h23
-rw-r--r--src/executor.cc52
-rw-r--r--src/executor.h3
-rw-r--r--src/executor_test.cc108
-rw-r--r--src/float16_helper.cc138
-rw-r--r--src/float16_helper.h45
-rw-r--r--src/float16_helper_test.cc33
-rw-r--r--src/format.h8
-rw-r--r--src/format_test.cc356
-rw-r--r--src/image.h26
-rw-r--r--src/parser.cc3
-rw-r--r--src/parser.h3
-rw-r--r--src/pipeline.cc675
-rw-r--r--src/pipeline.h197
-rw-r--r--src/pipeline_data.h2
-rw-r--r--src/pipeline_test.cc275
-rw-r--r--src/platform.h9
-rw-r--r--src/result.cc46
-rw-r--r--src/result_test.cc73
-rw-r--r--src/sampler.cc22
-rw-r--r--src/sampler.h105
-rw-r--r--src/script.cc50
-rw-r--r--src/script.h51
-rw-r--r--src/script_test.cc51
-rw-r--r--src/shader.h12
-rw-r--r--src/shader_compiler.cc54
-rw-r--r--src/shader_compiler.h23
-rw-r--r--src/shader_compiler_test.cc151
-rw-r--r--src/tokenizer.cc97
-rw-r--r--src/tokenizer.h9
-rw-r--r--src/tokenizer_test.cc102
-rw-r--r--src/type_parser.cc28
-rw-r--r--src/type_parser.h2
-rw-r--r--src/type_parser_test.cc2
-rw-r--r--src/type_test.cc18
-rw-r--r--src/verifier.cc167
-rw-r--r--src/verifier_test.cc98
-rw-r--r--src/virtual_file_store.cc48
-rw-r--r--src/virtual_file_store.h75
-rw-r--r--src/virtual_file_store_test.cc49
-rw-r--r--src/vkscript/command_parser.cc93
-rw-r--r--src/vkscript/command_parser_test.cc47
-rw-r--r--src/vkscript/parser.cc27
-rw-r--r--src/vkscript/parser.h1
-rw-r--r--src/vkscript/parser_test.cc122
-rw-r--r--src/vulkan/CMakeLists.txt13
-rw-r--r--src/vulkan/buffer_backed_descriptor.cc139
-rw-r--r--src/vulkan/buffer_backed_descriptor.h63
-rw-r--r--src/vulkan/buffer_descriptor.cc163
-rw-r--r--src/vulkan/buffer_descriptor.h48
-rw-r--r--src/vulkan/command_buffer.cc25
-rw-r--r--src/vulkan/command_buffer.h1
-rw-r--r--src/vulkan/descriptor.cc64
-rw-r--r--src/vulkan/descriptor.h105
-rw-r--r--src/vulkan/device.cc354
-rw-r--r--src/vulkan/device.h31
-rw-r--r--src/vulkan/engine_vulkan.cc272
-rw-r--r--src/vulkan/engine_vulkan.h17
-rw-r--r--src/vulkan/engine_vulkan_debugger.cc1180
-rw-r--r--src/vulkan/find_vulkan.cmake13
-rw-r--r--src/vulkan/frame_buffer.cc64
-rw-r--r--src/vulkan/frame_buffer.h10
-rw-r--r--src/vulkan/graphics_pipeline.cc131
-rw-r--r--src/vulkan/graphics_pipeline.h6
-rw-r--r--src/vulkan/image_descriptor.cc189
-rw-r--r--src/vulkan/image_descriptor.h58
-rw-r--r--src/vulkan/index_buffer.cc2
-rw-r--r--src/vulkan/pipeline.cc192
-rw-r--r--src/vulkan/pipeline.h12
-rw-r--r--src/vulkan/pipeline_test.cc53
-rw-r--r--src/vulkan/resource.cc11
-rw-r--r--src/vulkan/resource.h1
-rw-r--r--src/vulkan/sampler.cc96
-rw-r--r--src/vulkan/sampler.h40
-rw-r--r--src/vulkan/sampler_descriptor.cc70
-rw-r--r--src/vulkan/sampler_descriptor.h55
-rw-r--r--src/vulkan/transfer_buffer.cc50
-rw-r--r--src/vulkan/transfer_buffer.h8
-rw-r--r--src/vulkan/transfer_image.cc179
-rw-r--r--src/vulkan/transfer_image.h19
-rw-r--r--src/vulkan/vertex_buffer.cc90
-rw-r--r--src/vulkan/vertex_buffer.h38
-rw-r--r--src/vulkan/vertex_buffer_test.cc173
-rw-r--r--src/vulkan/vk-funcs-1-0.inc (renamed from src/vulkan/vk-funcs.inc)2
-rw-r--r--src/vulkan/vk-funcs-1-1.inc1
-rw-r--r--src/vulkan/vk-wrappers.h63
-rw-r--r--src/vulkan/vk-wrappers.inc252
-rwxr-xr-xtests/cases/address_modes_float.amber138
-rwxr-xr-xtests/cases/address_modes_int.amber138
-rw-r--r--tests/cases/buffer_load_binary.amber55
-rw-r--r--tests/cases/buffer_load_text.amber55
-rw-r--r--tests/cases/compute_descriptor_array_ssbo.amber49
-rw-r--r--tests/cases/compute_descriptor_array_storagetexelbuffer.amber63
-rw-r--r--tests/cases/compute_dynamic_buffers.amber54
-rw-r--r--tests/cases/compute_dynamic_buffers_descriptor_array.amber48
-rw-r--r--tests/cases/compute_mat2x4_row_major_col_major.amber89
-rw-r--r--tests/cases/debugger_hlsl_basic_compute.amber72
-rw-r--r--tests/cases/debugger_hlsl_basic_fragment.amber104
-rw-r--r--tests/cases/debugger_hlsl_basic_fragment_with_legalization.amber102
-rw-r--r--tests/cases/debugger_hlsl_basic_vertex.amber130
-rw-r--r--tests/cases/debugger_hlsl_basic_vertex_with_legalization.amber128
-rw-r--r--tests/cases/debugger_hlsl_function_call.amber212
-rw-r--r--tests/cases/debugger_hlsl_shadowed_vars.amber177
-rw-r--r--tests/cases/debugger_spirv_line_stepping.amber125
-rw-r--r--tests/cases/draw_array_instanced.amber66
-rw-r--r--tests/cases/draw_combined_image_sampler.amber92
-rw-r--r--tests/cases/draw_grid.amber54
-rw-r--r--tests/cases/draw_grid_multiple_color_attachment.amber40
-rw-r--r--tests/cases/draw_grid_multiple_pipeline.amber44
-rw-r--r--tests/cases/draw_grid_with_buffer.amber63
-rw-r--r--tests/cases/draw_grid_with_two_vertex_data_attached.expect_fail.amber74
-rw-r--r--tests/cases/draw_grids.amber78
-rw-r--r--tests/cases/draw_png_texture.amber66
-rw-r--r--tests/cases/draw_polygon_mode.amber90
-rw-r--r--tests/cases/draw_rect_and_ortho.vkscript14
-rw-r--r--tests/cases/draw_rect_multiple_pipeline.amber6
-rw-r--r--tests/cases/draw_rectangles.amber2
-rw-r--r--tests/cases/draw_rectangles_depth_test.amber136
-rw-r--r--tests/cases/draw_rectangles_depth_test_d24s8.amber135
-rw-r--r--tests/cases/draw_rectangles_depth_test_x8d24.amber135
-rw-r--r--tests/cases/draw_rectangles_stencil_test.amber149
-rw-r--r--tests/cases/draw_sampled_image.amber115
-rw-r--r--tests/cases/draw_storageimage.amber80
-rw-r--r--tests/cases/draw_storageimage_multisample.amber61
-rw-r--r--tests/cases/draw_storagetexelbuffer.amber53
-rw-r--r--tests/cases/draw_triangle_list_format.amber91
-rw-r--r--tests/cases/draw_triangle_list_offset.amber147
-rw-r--r--tests/cases/draw_triangle_list_stride.amber106
-rw-r--r--tests/cases/draw_uniformtexelbuffer.amber56
-rw-r--r--tests/cases/float16.amber50
-rw-r--r--tests/cases/glsl_read_and_write_image3d_rgba32i.amber76
-rw-r--r--tests/cases/graphics_descriptor_array_combined_image_sampler.amber95
-rw-r--r--tests/cases/graphics_descriptor_array_sampled_image.amber97
-rw-r--r--tests/cases/graphics_descriptor_array_sampler.amber57
-rw-r--r--tests/cases/graphics_dynamic_buffers.amber135
-rw-r--r--tests/cases/graphics_push_constants.amber2
-rw-r--r--tests/cases/graphics_uniform_buffer.vkscript35
-rw-r--r--tests/cases/image_data.amber92
-rw-r--r--tests/cases/int8.amber53
-rw-r--r--tests/cases/magfilter_linear.amber96
-rw-r--r--tests/cases/magfilter_nearest.amber97
-rw-r--r--tests/cases/matrices_uniform_draw.amber29
-rw-r--r--tests/cases/matrix_initialization.amber15
-rw-r--r--tests/cases/minfilter.expect_fail.amber126
-rw-r--r--tests/cases/multiple_samplers.amber126
-rw-r--r--tests/cases/multiple_ssbo_update_with_graphics_pipeline.vkscript6
-rw-r--r--tests/cases/multiple_ssbo_with_sparse_descriptor_set_in_compute_pipeline_less_than_4.vkscript31
-rw-r--r--tests/cases/opencl_generated_push_constants.amber79
-rw-r--r--tests/cases/opencl_read_and_write_image3d_rgba32i.amber74
-rw-r--r--tests/cases/opencl_read_image.amber68
-rw-r--r--tests/cases/opencl_read_image_literal_sampler.amber69
-rw-r--r--tests/cases/opencl_set_arg.amber12
-rw-r--r--tests/cases/opencl_write_image.amber45
-rw-r--r--tests/cases/position_to_ssbo.vkscript (renamed from tests/cases/position_to_ssbo.amber)0
-rw-r--r--tests/cases/relative_includes_hlsl.amber56
-rw-r--r--tests/cases/runtime_array_std140.amber42
-rw-r--r--tests/cases/runtime_array_std430.amber42
-rw-r--r--tests/cases/storage16.amber52
-rw-r--r--tests/cases/texture.pngbin0 -> 32776 bytes
-rw-r--r--tests/cases/texture_base_mip_level.amber81
-rw-r--r--tests/cases/texture_lod.amber101
-rw-r--r--tests/cases/vec4data.binbin0 -> 48 bytes
-rw-r--r--tests/cases/vec4data.txt1
-rw-r--r--tests/cases/vertex_data_two_locations.amber84
-rw-r--r--tests/cases/vertex_rate_instance.amber70
-rwxr-xr-xtests/run_tests.py209
-rw-r--r--third_party/CMakeLists.txt43
-rw-r--r--tools/amber-syntax.vim49
-rw-r--r--tools/amber.sublime-syntax2
-rwxr-xr-xtools/check_code_format.sh4
-rwxr-xr-xtools/check_language.py179
-rwxr-xr-xtools/check_language_test.py61
-rwxr-xr-xtools/format14
-rwxr-xr-xtools/git-sync-deps88
-rwxr-xr-xtools/roll-all60
-rwxr-xr-xtools/update_vk_wrappers.py64
290 files changed, 24789 insertions, 3023 deletions
diff --git a/.gitignore b/.gitignore
index ece4a6b..d5f3eb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,21 +1,26 @@
out
+build
third_party/clspv
third_party/clspv-clang
third_party/clspv-llvm
+third_party/cppdap
third_party/cpplint
third_party/dxc
third_party/glslang
third_party/googletest
+third_party/json
third_party/lodepng
third_party/shaderc
-third_party/spirv-tools
third_party/spirv-headers
+third_party/spirv-tools
third_party/swiftshader
third_party/vulkan-headers
-third_party/vulkan-validationlayers/
third_party/vulkan-loader
+third_party/vulkan-validationlayers/
.vs
+*.pyc
+
# Vim swap files
[._]*.s[a-w][a-z]
diff --git a/Android.bp b/Android.bp
index cb2230e..45d03c4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,74 +1,99 @@
-cc_defaults {
- cpp_std: "c++11",
- cppflags: [
- "-DAMBER_ENGINE_VULKAN=1",
- "-Wall",
- "-Werror",
+package {
+ default_applicable_licenses: ["external_deqp-deps_amber_license"],
+}
+
+// Added automatically by a large-scale-change
+//
+// large-scale-change included anything that looked like it might be a license
+// text as a license_text. e.g. LICENSE, NOTICE, COPYING etc.
+//
+// Please consider removing redundant or irrelevant files from 'license_text:'.
+// See: http://go/android-license-faq
+license {
+ name: "external_deqp-deps_amber_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
],
- export_include_dirs: [
- ".",
+ license_text: [
+ "LICENSE",
],
- name: "deqp_amber_defaults",
- rtti: false,
- sdk_version: "27",
- stl: "libc++_static",
- shared_libs: ["libvulkan"],
- export_shared_lib_headers: ["libvulkan"],
+}
+
+genrule {
+ name: "deqp_amber_gen_vk_wrappers",
+ out: ["vk-wrappers-1-0.inc", "vk-wrappers-1-0.h", "vk-wrappers-1-1.inc", "vk-wrappers-1-1.h"],
+ srcs: ["src/vulkan/vk-funcs-1-0.inc", "src/vulkan/vk-funcs-1-1.inc"],
+ tool_files: ["tools/update_vk_wrappers.py"],
+ cmd: "$(location) $(genDir) $$(dirname $(location src/vulkan/vk-funcs-1-0.inc))/../..",
}
cc_library_static {
- defaults: [
- "deqp_amber_defaults",
- ],
+ name: "deqp_amber",
+ defaults: ["deqp_and_deps_defaults"],
export_include_dirs: [
"include",
+ ".",
+ ],
+ generated_headers: [
+ "deqp_amber_gen_vk_wrappers",
+ ],
+ header_libs: [
+ "deqp_vulkan_headers",
],
- name: "deqp_amber",
srcs: [
"src/amber.cc",
+ "src/amberscript/parser.cc",
"src/buffer.cc",
- "src/command.cc",
"src/command_data.cc",
+ "src/command.cc",
+ "src/debug.cc",
"src/descriptor_set_and_binding_parser.cc",
"src/engine.cc",
"src/executor.cc",
+ "src/float16_helper.cc",
"src/format.cc",
"src/parser.cc",
- "src/pipeline.cc",
"src/pipeline_data.cc",
+ "src/pipeline.cc",
"src/recipe.cc",
"src/result.cc",
+ "src/sampler.cc",
"src/script.cc",
- "src/shader.cc",
"src/shader_compiler.cc",
+ "src/shader.cc",
+ "src/sleep.cc",
"src/tokenizer.cc",
"src/type_parser.cc",
"src/type.cc",
"src/value.cc",
"src/verifier.cc",
- "src/vulkan_engine_config.cc",
-
- "src/amberscript/parser.cc",
+ "src/virtual_file_store.cc",
"src/vkscript/command_parser.cc",
"src/vkscript/datum_type_parser.cc",
"src/vkscript/parser.cc",
"src/vkscript/section_parser.cc",
-
+ "src/vulkan_engine_config.cc",
+ "src/vulkan/buffer_backed_descriptor.cc",
"src/vulkan/buffer_descriptor.cc",
"src/vulkan/command_buffer.cc",
"src/vulkan/command_pool.cc",
"src/vulkan/compute_pipeline.cc",
+ "src/vulkan/descriptor.cc",
"src/vulkan/device.cc",
+ "src/vulkan/engine_vulkan_debugger.cc",
"src/vulkan/engine_vulkan.cc",
"src/vulkan/frame_buffer.cc",
"src/vulkan/graphics_pipeline.cc",
+ "src/vulkan/image_descriptor.cc",
"src/vulkan/index_buffer.cc",
"src/vulkan/pipeline.cc",
"src/vulkan/push_constant.cc",
"src/vulkan/resource.cc",
+ "src/vulkan/sampler_descriptor.cc",
+ "src/vulkan/sampler.cc",
"src/vulkan/transfer_buffer.cc",
"src/vulkan/transfer_image.cc",
"src/vulkan/vertex_buffer.cc",
-
],
}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bcfb6f8..9f65d5f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -28,11 +28,6 @@ enable_testing()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
-if (${AMBER_USE_CLSPV})
- set(CMAKE_CXX_STANDARD 14)
-else()
- set(CMAKE_CXX_STANDARD 11)
-endif()
include(CheckIncludeFile)
include(GNUInstallDirs)
@@ -52,6 +47,16 @@ option(AMBER_USE_LOCAL_VULKAN "Build with vulkan in third_party" OFF)
option(AMBER_USE_CLSPV "Build with Clspv support" OFF)
option(AMBER_ENABLE_SWIFTSHADER
"Build using SwiftShader" ${AMBER_ENABLE_SWIFTSHADER})
+option(AMBER_ENABLE_VK_DEBUGGING
+ "Build with cppdap debugging support" ${AMBER_ENABLE_VK_DEBUGGING})
+option(AMBER_ENABLE_RTTI
+ "Build with runtime type information" OFF)
+
+if (${AMBER_USE_CLSPV} OR ${AMBER_ENABLE_SWIFTSHADER})
+ set(CMAKE_CXX_STANDARD 14)
+else()
+ set(CMAKE_CXX_STANDARD 11)
+endif()
if(WIN32)
# On Windows, CMake by default compiles with the shared CRT.
@@ -104,13 +109,16 @@ else()
endif()
if (${AMBER_USE_CLSPV})
- enable_language(ASM)
set(AMBER_ENABLE_CLSPV TRUE)
set(AMBER_ENABLE_SPIRV_TOOLS TRUE)
else()
set(AMBER_ENABLE_CLSPV FALSE)
endif()
+if (${AMBER_USE_CLSPV} OR ${AMBER_ENABLE_SWIFTSHADER})
+ enable_language(ASM)
+endif()
+
message(STATUS "Using python3")
find_package(PythonInterp 3 REQUIRED)
@@ -122,6 +130,7 @@ message(STATUS "Amber enable lodepng: ${AMBER_ENABLE_LODEPNG}")
message(STATUS "Amber enable SwiftShader: ${AMBER_ENABLE_SWIFTSHADER}")
message(STATUS "Amber enable DXC: ${AMBER_ENABLE_DXC}")
message(STATUS "Amber enable Clspv: ${AMBER_ENABLE_CLSPV}")
+message(STATUS "Amber enable RTTI: ${AMBER_ENABLE_RTTI}")
include_directories("${PROJECT_SOURCE_DIR}/include")
include_directories("${PROJECT_SOURCE_DIR}")
@@ -144,6 +153,8 @@ add_definitions(-DAMBER_ENABLE_SHADERC=$<BOOL:${AMBER_ENABLE_SHADERC}>)
add_definitions(-DAMBER_ENABLE_DXC=$<BOOL:${AMBER_ENABLE_DXC}>)
add_definitions(-DAMBER_ENABLE_CLSPV=$<BOOL:${AMBER_ENABLE_CLSPV}>)
add_definitions(-DAMBER_ENABLE_LODEPNG=$<BOOL:${AMBER_ENABLE_LODEPNG}>)
+add_definitions(-DAMBER_ENABLE_VK_DEBUGGING=$<BOOL:${AMBER_ENABLE_VK_DEBUGGING}>)
+add_definitions(-DAMBER_ENABLE_RTTI=$<BOOL:${AMBER_ENABLE_RTTI}>)
set(CMAKE_DEBUG_POSTFIX "")
@@ -179,10 +190,10 @@ if(MSVC)
if(NOT ${AMBER_ENABLE_SHARED_CRT})
message(STATUS "Amber: Static C runtime selected: replacing /MD* with /MT*")
foreach (flag_var
- CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
- CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
- CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
- CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
+ CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
+ CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
+ CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
+ CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endforeach()
endif()
@@ -193,7 +204,6 @@ function(amber_default_compile_options TARGET)
target_compile_options(${TARGET} PRIVATE
-std=c++11
-fno-exceptions
- -fno-rtti
-fvisibility=hidden
-Wall
-Werror
@@ -204,6 +214,10 @@ function(amber_default_compile_options TARGET)
-pedantic-errors
)
+ if(NOT ${AMBER_ENABLE_RTTI})
+ target_compile_options(${TARGET} PRIVATE -fno-rtti)
+ endif()
+
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
target_compile_options(${TARGET} PRIVATE
-Wno-c++98-compat
diff --git a/DEPS b/DEPS
index 19dba96..ff7507f 100644
--- a/DEPS
+++ b/DEPS
@@ -5,65 +5,74 @@ vars = {
'khronos_git': 'https://github.com/KhronosGroup',
'llvm_git': 'https://github.com/llvm',
'lvandeve_git': 'https://github.com/lvandeve',
- 'swiftshader_git': 'https://swiftshader.googlesource.com',
'microsoft_git': 'https://github.com/Microsoft',
+ 'nlohmann_git': 'https://github.com/nlohmann',
+ 'swiftshader_git': 'https://swiftshader.googlesource.com',
- 'clspv_llvm_revision': '97aa42f5dfcd10ca6df230caf9ca7868da5f25af',
- 'clspv_revision': '8132fc9122f7709149dd82b25283e244bbe666a6',
- 'cpplint_revision': '5651966e0275572a9956199418d89c9ccc7b2b1a',
- 'dxc_revision': 'ec912b2ec95feb50925704dd631ef7abee1a5f09',
- 'glslang_revision': 'fe0b2bd694bb07004a2db859c5714c321c26b751',
- 'googletest_revision': '33a0d4f6d76a0ed6061e612848532cba82d42870',
- 'lodepng_revision': 'ba9fc1f084f03b5fbf8c9a5df9448173f27544b1',
- 'shaderc_revision': '5903ef1f95a0acdbbd3ae645af1a6d6b30320f10',
- 'spirv_headers_revision': 'b252a50953ac4375cb1864e94f4b0234db9d215d',
- 'spirv_tools_revision': '9d7428b052dbc73b45bcb7c3c7919bbbadd6f287',
- 'swiftshader_revision': 'ef44b4402722658648ec9d10a76bd990776be1c0',
- 'vulkan_headers_revision': 'e3f96a9ccab9397481eb81c4d9bce4ea7590dc33',
- 'vulkan_validationlayers_revision': '0e65e191c4b9044d8e42727cc82ccc04d8055b0a',
- 'vulkan_loader_revision': '1bb7f68564fe565de2927071c79008bd6ede5af5',
+ 'clspv_llvm_revision': '7e30989dabce9ddbca0cbad7a8f25fb4e756d334',
+ 'clspv_revision': 'e0406e7053d1bb46b4bbeb57f0f2bbfca32f5612',
+ 'cppdap_revision': '1fd23dda91e01550be1a421de307e6fedb2035a9',
+ 'cpplint_revision': '26470f9ccb354ff2f6d098f831271a1833701b28',
+ 'dxc_revision': '489c2e4d32417cd6693db5673ab071d82e1f5974',
+ 'glslang_revision': '7f6559d2802d0653541060f0909e33d137b9c8ba',
+ 'googletest_revision': '0555b0eacbc56df1fd762c6aa87bb84be9e4ce7e',
+ 'json_revision': '350ff4f7ced7c4117eae2fb93df02823c8021fcb',
+ 'lodepng_revision': '7fdcc96a5e5864eee72911c3ca79b1d9f0d12292',
+ 'shaderc_revision': '88f9156d7f6a2a30baed1ace196faa3bc5eccc05',
+ 'spirv_headers_revision': '5ab5c96198f30804a6a29961b8905f292a8ae600',
+ 'spirv_tools_revision': '1f2fcddd3963b9c29bf360daf7656c5977c2aadd',
+ 'swiftshader_revision': '04515da400d5fbc22d852af1369c4d46bd54991e',
+ 'vulkan_headers_revision': '11c6670b4a4f766ed4f1e777d1b3c3dc082dfa5f',
+ 'vulkan_loader_revision': 'be6ccb9ecaf77dfef59246a1e8502e99c8e1a511',
+ 'vulkan_validationlayers_revision': '0cb8cc8cfcb2b86a767c9516ac2d62edb4e38ebe',
}
deps = {
- 'third_party/clspv': vars['google_git'] + '/clspv.git@' +
- vars['clspv_revision'],
+ 'third_party/clspv': Var('google_git') + '/clspv.git@' +
+ Var('clspv_revision'),
+
+ 'third_party/clspv-llvm': Var('llvm_git') + '/llvm-project.git@' +
+ Var('clspv_llvm_revision'),
+
+ 'third_party/cppdap': Var('google_git') + '/cppdap.git@' +
+ Var('cppdap_revision'),
- 'third_party/clspv-llvm': vars['llvm_git'] + '/llvm-project.git@' +
- vars['clspv_llvm_revision'],
+ 'third_party/cpplint': Var('google_git') + '/styleguide.git@' +
+ Var('cpplint_revision'),
- 'third_party/cpplint': vars['google_git'] + '/styleguide.git@' +
- vars['cpplint_revision'],
+ 'third_party/dxc': Var('microsoft_git') + '/DirectXShaderCompiler.git@' +
+ Var('dxc_revision'),
- 'third_party/dxc': vars['microsoft_git'] + '/DirectXShaderCompiler.git@' +
- vars['dxc_revision'],
+ 'third_party/googletest': Var('google_git') + '/googletest.git@' +
+ Var('googletest_revision'),
- 'third_party/googletest': vars['google_git'] + '/googletest.git@' +
- vars['googletest_revision'],
+ 'third_party/json': Var('nlohmann_git') + '/json.git@' +
+ Var('json_revision'),
- 'third_party/glslang': vars['khronos_git'] + '/glslang.git@' +
- vars['glslang_revision'],
+ 'third_party/glslang': Var('khronos_git') + '/glslang.git@' +
+ Var('glslang_revision'),
- 'third_party/lodepng': vars['lvandeve_git'] + '/lodepng.git@' +
- vars['lodepng_revision'],
+ 'third_party/lodepng': Var('lvandeve_git') + '/lodepng.git@' +
+ Var('lodepng_revision'),
- 'third_party/shaderc': vars['google_git'] + '/shaderc.git@' +
- vars['shaderc_revision'],
+ 'third_party/shaderc': Var('google_git') + '/shaderc.git@' +
+ Var('shaderc_revision'),
- 'third_party/spirv-headers': vars['khronos_git'] + '/SPIRV-Headers.git@' +
- vars['spirv_headers_revision'],
+ 'third_party/spirv-headers': Var('khronos_git') + '/SPIRV-Headers.git@' +
+ Var('spirv_headers_revision'),
- 'third_party/spirv-tools': vars['khronos_git'] + '/SPIRV-Tools.git@' +
- vars['spirv_tools_revision'],
+ 'third_party/spirv-tools': Var('khronos_git') + '/SPIRV-Tools.git@' +
+ Var('spirv_tools_revision'),
- 'third_party/swiftshader': vars['swiftshader_git'] + '/SwiftShader.git@' +
- vars['swiftshader_revision'],
+ 'third_party/swiftshader': Var('swiftshader_git') + '/SwiftShader.git@' +
+ Var('swiftshader_revision'),
- 'third_party/vulkan-headers': vars['khronos_git'] + '/Vulkan-Headers.git@' +
- vars['vulkan_headers_revision'],
+ 'third_party/vulkan-headers': Var('khronos_git') + '/Vulkan-Headers.git@' +
+ Var('vulkan_headers_revision'),
- 'third_party/vulkan-validationlayers': vars['khronos_git'] + '/Vulkan-ValidationLayers.git@' +
- vars['vulkan_validationlayers_revision'],
+ 'third_party/vulkan-validationlayers': Var('khronos_git') + '/Vulkan-ValidationLayers.git@' +
+ Var('vulkan_validationlayers_revision'),
- 'third_party/vulkan-loader': vars['khronos_git'] + '/Vulkan-Loader.git@' +
- vars['vulkan_loader_revision'],
+ 'third_party/vulkan-loader': Var('khronos_git') + '/Vulkan-Loader.git@' +
+ Var('vulkan_loader_revision'),
}
diff --git a/Doxyfile b/Doxyfile
index b216412..4b9ba52 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -1336,7 +1336,7 @@ CHM_FILE =
HHC_LOCATION =
# The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
+# (YES) or that it should be included in the primary .chm file (NO).
# The default value is: NO.
# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+ license_type: NOTICE
+}
diff --git a/README.md b/README.md
index c981e5b..1ad5c74 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,8 @@ Amber lets you capture and communicate shader bugs with the fluidity and ease of
a scripting flow:
* No graphics API programming is required.
- * WIP: Supports Vulkan and [Dawn][Dawn] graphics APIs.
+ * Supports Vulkan graphics API
+ * WIP: Supports [Dawn][Dawn] graphics API
* A single text string (or file) maps to a single graphics API pipeline test
case. The text includes:
* Input data, including buffers and images.
@@ -116,7 +117,7 @@ Alternatives:
### Android
-* Android build needs Android SDK 28, Android NDK 16, Java 8. If you prefer
+* Android build needs Android SDK 28, Android NDK r21, Java 8. If you prefer
other versions of Android SDK, Android NDK, Java, then you can change
`ANDROID_PLATFORM` and `ANDROID_BUILD_TOOL_VERSION` in
`tools/build-amber-sample.sh`.
@@ -255,12 +256,29 @@ CMake variables when configuring Amber:
* `Dawn_LIBRARY_DIR`: The directory containing the `dawn_native` library (in
the build output tree).
+### Using SwiftShader as a backend
+
+SwiftShader, if available, can be used by by exporting the `VK_ICD_FILENAMES`
+environment variable and using it directly. If SwiftShader is not installed it
+can be built with Amber by setting `AMBER_ENABLE_SWIFTSHADER` during the
+configure step of CMake.
+
+
+```
+mkdir out/sw
+cd out/sw
+cmake -GNinja -DAMBER_ENABLE_SWIFTSHADER=TRUE ../..
+ninja
+export VK_ICD_FILENAMES=$PWD/Linux/vk_swiftshader_icd.json
+./amber -d -V # Should see SwiftShader listed as device
+./amber -d ../../tests/cases/clear.amber
+```
+
## Amber Samples
The build will generate an `out/Debug/amber` executable which can be used to
run amber scripts. The script can be used as
-`out/Debug/amber <path to amber file>`. Where, currently, the amber file is
-in the [VkScript](docs/vk_script.md) format.
+`out/Debug/amber <path to amber file>`.
```
out/Debug/amber tests/cases/clear.amber
@@ -269,6 +287,16 @@ out/Debug/amber tests/cases/clear.amber
The sample app returns a value of 0 on success or non-zero on error. Any issues
encountered should be displayed on the console.
+Run `out/Debug/amber -h` to see a description of the program's command line options.
+
+Example AmberScript files can be found in the [tests/cases](tests/cases)
+directory in this repository.
+Also the [Vulkan Conformance Test
+Suite](https://github.com/KhronosGroup/VK-GL-CTS) contains many real-world
+examples in its
+[external/vulkancts/data/vulkan/amber](https://github.com/KhronosGroup/VK-GL-CTS/tree/master/external/vulkancts/data/vulkan/amber)
+subdirectory.
+
By default, `out/Debug/amber` supports saving the output image into '.png'
file. You can disable this by passing `-DAMBER_SKIP_LODEPNG=true` to cmake.
@@ -281,25 +309,9 @@ Please see the [CONTRIBUTING](CONTRIBUTING.md) and
[CODE_OF_CONDUCT](CODE_OF_CONDUCT.md) files on how to contribute to Amber.
+## References
[Dawn]: https://dawn.googlesource.com/dawn/
[Talvos]: https://talvos.github.io/
[Vulkan-Headers]: https://github.com/KhronosGroup/Vulkan-Headers
[VkRunner]: https://github.com/igalia/vkrunner
-### Using SwiftShader as a backend
-
-SwiftShader if installed it can be used by by exporting the `VK_ICD_FILENAMES`
-environment variable and using it directly. If SwiftShader is not installed it
-can be built with Amber by setting `AMBER_ENABLE_SWIFTSHADER` during the
-configure step of CMake.
-
-
-```
-mkdir out/sw
-cd out/sw
-cmake -GNinja -DAMBER_ENABLE_SWIFTSHADER=TRUE ../..
-ninja
-export VK_ICD_FILENAMES=$PWD/Linux/vk_swiftshader_icd.json
-./amber -d -V # Should see SwiftShader listed as device
-./amber -d ../../tests/cases/clear.amber
-```
diff --git a/android_gradle/.gitignore b/android_gradle/.gitignore
new file mode 100644
index 0000000..2c64069
--- /dev/null
+++ b/android_gradle/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/misc.xml
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+build/
+/captures
+.externalNativeBuild
+.cxx
diff --git a/android_gradle/amber.sh b/android_gradle/amber.sh
new file mode 100755
index 0000000..e58e62e
--- /dev/null
+++ b/android_gradle/amber.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+args=()
+index=1
+
+# The @Q expansion operator requires Bash 4.4.
+
+for arg in "$@"
+do
+args+=("-e")
+args+=("arg${index}")
+args+=("${arg}")
+index=$((index+1))
+done
+
+adb shell rm -f /sdcard/Android/data/com.google.amber/cache/amber_stdout.txt
+adb shell rm -f /sdcard/Android/data/com.google.amber/cache/amber_stderr.txt
+adb shell am instrument -w \
+ -e stdout /sdcard/Android/data/com.google.amber/cache/amber_stdout.txt \
+ -e stderr /sdcard/Android/data/com.google.amber/cache/amber_stderr.txt \
+ "${args[@]@Q}" \
+ com.google.amber.test/androidx.test.runner.AndroidJUnitRunner
+adb shell cat /sdcard/Android/data/com.google.amber/cache/amber_stdout.txt
+adb shell cat /sdcard/Android/data/com.google.amber/cache/amber_stderr.txt
diff --git a/android_gradle/app/build.gradle b/android_gradle/app/build.gradle
new file mode 100644
index 0000000..40c4c54
--- /dev/null
+++ b/android_gradle/app/build.gradle
@@ -0,0 +1,49 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.2"
+
+ defaultConfig {
+ applicationId "com.google.amber"
+ minSdkVersion 24
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ sourceSets {
+ androidTest.manifest.srcFile "src/androidTest/AndroidManifest.xml"
+ }
+
+ dependencies {
+ androidTestImplementation 'androidx.test:runner:1.1.0'
+ androidTestImplementation 'androidx.test:rules:1.1.0'
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
+ }
+ }
+
+ sourceSets {
+ main {
+ jniLibs.srcDirs = ["jniLibs"]
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+}
diff --git a/android_gradle/app/jniLibs/.gitignore b/android_gradle/app/jniLibs/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/android_gradle/app/jniLibs/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/android_gradle/app/src/androidTest/AndroidManifest.xml b/android_gradle/app/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..18dcff0
--- /dev/null
+++ b/android_gradle/app/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.amber.test">
+ <application>
+ <meta-data
+ android:name="com.android.graphics.developerdriver.enable"
+ android:value="true" />
+ </application>
+</manifest>
diff --git a/android_gradle/app/src/androidTest/java/com/google/amber/AmberLauncher.java b/android_gradle/app/src/androidTest/java/com/google/amber/AmberLauncher.java
new file mode 100644
index 0000000..4864843
--- /dev/null
+++ b/android_gradle/app/src/androidTest/java/com/google/amber/AmberLauncher.java
@@ -0,0 +1,67 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.amber;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+// Instrumented test used for launching Amber.
+public class AmberLauncher {
+ @Test
+ public void LaunchAmber() {
+ Context app_context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.google.amber", app_context.getPackageName());
+
+ Bundle args = InstrumentationRegistry.getArguments();
+
+ int arg_index = 1;
+ List<String> args_list = new ArrayList<>();
+
+ while (true) {
+ String arg = args.getString("arg" + arg_index);
+ if (arg == null) {
+ break;
+ }
+ args_list.add(arg);
+ ++arg_index;
+ }
+
+ File outputDir = app_context.getExternalCacheDir();
+
+ // This will typically be: /sdcard/Android/data/com.google.amber/cache/amber_std{out,err}.txt
+ String stdout_file = args.getString("stdout", new File(outputDir, "amber_stdout.txt").toString());
+ String stderr_file = args.getString("stderr", new File(outputDir, "amber_stderr.txt").toString());
+
+ int res = Amber.androidMain(args_list.toArray(new String[0]), stdout_file, stderr_file);
+
+ // If the process crashes during the above call or we call System.exit below, the output
+ // from `adb shell am instrument ...` will be:
+ // com.google.amber.AmberLauncher:INSTRUMENTATION_RESULT: shortMsg=Process crashed.
+ // INSTRUMENTATION_CODE: 0
+ if (res != 0) {
+ System.exit(res);
+ }
+ }
+}
diff --git a/android_gradle/app/src/main/AndroidManifest.xml b/android_gradle/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0ab9856
--- /dev/null
+++ b/android_gradle/app/src/main/AndroidManifest.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.google.amber">
+
+ <application
+ android:allowBackup="true"
+ android:label="Amber"
+ android:supportsRtl="true">
+ <meta-data
+ android:name="com.android.graphics.developerdriver.enable"
+ android:value="true" />
+ </application>
+
+</manifest>
diff --git a/android_gradle/app/src/main/java/com/google/amber/Amber.java b/android_gradle/app/src/main/java/com/google/amber/Amber.java
new file mode 100644
index 0000000..8319ccf
--- /dev/null
+++ b/android_gradle/app/src/main/java/com/google/amber/Amber.java
@@ -0,0 +1,23 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.amber;
+
+public class Amber {
+ static {
+ System.loadLibrary("amber_ndk");
+ }
+
+ public static native int androidMain(String[] args, String stdout_file, String stderr_file);
+}
diff --git a/android_gradle/build.gradle b/android_gradle/build.gradle
new file mode 100644
index 0000000..82c93bf
--- /dev/null
+++ b/android_gradle/build.gradle
@@ -0,0 +1,28 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+
+ repositories {
+ google()
+ jcenter()
+ maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.0.0-alpha04'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/android_gradle/gradle.properties b/android_gradle/gradle.properties
new file mode 100644
index 0000000..199d16e
--- /dev/null
+++ b/android_gradle/gradle.properties
@@ -0,0 +1,20 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+
diff --git a/android_gradle/gradle/wrapper/gradle-wrapper.jar b/android_gradle/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
--- /dev/null
+++ b/android_gradle/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/android_gradle/gradle/wrapper/gradle-wrapper.properties b/android_gradle/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..421d5b6
--- /dev/null
+++ b/android_gradle/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 09 14:37:37 GMT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-rc-1-all.zip
diff --git a/android_gradle/gradlew b/android_gradle/gradlew
new file mode 100755
index 0000000..9a27a6c
--- /dev/null
+++ b/android_gradle/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ]; do
+ ls=$(ls -ld "$PRG")
+ link=$(expr "$ls" : '.*-> \(.*\)$')
+ if expr "$link" : '/.*' >/dev/null; then
+ PRG="$link"
+ else
+ PRG=$(dirname "$PRG")"/$link"
+ fi
+done
+SAVED="$(pwd)"
+cd "$(dirname \"$PRG\")/" >/dev/null
+APP_HOME="$(pwd -P)"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=$(basename "$0")
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn() {
+ echo "$*"
+}
+
+die() {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$(uname)" in
+CYGWIN*)
+ cygwin=true
+ ;;
+Darwin*)
+ darwin=true
+ ;;
+MINGW*)
+ msys=true
+ ;;
+NONSTOP*)
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ]; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then
+ MAX_FD_LIMIT=$(ulimit -H -n)
+ if [ $? -eq 0 ]; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ]; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ APP_HOME=$(cygpath --path --mixed "$APP_HOME")
+ CLASSPATH=$(cygpath --path --mixed "$CLASSPATH")
+ JAVACMD=$(cygpath --unix "$JAVACMD")
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
+ SEP=""
+ for dir in $ROOTDIRSRAW; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ]; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@"; do
+ CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -)
+ CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition
+ eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg")
+ else
+ eval $(echo args$i)="\"$arg\""
+ fi
+ i=$((i + 1))
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save() {
+ for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/android_gradle/gradlew.bat b/android_gradle/gradlew.bat
new file mode 100644
index 0000000..e95643d
--- /dev/null
+++ b/android_gradle/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android_gradle/settings.gradle b/android_gradle/settings.gradle
new file mode 100644
index 0000000..9a979bb
--- /dev/null
+++ b/android_gradle/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name='Amber'
+include ':app'
diff --git a/android_test/jni/Application.mk b/android_test/jni/Application.mk
index e17fb4c..549f512 100644
--- a/android_test/jni/Application.mk
+++ b/android_test/jni/Application.mk
@@ -15,5 +15,4 @@
APP_ABI := arm64-v8a # armeabi-v7a x86 x86_64
APP_BUILD_SCRIPT := Android.mk
APP_STL := c++_static
-APP_PLATFORM := android-14
-NDK_TOOLCHAIN_VERSION := 4.9
+APP_PLATFORM := android-24 # minimal version to get libvulkan
diff --git a/android_test/test.cc b/android_test/test.cc
index 81c50f3..ef14800 100644
--- a/android_test/test.cc
+++ b/android_test/test.cc
@@ -17,5 +17,5 @@
#include "amber/amber.h"
void android_main(struct android_app* /*state*/) {
- amber::Amber amber;
+ amber::Amber amber(nullptr);
}
diff --git a/docs/amber_script.md b/docs/amber_script.md
index 55d87db..57e0632 100644
--- a/docs/amber_script.md
+++ b/docs/amber_script.md
@@ -34,8 +34,21 @@ DEVICE_FEATURE VariablePointerFeatures.variablePointersStorageBuffer
```
Currently each of the items in `VkPhysicalDeviceFeatures` are recognized along
-with `VariablePointerFeatures.variablePointers` and
-`VariablePointerFeatures.variablePointersStorageBuffer`.
+with:
+ * `VariablePointerFeatures.variablePointers`
+ * `VariablePointerFeatures.variablePointersStorageBuffer`
+ * `Float16Int8Features.shaderFloat16`
+ * `Float16Int8Features.shaderInt8`
+ * `Storage8BitFeatures.storageBuffer8BitAccess`
+ * `Storage8BitFeatures.uniformAndStorageBuffer8BitAccess`
+ * `Storage8BitFeatures.storagePushConstant8`
+ * `Storage16BitFeatures.storageBuffer16BitAccess`
+ * `Storage16BitFeatures.uniformAndStorageBuffer16BitAccess`
+ * `Storage16BitFeatures.storagePushConstant16`
+ * `Storage16BitFeatures.storageInputOutput16`
+ * `SubgroupSizeControl.subgroupSizeControl`
+ * `SubgroupSizeControl.computeFullSubgroups`
+
Extensions can be enabled with the `DEVICE_EXTENSION` and `INSTANCE_EXTENSION`
commands.
@@ -58,8 +71,63 @@ set of data types.
SET ENGINE_DATA {engine data variable} {value}*
```
+### Virtual File Store
+
+Each amber script contains a virtual file system that can store files of textual
+data. This lets you bundle multiple source files into a single, hermetic amber
+script file.
+
+Virtual files are declared using the `VIRTUAL_FILE` command:
+
+```groovy
+VIRTUAL_FILE {path}
+ {file-content}
+END
+```
+
+Paths must be unique.
+
+Shaders can directly reference these virtual files for their source. \
+HLSL shaders that `#include` other `.hlsl` files will first check the virtual
+file system, before falling back to the standard file system.
+
### Shaders
+Shader programs are declared using the `SHADER` command. \
+Shaders can be declared as `PASSTHROUGH`, with inlined source or using source
+from a `VIRTUAL_FILE`.
+
+Pass-through shader:
+
+```groovy
+# Creates a passthrough vertex shader. The shader passes the vec4 at input
+# location 0 through to the `gl_Position`.
+SHADER vertex {shader_name} PASSTHROUGH
+```
+
+Shader using inlined source:
+
+```groovy
+# Creates a shader of |shader_type| with the given |shader_name|. The shader
+# will be of |shader_format|. The shader source then follows and is terminated
+# with the |END| tag.
+SHADER {shader_type} {shader_name} {shader_format} [ TARGET_ENV {target_env} ]
+{shader_source}
+END
+```
+
+Shader using source from `VIRTUAL_FILE`:
+
+```groovy
+# Creates a shader of |shader_type| with the given |shader_name|. The shader
+# will be of |shader_format|. The shader will use the virtual file with |path|.
+SHADER {shader_type} {shader_name} {shader_format} [ TARGET_ENV {target_env} ] VIRTUAL_FILE {path}
+```
+
+`{shader_name}` is used to identify the shader to attach to `PIPELINE`s,
+
+`{shader_type}` and `{shader_format}` are described below:
+
#### Shader Type
* `vertex`
* `fragment`
@@ -82,23 +150,44 @@ types, but in that case must only provide a single shader type in the module.
#### Shader Format
* `GLSL`  (with glslang)
- * `HLSL`  (with dxc or glslang if dxc disabled) -- future
- * `SPIRV-ASM` (with spirv-as)
+ * `HLSL`  (with dxc or glslang if dxc disabled)
+ * `SPIRV-ASM` (with spirv-as; specifying `TARGET_ENV` is _highly recommended_
+ in this case, as explained below)
* `SPIRV-HEX` (decoded straight to SPIR-V)
* `OPENCL-C` (with clspv)
-```groovy
-# Creates a passthrough vertex shader. The shader passes the vec4 at input
-# location 0 through to the `gl_Position`.
-SHADER vertex {shader_name} PASSTHROUGH
+### Target environment
-# Creates a shader of |shader_type| with the given |shader_name|. The shader
-# will be of |shader_format|. The shader should then be inlined before the
-# |END| tag.
-SHADER {shader_type} {shader_name} {shader_format}
-...
-END
-```
+Specifying `TARGET_ENV` is optional and can be used to select a target
+SPIR-V environment. For example:
+
+ * `spv1.0`
+ * `spv1.5`
+ * `vulkan1.0`
+ * `vulkan1.2`
+
+Check the help text of the corresponding tool (e.g. spirv-as, glslangValidator)
+for the full list. The `SPIRV-HEX` shader format is not affected by the target
+environment.
+
+The specified target environment for the shader overrides the default (`spv1.0`)
+or the one specified on the command line.
+
+Specifying the target environment when using the `SPIRV-ASM` shader format
+is _highly recommended_, otherwise the SPIR-V version of the final SPIR-V binary
+shader passed to the graphics device might not be what you expect.
+Typically, SPIR-V assembly text will contain a comment near the beginning similar
+to `; Version: 1.0` but this is _ignored_ by the spirv-as assembler.
+Thus, you should specify the equivalent target environment (e.g. `spv1.0`)
+in the `SHADER` command.
+
+Specifying the target environment for other shader formats depends on whether
+you want to vary the final SPIR-V shader binary based on the target environment
+specified on the command line. For example, you could write one AmberScript file
+that contains a GLSL shader without specifying a target environment.
+You could then run the AmberScript file several times with different
+target environments specified on the command line
+(`spv1.0`, `spv1.1`, `spv1.2`, etc.) to test the different SPIR-V shader variants.
### Buffers
@@ -114,35 +203,95 @@ either image buffers or, what the target API would refer to as a buffer.
* `uint16`
* `uint32`
* `uint64`
+ * `float16`
* `float`
* `double`
* vec[2,3,4]{type}
* mat[2,3,4]x[2,3,4]{type} (mat<columns>x<rows>)
* Any of the `Image Formats` listed below.
+ * For any of the non-Image Formats types above appending '[]' will treat the
+ data as an array. e.g. int8[], vec2<float>[]
Sized arrays and structures are not currently representable.
```groovy
-# Filling the buffer with a given set of data. The values must be
-# of |type| data. The data can be provided as the type or as a hex value.
-# Buffers are STD430 by default.
-BUFFER {name} DATA_TYPE {type} {STD140 | STD430} DATA
-_value_+
-END
+# Filling the buffer with a given initializer. Initializer data must be
+# of |type|. Buffers are STD430 by default.
+BUFFER {name} DATA_TYPE {type} {STD140 | STD430} {initializer}
# Defines a buffer which is filled with data as specified by the `initializer`.
BUFFER {name} DATA_TYPE {type} {STD140 | STD430} SIZE _size_in_items_ \
{initializer}
+# Deprecated
+# Defines a buffer with width and height and filled by data as specified by the
+# `initializer`.
+BUFFER {name} DATA_TYPE {type} {STD140 | STD430} WIDTH {w} HEIGHT {h} \
+ {initializer}
+
+# Defines a buffer which is filled with binary data from a file specified
+# by `FILE`.
+BUFFER {name} DATA_TYPE {type} {STD140 | STD430} SIZE _size_in_items_ \
+ FILE BINARY {file_name}
+
+# Defines a buffer which is filled with text data parsed from a file specified
+# by `FILE`.
+BUFFER {name} DATA_TYPE {type} {STD140 | STD430} SIZE _size_in_items_ \
+ FILE TEXT {file_name}
+
# Creates a buffer which will store the given `FORMAT` of data. These
# buffers are used as image and depth buffers in the `PIPELINE` commands.
# The buffer will be sized based on the `RENDER_SIZE` of the `PIPELINE`.
-BUFFER {name} FORMAT {format_string}
+# For multisampled images use value greater than one for `SAMPLES`. Allowed
+# sample counts are 1, 2, 4, 8, 16, 32, and 64. Note that Amber doesn't
+# preserve multisampled images across pipelines.
+BUFFER {name} FORMAT {format_string} \
+ [ MIP_LEVELS _mip_levels_ (default 1) ] \
+ [ SAMPLES _samples_ (default 1) ]
+
+# Load buffer data from a PNG image with file name specified by `FILE`.
+# The file path is relative to the script file being run. Format specified
+# by `FORMAT` must match the image format.
+BUFFER {name} FORMAT {format_string} FILE PNG {file_name.png}
+```
+
+#### Images
+
+An AmberScript image is a specialized buffer that specifies image-specific
+attributes.
+
+##### Dimensionality
+ * `DIM_1D` -- A 1-dimensional image
+ * `DIM_2D` -- A 2-dimensional image
+ * `DIM_3D` -- A 3-dimensional image
+
+```groovy
+# Specify an image buffer with a format. HEIGHT is necessary for DIM_2D and
+# DIM_3D. DEPTH is necessary for DIM_3D.
+IMAGE {name} FORMAT {format_string} [ MIP_LEVELS _mip_levels_ (default 1) ] \
+ [ SAMPLES _samples_ (default 1) ] \
+ {dimensionality} \
+ WIDTH {w} [ HEIGHT {h} [ DEPTH {d} ] ] \
+ {initializer}
+
+# Specify an image buffer with a data type. HEIGHT is necessary for DIM_2D and
+# DIM_3D. DEPTH is necessary for DIM_3D.
+IMAGE {name} DATA_TYPE {type} {dimensionality} \
+ WIDTH {w} [ HEIGHT {h} [ DEPTH {d} ] ] \
+ {intializer}
```
#### Buffer Initializers
```groovy
+# Filling the buffer with a given set of data. The values must be
+# of the correct type. The data can be provided as the type or as a hex
+# value.
+DATA
+_value_+
+END
+
+```groovy
# Fill the buffer with a single value.
FILL _value_
@@ -163,6 +312,57 @@ SERIES_FROM _start_ INC_BY _inc_
COPY {buffer_from} TO {buffer_to}
```
+### Samplers
+
+Samplers are used for sampling buffers that are bound to a pipeline as
+sampled image or combined image sampler.
+
+#### Filter types
+ * `nearest`
+ * `linear`
+
+#### Address modes
+ * `repeat`
+ * `mirrored_repeat`
+ * `clamp_to_edge`
+ * `clamp_to_border`
+ * `mirrored_clamp_to_edge`
+
+#### Border colors
+ * `float_transparent_black`
+ * `int_transparent_black`
+ * `float_opaque_black`
+ * `int_opaque_black`
+ * `float_opaque_white`
+ * `int_opaque_white`
+
+```groovy
+
+# Creates a sampler with |name|.
+SAMPLER {name} \
+ [ MAG_FILTER {filter_type} (default nearest) ] \
+ [ MIN_FILTER {filter_type} (default nearest) ] \
+ [ ADDRESS_MODE_U {address_mode} (default repeat) ] \
+ [ ADDRESS_MODE_V {address_mode} (default repeat) ] \
+ [ ADDRESS_MODE_W {address_mode} (default repeat) ] \
+ [ BORDER_COLOR {border_color} (default float_transparent_black) ] \
+ [ MIN_LOD _val_ (default 0.0) ] \
+ [ MAX_LOD _val_ (default 1.0) ] \
+ [ NORMALIZED_COORDS | UNNORMALIZED_COORDS (default NORMALIZED_COORDS) ]
+```
+
+Note: unnormalized coordinates will override MIN\_LOD and MAX\_LOD to 0.0.
+
+#### OpenCL Literal Samplers
+
+Literal constant samplers defined in the OpenCL program are automatically
+generated and bound to the pipeline in Amber.
+
+Note: currently the border color is always transparent black.
+
+Note: the addressing mode is used for all coordinates currently. Arrayed images
+should use `clamp_to_edge` for the array index.
+
### Pipelines
#### Pipeline type
@@ -186,15 +386,11 @@ END
The following commands are all specified within the `PIPELINE` command.
```groovy
- # Attach the shader provided by |name_of_shader| to the pipeline and set
- # the entry point to be |name|. The provided shader for ATTACH must _not_ be
- # a 'multi' shader.
- ATTACH {name_of_shader} ENTRY_POINT {name}
-
- # Attach the shader provided by |name_of_shader| to the pipeline and set
- # the entry point to be 'main'. The provided shader for ATTACH must _not_ be
+ # Attach the shader provided by |name_of_shader| to the pipeline with an
+ # entry point name of |name|. The provided shader for ATTACH must _not_ be
# a 'multi' shader.
- ATTACH {name_of_shader}
+ ATTACH {name_of_shader} \
+ [ ENTRY_POINT {name} (default "main") ]
# Attach a 'multi' shader to the pipeline of |shader_type| and use the entry
# point with |name|. The provided shader _must_ be a 'multi' shader.
@@ -203,8 +399,10 @@ The following commands are all specified within the `PIPELINE` command.
# Attach specialized shader. Specialization can be specified multiple times.
# Specialization values must be a 32-bit type. Shader type and entry point
# must be specified prior to specializing the shader.
- ATTACH {name_of_shader} SPECIALIZE 1 AS uint32 4
- ATTACH {name_of_shader} SPECIALIZE 1 AS uint32 4 SPECIALIZE 4 AS float 1.0
+ ATTACH {name_of_shader} SPECIALIZE _id_ AS uint32 _value_
+ ATTACH {name_of_shader} \
+ SPECIALIZE _id_ AS uint32 _value_ \
+ SPECIALIZE _id_ AS float _value_
```
```groovy
@@ -224,28 +422,107 @@ The following commands are all specified within the `PIPELINE` command.
```
```groovy
+ # Set the polygon mode used for all drawing with the pipeline.
+ # |mode| is fill, line, or point and it defaults to fill.
+ POLYGON_MODE {mode}
+```
+
+#### Compare operations
+ * `never`
+ * `less`
+ * `equal`
+ * `less_or_equal`
+ * `greater`
+ * `not_equal`
+ * `greater_or_equal`
+ * `always`
+
+```groovy
+ # Set depth test settings. All enable options are specified with keywords on and off.
+ # BOUNDS and BIAS values are specified with decimal numbers. |compare_op| is selected
+ # from the list of compare operations above.
+ DEPTH
+ TEST {test_enable}
+ WRITE {write_enable}
+ COMPARE_OP {compare_op}
+ CLAMP {clamp_enable}
+ BOUNDS min {bound_min} max {bounds_max}
+ BIAS constant {bias_constant} clamp {bias_clamp} slope {bias_slope}
+ END
+```
+
+#### Stencil operations
+ * `keep`
+ * `replace`
+ * `increment_and_clamp`
+ * `decrement_and_clamp`
+ * `invert`
+ * `increment_and_wrap`
+ * `decrement_and_wrap`
+
+```groovy
+ # Set stencil test settings. |face| can be front, back, or front_and_back.
+ # |test_enable| is either on or off and affects both faces. |fail_op|, |pass_op|,
+ # and |depth_fail_op| are selected from the stencil operations table above,
+ # and |compare_op| from the compare operations table. |compare_mask|, |write_mask|,
+ # and |reference| are 8bit unsigned integer values (range 0..255).
+ STENCIL {face}
+ TEST {test_enable}
+ FAIL_OP {fail_op}
+ PASS_OP {pass_op}
+ DEPTH_FAIL_OP {depth_fail_op}
+ COMPARE_OP {compare_op}
+ COMPARE_MASK {compare_mask}
+ WRITE_MASK {write_mask}
+ REFERENCE {reference}
+ END
+```
+
+```groovy
# Set the size of the render buffers. |width| and |height| are integers and
# default to 250x250.
FRAMEBUFFER_SIZE _width_ _height_
```
+```groovy
+ # Set subgroup size control setting. Require that subgroups must be launched
+ # with all invocations active for given shader. Allow SubgroupSize to vary
+ # for given shader. Require a specific SubgroupSize the for given shader.
+ # |fully_populated_enable| and |varying_size_enable| can be on or off.
+ # |subgroup_size| can be set one of the values below:
+ # - a power-of-two integer that _must_ be greater or equal to minSubgroupSize
+ # and be less than or equal to maxSubgroupSize
+ # - MIN to set the required subgroup size to the minSubgroupSize
+ # - MAX to set the required subgroup size to the maxSubgroupSize
+ SUBROUP {name_of_shader}
+ FULLY_POPULATED {fully_populated_enable}
+ VARYING_SIZE {varying_size_enable}
+ REQUIRED_SIZE {subgroup_size}
+ END
+```
+
### Pipeline Buffers
#### Buffer Types
* `uniform`
* `storage`
+ * `uniform_dynamic`
+ * `storage_dynamic`
+ * `uniform_texel_buffer`
+ * `storage_texel_buffer`
TODO(dsinclair): Sync the BufferTypes with the list of Vulkan Descriptor types.
-A `pipeline` can have buffers bound. This includes buffers to contain image
-attachment content, depth/stencil content, uniform buffers, etc.
+A `pipeline` can have buffers or samplers bound. This includes buffers to
+contain image attachment content, depth/stencil content, uniform buffers, etc.
```groovy
# Attach |buffer_name| as an output color attachment at location |idx|.
# The provided buffer must be a `FORMAT` buffer. If no color attachments are
# provided a single attachment with format `B8G8R8A8_UNORM` will be created
- # for graphics pipelines.
- BIND BUFFER {buffer_name} AS color LOCATION _idx_
+ # for graphics pipelines. The MIP level will have a base of |level|.
+ BIND BUFFER {buffer_name} AS color LOCATION _idx_ \
+ [ BASE_MIP_LEVEL _level_ (default 0) ]
# Attach |buffer_name| as the depth/stencil buffer. The provided buffer must
# be a `FORMAT` buffer. If no depth/stencil buffer is specified a default
@@ -255,31 +532,75 @@ attachment content, depth/stencil content, uniform buffers, etc.
# Attach |buffer_name| as the push_constant buffer. There can be only one
# push constant buffer attached to a pipeline.
- BIND BUFFER <buffer_name> AS push_constant
-
- # Bind the buffer of the given |buffer_type| at the given descriptor set
- # and binding. The buffer will use a start index of 0.
- BIND BUFFER {buffer_name} AS {buffer_type} DESCRIPTOR_SET _id_ \
- BINDING _id_
-
- # Bind the sampler at the given descriptor set and binding.
- BIND SAMPLER {sampler_name} DESCRIPTOR_SET _id_ BINDING _id_
+ BIND BUFFER {buffer_name} AS push_constant
# Bind OpenCL argument buffer by name. Specifying the buffer type is optional.
# Amber will set the type as appropriate for the argument buffer. All uses
# of the buffer must have a consistent |buffer_type| across all pipelines.
- BIND BUFFER {buffer_name} [AS {buffer_type}] KERNEL ARG_NAME _name_
+ BIND BUFFER {buffer_name} [ AS {buffer_type} (default computed)] \
+ KERNEL ARG_NAME _name_
# Bind OpenCL argument buffer by argument ordinal. Arguments use 0-based
# numbering. Specifying the buffer type is optional. Amber will set the
# type as appropriate for the argument buffer. All uses of the buffer
# must have a consistent |buffer_type| across all pipelines.
- BIND BUFFER {buffer_name} [AS {buffer_type}] KERNEL ARG_NUMBER _number_
+ BIND BUFFER {buffer_name} [ AS {buffer_type} (default computed)] \
+ KERNEL ARG_NUMBER _number_
+
+ # Bind OpenCL argument sampler by argument name.
+ BIND SAMPLER {sampler_name} KERNEL ARG_NAME _name_
+
+ # Bind OpenCL argument sampler by argument ordinal. Arguments use 0-based
+ # numbering.
+ BIND SAMPLER {sampler_name} KERNEL ARG_NUMBER _number_
```
+All BIND BUFFER and BIND SAMPLER commands below define a descriptor set and binding ID.
+These commands can be replaced with BIND BUFFER_ARRAY and BIND SAMPLER_ARRAY commands.
+In these cases multiple buffer or sampler names need to be provided, separated by spaces.
+This creates a descriptor array of buffers or samplers bound to the same descriptor set
+and binding ID. An array of offsets should be provided via `OFFSET offset1 offset2 ...`
+when using dynamic buffers with BUFFER_ARRAY.
```groovy
- # Set |buffer_name| as the vertex data at location |val|.
- VERTEX_DATA {buffer_name} LOCATION _val_
+ # Bind the buffer of the given |buffer_type| at the given descriptor set
+ # and binding. The buffer will use a start index of 0.
+ BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS {buffer_type} DESCRIPTOR_SET _id_ \
+ BINDING _id_
+
+ # Attach |buffer_name| as a storage image. The MIP level will have a base
+ # value of |level|.
+ BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS storage_image \
+ DESCRIPTOR_SET _id_ BINDING _id_ [ BASE_MIP_LEVEL _level_ (default 0) ]
+
+ # Attach |buffer_name| as a sampled image. The MIP level will have a base
+ # value of |level|.
+ BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS sampled_image \
+ DESCRIPTOR_SET _id_ BINDING _id_ [ BASE_MIP_LEVEL _level_ (default 0) ]
+
+ # Attach |buffer_name| as a combined image sampler. A sampler |sampler_name|
+ # must also be specified. The MIP level will have a base value of 0.
+ BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS combined_image_sampler SAMPLER {sampler_name} \
+ DESCRIPTOR_SET _id_ BINDING _id_ [ BASE_MIP_LEVEL _level_ (default 0) ]
+
+ # Bind the sampler at the given descriptor set and binding.
+ BIND {SAMPLER | SAMPLER_ARRAY} {sampler_name} DESCRIPTOR_SET _id_ BINDING _id_
+
+ # Bind |buffer_name| as dynamic uniform/storage buffer at the given descriptor set
+ # and binding. The buffer will use a start index of |offset|.
+ BIND {BUFFER | BUFFER_ARRAY} {buffer_name} AS {uniform_dynamic | storage_dynamic} \
+ DESCRIPTOR_SET _id_ BINDING _id_ OFFSET _offset_
+```
+
+```groovy
+ # Set |buffer_name| as the vertex data at location |val|. RATE defines the
+ # input rate for vertex attribute reading. OFFSET sets the byte offset for the
+ # vertex data within the buffer |buffer_name|, which by default is 0. FORMAT
+ # sets the vertex buffer format, which by default is the format of the buffer
+ # |buffer_name|. STRIDE sets the byte stride, which by default is the stride
+ # of the format (set explicitly via FORMAT or from the format of the buffer
+ # |buffer_name|).
+ VERTEX_DATA {buffer_name} LOCATION _val_ [ RATE { vertex | instance } (default vertex) ] \
+ [ FORMAT {format} ] [ OFFSET {offset} ] [ STRIDE {stride} ]
# Set |buffer_name| as the index data to use for `INDEXED` draw commands.
INDEX_DATA {buffer_name}
@@ -300,17 +621,17 @@ buffers for the pipeline populated with the specified data.
```
#### Topologies
- * `point_list`
- * `line_list`
- * `line_list_with_adjacency`
- * `line_strip`
- * `line_strip_with_adjacency`
- * `triangle_list`
- * `triangle_list_with_adjacency`
- * `triangle_strip`
- * `triangle_strip_with_adjacency`
- * `triangle_fan`
- * `patch_list`
+ * `POINT_LIST`
+ * `LINE_LIST`
+ * `LINE_LIST_WITH_ADJACENCY`
+ * `LINE_STRIP`
+ * `LINE_STRIP_WITH_ADJACENCY`
+ * `TRIANGLE_LIST`
+ * `TRIANGLE_LIST_WITH_ADJACENCY`
+ * `TRIANGLE_STRIP`
+ * `TRIANGLE_STRIP_WITH_ADJACENCY`
+ * `TRIANGLE_fan`
+ * `PATCH_LIST`
### Run a pipeline.
@@ -323,14 +644,17 @@ To run an indexed draw, attach the index data to the `PIPELINE` with an
For the commands which take a `START_IDX` and a `COUNT` they can be left off the
command (although, `START_IDX` is required if `COUNT` is provided). The default
value for `START_IDX` is 0. The default value for `COUNT` is the item count of
-vertex buffer minus the `START_IDX`.
+vertex buffer minus the `START_IDX`. The same applies to `START_INSTANCE`
+(default 0) and `INSTANCE_COUNT` (default 1).
```groovy
# Run the given |pipeline_name| which must be a `compute` pipeline. The
# pipeline will be run with the given number of workgroups in the |x|, |y|, |z|
# dimensions. Each of the x, y and z values must be a uint32.
RUN {pipeline_name} _x_ _y_ _z_
+```
+```groovy
# Run the given |pipeline_name| which must be a `graphics` pipeline. The
# rectangle at |x|, |y|, |width|x|height| will be rendered. Ignores VERTEX_DATA
# and INDEX_DATA on the given pipeline.
@@ -340,43 +664,45 @@ RUN {pipeline_name} \
```
```groovy
-# Run the |pipeline_name| which must be a `graphics` pipeline. The vertex
-# data must be attached to the pipeline. A start index of 0 will be used
-# and a count of the number of elements in the vertex buffer.
-RUN {pipeline_name} DRAW_ARRAY AS {topology}
-
-# Run the |pipeline_name| which must be a `graphics` pipeline. The vertex
-# data must be attached to the pipeline. A start index of |value| will be used
-# and a count of the number of items from |value| to the end of the vertex
-# buffer.
-RUN {pipeline_name} DRAW_ARRAY AS {topology} START_IDX _value_
-
-# Run the |pipeline_name| which must be a `graphics` pipeline. The vertex
-# data must be attached to the pipeline. A start index of |value| will be used
-# and a count |count_value| will be used.
-RUN {pipeline_name} DRAW_ARRAY AS {topology} START_IDX _value_ \
- COUNT _count_value_
+# Run the given |pipeline_name| which must be a `graphics` pipeline. The
+# grid at |x|, |y|, |width|x|height|, |columns|x|rows| will be rendered.
+# Ignores VERTEX_DATA and INDEX_DATA on the given pipeline.
+# For columns, rows of (5, 4) a total of 5*4=20 rectangles will be drawn.
+RUN {pipeline_name} \
+  DRAW_GRID POS _x_in_pixels_ _y_in_pixels_ \
+  SIZE _width_in_pixels_ _height_in_pixels_ \
+ CELLS _columns_of_cells_ _rows_of_cells_
```
```groovy
# Run the |pipeline_name| which must be a `graphics` pipeline. The vertex
-# data and index data must be attached to the pipeline. The vertices will be
-# drawn using the given |topology|. A start index of 0 will be used and the
-# count will be determined by the size of the index data buffer.
-RUN {pipeline_name} DRAW_ARRAY AS {topology} INDEXED
-
-# Run the |pipeline_name| which must be a `graphics` pipeline. The vertex
-# data and index data must be attached to the pipeline. The vertices will be
-# drawn using the given |topology|. A start index of |value| will be used and
-# the count will be determined by the size of the index data buffer.
-RUN {pipeline_name} DRAW_ARRAY AS {topology} INDEXED START_IDX _value_
+# data must be attached to the pipeline.
+
+# A start index of |value| will be used and the count of |count_value| items
+# will be processed. The draw is instanced if |inst_count_value| is greater
+# than one. In case of instanced draw |inst_value| controls the starting
+# instance ID.
+RUN {pipeline_name} DRAW_ARRAY AS {topology} \
+ [ START_IDX _value_ (default 0) ] \
+ [ COUNT _count_value_ (default vertex_buffer size - start_idx) ] \
+ [ START_INSTANCE _inst_value_ (default 0) ] \
+ [ INSTANCE_COUNT _inst_count_value_ (default 1) ]
+```
+```groovy
# Run the |pipeline_name| which must be a `graphics` pipeline. The vertex
# data and index data must be attached to the pipeline. The vertices will be
-# drawn using the given |topology|. A start index of |value| will be used and
-# the count of |count_value| items will be processed.
-RUN {pipeline_name} DRAW_ARRAY AS {topology} INDEXED \
- START_IDX _value_ COUNT _count_value_
+# drawn using the given |topology|.
+#
+# A start index of |value| will be used and the count of |count_value| items
+# will be processed. The draw is instanced if |inst_count_value| is greater
+# than one. In case of instanced draw |inst_value| controls the starting
+# instance ID.
+RUN {pipeline_name} DRAW_ARRAY AS {topology} INDEXED \
+ [ START_IDX _value_ (default 0) ] \
+ [ COUNT _count_value_ (default index_buffer size - start_idx) ] \
+ [ START_INSTANCE _inst_value_ (default 0) ] \
+ [ INSTANCE_COUNT _inst_count_value_ (default 1) ]
```
### Repeating commands
@@ -392,6 +718,8 @@ END
The commands which can be used inside a `REPEAT` block are:
* `CLEAR`
* `CLEAR_COLOR`
+ * `CLEAR_DEPTH`
+ * `CLEAR_STENCIL`
* `COPY`
* `EXPECT`
* `RUN`
@@ -400,9 +728,17 @@ The commands which can be used inside a `REPEAT` block are:
```groovy
# Sets the clear color to use for |pipeline| which must be a graphics
-# pipeline. The colors are integers from 0 - 255.
+# pipeline. The colors are integers from 0 - 255. Defaults to (0, 0, 0, 0)
CLEAR_COLOR {pipeline} _r (0 - 255)_ _g (0 - 255)_ _b (0 - 255)_ _a (0 - 255)_
+# Sets the depth clear value to use for |pipeline| which must be a graphics
+# pipeline. |value| must be a decimal number.
+CLEAR_DEPTH {pipeline} _value_
+
+# Sets the stencil clear value to use for |pipeline| which must be a graphics
+# pipeline. |value| must be an integer from 0 - 255.
+CLEAR_STENCIL {pipeline} _value_
+
# Instructs the |pipeline| which must be a graphics pipeline to execute the
# clear command.
CLEAR {pipeline}
@@ -676,7 +1012,7 @@ END  # pipeline
CLEAR_COLOR kGraphicsPipeline 255 0 0 255
CLEAR kGraphicsPipeline
-RUN kGraphicsPipeline DRAW_ARRAY AS triangle_list START_IDX 0 COUNT 24
+RUN kGraphicsPipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 24
```
### OpenCL-C Shaders
diff --git a/docs/debugger.md b/docs/debugger.md
new file mode 100644
index 0000000..6c3208b
--- /dev/null
+++ b/docs/debugger.md
@@ -0,0 +1,192 @@
+# Debugger Testing
+
+This document describes Amber's shader debugger testing framework, which allows developers to write tests for Vulkan drivers that expose shader debugging functionality via the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/).
+
+---
+**Work In Progress**
+
+Note that shader debugging is very much work-in-progress:
+
+* Vulkan shader debugging currently does not have a formalized specification. A shader debugger implementation is being developed in [SwiftShader](https://swiftshader.googlesource.com/SwiftShader/), which one day may become a reference implementation for a formal specifiction.
+* Currently SwiftShader is the only Vulkan driver to implement a [DAP based shader debugger](https://swiftshader.googlesource.com/SwiftShader/+/refs/heads/master/docs/VulkanShaderDebugging.md). This implementation is also work-in-progress, and may significantly change.
+* Currently the debugger connection uses localhost sockets to connect Amber to the driver. The `VK_DEBUGGER_PORT` environment variable must be set to an unused localhost port number before attempting to run any Amber scripts that use the `DEBUG` command.
+* [`OpenCL.DebugInfo.100`](https://www.khronos.org/registry/spir-v/specs/unified1/OpenCL.DebugInfo.100.mobile.html) is a SPIR-V extended instruction set that adds rich debug information to the shader program, allowing for high-level shader source debugging. `OpenCL.DebugInfo.100` is not currently generated by [DXC](https://github.com/microsoft/DirectXShaderCompiler) or [glslang](https://github.com/KhronosGroup/glslang), but initial work has started to try and add `OpenCL.DebugInfo.100` support to DXC.
+* `OpenCL.DebugInfo.100` insstructions are not currently preserved by many of the [SPIR-V Tools](https://github.com/KhronosGroup/SPIRV-Tools) optimization passes, so these optimizations should not currently be used.
+* `OpenCL.DebugInfo.100` may be incorrectly interpreted by the [SPIR-V Tools](https://github.com/KhronosGroup/SPIRV-Tools) validator, so Amber should currently be invoked with the `--disable-spirv-val` flag.
+
+---
+
+## Usage
+
+### Building
+
+The debugger testing functionality is disabled by default, and has to be enabled with the `AMBER_ENABLE_VK_DEBUGGING` CMake flag.
+
+As SwiftShader is currently the only Vulkan driver that supports DAP-based shader debugging, you will also likely want to build SwiftShader as part of Amber, using the `AMBER_ENABLE_SWIFTSHADER` flag.
+
+Both of these can be set by running CMake with:
+
+```
+cmake <path-to-amber-root> -DAMBER_ENABLE_SWIFTSHADER=1 -DAMBER_ENABLE_VK_DEBUGGING=1
+```
+
+### AmberScript
+
+Debugger tests must be written in AmberScript.
+
+The `DEBUG` command extends the [`RUN` command](amber_script.md#Run-a-pipeline) to execute a draw or compute shader.
+`DEBUG` expects exactly the same arguments to follow as `RUN` ([see: 'Run a pipeline'](amber_script.md#Run-a-pipeline)). However, unlike `RUN`, `DEBUG` begins a block that must be terminated with an `END`.
+
+Within this `RUN` block, you may declare any number of `THREAD` command blocks that list a sequence of debugger commands to perform on a single compute, vertex or fragment shader invocation:
+
+* `THREAD GLOBAL_INVOCATION_ID` \<x\> \<y\> \<z\>
+
+ Declares a sequence of debugger commands to run when the compute shader invocation with the given global invocation identifier is executed.
+
+* `THREAD VERTEX_INDEX` \<index\>
+
+ Declares a sequence of debugger commands to run when the shader invocation for the vertex with the given index is executed.
+
+* `THREAD FRAGMENT_WINDOW_SPACE_POSITION` \<x\> \<y\>
+
+ Defines a sequence of debugger commands to run when the shader invocation for the fragment with the given window space position is executed.
+
+Each of the `THREAD` commands begins a block that must be terminated with an `END`.
+
+Within each `THREAD` command block, you may use any of the following commands to control execution of the shader, and to verify the debugger's behaviour:
+
+* `STEP_IN`
+
+ Single line step execution of the thread, stepping into any function calls.
+
+* `STEP_OVER`
+
+ Single line step execution of the thread, stepping over any function calls.
+
+* `STEP_OUT`
+
+ Run, without stopping to the end of the currently executing function. If the current function is not the top most of the call stack, then the debugger will pause at the next line after the function call.
+
+* `EXPECT LOCATION` \<file name\> \<line number\> [\<line source\>]
+
+ Verifies that the debugger is currently paused at the given line location.
+ The [\<line source\>] is an additional, optional check that verifies the line of the file reported by the debugger is as expected.
+
+* `EXPECT CALLSTACK`
+
+ Verifies that the debugger is currently paused with the given complete stack frame.
+ Each frame must be declared on a separate line, starting with the most nested call, and has the form:
+
+ \<function name\> [\<file name\> [\<line number\>]]
+
+ The [\<file name\>] and [\<line number\>] fields are additional, optionals checks that verify the file and line numbers reported by the debugger for the frame are as expected.
+
+ The list of stack frames is terminated with `END`.
+
+* `EXPECT LOCAL` \<name\> `EQ` [\<value\>]
+
+ Verifies that the local variable with the given name holds the expected value. \<name\> may contain `.` delimiters to index structure or array types.
+
+Every shader invocation covered by a `THREAD` block must be executed.
+It is a test failure if the debugger does not break at all the `THREAD` shader invocations declared in the `DEBUG` block.
+
+#### Example:
+
+Given the following HLSL vertex shader:
+
+```hlsl
+/* 1 */ // simple_vs.hlsl
+/* 2 */ struct VS_OUTPUT {
+/* 3 */ float4 pos : SV_POSITION;
+/* 4 */ float4 color : COLOR;
+/* 5 */ };
+/* 6 */
+/* 7 */ VS_OUTPUT main(float4 pos : POSITION,
+/* 8 */ float4 color : COLOR) {
+/* 9 */ VS_OUTPUT vout;
+/* 10 */ vout.pos = pos;
+/* 11 */ vout.color = color;
+/* 12 */ return vout;
+/* 13 */ }
+```
+
+The following performs a basic debugger test for the 3rd vertex in the triangle list:
+
+```groovy
+DEBUG pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+ THREAD VERTEX_INDEX 2
+ // Debugger starts at line 9. Inspect input variables.
+ EXPECT LOCATION "simple_vs.hlsl" 9 " VS_OUTPUT vout;"
+ EXPECT LOCAL "pos.x" EQ -1.007874
+ EXPECT LOCAL "pos.y" EQ 1.000000
+ EXPECT LOCAL "pos.z" EQ 0.000000
+ EXPECT LOCAL "color.x" EQ 1.000000
+ EXPECT LOCAL "color.y" EQ 0.000000
+ EXPECT LOCAL "color.z" EQ 0.000000
+ // Step to line 10.
+ STEP_IN
+ EXPECT LOCATION "simple_vs.hlsl" 10 " vout.pos = pos;"
+ // Step to line 11 and read result of line 10.
+ STEP_IN
+ EXPECT LOCAL "vout.pos.x" EQ -1.007874
+ EXPECT LOCAL "vout.pos.y" EQ 1.000000
+ EXPECT LOCAL "vout.pos.z" EQ 0.000000
+ EXPECT LOCATION "simple_vs.hlsl" 11 " vout.color = color;"
+ // Step to line 12 and read result of line 11.
+ STEP_IN
+ EXPECT LOCAL "vout.color.x" EQ 1.000000
+ EXPECT LOCAL "vout.color.y" EQ 0.000000
+ EXPECT LOCAL "vout.color.z" EQ 0.000000
+ EXPECT LOCATION "simple_vs.hlsl" 12 " return vout;"
+ CONTINUE
+ END
+END
+```
+
+## Implementation
+
+This section covers the design of how the debugger testing is implemented in Amber.
+
+### Parsing
+
+`Parser::ParseDebug()` starts by immediately calling `ParseRun()`, which parses the tokens that normally immediately follow a `RUN` command. On successful parse, `ParseRun()` appends a command into the `command_list_` vector. \
+`ParseDebug()` then constructs a new `amber::debug::Script`, and parses the debugger command block. The `amber::debug::Script` is then assigned to the command at the back of the `command_list_` (added by `ParseRun()`).
+
+### `amber::debug::Script`
+
+`amber::debug::Script` implements the `amber::debug::Events` interface, and records the calls made on it.
+These calls can be replayed to another `amber::debug::Events`, using the `amber::debug::Script::Run(Events*)` method.
+
+### `amber::debug::Events`
+
+The `amber::debug::Events` represents the `THREAD` commands in the Amber script, providing methods that set breakpoints for particular shader invocations.
+
+`amber::debug::Events` is the interface implemented by `amber::debug::Script` for recording the parsed debugger script, and extended by the `amber::Engine::Debugger` interface, used to actually drive an engine debugger.
+
+The `amber::debug::Events` interface has a number of `BreakOn`XXX`()` methods that have parameters for the shader invocation of interest (global invocation ID, vertex index, etc) and a `OnThread` function callback parameter.
+
+The `OnThread` callback has the signature `void(amber::debug::Thread*)` which is called to control the debugger thread of execution and perform test verifications.
+
+### `amber::debug::Thread`
+
+`amber::debug::Thread` is the interface used to control a debugger thread of execution, and represents the script commands within the `THREAD` script blocks.
+
+The following implementations of the `amber::debug::Thread` interface are returned by the `amber::debug::Script::BreakOn`XXX`()` methods:
+* `amber::debug::Script` implements this interface to record the `THREAD` commands in the Amber script, which will be replayed when calling `amber::debug::Script::Run(amber::debug::Events*)`.
+* `amber::Engine::Debugger` implementations will implement the `amber::debug::Thread` interface to actually control the debugger thread of execution and perform test verifications.
+
+### `amber::Engine::Debugger`
+
+The `amber::Engine::Debugger` interface extends the `amber::debug::Events` interface with a single `Flush()` method that ensures that all the previous event have been executed. `Flush()` returns a `amber::Result`, which holds the results of all the `amber::debug::Thread::Expect`XXX`()` calls.
+
+The `amber::Engine::Debugger` interface can be obtained from the `amber::Engine` using the `amber::Engine::GetDebugger()` method.
+
+### Debugger Execution
+
+`amber::Executor::Execute()` drives the `amber::Engine::Debugger`:
+
+* The before executing the first `amber::Command` that holds a `amber::debug::Script`, a `amber::Engine::Debugger` is obtained from the `amber::Engine`, and `amber::Engine::Debugger::Connect()` is called to create a connection to the Vulkan shader debugger.
+* If the `amber::Command` holds a `amber::debug::Script`, then this script is executed on the `amber::Engine::Debugger` using the `amber::debug::Script::Run(amber::debug::Events *)` method, before the Vulkan command is executed.
+* The command is then executed with `amber::Executor::ExecuteCommand()`
+* Once the command has completed, all debugger threads are synchronized and debugger test results are collected with a call to `amber::Engine::Debugger::Flush()`.
+* This process is repeated for all commands in the script. \ No newline at end of file
diff --git a/docs/engines.md b/docs/engines.md
index bca19e1..0334b05 100644
--- a/docs/engines.md
+++ b/docs/engines.md
@@ -168,6 +168,12 @@ at (x,y) of size (width,height). The buffers must be read back into the backing
`amber::Buffer` at the end of this method.
+#### `DoDrawGrid`
+The `DoDrawGrid` instructs the engine to draw the given pipeline in the box
+at (x,y) of size (width,height) split into cells (columns, rows). The buffers
+must be read back into the backing `amber::Buffer` at the end of this method.
+
+
#### `DoDrawArrays`
The `DoDrawArrays` instructs the engine to draw the given pipeline using
the information in the attached vertex and index buffers. The buffers must be
diff --git a/framebuffer.png b/framebuffer.png
new file mode 100644
index 0000000..74c3212
--- /dev/null
+++ b/framebuffer.png
Binary files differ
diff --git a/include/amber/amber.h b/include/amber/amber.h
index 1fffd38..0c679a3 100644
--- a/include/amber/amber.h
+++ b/include/amber/amber.h
@@ -50,7 +50,7 @@ struct EngineConfig {
virtual ~EngineConfig();
};
-/// Stores information for a biffer.
+/// Stores information for a buffer.
struct BufferInfo {
BufferInfo();
BufferInfo(const BufferInfo&);
@@ -70,6 +70,18 @@ struct BufferInfo {
std::vector<Value> values;
};
+/// Types of source file to load buffer data from.
+enum class BufferDataFileType : int8_t {
+ /// Unknown file type
+ kUnknown = -1,
+ /// A text file
+ kText = 0,
+ /// A binary file
+ kBinary,
+ /// A PNG file
+ kPng
+};
+
/// Delegate class for various hook functions
class Delegate {
public:
@@ -85,6 +97,10 @@ class Delegate {
virtual uint64_t GetTimestampNs() const = 0;
/// Tells whether to log each test as it's executed
virtual bool LogExecuteCalls() const = 0;
+ /// Loads buffer data from a file
+ virtual amber::Result LoadBufferData(const std::string file_name,
+ BufferDataFileType file_type,
+ amber::BufferInfo* buffer) const = 0;
};
/// Stores configuration options for Amber.
@@ -114,14 +130,12 @@ struct Options {
/// If true, disables SPIR-V validation. If false, SPIR-V shaders will be
/// validated using the Validator component (spirv-val) from SPIRV-Tools.
bool disable_spirv_validation;
- /// Delegate implementation
- Delegate* delegate;
};
/// Main interface to the Amber environment.
class Amber {
public:
- Amber();
+ explicit Amber(Delegate* delegate);
~Amber();
/// Parse the given |data| into the |recipe|.
@@ -145,6 +159,12 @@ class Amber {
amber::Result ExecuteWithShaderData(const amber::Recipe* recipe,
Options* opts,
const ShaderMap& shader_data);
+
+ /// Returns the delegate object.
+ Delegate* GetDelegate() const { return delegate_; }
+
+ private:
+ Delegate* delegate_;
};
} // namespace amber
diff --git a/include/amber/result.h b/include/amber/result.h
index 6e916be..c85c281 100644
--- a/include/amber/result.h
+++ b/include/amber/result.h
@@ -24,23 +24,30 @@ namespace amber {
class Result {
public:
/// Creates a result which succeeded.
- Result();
+ Result() = default;
+
/// Creates a result which failed and will return |err|.
explicit Result(const std::string& err);
- Result(const Result&);
- ~Result();
+ inline Result(const Result&) = default;
+ inline Result(Result&&) = default;
+
+ inline Result& operator=(const Result&) = default;
+ inline Result& operator=(Result&&) = default;
+
+ /// Adds the errors from |res| to this Result.
+ Result& operator+=(const Result& res);
- Result& operator=(const Result&);
+ /// Adds the error |err| to this Result.
+ Result& operator+=(const std::string& err);
/// Returns true if the result is a success.
- bool IsSuccess() const { return succeeded_; }
+ bool IsSuccess() const { return errors_.size() == 0; }
/// Returns the error string if |IsSuccess| is false.
- const std::string& Error() const { return error_; }
+ std::string Error() const;
private:
- bool succeeded_;
- std::string error_;
+ std::vector<std::string> errors_;
};
} // namespace amber
diff --git a/include/amber/shader_info.h b/include/amber/shader_info.h
index 713b18c..6c9126a 100644
--- a/include/amber/shader_info.h
+++ b/include/amber/shader_info.h
@@ -54,6 +54,10 @@ struct ShaderInfo {
std::string shader_source;
/// A list of SPIR-V optimization passes to execute on the shader.
std::vector<std::string> optimizations;
+ /// Target environment for the shader compilation.
+ std::string target_env;
+ /// The shader SPIR-V if it was compiled by Amber
+ std::vector<uint32_t> shader_data;
};
} // namespace amber
diff --git a/kokoro/android/build.sh b/kokoro/android/build.sh
index b3b2cad..e99704b 100644..100755
--- a/kokoro/android/build.sh
+++ b/kokoro/android/build.sh
@@ -15,16 +15,16 @@
set -e # Fail on error
set -x # Display commands as run
-BUILD_ROOT=$PWD
-SRC=$PWD/github/amber
-BUILD_TYPE=Release
+BUILD_ROOT="$PWD"
+SRC="$PWD/github/amber"
+BUILD_TYPE="Release"
-export ANDROID_NDK=/opt/android-ndk-r15c
-ANDROID_STL=c++_static
+export ANDROID_NDK="$BUILD_ROOT/android-ndk-r20"
+ANDROID_STL="c++_static"
ANDROID_PLATFORM="android-14"
ANDROID_ABI="armeabi-v7a with NEON"
-TOOLCHAIN_PATH=$ANDROID_NDK/build/cmake/android.toolchain.cmake
+TOOLCHAIN_PATH="$ANDROID_NDK/build/cmake/android.toolchain.cmake"
# removing the old version
echo y | sudo apt-get purge --auto-remove cmake
@@ -32,34 +32,39 @@ echo y | sudo apt-get purge --auto-remove cmake
# Installing the 3.10.2 version
wget http://www.cmake.org/files/v3.10/cmake-3.10.2.tar.gz
tar -xvzf cmake-3.10.2.tar.gz
-cd cmake-3.10.2/
+pushd cmake-3.10.2/
./configure
make
sudo make install
-echo $(date): $(cmake --version)
+echo "$(date): $(cmake --version)"
+popd
# Get NINJA.
wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip
unzip -q ninja-linux.zip
export PATH="$PWD:$PATH"
-cd $SRC
+# Get Android NDK.
+wget -q https://dl.google.com/android/repository/android-ndk-r20-linux-x86_64.zip
+unzip -q android-ndk-r20-linux-x86_64.zip
+# ANDROID_NDK is set earlier.
+
+cd "$SRC"
./tools/git-sync-deps
-mkdir build && cd $SRC/build
+mkdir build && cd "$SRC/build"
# Invoke the build.
-BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
-echo $(date): Starting build...
+echo "$(date): Starting build..."
cmake -GNinja \
- -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
- -DANDROID_ABI=$ANDROID_ABI \
- -DANDROID_PLATFORM=$ANDROID_PLATFORM \
- -DANDROID_NDK=$ANDROID_NDK \
- -DANDROID_STL=$ANDROID_STL \
- -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN_PATH \
+ "-DCMAKE_BUILD_TYPE=$BUILD_TYPE" \
+ "-DANDROID_ABI=$ANDROID_ABI" \
+ "-DANDROID_PLATFORM=$ANDROID_PLATFORM" \
+ "-DANDROID_NDK=$ANDROID_NDK" \
+ "-DANDROID_STL=$ANDROID_STL" \
+ "-DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN_PATH" \
..
-echo $(date): Build everything...
+echo "$(date): Build everything..."
ninja
-echo $(date): Build completed.
+echo "$(date): Build completed."
diff --git a/kokoro/check-format/build.sh b/kokoro/check-format/build.sh
index 9c104f0..9c104f0 100644..100755
--- a/kokoro/check-format/build.sh
+++ b/kokoro/check-format/build.sh
diff --git a/kokoro/license-check/build-docker.sh b/kokoro/license-check/build-docker.sh
new file mode 100755
index 0000000..8d15904
--- /dev/null
+++ b/kokoro/license-check/build-docker.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+set -e # Fail on any error.
+set -x # Display commands being run.
+
+license-checker --dir="$ROOT_DIR"
diff --git a/kokoro/license-check/build.sh b/kokoro/license-check/build.sh
new file mode 100755
index 0000000..01b19ad
--- /dev/null
+++ b/kokoro/license-check/build.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+set -e # Fail on any error.
+
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+ROOT_DIR="$( cd "${SCRIPT_DIR}/../.." >/dev/null 2>&1 && pwd )"
+
+docker run --rm -i \
+ --volume "${ROOT_DIR}:${ROOT_DIR}:ro" \
+ --workdir "${ROOT_DIR}" \
+ --env ROOT_DIR="${ROOT_DIR}" \
+ --env SCRIPT_DIR="${SCRIPT_DIR}" \
+ --entrypoint "${SCRIPT_DIR}/build-docker.sh" \
+ "gcr.io/shaderc-build/radial-build:latest"
diff --git a/kokoro/license-check/presubmit.cfg b/kokoro/license-check/presubmit.cfg
new file mode 100644
index 0000000..3734663
--- /dev/null
+++ b/kokoro/license-check/presubmit.cfg
@@ -0,0 +1,15 @@
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+build_file: "amber/kokoro/license-check/build.sh"
diff --git a/kokoro/linux-clang-debug/build.sh b/kokoro/linux-clang-debug/build.sh
index 83f1dfb..c275425 100644..100755
--- a/kokoro/linux-clang-debug/build.sh
+++ b/kokoro/linux-clang-debug/build.sh
@@ -17,4 +17,6 @@ set -e # fail on error
set -x # display commands
SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG clang
+source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG "clang-10.0.0" \
+ -DAMBER_ENABLE_SWIFTSHADER=TRUE \
+ -DAMBER_ENABLE_VK_DEBUGGING=TRUE
diff --git a/kokoro/linux-clang-release/build.sh b/kokoro/linux-clang-release/build.sh
index 2b691fd..b537ef1 100644..100755
--- a/kokoro/linux-clang-release/build.sh
+++ b/kokoro/linux-clang-release/build.sh
@@ -17,4 +17,7 @@ set -e # fail on error
set -x # display commands
SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE clang
+source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE "clang-10.0.0" \
+ -DAMBER_ENABLE_SWIFTSHADER=TRUE \
+ -DAMBER_ENABLE_VK_DEBUGGING=TRUE \
+ -DAMBER_USE_DXC=TRUE
diff --git a/kokoro/linux-gcc-debug-dawn/build.sh b/kokoro/linux-gcc-debug-dawn/build.sh
index 30c4181..4f35b13 100644..100755
--- a/kokoro/linux-gcc-debug-dawn/build.sh
+++ b/kokoro/linux-gcc-debug-dawn/build.sh
@@ -17,4 +17,4 @@ set -e # fail on error
set -x # display commands
SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/linux/build_dawn.sh DEBUG gcc
+source $SCRIPT_DIR/../scripts/linux/build_dawn.sh DEBUG "gcc-9"
diff --git a/kokoro/linux-gcc-debug/build.sh b/kokoro/linux-gcc-debug/build.sh
index ead61e1..92463c1 100644..100755
--- a/kokoro/linux-gcc-debug/build.sh
+++ b/kokoro/linux-gcc-debug/build.sh
@@ -17,4 +17,6 @@ set -e # fail on error
set -x # display commands
SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG gcc
+source $SCRIPT_DIR/../scripts/linux/build.sh DEBUG "gcc-9" \
+ -DAMBER_ENABLE_SWIFTSHADER=TRUE \
+ -DAMBER_ENABLE_VK_DEBUGGING=TRUE
diff --git a/kokoro/linux-gcc-release-clspv/build.sh b/kokoro/linux-gcc-release-clspv/build.sh
index fde60fb..2a13327 100644..100755
--- a/kokoro/linux-gcc-release-clspv/build.sh
+++ b/kokoro/linux-gcc-release-clspv/build.sh
@@ -17,4 +17,6 @@ set -e # fail on error
set -x # display commands
SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc -DAMBER_USE_CLSPV=ON
+source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE "gcc-9" \
+ -DAMBER_USE_CLSPV=TRUE \
+ -DAMBER_ENABLE_SWIFTSHADER=TRUE
diff --git a/kokoro/linux-gcc-release/build.sh b/kokoro/linux-gcc-release/build.sh
index dd0bef0..d428ffe 100644..100755
--- a/kokoro/linux-gcc-release/build.sh
+++ b/kokoro/linux-gcc-release/build.sh
@@ -17,4 +17,7 @@ set -e # fail on error
set -x # display commands
SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE gcc
+source $SCRIPT_DIR/../scripts/linux/build.sh RELEASE "gcc-9" \
+ -DAMBER_ENABLE_SWIFTSHADER=TRUE \
+ -DAMBER_ENABLE_VK_DEBUGGING=TRUE \
+ -DAMBER_USE_DXC=TRUE
diff --git a/kokoro/macos-clang-debug/build.sh b/kokoro/macos-clang-debug/build.sh
index b345eff..1fb25b8 100644..100755
--- a/kokoro/macos-clang-debug/build.sh
+++ b/kokoro/macos-clang-debug/build.sh
@@ -17,4 +17,6 @@ set -e # fail on error
set -x # display commands
SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/macos/build.sh DEBUG clang
+source $SCRIPT_DIR/../scripts/macos/build.sh DEBUG clang \
+ -DAMBER_ENABLE_SWIFTSHADER=TRUE \
+ -DAMBER_ENABLE_VK_DEBUGGING=TRUE
diff --git a/kokoro/macos-clang-release/build.sh b/kokoro/macos-clang-release/build.sh
index 73888f6..372e9a0 100644..100755
--- a/kokoro/macos-clang-release/build.sh
+++ b/kokoro/macos-clang-release/build.sh
@@ -17,4 +17,6 @@ set -e # fail on error
set -x # display commands
SCRIPT_DIR=`dirname "$BASH_SOURCE"`
-source $SCRIPT_DIR/../scripts/macos/build.sh RELEASE clang
+source $SCRIPT_DIR/../scripts/macos/build.sh RELEASE clang \
+ -DAMBER_ENABLE_SWIFTSHADER=TRUE \
+ -DAMBER_ENABLE_VK_DEBUGGING=TRUE
diff --git a/kokoro/ndk-build/build.sh b/kokoro/ndk-build/build.sh
index 6d6bdc9..fa80114 100644..100755
--- a/kokoro/ndk-build/build.sh
+++ b/kokoro/ndk-build/build.sh
@@ -16,42 +16,47 @@
set -e # fail on error
set -x # display commands
-BUILD_ROOT=$PWD
-SRC=$PWD/github/amber
+BUILD_ROOT="$PWD"
+SRC="$PWD/github/amber"
# NDK Path
-export ANDROID_NDK=/opt/android-ndk-r16b
+export ANDROID_NDK="$BUILD_ROOT/android-ndk-r21"
# Get NINJA.
wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip
unzip -q ninja-linux.zip
export PATH="$PWD:$PATH"
-cd $SRC
+# Get Android NDK.
+wget -q https://dl.google.com/android/repository/android-ndk-r21-linux-x86_64.zip
+unzip -q android-ndk-r21-linux-x86_64.zip
+# ANDROID_NDK is set earlier.
+
+cd "$SRC"
./tools/git-sync-deps
./tools/update_build_version.py . samples/ third_party/
./tools/update_vk_wrappers.py . .
mkdir -p build/libs build/app
-cd $SRC/build
+cd "$SRC/build"
# Invoke the build.
-BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
-echo $(date): Starting ndk-build for android_test ...
-$ANDROID_NDK/ndk-build \
- -C $SRC/android_test \
+echo "$(date): Starting ndk-build for android_test ..."
+"$ANDROID_NDK/ndk-build" \
+ -C "$SRC/android_test" \
NDK_PROJECT_PATH=. \
- NDK_LIBS_OUT=`pwd`/libs \
- NDK_APP_OUT=`pwd`/app \
+ "NDK_LIBS_OUT=$(pwd)/libs" \
+ "NDK_APP_OUT=$(pwd)/app" \
-j8
-echo $(date): ndk-build for android_test completed.
-echo $(date): Starting ndk-build for samples ...
-$ANDROID_NDK/ndk-build \
- -C $SRC/samples \
+echo "$(date): ndk-build for android_test completed."
+
+echo "$(date): Starting ndk-build for samples ..."
+"$ANDROID_NDK/ndk-build" \
+ -C "$SRC/samples" \
NDK_PROJECT_PATH=. \
- NDK_LIBS_OUT=`pwd`/libs \
- NDK_APP_OUT=`pwd`/app \
+ "NDK_LIBS_OUT=$(pwd)/libs" \
+ "NDK_APP_OUT=$(pwd)/app" \
-j8
-echo $(date): ndk-build for samples completed.
+echo "$(date): ndk-build for samples completed."
diff --git a/kokoro/scripts/linux/build-docker.sh b/kokoro/scripts/linux/build-docker.sh
new file mode 100755
index 0000000..db89da3
--- /dev/null
+++ b/kokoro/scripts/linux/build-docker.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # fail on error
+
+. /bin/using.sh # Declare the bash `using` function for configuring toolchains.
+
+set -x # show commands
+
+BUILD_TYPE="Debug"
+
+using cmake-3.17.2
+using ninja-1.10.0
+
+if [ ! -z "$COMPILER" ]; then
+ using "$COMPILER"
+fi
+
+# Possible configurations are:
+# DEBUG, RELEASE
+
+if [ $CONFIG = "RELEASE" ]
+then
+ BUILD_TYPE="RelWithDebInfo"
+fi
+
+DEPS_ARGS=""
+if [[ "$EXTRA_CONFIG" =~ "USE_CLSPV=TRUE" ]]; then
+ DEPS_ARGS+=" --with-clspv"
+fi
+if [[ "$EXTRA_CONFIG" =~ "USE_DXC=TRUE" ]]; then
+ DEPS_ARGS+=" --with-dxc"
+fi
+if [[ "$EXTRA_CONFIG" =~ "ENABLE_SWIFTSHADER=TRUE" ]]; then
+ DEPS_ARGS+=" --with-swiftshader"
+fi
+
+cd $ROOT_DIR
+./tools/git-sync-deps $DEPS_ARGS
+
+mkdir -p build
+cd $ROOT_DIR/build
+
+# Invoke the build.
+BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
+echo $(date): Starting build...
+cmake -GNinja -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
+ -DAMBER_USE_LOCAL_VULKAN=1 \
+ $EXTRA_CONFIG ..
+
+echo $(date): Build everything...
+ninja
+echo $(date): Build completed.
+
+echo $(date): Starting amber_unittests...
+./amber_unittests
+echo $(date): amber_unittests completed.
+
+# Swiftshader is only built with gcc, so only run the integration tests with gcc
+if [[ "$EXTRA_CONFIG" =~ "ENABLE_SWIFTSHADER=TRUE" ]]; then
+ OPTS=
+ if [[ $EXTRA_CONFIG =~ "USE_CLSPV=ON" ]]; then
+ OPTS="--use-opencl"
+ fi
+ if [[ "$EXTRA_CONFIG" =~ "USE_DXC=TRUE" ]]; then
+ OPTS+=" --use-dxc"
+ fi
+ if [[ "$EXTRA_CONFIG" =~ "ENABLE_VK_DEBUGGING=TRUE" ]]; then
+ OPTS+=" --test-debugger"
+ fi
+
+ echo $(date): Starting integration tests..
+ export LD_LIBRARY_PATH=$ROOT_DIR/build/third_party/vulkan-loader/loader
+ export VK_LAYER_PATH=$ROOT_DIR/build/third_party/vulkan-validationlayers/layers
+ export VK_ICD_FILENAMES=$ROOT_DIR/build/Linux/vk_swiftshader_icd.json
+ cd $ROOT_DIR
+ python3 ./tests/run_tests.py --build-dir $ROOT_DIR/build --use-swiftshader $OPTS
+ echo $(date): integration tests completed.
+fi
diff --git a/kokoro/scripts/linux/build.sh b/kokoro/scripts/linux/build.sh
index 44218f3..e48033d 100644..100755
--- a/kokoro/scripts/linux/build.sh
+++ b/kokoro/scripts/linux/build.sh
@@ -1,5 +1,6 @@
#!/bin/bash
-# Copyright 2018 The Amber Authors.
+
+# Copyright (C) 2020 The Amber Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,73 +13,29 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+#
+# Linux Build Script.
-set -e # fail on error
-set -x # show commands
-
-BUILD_ROOT=$PWD
-SRC=$PWD/github/amber
-CONFIG=$1
-shift
-COMPILER=$1
-shift
-EXTRA_CONFIG=$@
-
-BUILD_TYPE="Debug"
-
-CMAKE_C_CXX_COMPILER=""
-if [ $COMPILER = "clang" ]
-then
- CMAKE_C_CXX_COMPILER="-DCMAKE_C_COMPILER=/usr/bin/clang-5.0 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-5.0"
-else
- # Use newer gcc than default.
- CMAKE_C_CXX_COMPILER="-DCMAKE_C_COMPILER=/usr/bin/gcc-5 -DCMAKE_CXX_COMPILER=/usr/bin/g++-5"
-fi
-
-# Possible configurations are:
-# DEBUG, RELEASE
-
-if [ $CONFIG = "RELEASE" ]
-then
- BUILD_TYPE="RelWithDebInfo"
-fi
-
-# removing the old version
-echo y | sudo apt-get purge --auto-remove cmake
-
-# Installing the 3.10.2 version
-wget http://www.cmake.org/files/v3.10/cmake-3.10.2.tar.gz
-tar -xvzf cmake-3.10.2.tar.gz
-cd cmake-3.10.2/
-./configure
-make
-sudo make install
-
-echo $(date): $(cmake --version)
-
-# Get ninja
-wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip
-unzip -q ninja-linux.zip
-export PATH="$PWD:$PATH"
-
-cd $SRC
-./tools/git-sync-deps
-
-mkdir build && cd $SRC/build
-
-# Invoke the build.
-BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
-echo $(date): Starting build...
-cmake -GNinja -DCMAKE_BUILD_TYPE=$BUILD_TYPE $CMAKE_C_CXX_COMPILER -DAMBER_USE_LOCAL_VULKAN=1 $EXTRA_CONFIG ..
-
-echo $(date): Build everything...
-ninja
-echo $(date): Build completed.
+set -e # Fail on any error.
-echo $(date): Starting amber_unittests...
-./amber_unittests
-echo $(date): amber_unittests completed.
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+ROOT_DIR="$( cd "${SCRIPT_DIR}/../../.." >/dev/null 2>&1 && pwd )"
-#echo $(date): Starting integration tests..
-#../../test/run_tests.py
-#echo $(date): integration tests completed.
+CONFIG=$1
+COMPILER=$2
+EXTRA_CONFIG=${@:3}
+
+docker run --rm -i \
+ --volume "${ROOT_DIR}:${ROOT_DIR}" \
+ --volume "${KOKORO_ARTIFACTS_DIR}:${KOKORO_ARTIFACTS_DIR}" \
+ --workdir "${ROOT_DIR}" \
+ --env ROOT_DIR="${ROOT_DIR}" \
+ --env SCRIPT_DIR="${SCRIPT_DIR}" \
+ --env CONFIG="${CONFIG}" \
+ --env COMPILER="${COMPILER}" \
+ --env EXTRA_CONFIG="${EXTRA_CONFIG}" \
+ --env KOKORO_ARTIFACTS_DIR="${KOKORO_ARTIFACTS_DIR}" \
+ --entrypoint "${SCRIPT_DIR}/build-docker.sh" \
+ "gcr.io/shaderc-build/radial-build:latest"
+
+sudo chown -R "$(id -u):$(id -g)" "${ROOT_DIR}/build"
diff --git a/kokoro/scripts/linux/build_dawn-docker.sh b/kokoro/scripts/linux/build_dawn-docker.sh
new file mode 100755
index 0000000..0171846
--- /dev/null
+++ b/kokoro/scripts/linux/build_dawn-docker.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e # fail on error
+
+. /bin/using.sh # Declare the bash `using` function for configuring toolchains.
+
+set -x # show commands
+
+BUILD_TYPE="Debug"
+
+using cmake-3.17.2
+using ninja-1.10.0
+
+if [ ! -z "$COMPILER" ]; then
+ using "$COMPILER"
+fi
+
+# Possible configurations are:
+# DEBUG, RELEASE
+
+if [ $CONFIG = "RELEASE" ]
+then
+ BUILD_TYPE="RelWithDebInfo"
+fi
+
+# Make a directory for Dawn dependencies
+mkdir -p $ROOT_DIR/build/out/dawn-deps && cd $ROOT_DIR/build/out/dawn-deps
+
+# Get depot tools
+git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
+export PATH="$PWD/depot_tools:$PATH"
+
+# Clone the repo as "dawn"
+git clone https://dawn.googlesource.com/dawn dawn && cd dawn
+DAWN=$PWD
+
+# Bootstrap the gclient configuration
+cp scripts/standalone.gclient .gclient
+
+# Fetch external dependencies and toolchains with gclient
+gclient sync
+
+# Generate build files
+mkdir -p out/Release
+touch out/Release/args.gn
+gn gen out/Release
+
+# build dawn
+ninja -C out/Release
+
+cd $ROOT_DIR
+./tools/git-sync-deps
+
+cd $ROOT_DIR/build
+
+# Invoke the build.
+BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
+echo $(date): Starting build...
+
+cmake -GNinja ..\
+ -DCMAKE_BUILD_TYPE=$BUILD_TYPE\
+ -DDawn_INCLUDE_DIR=$DAWN/src/include\
+ -DDawn_GEN_INCLUDE_DIR=$DAWN/out/Release/gen/src/include\
+ -DDawn_LIBRARY_DIR=$DAWN/out/Release
+
+echo $(date): Build everything...
+ninja
+echo $(date): Build completed.
+
+echo $(date): Starting amber_unittests...
+./amber_unittests
+echo $(date): amber_unittests completed.
diff --git a/kokoro/scripts/linux/build_dawn.sh b/kokoro/scripts/linux/build_dawn.sh
index c5572d8..75a0353 100644..100755
--- a/kokoro/scripts/linux/build_dawn.sh
+++ b/kokoro/scripts/linux/build_dawn.sh
@@ -1,5 +1,6 @@
#!/bin/bash
-# Copyright 2019 The Amber Authors.
+
+# Copyright (C) 2020 The Amber Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -12,95 +13,29 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
+#
+# Linux Build Script.
+
+set -e # Fail on any error.
-set -e # fail on error
-set -x # show commands
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd )"
+ROOT_DIR="$( cd "${SCRIPT_DIR}/../../.." >/dev/null 2>&1 && pwd )"
-BUILD_ROOT=$PWD
-SRC=$PWD/github/amber
CONFIG=$1
COMPILER=$2
-
-BUILD_TYPE="Debug"
-
-CMAKE_C_CXX_COMPILER=""
-if [ $COMPILER = "clang" ]
-then
- sudo ln -s /usr/bin/clang-3.8 /usr/bin/clang
- sudo ln -s /usr/bin/clang++-3.8 /usr/bin/clang++
- CMAKE_C_CXX_COMPILER="-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++"
-fi
-
-# Possible configurations are:
-# DEBUG, RELEASE
-
-if [ $CONFIG = "RELEASE" ]
-then
- BUILD_TYPE="RelWithDebInfo"
-fi
-
-# removing the old version
-echo y | sudo apt-get purge --auto-remove cmake
-
-# Installing the 3.10.2 version
-wget http://www.cmake.org/files/v3.10/cmake-3.10.2.tar.gz
-tar -xvzf cmake-3.10.2.tar.gz
-cd cmake-3.10.2/
-./configure
-make
-sudo make install
-echo $(date): $(cmake --version)
-
-# Get ninja
-wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip
-unzip -q ninja-linux.zip
-export PATH="$PWD:$PATH"
-
-# Make a directory for Dawn dependencies
-mkdir -p $SRC/build/out/dawn-deps && cd $SRC/build/out/dawn-deps
-
-# Get depot tools
-git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
-export PATH="$PWD/depot_tools:$PATH"
-
-# Clone the repo as "dawn"
-git clone https://dawn.googlesource.com/dawn dawn && cd dawn
-DAWN=$PWD
-
-# Bootstrap the gclient configuration
-cp scripts/standalone.gclient .gclient
-
-# Fetch external dependencies and toolchains with gclient
-gclient sync
-
-# Generate build files
-mkdir -p out/Release
-touch out/Release/args.gn
-gn gen out/Release
-
-# build dawn
-ninja -C out/Release
-
-cd $SRC
-./tools/git-sync-deps
-
-cd $SRC/build
-
-# Invoke the build.
-BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
-echo $(date): Starting build...
-
-cmake -GNinja ..\
- $CMAKE_C_CXX_COMPILER\
- -DCMAKE_BUILD_TYPE=$BUILD_TYPE\
- -DDawn_INCLUDE_DIR=$DAWN/src/include\
- -DDawn_GEN_INCLUDE_DIR=$DAWN/out/Release/gen/src/include\
- -DDawn_LIBRARY_DIR=$DAWN/out/Release
-
-echo $(date): Build everything...
-ninja
-echo $(date): Build completed.
-
-echo $(date): Starting amber_unittests...
-./amber_unittests
-echo $(date): amber_unittests completed.
+EXTRA_CONFIG=${@:3}
+
+docker run --rm -i \
+ --volume "${ROOT_DIR}:${ROOT_DIR}" \
+ --volume "${KOKORO_ARTIFACTS_DIR}:${KOKORO_ARTIFACTS_DIR}" \
+ --workdir "${ROOT_DIR}" \
+ --env ROOT_DIR="${ROOT_DIR}" \
+ --env SCRIPT_DIR="${SCRIPT_DIR}" \
+ --env CONFIG="${CONFIG}" \
+ --env COMPILER="${COMPILER}" \
+ --env EXTRA_CONFIG="${EXTRA_CONFIG}" \
+ --env KOKORO_ARTIFACTS_DIR="${KOKORO_ARTIFACTS_DIR}" \
+ --entrypoint "${SCRIPT_DIR}/build_dawn-docker.sh" \
+ "gcr.io/shaderc-build/radial-build:latest"
+
+sudo chown -R "$(id -u):$(id -g)" "${ROOT_DIR}/build"
diff --git a/kokoro/scripts/macos/build.sh b/kokoro/scripts/macos/build.sh
index 6c6fb63..5f4808c 100644..100755
--- a/kokoro/scripts/macos/build.sh
+++ b/kokoro/scripts/macos/build.sh
@@ -19,6 +19,8 @@ set -x # show commands
BUILD_ROOT=$PWD
SRC=$PWD/github/amber
BUILD_TYPE=$1
+shift
+EXTRA_CONFIG=$@
# Get ninja
wget -q https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-mac.zip
@@ -28,8 +30,13 @@ export PATH="$PWD:$PATH"
echo $(date): $(cmake --version)
+DEPS_ARGS=""
+if [[ "$EXTRA_CONFIG" =~ "ENABLE_SWIFTSHADER=TRUE" ]]; then
+ DEPS_ARGS+=" --with-swiftshader"
+fi
+
cd $SRC
-./tools/git-sync-deps
+./tools/git-sync-deps $DEPS_ARGS
mkdir build && cd $SRC/build
@@ -38,7 +45,10 @@ CMAKE_C_CXX_COMPILER="-DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++"
# Invoke the build.
BUILD_SHA=${KOKORO_GITHUB_COMMIT:-$KOKORO_GITHUB_PULL_REQUEST_COMMIT}
echo $(date): Starting build...
-cmake -GNinja -DCMAKE_BUILD_TYPE=$BUILD_TYPE $CMAKE_C_CXX_COMPILER -DAMBER_USE_LOCAL_VULKAN=1 ..
+cmake -GNinja -DCMAKE_BUILD_TYPE=$BUILD_TYPE $CMAKE_C_CXX_COMPILER \
+ -DAMBER_USE_LOCAL_VULKAN=1 \
+ -DAMBER_ENABLE_SWIFTSHADER=1 \
+ ..
echo $(date): Build everything...
ninja
@@ -48,6 +58,13 @@ echo $(date): Starting amber_unittests...
./amber_unittests
echo $(date): amber_unittests completed.
-#echo $(date): Starting integration tests..
-#../../test/run_tests.py
-#echo $(date): integration tests completed.
+# Tests currently fail on Debug build on the bots
+if [[ "$EXTRA_CONFIG" =~ "ENABLE_SWIFTSHADER=TRUE" ]]; then
+ echo $(date): Starting integration tests..
+ export LD_LIBRARY_PATH=build/third_party/vulkan-loader/loader
+ export VK_LAYER_PATH=build/third_party/vulkan-validationlayers/layers
+ export VK_ICD_FILENAMES=build/Darwin/vk_swiftshader_icd.json
+ cd $SRC
+ ./tests/run_tests.py --build-dir $SRC/build --use-swiftshader
+ echo $(date): integration tests completed.
+fi
diff --git a/kokoro/scripts/windows/build.bat b/kokoro/scripts/windows/build.bat
index bb47403..42ccff8 100644
--- a/kokoro/scripts/windows/build.bat
+++ b/kokoro/scripts/windows/build.bat
@@ -18,8 +18,8 @@ set BUILD_ROOT=%cd%
set SRC=%cd%\github\amber
set BUILD_TYPE=%1
-choco install cmake --yes --no-progress
-choco upgrade cmake --yes --no-progress
+choco install cmake --yes --no-progress --limit-output
+choco upgrade cmake --yes --no-progress --limit-output
:: Force usage of python 3.6 and add cmake to the path.
set PATH=C:\python36;"C:\Program Files\CMake\bin";%PATH%
diff --git a/license-checker.cfg b/license-checker.cfg
new file mode 100644
index 0000000..72a470b
--- /dev/null
+++ b/license-checker.cfg
@@ -0,0 +1,43 @@
+{
+ "licenses": [
+ "Apache-2.0",
+ "Apache-2.0-Header"
+ ],
+ "paths": [
+ {
+ "exclude": [
+ ".clang-format",
+ "AUTHORS",
+ "CPPLINT.cfg",
+ "DEPS",
+ "Doxyfile",
+ "LICENSE",
+
+ "**.gitignore",
+ "**.md",
+ "**.png",
+
+ "build/**",
+ "third_party/*/**",
+ "out/**",
+
+ "android_gradle/**/AndroidManifest.xml",
+ "android_gradle/app/build.gradle",
+ "android_gradle/build.gradle",
+ "android_gradle/gradle.properties",
+ "android_gradle/gradle/wrapper/gradle-wrapper.jar",
+ "android_gradle/gradle/wrapper/gradle-wrapper.properties",
+ "android_gradle/gradlew.bat",
+ "android_gradle/gradlew",
+ "android_gradle/settings.gradle",
+ "android_sample/assets/amber/*.spv",
+ "src/vulkan/vk-funcs*.inc",
+ "tests/cases/*.bin",
+ "tests/cases/*.txt",
+ "tools/amber-syntax.vim",
+ "tools/amber.sublime-syntax",
+ "tools/doxy.css"
+ ]
+ }
+ ]
+}
diff --git a/samples/amber.cc b/samples/amber.cc
index aa94e7b..d037ba4 100644
--- a/samples/amber.cc
+++ b/samples/amber.cc
@@ -14,13 +14,17 @@
#include "amber/amber.h"
+#include <stdio.h>
+
#include <algorithm>
#include <cassert>
+#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <set>
+#include <string>
#include <utility>
#include <vector>
@@ -31,6 +35,10 @@
#include "src/build-versions.h"
#include "src/make_unique.h"
+#if AMBER_ENABLE_SPIRV_TOOLS
+#include "spirv-tools/libspirv.hpp"
+#endif
+
#if AMBER_ENABLE_LODEPNG
#include "samples/png.h"
#endif // AMBER_ENABLE_LODEPNG
@@ -49,6 +57,7 @@ struct Options {
uint32_t engine_major = 1;
uint32_t engine_minor = 0;
int32_t fence_timeout = -1;
+ int32_t selected_device = -1;
bool parse_only = false;
bool pipeline_create_only = false;
bool disable_validation_layer = false;
@@ -59,6 +68,7 @@ struct Options {
bool log_graphics_calls_time = false;
bool log_execute_calls = false;
bool disable_spirv_validation = false;
+ std::string shader_filename;
amber::EngineType engine = amber::kEngineTypeVulkan;
std::string spv_env;
};
@@ -70,8 +80,9 @@ const char kUsage[] = R"(Usage: amber [options] SCRIPT [SCRIPTS...]
-ps -- Parse input files, create pipelines; Don't execute.
-q -- Disable summary output.
-d -- Disable validation layers.
+ -D <ID> -- ID of device to run with (Vulkan only).
-f <value> -- Sets the fence timeout value to |value|
- -t <spirv_env> -- The target SPIR-V environment e.g., spv1.3, vulkan1.1.
+ -t <spirv_env> -- The target SPIR-V environment e.g., spv1.3, vulkan1.1, vulkan1.2.
If a SPIR-V environment, assume the lowest version of Vulkan that
requires support of that version of SPIR-V.
If a Vulkan environment, use the highest version of SPIR-V required
@@ -82,8 +93,9 @@ const char kUsage[] = R"(Usage: amber [options] SCRIPT [SCRIPTS...]
or as a PPM image otherwise.
-I <buffername> -- Name of framebuffer to dump. Defaults to 'framebuffer'.
-b <filename> -- Write contents of a UBO or SSBO to <filename>.
- -B [<desc set>:]<binding> -- Descriptor set and binding of buffer to write.
- Default is [0:]0.
+ -B [<pipeline name>:][<desc set>:]<binding> -- Identifier of buffer to write.
+ Default is [first pipeline:][0:]0.
+ -w <filename> -- Write shader assembly to |filename|
-e <engine> -- Specify graphics engine: vulkan, dawn. Default is vulkan.
-v <engine version> -- Engine version (eg, 1.1 for Vulkan). Default 1.0.
-V, --version -- Output version information for Amber and libraries.
@@ -94,6 +106,29 @@ const char kUsage[] = R"(Usage: amber [options] SCRIPT [SCRIPTS...]
-h -- This help text.
)";
+// Parses a decimal integer from the given string, and writes it to |retval|.
+// Returns true if parsing succeeded and consumed the whole string.
+static bool ParseOneInt(const char* str, int* retval) {
+ char trailing = 0;
+#if defined(_MSC_VER)
+ return sscanf_s(str, "%d%c", retval, &trailing, 1) == 1;
+#else
+ return std::sscanf(str, "%d%c", retval, &trailing) == 1;
+#endif
+}
+
+// Parses a decimal integer, then a period (.), then a decimal integer from the
+// given string, and writes it to |retval|. Returns true if parsing succeeded
+// and consumed the whole string.
+static int ParseIntDotInt(const char* str, int* retval0, int* retval1) {
+ char trailing = 0;
+#if defined(_MSC_VER)
+ return sscanf_s(str, "%d.%d%c", retval0, retval1, &trailing, 1) == 2;
+#else
+ return std::sscanf(str, "%d.%d%c", retval0, retval1, &trailing) == 2;
+#endif
+}
+
bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
for (size_t i = 1; i < args.size(); ++i) {
const std::string& arg = args[i];
@@ -129,6 +164,13 @@ bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
}
opts->buffer_to_dump.emplace_back();
opts->buffer_to_dump.back().buffer_name = args[i];
+ } else if (arg == "-w") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing value for -w argument." << std::endl;
+ return false;
+ }
+ opts->shader_filename = args[i];
} else if (arg == "-e") {
++i;
if (i >= args.size()) {
@@ -146,6 +188,24 @@ bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
<< std::endl;
return false;
}
+ } else if (arg == "-D") {
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing ID for -D argument." << std::endl;
+ return false;
+ }
+
+ int32_t val = 0;
+ if (!ParseOneInt(args[i].c_str(), &val)) {
+ std::cerr << "Invalid device ID: " << args[i] << std::endl;
+ return false;
+ }
+ if (val < 0) {
+ std::cerr << "Device ID must be non-negative" << std::endl;
+ return false;
+ }
+ opts->selected_device = val;
+
} else if (arg == "-f") {
++i;
if (i >= args.size()) {
@@ -153,7 +213,11 @@ bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
return false;
}
- int32_t val = std::stoi(std::string(args[i]));
+ int32_t val = 0;
+ if (!ParseOneInt(args[i].c_str(), &val)) {
+ std::cerr << "Invalid fence timeout: " << args[i] << std::endl;
+ return false;
+ }
if (val < 0) {
std::cerr << "Fence timeout must be non-negative" << std::endl;
return false;
@@ -175,23 +239,25 @@ bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
std::cerr << "Missing value for -v argument." << std::endl;
return false;
}
- const std::string& ver = args[i];
-
- size_t dot_pos = 0;
- int32_t val = std::stoi(ver, &dot_pos);
- if (val < 0) {
- std::cerr << "Version major must be non-negative" << std::endl;
- return false;
- }
-
- opts->engine_major = static_cast<uint32_t>(val);
- if (dot_pos != std::string::npos && (dot_pos + 1) < ver.size()) {
- val = std::stoi(ver.substr(dot_pos + 1));
- if (val < 0) {
+ const std::string& ver = std::string(args[i]);
+
+ int32_t major = 0;
+ int32_t minor = 0;
+ if (ParseIntDotInt(ver.c_str(), &major, &minor) ||
+ ParseOneInt(ver.c_str(), &major)) {
+ if (major < 0) {
+ std::cerr << "Version major must be non-negative" << std::endl;
+ return false;
+ }
+ if (minor < 0) {
std::cerr << "Version minor must be non-negative" << std::endl;
return false;
}
- opts->engine_minor = static_cast<uint32_t>(val);
+ opts->engine_major = static_cast<uint32_t>(major);
+ opts->engine_minor = static_cast<uint32_t>(minor);
+ } else {
+ std::cerr << "Invalid engine version number: " << ver << std::endl;
+ return false;
}
} else if (arg == "-V" || arg == "--version") {
opts->show_version_info = true;
@@ -225,7 +291,7 @@ bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
return true;
}
-std::string ReadFile(const std::string& input_file) {
+std::vector<char> ReadFile(const std::string& input_file) {
FILE* file = nullptr;
#if defined(_MSC_VER)
fopen_s(&file, input_file.c_str(), "rb");
@@ -258,7 +324,7 @@ std::string ReadFile(const std::string& input_file) {
return {};
}
- return std::string(data.begin(), data.end());
+ return data;
}
class SampleDelegate : public amber::Delegate {
@@ -295,17 +361,96 @@ class SampleDelegate : public amber::Delegate {
return timestamp::SampleGetTimestampNs();
}
+ void SetScriptPath(std::string path) { path_ = path; }
+
+ amber::Result LoadBufferData(const std::string file_name,
+ amber::BufferDataFileType file_type,
+ amber::BufferInfo* buffer) const override {
+ if (file_type == amber::BufferDataFileType::kPng) {
+#if AMBER_ENABLE_LODEPNG
+ return png::LoadPNG(path_ + file_name, &buffer->width, &buffer->height,
+ &buffer->values);
+#else
+ return amber::Result("PNG support is not enabled in compile options.");
+#endif // AMBER_ENABLE_LODEPNG
+ } else {
+ auto data = ReadFile(path_ + file_name);
+ if (data.empty())
+ return amber::Result("Failed to load buffer data " + file_name);
+
+ for (auto d : data) {
+ amber::Value v;
+ v.SetIntValue(static_cast<uint64_t>(d));
+ buffer->values.push_back(v);
+ }
+
+ buffer->width = 1;
+ buffer->height = 1;
+ }
+
+ return {};
+ }
+
private:
bool log_graphics_calls_ = false;
bool log_graphics_calls_time_ = false;
bool log_execute_calls_ = false;
+ std::string path_ = "";
};
+std::string disassemble(const std::string& env,
+ const std::vector<uint32_t>& data) {
+#if AMBER_ENABLE_SPIRV_TOOLS
+ std::string spv_errors;
+
+ spv_target_env target_env = SPV_ENV_UNIVERSAL_1_0;
+ if (!env.empty()) {
+ if (!spvParseTargetEnv(env.c_str(), &target_env))
+ return "";
+ }
+
+ auto msg_consumer = [&spv_errors](spv_message_level_t level, const char*,
+ const spv_position_t& position,
+ const char* message) {
+ switch (level) {
+ case SPV_MSG_FATAL:
+ case SPV_MSG_INTERNAL_ERROR:
+ case SPV_MSG_ERROR:
+ spv_errors += "error: line " + std::to_string(position.index) + ": " +
+ message + "\n";
+ break;
+ case SPV_MSG_WARNING:
+ spv_errors += "warning: line " + std::to_string(position.index) + ": " +
+ message + "\n";
+ break;
+ case SPV_MSG_INFO:
+ spv_errors += "info: line " + std::to_string(position.index) + ": " +
+ message + "\n";
+ break;
+ case SPV_MSG_DEBUG:
+ break;
+ }
+ };
+
+ spvtools::SpirvTools tools(target_env);
+ tools.SetMessageConsumer(msg_consumer);
+
+ std::string result;
+ tools.Disassemble(data, &result,
+ SPV_BINARY_TO_TEXT_OPTION_INDENT |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ return result;
+#else
+ return "";
+#endif // AMBER_ENABLE_SPIRV_TOOLS
+}
+
} // namespace
int main(int argc, const char** argv) {
std::vector<std::string> args(argv, argv + argc);
Options options;
+ SampleDelegate delegate;
if (!ParseArgs(args, &options)) {
std::cerr << "Failed to parse arguments." << std::endl;
@@ -337,14 +482,18 @@ int main(int argc, const char** argv) {
};
std::vector<RecipeData> recipe_data;
for (const auto& file : options.input_filenames) {
- auto data = ReadFile(file);
+ auto char_data = ReadFile(file);
+ auto data = std::string(char_data.begin(), char_data.end());
if (data.empty()) {
std::cerr << file << " is empty." << std::endl;
failures.push_back(file);
continue;
}
- amber::Amber am;
+ // Parse file path and set it for delegate to use when loading buffer data.
+ delegate.SetScriptPath(file.substr(0, file.find_last_of("/\\") + 1));
+
+ amber::Amber am(&delegate);
std::unique_ptr<amber::Recipe> recipe = amber::MakeUnique<amber::Recipe>();
result = am.Parse(data, recipe.get());
@@ -365,7 +514,6 @@ int main(int argc, const char** argv) {
if (options.parse_only)
return 0;
- SampleDelegate delegate;
if (options.log_graphics_calls)
delegate.SetLogGraphicsCalls(true);
if (options.log_graphics_calls_time)
@@ -379,7 +527,6 @@ int main(int argc, const char** argv) {
amber_options.execution_type = options.pipeline_create_only
? amber::ExecutionType::kPipelineCreateOnly
: amber::ExecutionType::kExecute;
- amber_options.delegate = &delegate;
amber_options.disable_spirv_validation = options.disable_spirv_validation;
std::set<std::string> required_features;
@@ -405,6 +552,7 @@ int main(int argc, const char** argv) {
amber::Result r = config_helper.CreateConfig(
amber_options.engine, options.engine_major, options.engine_minor,
+ options.selected_device,
std::vector<std::string>(required_features.begin(),
required_features.end()),
std::vector<std::string>(required_instance_extensions.begin(),
@@ -454,7 +602,7 @@ int main(int argc, const char** argv) {
const auto* recipe = recipe_data_elem.recipe.get();
const auto& file = recipe_data_elem.file;
- amber::Amber am;
+ amber::Amber am(&delegate);
result = am.Execute(recipe, &amber_options);
if (!result.IsSuccess()) {
std::cerr << file << ": " << result.Error() << std::endl;
@@ -463,6 +611,29 @@ int main(int argc, const char** argv) {
// give clues as to the failure.
}
+ // Dump the shader assembly
+ if (!options.shader_filename.empty()) {
+#if AMBER_ENABLE_SPIRV_TOOLS
+ std::ofstream shader_file;
+ shader_file.open(options.shader_filename, std::ios::out);
+ if (!shader_file.is_open()) {
+ std::cerr << "Cannot open file for shader dump: ";
+ std::cerr << options.shader_filename << std::endl;
+ } else {
+ auto info = recipe->GetShaderInfo();
+ for (const auto& sh : info) {
+ shader_file << ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"
+ << std::endl;
+ shader_file << "; " << sh.shader_name << std::endl
+ << ";" << std::endl;
+ shader_file << disassemble(options.spv_env, sh.shader_data)
+ << std::endl;
+ }
+ shader_file.close();
+ }
+#endif // AMBER_ENABLE_SPIRV_TOOLS
+ }
+
for (size_t i = 0; i < options.image_filenames.size(); ++i) {
std::vector<uint8_t> out_buf;
auto image_filename = options.image_filenames[i];
@@ -471,6 +642,22 @@ int main(int argc, const char** argv) {
pos != std::string::npos && image_filename.substr(pos + 1) == "png";
for (const amber::BufferInfo& buffer_info : amber_options.extractions) {
if (buffer_info.buffer_name == options.fb_names[i]) {
+ if (buffer_info.values.size() !=
+ (buffer_info.width * buffer_info.height)) {
+ result = amber::Result(
+ "Framebuffer (" + buffer_info.buffer_name + ") size (" +
+ std::to_string(buffer_info.values.size()) +
+ ") != " + "width * height (" +
+ std::to_string(buffer_info.width * buffer_info.height) + ")");
+ break;
+ }
+
+ if (buffer_info.values.empty()) {
+ result = amber::Result("Framebuffer (" + buffer_info.buffer_name +
+ ") empty or non-existent.");
+ break;
+ }
+
if (usePNG) {
#if AMBER_ENABLE_LODEPNG
result = png::ConvertToPNG(buffer_info.width, buffer_info.height,
diff --git a/samples/android_main.cc b/samples/android_main.cc
new file mode 100644
index 0000000..b24a9bb
--- /dev/null
+++ b/samples/android_main.cc
@@ -0,0 +1,57 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 <jni.h>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+extern int main(int argc, const char** argv);
+
+extern "C" JNIEXPORT JNICALL int Java_com_google_amber_Amber_androidMain(
+ JNIEnv* env,
+ jobject,
+ jobjectArray args,
+ jstring stdoutFile,
+ jstring stderrFile) {
+ const char* stdout_file_cstr = env->GetStringUTFChars(stdoutFile, NULL);
+ const char* stderr_file_cstr = env->GetStringUTFChars(stderrFile, NULL);
+
+ // Redirect std output to a file
+ freopen(stdout_file_cstr, "w", stdout);
+ freopen(stderr_file_cstr, "w", stderr);
+
+ env->ReleaseStringUTFChars(stdoutFile, stdout_file_cstr);
+ env->ReleaseStringUTFChars(stderrFile, stderr_file_cstr);
+
+ jsize arg_count = env->GetArrayLength(args);
+
+ std::vector<std::string> argv_string;
+ argv_string.push_back("amber");
+
+ for (jsize i = 0; i < arg_count; i++) {
+ jstring js = static_cast<jstring>(env->GetObjectArrayElement(args, i));
+ const char* arg_cstr = env->GetStringUTFChars(js, NULL);
+ argv_string.push_back(arg_cstr);
+ env->ReleaseStringUTFChars(js, arg_cstr);
+ }
+
+ std::vector<const char*> argv;
+
+ for (const std::string& arg : argv_string)
+ argv.push_back(arg.c_str());
+
+ return main(argv.size(), argv.data());
+}
diff --git a/samples/config_helper.cc b/samples/config_helper.cc
index a1a8016..4cceb85 100644
--- a/samples/config_helper.cc
+++ b/samples/config_helper.cc
@@ -40,6 +40,7 @@ amber::Result ConfigHelper::CreateConfig(
amber::EngineType engine,
uint32_t engine_major,
uint32_t engine_minor,
+ int32_t selected_device,
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_instance_extensions,
const std::vector<std::string>& required_device_extensions,
@@ -67,7 +68,7 @@ amber::Result ConfigHelper::CreateConfig(
return amber::Result("Unable to create config helper");
return impl_->CreateConfig(
- engine_major, engine_minor, required_features,
+ engine_major, engine_minor, selected_device, required_features,
required_instance_extensions, required_device_extensions,
disable_validation_layer, show_version_info, config);
}
diff --git a/samples/config_helper.h b/samples/config_helper.h
index cc351d6..fbed9ef 100644
--- a/samples/config_helper.h
+++ b/samples/config_helper.h
@@ -35,6 +35,7 @@ class ConfigHelperImpl {
virtual amber::Result CreateConfig(
uint32_t engine_major,
uint32_t engine_minor,
+ int32_t selected_device,
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_instance_extensions,
const std::vector<std::string>& required_device_extensions,
@@ -58,6 +59,7 @@ class ConfigHelper {
amber::EngineType engine,
uint32_t engine_major,
uint32_t engine_minor,
+ int32_t selected_device,
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_instance_extensions,
const std::vector<std::string>& required_device_extensions,
diff --git a/samples/config_helper_dawn.cc b/samples/config_helper_dawn.cc
index b160756..6fbb079 100644
--- a/samples/config_helper_dawn.cc
+++ b/samples/config_helper_dawn.cc
@@ -51,6 +51,7 @@ void PrintDeviceError(DawnErrorType errorType, const char* message, void*) {
amber::Result ConfigHelperDawn::CreateConfig(
uint32_t,
uint32_t,
+ int32_t,
const std::vector<std::string>&,
const std::vector<std::string>&,
const std::vector<std::string>&,
diff --git a/samples/config_helper_dawn.h b/samples/config_helper_dawn.h
index 1f0fdff..8e8c56c 100644
--- a/samples/config_helper_dawn.h
+++ b/samples/config_helper_dawn.h
@@ -39,6 +39,7 @@ class ConfigHelperDawn : public ConfigHelperImpl {
amber::Result CreateConfig(
uint32_t engine_major,
uint32_t engine_minor,
+ int32_t selected_device,
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_instance_extensions,
const std::vector<std::string>& required_device_extensions,
diff --git a/samples/config_helper_vulkan.cc b/samples/config_helper_vulkan.cc
index 551fc12..68114ab 100644
--- a/samples/config_helper_vulkan.cc
+++ b/samples/config_helper_vulkan.cc
@@ -22,6 +22,7 @@
#include <iterator>
#include <set>
#include <sstream>
+#include <utility>
#include "samples/log.h"
@@ -36,7 +37,7 @@ const char* const kRequiredValidationLayers[] = {
"VK_LAYER_LUNARG_object_tracker", "VK_LAYER_LUNARG_core_validation",
"VK_LAYER_GOOGLE_unique_objects",
#else // __ANDROID__
- "VK_LAYER_LUNARG_standard_validation",
+ "VK_LAYER_KHRONOS_validation",
#endif // __ANDROID__
};
@@ -46,6 +47,25 @@ const size_t kNumberOfRequiredValidationLayers =
const char kVariablePointers[] = "VariablePointerFeatures.variablePointers";
const char kVariablePointersStorageBuffer[] =
"VariablePointerFeatures.variablePointersStorageBuffer";
+const char kFloat16Int8_Float16[] = "Float16Int8Features.shaderFloat16";
+const char kFloat16Int8_Int8[] = "Float16Int8Features.shaderInt8";
+const char k8BitStorage_Storage[] =
+ "Storage8BitFeatures.storageBuffer8BitAccess";
+const char k8BitStorage_UniformAndStorage[] =
+ "Storage8BitFeatures.uniformAndStorageBuffer8BitAccess";
+const char k8BitStorage_PushConstant[] =
+ "Storage8BitFeatures.storagePushConstant8";
+const char k16BitStorage_Storage[] =
+ "Storage16BitFeatures.storageBuffer16BitAccess";
+const char k16BitStorage_UniformAndStorage[] =
+ "Storage16BitFeatures.uniformAndStorageBuffer16BitAccess";
+const char k16BitStorage_PushConstant[] =
+ "Storage16BitFeatures.storagePushConstant16";
+const char k16BitStorage_InputOutput[] =
+ "Storage16BitFeatures.storageInputOutput16";
+
+const char kSubgroupSizeControl[] = "SubgroupSizeControl.subgroupSizeControl";
+const char kComputeFullSubgroups[] = "SubgroupSizeControl.computeFullSubgroups";
const char kExtensionForValidationLayer[] = "VK_EXT_debug_report";
@@ -593,13 +613,38 @@ std::string deviceTypeToName(VkPhysicalDeviceType type) {
return "unknown";
}
+std::string stageFlagBitsToNames(const VkShaderStageFlags bits) {
+ static const std::pair<VkShaderStageFlagBits, const char*> stages[] = {
+ std::make_pair(VK_SHADER_STAGE_VERTEX_BIT, "vert"),
+ std::make_pair(VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, "tessc"),
+ std::make_pair(VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, "tesse"),
+ std::make_pair(VK_SHADER_STAGE_GEOMETRY_BIT, "geom"),
+ std::make_pair(VK_SHADER_STAGE_FRAGMENT_BIT, "frag"),
+ std::make_pair(VK_SHADER_STAGE_COMPUTE_BIT, "comp")};
+ std::ostringstream result;
+ bool addSeparator = false;
+ for (const auto& stage : stages) {
+ if ((bits & stage.first) != 0) {
+ if (addSeparator)
+ result << ", ";
+ result << stage.second;
+ addSeparator = true;
+ }
+ }
+ return result.str();
+}
+
} // namespace
ConfigHelperVulkan::ConfigHelperVulkan()
: available_features_(VkPhysicalDeviceFeatures()),
available_features2_(VkPhysicalDeviceFeatures2KHR()),
- variable_pointers_feature_(VkPhysicalDeviceVariablePointerFeaturesKHR()) {
-}
+ variable_pointers_feature_(VkPhysicalDeviceVariablePointerFeaturesKHR()),
+ float16_int8_feature_(VkPhysicalDeviceFloat16Int8FeaturesKHR()),
+ storage_8bit_feature_(VkPhysicalDevice8BitStorageFeaturesKHR()),
+ storage_16bit_feature_(VkPhysicalDevice16BitStorageFeaturesKHR()),
+ subgroup_size_control_feature_(
+ VkPhysicalDeviceSubgroupSizeControlFeaturesEXT()) {}
ConfigHelperVulkan::~ConfigHelperVulkan() {
if (vulkan_device_)
@@ -627,7 +672,11 @@ amber::Result ConfigHelperVulkan::CreateVulkanInstance(
bool disable_validation_layer) {
VkApplicationInfo app_info = VkApplicationInfo();
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
app_info.apiVersion = VK_MAKE_VERSION(engine_major, engine_minor, 0);
+#pragma clang diagnostic pop
VkInstanceCreateInfo instance_info = VkInstanceCreateInfo();
instance_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
@@ -646,19 +695,25 @@ amber::Result ConfigHelperVulkan::CreateVulkanInstance(
required_extensions.push_back(kExtensionForValidationLayer);
}
+ available_instance_extensions_ = GetAvailableInstanceExtensions();
if (!required_extensions.empty()) {
- available_instance_extensions_ = GetAvailableInstanceExtensions();
if (!AreAllExtensionsSupported(available_instance_extensions_,
required_extensions)) {
return amber::Result("Missing required instance extensions");
}
}
+ if (std::find(available_instance_extensions_.begin(),
+ available_instance_extensions_.end(),
+ "VK_KHR_get_physical_device_properties2") !=
+ available_instance_extensions_.end()) {
+ required_extensions.push_back("VK_KHR_get_physical_device_properties2");
+ }
+
// Determine if VkPhysicalDeviceProperties2KHR should be used
for (auto& ext : required_extensions) {
- if (ext == "VK_KHR_get_physical_device_properties2") {
- use_physical_device_features2_ = true;
- }
+ if (ext == "VK_KHR_get_physical_device_properties2")
+ supports_get_physical_device_properties2_ = true;
}
std::vector<const char*> required_extensions_in_char;
@@ -699,9 +754,103 @@ amber::Result ConfigHelperVulkan::CreateDebugReportCallback() {
return {};
}
-amber::Result ConfigHelperVulkan::ChooseVulkanPhysicalDevice(
+amber::Result ConfigHelperVulkan::CheckVulkanPhysicalDeviceRequirements(
+ const VkPhysicalDevice physical_device,
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_extensions) {
+ VkPhysicalDeviceFeatures required_vulkan_features =
+ VkPhysicalDeviceFeatures();
+
+ if (supports_get_physical_device_properties2_) {
+ VkPhysicalDeviceSubgroupSizeControlFeaturesEXT size_control =
+ VkPhysicalDeviceSubgroupSizeControlFeaturesEXT();
+ size_control.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
+ size_control.pNext = nullptr;
+
+ VkPhysicalDeviceVariablePointerFeaturesKHR var_ptrs =
+ VkPhysicalDeviceVariablePointerFeaturesKHR();
+ var_ptrs.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
+ var_ptrs.pNext = &size_control;
+
+ VkPhysicalDeviceFeatures2KHR features2 = VkPhysicalDeviceFeatures2KHR();
+ features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
+ features2.pNext = &var_ptrs;
+
+ auto vkGetPhysicalDeviceFeatures2KHR =
+ reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
+ vkGetInstanceProcAddr(vulkan_instance_,
+ "vkGetPhysicalDeviceFeatures2KHR"));
+ vkGetPhysicalDeviceFeatures2KHR(physical_device, &features2);
+ available_features_ = features2.features;
+
+ std::vector<std::string> required_features1;
+ for (const auto& feature : required_features) {
+ // No dot means this is a features1 feature.
+ if (feature.find_first_of('.') == std::string::npos) {
+ required_features1.push_back(feature);
+ continue;
+ }
+
+ if ((feature == kVariablePointers &&
+ var_ptrs.variablePointers == VK_FALSE) ||
+ (feature == kVariablePointersStorageBuffer &&
+ var_ptrs.variablePointersStorageBuffer == VK_FALSE) ||
+ (feature == kSubgroupSizeControl &&
+ size_control.subgroupSizeControl == VK_FALSE) ||
+ (feature == kComputeFullSubgroups &&
+ size_control.computeFullSubgroups == VK_FALSE)) {
+ return amber::Result("Device does not support all required features");
+ }
+ }
+
+ amber::Result r =
+ NamesToVulkanFeatures(required_features1, &required_vulkan_features);
+ if (!r.IsSuccess())
+ return r;
+
+ } else {
+ amber::Result r =
+ NamesToVulkanFeatures(required_features, &required_vulkan_features);
+ if (!r.IsSuccess())
+ return r;
+
+ vkGetPhysicalDeviceFeatures(physical_device, &available_features_);
+ }
+ if (!AreAllRequiredFeaturesSupported(available_features_,
+ required_vulkan_features)) {
+ return amber::Result("Device does not support all required features");
+ }
+
+ available_device_extensions_ = GetAvailableDeviceExtensions(physical_device);
+ if (!AreAllExtensionsSupported(available_device_extensions_,
+ required_extensions)) {
+ return amber::Result("Device does not support all required extensions");
+ }
+ for (const auto& ext : available_device_extensions_) {
+ if (ext == "VK_KHR_shader_float16_int8")
+ supports_shader_float16_int8_ = true;
+ else if (ext == "VK_KHR_8bit_storage")
+ supports_shader_8bit_storage_ = true;
+ else if (ext == "VK_KHR_16bit_storage")
+ supports_shader_16bit_storage_ = true;
+ else if (ext == "VK_EXT_subgroup_size_control")
+ supports_subgroup_size_control_ = true;
+ }
+
+ vulkan_queue_family_index_ = ChooseQueueFamilyIndex(physical_device);
+ if (vulkan_queue_family_index_ == std::numeric_limits<uint32_t>::max()) {
+ return amber::Result("Device does not support required queue flags");
+ }
+
+ return {};
+}
+
+amber::Result ConfigHelperVulkan::ChooseVulkanPhysicalDevice(
+ const std::vector<std::string>& required_features,
+ const std::vector<std::string>& required_extensions,
+ const int32_t selected_device) {
uint32_t count = 0;
std::vector<VkPhysicalDevice> physical_devices;
@@ -716,72 +865,24 @@ amber::Result ConfigHelperVulkan::ChooseVulkanPhysicalDevice(
return amber::Result("Unable to enumerate physical devices");
}
- VkPhysicalDeviceFeatures required_vulkan_features =
- VkPhysicalDeviceFeatures();
- for (uint32_t i = 0; i < count; ++i) {
- if (use_physical_device_features2_) {
- VkPhysicalDeviceVariablePointerFeaturesKHR var_ptrs =
- VkPhysicalDeviceVariablePointerFeaturesKHR();
- var_ptrs.sType =
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
- var_ptrs.pNext = nullptr;
-
- VkPhysicalDeviceFeatures2KHR features2 = VkPhysicalDeviceFeatures2KHR();
- features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
- features2.pNext = &var_ptrs;
-
- auto vkGetPhysicalDeviceFeatures2KHR =
- reinterpret_cast<PFN_vkGetPhysicalDeviceFeatures2KHR>(
- vkGetInstanceProcAddr(vulkan_instance_,
- "vkGetPhysicalDeviceFeatures2KHR"));
- vkGetPhysicalDeviceFeatures2KHR(physical_devices[i], &features2);
- available_features_ = features2.features;
-
- std::vector<std::string> required_features1;
- for (const auto& feature : required_features) {
- // No dot means this is a features1 feature.
- if (feature.find_first_of('.') == std::string::npos) {
- required_features1.push_back(feature);
- continue;
- }
-
- if (feature == kVariablePointers &&
- var_ptrs.variablePointers != VK_TRUE) {
- continue;
- }
- if (feature == kVariablePointersStorageBuffer &&
- var_ptrs.variablePointersStorageBuffer != VK_TRUE) {
- continue;
- }
- }
-
- amber::Result r =
- NamesToVulkanFeatures(required_features1, &required_vulkan_features);
- if (!r.IsSuccess())
- return r;
-
- } else {
- amber::Result r =
- NamesToVulkanFeatures(required_features, &required_vulkan_features);
- if (!r.IsSuccess())
- return r;
-
- vkGetPhysicalDeviceFeatures(physical_devices[i], &available_features_);
- }
- if (!AreAllRequiredFeaturesSupported(available_features_,
- required_vulkan_features)) {
- continue;
- }
-
- available_device_extensions_ =
- GetAvailableDeviceExtensions(physical_devices[i]);
- if (!AreAllExtensionsSupported(available_device_extensions_,
- required_extensions)) {
- continue;
+ if (selected_device > -1) {
+ uint32_t deviceID = static_cast<uint32_t>(selected_device);
+ if (deviceID >= count) {
+ return amber::Result("Unable to find Vulkan device with ID " +
+ std::to_string(deviceID));
}
-
- vulkan_queue_family_index_ = ChooseQueueFamilyIndex(physical_devices[i]);
- if (vulkan_queue_family_index_ != std::numeric_limits<uint32_t>::max()) {
+ amber::Result r = CheckVulkanPhysicalDeviceRequirements(
+ physical_devices[deviceID], required_features, required_extensions);
+ if (!r.IsSuccess())
+ return r;
+ vulkan_physical_device_ = physical_devices[deviceID];
+ return {};
+ } else {
+ for (uint32_t i = 0; i < count; ++i) {
+ amber::Result r = CheckVulkanPhysicalDeviceRequirements(
+ physical_devices[i], required_features, required_extensions);
+ if (!r.IsSuccess())
+ continue;
vulkan_physical_device_ = physical_devices[i];
return {};
}
@@ -822,7 +923,7 @@ amber::Result ConfigHelperVulkan::CreateVulkanDevice(
static_cast<uint32_t>(required_extensions_in_char.size());
info.ppEnabledExtensionNames = required_extensions_in_char.data();
- if (use_physical_device_features2_)
+ if (supports_get_physical_device_properties2_)
return CreateDeviceWithFeatures2(required_features, &info);
return CreateDeviceWithFeatures1(required_features, &info);
}
@@ -848,6 +949,44 @@ amber::Result ConfigHelperVulkan::CreateDeviceWithFeatures2(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
variable_pointers_feature_.pNext = nullptr;
+ float16_int8_feature_.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
+ float16_int8_feature_.pNext = nullptr;
+
+ storage_8bit_feature_.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR;
+ storage_8bit_feature_.pNext = nullptr;
+
+ storage_16bit_feature_.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR;
+ storage_16bit_feature_.pNext = nullptr;
+
+ subgroup_size_control_feature_.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT;
+ subgroup_size_control_feature_.pNext = nullptr;
+
+ void** next_ptr = &variable_pointers_feature_.pNext;
+
+ if (supports_shader_float16_int8_) {
+ *next_ptr = &float16_int8_feature_;
+ next_ptr = &float16_int8_feature_.pNext;
+ }
+
+ if (supports_shader_8bit_storage_) {
+ *next_ptr = &storage_8bit_feature_;
+ next_ptr = &storage_8bit_feature_.pNext;
+ }
+
+ if (supports_shader_16bit_storage_) {
+ *next_ptr = &storage_16bit_feature_;
+ next_ptr = &storage_16bit_feature_.pNext;
+ }
+
+ if (supports_subgroup_size_control_) {
+ *next_ptr = &subgroup_size_control_feature_;
+ next_ptr = &subgroup_size_control_feature_.pNext;
+ }
+
available_features2_.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
available_features2_.pNext = &variable_pointers_feature_;
@@ -863,6 +1002,28 @@ amber::Result ConfigHelperVulkan::CreateDeviceWithFeatures2(
variable_pointers_feature_.variablePointers = VK_TRUE;
else if (feature == kVariablePointersStorageBuffer)
variable_pointers_feature_.variablePointersStorageBuffer = VK_TRUE;
+ else if (feature == kFloat16Int8_Float16)
+ float16_int8_feature_.shaderFloat16 = VK_TRUE;
+ else if (feature == kFloat16Int8_Int8)
+ float16_int8_feature_.shaderInt8 = VK_TRUE;
+ else if (feature == k8BitStorage_Storage)
+ storage_8bit_feature_.storageBuffer8BitAccess = VK_TRUE;
+ else if (feature == k8BitStorage_UniformAndStorage)
+ storage_8bit_feature_.uniformAndStorageBuffer8BitAccess = VK_TRUE;
+ else if (feature == k8BitStorage_PushConstant)
+ storage_8bit_feature_.storagePushConstant8 = VK_TRUE;
+ else if (feature == k16BitStorage_Storage)
+ storage_16bit_feature_.storageBuffer16BitAccess = VK_TRUE;
+ else if (feature == k16BitStorage_UniformAndStorage)
+ storage_16bit_feature_.uniformAndStorageBuffer16BitAccess = VK_TRUE;
+ else if (feature == k16BitStorage_PushConstant)
+ storage_16bit_feature_.storagePushConstant16 = VK_TRUE;
+ else if (feature == k16BitStorage_InputOutput)
+ storage_16bit_feature_.storageInputOutput16 = VK_TRUE;
+ else if (feature == kSubgroupSizeControl)
+ subgroup_size_control_feature_.subgroupSizeControl = VK_TRUE;
+ else if (feature == kComputeFullSubgroups)
+ subgroup_size_control_feature_.computeFullSubgroups = VK_TRUE;
}
VkPhysicalDeviceFeatures required_vulkan_features =
@@ -888,27 +1049,113 @@ amber::Result ConfigHelperVulkan::DoCreateDevice(VkDeviceCreateInfo* info) {
}
void ConfigHelperVulkan::DumpPhysicalDeviceInfo() {
- VkPhysicalDeviceProperties props;
- vkGetPhysicalDeviceProperties(vulkan_physical_device_, &props);
+ VkPhysicalDeviceProperties2KHR properties2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
+ nullptr, // pNext: will point to our driver_properties struct if the
+ // "VK_KHR_get_physical_device_properties2" and
+ // "VK_KHR_driver_properties" extensions are both available.
+ {}, // properties: this is the older VkPhysicalDeviceProperties struct,
+ // wrapped by this newer struct that adds the pNext member. We use
+ // this older struct if the "VK_KHR_get_physical_device_properties2"
+ // extension is unavailable.
+ };
+
+ VkPhysicalDeviceDriverPropertiesKHR driver_properties = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
+ nullptr,
+ {},
+ {},
+ {},
+ {},
+ };
+
+ VkPhysicalDeviceSubgroupSizeControlPropertiesEXT
+ subgroup_size_control_properties = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT, // NOLINT(whitespace/line_length)
+ nullptr,
+ {},
+ {},
+ {},
+ {}};
+
+ // If the vkGetPhysicalDeviceProperties2KHR function is unavailable (because
+ // the "VK_KHR_get_physical_device_properties2" extension is unavailable or
+ // because vkGetInstanceProcAddr failed) or the "VK_KHR_driver_properties"
+ // extension is unavailable, then this will stay as nullptr and we will
+ // instead call the older vkGetPhysicalDeviceProperties function.
+ PFN_vkGetPhysicalDeviceProperties2KHR vkGetPhysicalDeviceProperties2KHR =
+ nullptr;
+
+ if (supports_get_physical_device_properties2_ &&
+ std::find(available_device_extensions_.begin(),
+ available_device_extensions_.end(),
+ "VK_KHR_driver_properties") !=
+ available_device_extensions_.end()) {
+ properties2.pNext = &driver_properties;
+
+ vkGetPhysicalDeviceProperties2KHR =
+ reinterpret_cast<PFN_vkGetPhysicalDeviceProperties2KHR>(
+ vkGetInstanceProcAddr(vulkan_instance_,
+ "vkGetPhysicalDeviceProperties2KHR"));
+ if (!vkGetPhysicalDeviceProperties2KHR) {
+ std::cout << "Warning: device claimed to support "
+ "vkGetPhysicalDeviceProperties2KHR but could not find this "
+ "function."
+ << std::endl;
+ }
+ if (supports_subgroup_size_control_) {
+ driver_properties.pNext = &subgroup_size_control_properties;
+ }
+ }
+
+ if (vkGetPhysicalDeviceProperties2KHR) {
+ vkGetPhysicalDeviceProperties2KHR(vulkan_physical_device_, &properties2);
+ } else {
+ vkGetPhysicalDeviceProperties(vulkan_physical_device_,
+ &properties2.properties);
+ }
+
+ const VkPhysicalDeviceProperties& props = properties2.properties;
uint32_t api_version = props.apiVersion;
std::cout << std::endl;
std::cout << "Physical device properties:" << std::endl;
- std::cout << " apiVersion: " << static_cast<uint32_t>(api_version >> 22)
- << "." << static_cast<uint32_t>((api_version >> 12) & 0x3ff) << "."
- << static_cast<uint32_t>(api_version & 0xfff) << std::endl;
+ std::cout << " apiVersion: " << static_cast<uint32_t>(api_version >> 22u)
+ << "." << static_cast<uint32_t>((api_version >> 12u) & 0x3ffu)
+ << "." << static_cast<uint32_t>(api_version & 0xfffu) << std::endl;
std::cout << " driverVersion: " << props.driverVersion << std::endl;
std::cout << " vendorID: " << props.vendorID << std::endl;
std::cout << " deviceID: " << props.deviceID << std::endl;
std::cout << " deviceType: " << deviceTypeToName(props.deviceType)
<< std::endl;
std::cout << " deviceName: " << props.deviceName << std::endl;
+ if (vkGetPhysicalDeviceProperties2KHR) {
+ std::cout << " driverName: " << driver_properties.driverName << std::endl;
+ std::cout << " driverInfo: " << driver_properties.driverInfo << std::endl;
+ if (supports_subgroup_size_control_) {
+ std::cout << " minSubgroupSize: "
+ << subgroup_size_control_properties.minSubgroupSize
+ << std::endl;
+ std::cout << " maxSubgroupSize: "
+ << subgroup_size_control_properties.maxSubgroupSize
+ << std::endl;
+ std::cout << " maxComputeWorkgroupSubgroups: "
+ << subgroup_size_control_properties.maxComputeWorkgroupSubgroups
+ << std::endl;
+ std::cout << " requiredSubgroupSizeStages: "
+ << stageFlagBitsToNames(subgroup_size_control_properties
+ .requiredSubgroupSizeStages)
+ << std::endl;
+ }
+ }
+ std::cout << "End of physical device properties." << std::endl;
}
amber::Result ConfigHelperVulkan::CreateConfig(
uint32_t engine_major,
uint32_t engine_minor,
+ int32_t selected_device,
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_instance_extensions,
const std::vector<std::string>& required_device_extensions,
@@ -927,7 +1174,8 @@ amber::Result ConfigHelperVulkan::CreateConfig(
return r;
}
- r = ChooseVulkanPhysicalDevice(required_features, required_device_extensions);
+ r = ChooseVulkanPhysicalDevice(required_features, required_device_extensions,
+ selected_device);
if (!r.IsSuccess())
return r;
diff --git a/samples/config_helper_vulkan.h b/samples/config_helper_vulkan.h
index d0d312d..70f90e4 100644
--- a/samples/config_helper_vulkan.h
+++ b/samples/config_helper_vulkan.h
@@ -44,6 +44,7 @@ class ConfigHelperVulkan : public ConfigHelperImpl {
amber::Result CreateConfig(
uint32_t engine_major,
uint32_t engine_minor,
+ int32_t selected_device,
const std::vector<std::string>& required_features,
const std::vector<std::string>& required_instance_extensions,
const std::vector<std::string>& required_device_extensions,
@@ -63,11 +64,19 @@ class ConfigHelperVulkan : public ConfigHelperImpl {
/// via debugCallback() function in config_helper_vulkan.cc.
amber::Result CreateDebugReportCallback();
+ /// Check if |physical_device| supports both
+ /// |required_features| and |required_extensions|.
+ amber::Result CheckVulkanPhysicalDeviceRequirements(
+ const VkPhysicalDevice physical_device,
+ const std::vector<std::string>& required_features,
+ const std::vector<std::string>& required_extensions);
+
/// Choose Vulkan physical device that supports both
/// |required_features| and |required_extensions|.
amber::Result ChooseVulkanPhysicalDevice(
const std::vector<std::string>& required_features,
- const std::vector<std::string>& required_extensions);
+ const std::vector<std::string>& required_extensions,
+ const int32_t selected_device);
/// Create Vulkan logical device that enables both
/// |required_features| and |required_extensions|.
@@ -99,10 +108,18 @@ class ConfigHelperVulkan : public ConfigHelperImpl {
VkQueue vulkan_queue_ = VK_NULL_HANDLE;
VkDevice vulkan_device_ = VK_NULL_HANDLE;
- bool use_physical_device_features2_ = false;
+ bool supports_get_physical_device_properties2_ = false;
+ bool supports_shader_float16_int8_ = false;
+ bool supports_shader_8bit_storage_ = false;
+ bool supports_shader_16bit_storage_ = false;
+ bool supports_subgroup_size_control_ = false;
VkPhysicalDeviceFeatures available_features_;
VkPhysicalDeviceFeatures2KHR available_features2_;
VkPhysicalDeviceVariablePointerFeaturesKHR variable_pointers_feature_;
+ VkPhysicalDeviceFloat16Int8FeaturesKHR float16_int8_feature_;
+ VkPhysicalDevice8BitStorageFeaturesKHR storage_8bit_feature_;
+ VkPhysicalDevice16BitStorageFeaturesKHR storage_16bit_feature_;
+ VkPhysicalDeviceSubgroupSizeControlFeaturesEXT subgroup_size_control_feature_;
};
} // namespace sample
diff --git a/samples/image_diff.cc b/samples/image_diff.cc
index 7c8db69..ec541fc 100644
--- a/samples/image_diff.cc
+++ b/samples/image_diff.cc
@@ -13,6 +13,7 @@
// limitations under the License.
#include <iostream>
+#include <sstream>
#include <vector>
#include "src/buffer.h"
@@ -37,39 +38,87 @@ struct Options {
const char kUsage[] = R"(Usage: image_diff [options] image1.png image2.png
- options:
- --rmse -- Compare using RMSE algorithm (default).
- --histogram_emd -- Compare using histogram EMD algorithm.
- -t | --tolerance <float> -- Tolerance value for comparison.
- -h | --help -- This help text.
+Exactly one algorithm (and its parameters) must be specified.
+
+Algorithms:
+
+ --rmse TOLERANCE
+ Compare using the Root Mean Square Error (RMSE) algorithm with
+ a floating point TOLERANCE value in the range 0..255, where 0
+ indicates identical images and 255 indicates images where every
+ color channel has the maximum difference.
+
+ --histogram_emd TOLERANCE
+ Compare the per-channel color histograms of the images using a
+ variant of the Earth Mover's Distance (EMD) algorithm with a
+ floating point TOLERANCE value in the range 0.0..1.0, where 0.0
+ indicates identical histograms and 1.0 indicates completely
+ different histograms for at least one of the color channels.
+ E.g. an image with red=255 for every pixel vs. an image with
+ red=0 for every pixel.
+
+Other options:
+
+ -h | --help This help text.
)";
bool ParseArgs(const std::vector<std::string>& args, Options* opts) {
+ int num_algorithms = 0;
for (size_t i = 1; i < args.size(); ++i) {
const std::string& arg = args[i];
if (arg == "-h" || arg == "--help") {
opts->show_help = true;
return true;
} else if (arg == "--rmse") {
+ num_algorithms++;
opts->compare_algorithm = CompareAlgorithm::kRMSE;
+ ++i;
+ if (i >= args.size()) {
+ std::cerr << "Missing tolerance value for RMSE comparison."
+ << std::endl;
+ return false;
+ }
+ std::stringstream sstream(args[i]);
+ sstream >> opts->tolerance;
+ if (sstream.fail()) {
+ std::cerr << "Invalid tolerance value " << args[i] << std::endl;
+ return false;
+ }
+ if (opts->tolerance < 0 || opts->tolerance > 255) {
+ std::cerr << "Tolerance must be in the range 0..255." << std::endl;
+ return false;
+ }
+
} else if (arg == "--histogram_emd") {
+ num_algorithms++;
opts->compare_algorithm = CompareAlgorithm::kHISTOGRAM_EMD;
- } else if (arg == "-t" || arg == "--tolerance") {
++i;
if (i >= args.size()) {
- std::cerr << "Missing value for " << args[i - 1] << " argument."
+ std::cerr << "Missing tolerance value for histogram EMD comparison."
<< std::endl;
return false;
}
- opts->tolerance = std::stof(std::string(args[i]));
- if (opts->tolerance < 0) {
- std::cerr << "Tolerance must be non-negative." << std::endl;
+ std::stringstream sstream(args[i]);
+ sstream >> opts->tolerance;
+ if (sstream.fail()) {
+ std::cerr << "Invalid tolerance value " << args[i] << std::endl;
+ return false;
+ }
+ if (opts->tolerance < 0 || opts->tolerance > 1) {
+ std::cerr << "Tolerance must be in the range 0..1." << std::endl;
return false;
}
} else if (!arg.empty()) {
opts->input_filenames.push_back(arg);
}
}
+ if (num_algorithms == 0) {
+ std::cerr << "No comparison algorithm specified." << std::endl;
+ return false;
+ } else if (num_algorithms > 1) {
+ std::cerr << "Only one comparison algorithm can be specified." << std::endl;
+ return false;
+ }
return true;
}
diff --git a/samples/png.cc b/samples/png.cc
index b23e002..3dbf4fd 100644
--- a/samples/png.cc
+++ b/samples/png.cc
@@ -50,16 +50,14 @@ amber::Result ConvertToPNG(uint32_t width,
uint32_t height,
const std::vector<amber::Value>& values,
std::vector<uint8_t>* buffer) {
- if (values.size() != (width * height)) {
- return amber::Result("Values size (" + std::to_string(values.size()) +
- ") != " + "width * height (" +
- std::to_string(width * height) + ")");
- }
+ assert(values.size() == (width * height) &&
+ "Buffer values != width * height");
+ assert(!values.empty() && "Buffer empty");
std::vector<uint8_t> data;
// Prepare data as lodepng expects it
- for (amber::Value value : values) {
+ for (const amber::Value& value : values) {
const uint32_t pixel = value.AsUint32();
data.push_back(byte2(pixel)); // R
data.push_back(byte1(pixel)); // G
@@ -83,4 +81,23 @@ amber::Result ConvertToPNG(uint32_t width,
return {};
}
+amber::Result LoadPNG(const std::string file_name,
+ uint32_t* width,
+ uint32_t* height,
+ std::vector<amber::Value>* values) {
+ std::vector<uint8_t> decoded_buffer;
+ if (lodepng::decode(decoded_buffer, *width, *height, file_name,
+ LodePNGColorType::LCT_RGBA, 8) != 0) {
+ return amber::Result("lodepng::decode() returned non-zero");
+ }
+
+ for (auto d : decoded_buffer) {
+ amber::Value v;
+ v.SetIntValue(d);
+ values->push_back(v);
+ }
+
+ return {};
+}
+
} // namespace png
diff --git a/samples/png.h b/samples/png.h
index 74802e7..b08d74d 100644
--- a/samples/png.h
+++ b/samples/png.h
@@ -31,6 +31,14 @@ amber::Result ConvertToPNG(uint32_t width,
const std::vector<amber::Value>& values,
std::vector<uint8_t>* buffer);
+/// Loads a PNG image from |file_name|. Image dimensions of the loaded file are
+/// stored into |width| and |height|, and the image data is stored in a one
+/// byte per data element format into |values|.
+amber::Result LoadPNG(const std::string file_name,
+ uint32_t* width,
+ uint32_t* height,
+ std::vector<amber::Value>* values);
+
} // namespace png
#endif // SAMPLES_PNG_H_
diff --git a/samples/ppm.cc b/samples/ppm.cc
index 9cd3ae8..a842512 100644
--- a/samples/ppm.cc
+++ b/samples/ppm.cc
@@ -42,11 +42,9 @@ amber::Result ConvertToPPM(uint32_t width,
uint32_t height,
const std::vector<amber::Value>& values,
std::vector<uint8_t>* buffer) {
- if (values.size() != (width * height)) {
- return amber::Result("Values size (" + std::to_string(values.size()) +
- ") != " + "width * height (" +
- std::to_string(width * height) + ")");
- }
+ assert(values.size() == (width * height) &&
+ "Buffer values != width * height");
+ assert(!values.empty() && "Buffer empty");
// Write PPM header
std::string image = "P6\n";
diff --git a/samples/timestamp.cc b/samples/timestamp.cc
index 50c6539..29f5a99 100644
--- a/samples/timestamp.cc
+++ b/samples/timestamp.cc
@@ -19,7 +19,7 @@
#if defined(_WIN32) || defined(_WIN64)
#define SAMPLE_PLATFORM_WINDOWS 1
#define SAMPLE_PLATFORM_POSIX 0
-#elif defined(__linux__) || defined(__APPLE__)
+#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
#define SAMPLE_PLATFORM_POSIX 1
#define SAMPLE_PLATFORM_WINDOWS 0
#endif
diff --git a/setup_debug_local.env b/setup_debug_local.env
index 69a37ce..3db2fdb 100644
--- a/setup_debug_local.env
+++ b/setup_debug_local.env
@@ -1,3 +1,17 @@
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
export VK_LAYER_PATH=out/Debug/third_party/vulkan-validationlayers/layers
export LD_LIBRARY_PATH=out/Debug/third_party/vulkan-loader/loader
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 70ca4fa..00e3e54 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -18,24 +18,28 @@ set(AMBER_SOURCES
buffer.cc
command.cc
command_data.cc
+ debug.cc
descriptor_set_and_binding_parser.cc
engine.cc
executor.cc
+ float16_helper.cc
format.cc
parser.cc
pipeline.cc
pipeline_data.cc
recipe.cc
result.cc
- sleep.cc
+ sampler.cc
script.cc
shader.cc
shader_compiler.cc
+ sleep.cc
tokenizer.cc
type.cc
type_parser.cc
value.cc
verifier.cc
+ virtual_file_store.cc
vkscript/command_parser.cc
vkscript/datum_type_parser.cc
vkscript/parser.cc
@@ -61,7 +65,9 @@ endif()
add_library(libamber ${AMBER_SOURCES})
amber_default_compile_options(libamber)
-target_include_directories(libamber PRIVATE "${CMAKE_BINARY_DIR}")
+target_include_directories(libamber PRIVATE
+ "${CMAKE_BINARY_DIR}"
+ ${SPIRV-Headers_SOURCE_DIR}/include)
set_target_properties(libamber PROPERTIES OUTPUT_NAME "amber")
if (${AMBER_ENABLE_DXC})
@@ -74,15 +80,23 @@ if (${AMBER_ENABLE_DXC})
target_link_libraries(libamber
dxcompiler
LLVMDxcSupport
- LLVMOption
LLVMHLSL
- LLVMScalarOpts
+ LLVMOption
+ LLVMSupport
)
+
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+ target_compile_options(libamber PRIVATE
+ -fms-extensions
+ -Wno-language-extension-token
+ -fexceptions
+ )
+ endif()
endif()
if (${AMBER_ENABLE_CLSPV})
target_include_directories(libamber PRIVATE
- "${PROJECT_SOURCE_DIR}/third_party/clspv/include"
+ "${clspv_SOURCE_DIR}/clspv/include"
)
target_link_libraries(libamber
@@ -99,12 +113,17 @@ if (${AMBER_ENABLE_SHADERC})
target_link_libraries(libamber shaderc SPIRV)
endif()
+if (${AMBER_ENABLE_VK_DEBUGGING})
+ target_link_libraries(libamber cppdap)
+endif()
+
if (NOT MSVC AND NOT ANDROID)
target_link_libraries(libamber pthread)
endif()
if (${Vulkan_FOUND})
target_link_libraries(libamber libamberenginevulkan)
+ target_include_directories(libamber PRIVATE "${VulkanHeaders_INCLUDE_DIR}")
endif()
if (${Dawn_FOUND})
target_link_libraries(libamber libamberenginedawn)
@@ -116,26 +135,35 @@ if (${AMBER_ENABLE_TESTS})
amberscript/parser_bind_test.cc
amberscript/parser_buffer_test.cc
amberscript/parser_clear_color_test.cc
+ amberscript/parser_clear_depth_test.cc
+ amberscript/parser_clear_stencil_test.cc
amberscript/parser_clear_test.cc
amberscript/parser_compile_options_test.cc
amberscript/parser_copy_test.cc
+ amberscript/parser_debug_test.cc
+ amberscript/parser_depth_test.cc
amberscript/parser_device_feature_test.cc
amberscript/parser_expect_test.cc
amberscript/parser_extension_test.cc
amberscript/parser_framebuffer_test.cc
+ amberscript/parser_image_test.cc
amberscript/parser_pipeline_test.cc
amberscript/parser_pipeline_set_test.cc
amberscript/parser_repeat_test.cc
amberscript/parser_run_test.cc
+ amberscript/parser_sampler_test.cc
amberscript/parser_set_test.cc
amberscript/parser_shader_opt_test.cc
amberscript/parser_shader_test.cc
+ amberscript/parser_stencil_test.cc
amberscript/parser_struct_test.cc
+ amberscript/parser_subgroup_size_control_test.cc
amberscript/parser_test.cc
buffer_test.cc
command_data_test.cc
descriptor_set_and_binding_parser_test.cc
executor_test.cc
+ float16_helper_test.cc
format_test.cc
pipeline_test.cc
result_test.cc
@@ -145,6 +173,7 @@ if (${AMBER_ENABLE_TESTS})
type_parser_test.cc
type_test.cc
verifier_test.cc
+ virtual_file_store_test.cc
vkscript/command_parser_test.cc
vkscript/datum_type_parser_test.cc
vkscript/parser_test.cc
@@ -154,7 +183,9 @@ if (${AMBER_ENABLE_TESTS})
)
if (${Vulkan_FOUND})
- list(APPEND TEST_SRCS vulkan/vertex_buffer_test.cc)
+ list(APPEND TEST_SRCS
+ vulkan/vertex_buffer_test.cc
+ vulkan/pipeline_test.cc)
endif()
if (${Dawn_FOUND})
@@ -176,6 +207,10 @@ if (${AMBER_ENABLE_TESTS})
amber_default_compile_options(amber_unittests)
add_test(NAME amber_unittests COMMAND amber_unittests)
+ if (${Vulkan_FOUND})
+ target_include_directories(amber_unittests PRIVATE "${VulkanHeaders_INCLUDE_DIR}" "${CMAKE_BINARY_DIR}")
+ endif()
+
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
# vulkan/vulkan.h defines VK_NULL_HANDLE as 0u and that also serves as a null pointer.
# Disable Clang's warning that will alwaays fire on that. This is required to build
diff --git a/src/amber.cc b/src/amber.cc
index 4db3055..9bf806e 100644
--- a/src/amber.cc
+++ b/src/amber.cc
@@ -68,8 +68,7 @@ Options::Options()
: engine(amber::EngineType::kEngineTypeVulkan),
config(nullptr),
execution_type(ExecutionType::kExecute),
- disable_spirv_validation(false),
- delegate(nullptr) {}
+ disable_spirv_validation(false) {}
Options::~Options() = default;
@@ -83,7 +82,7 @@ BufferInfo& BufferInfo::operator=(const BufferInfo&) = default;
Delegate::~Delegate() = default;
-Amber::Amber() = default;
+Amber::Amber(Delegate* delegate) : delegate_(delegate) {}
Amber::~Amber() = default;
@@ -93,9 +92,9 @@ amber::Result Amber::Parse(const std::string& input, amber::Recipe* recipe) {
std::unique_ptr<Parser> parser;
if (input.substr(0, 7) == "#!amber")
- parser = MakeUnique<amberscript::Parser>();
+ parser = MakeUnique<amberscript::Parser>(GetDelegate());
else
- parser = MakeUnique<vkscript::Parser>();
+ parser = MakeUnique<vkscript::Parser>(GetDelegate());
Result r = parser->Parse(input);
if (!r.IsSuccess())
@@ -113,6 +112,7 @@ namespace {
// pointer is borrowed, and should not be freed.
Result CreateEngineAndCheckRequirements(const Recipe* recipe,
Options* opts,
+ Delegate* delegate,
std::unique_ptr<Engine>* engine_ptr,
Script** script_ptr) {
if (!recipe)
@@ -131,10 +131,10 @@ Result CreateEngineAndCheckRequirements(const Recipe* recipe,
// Engine initialization checks requirements. Current backends don't do
// much else. Refactor this if they end up doing to much here.
- Result r = engine->Initialize(opts->config, opts->delegate,
- script->GetRequiredFeatures(),
- script->GetRequiredInstanceExtensions(),
- script->GetRequiredDeviceExtensions());
+ Result r =
+ engine->Initialize(opts->config, delegate, script->GetRequiredFeatures(),
+ script->GetRequiredInstanceExtensions(),
+ script->GetRequiredDeviceExtensions());
if (!r.IsSuccess())
return r;
@@ -150,7 +150,8 @@ amber::Result Amber::AreAllRequirementsSupported(const amber::Recipe* recipe,
std::unique_ptr<Engine> engine;
Script* script = nullptr;
- return CreateEngineAndCheckRequirements(recipe, opts, &engine, &script);
+ return CreateEngineAndCheckRequirements(recipe, opts, GetDelegate(), &engine,
+ &script);
}
amber::Result Amber::Execute(const amber::Recipe* recipe, Options* opts) {
@@ -163,14 +164,15 @@ amber::Result Amber::ExecuteWithShaderData(const amber::Recipe* recipe,
const ShaderMap& shader_data) {
std::unique_ptr<Engine> engine;
Script* script = nullptr;
- Result r = CreateEngineAndCheckRequirements(recipe, opts, &engine, &script);
+ Result r = CreateEngineAndCheckRequirements(recipe, opts, GetDelegate(),
+ &engine, &script);
if (!r.IsSuccess())
return r;
script->SetSpvTargetEnv(opts->spv_env);
Executor executor;
Result executor_result =
- executor.Execute(engine.get(), script, shader_data, opts);
+ executor.Execute(engine.get(), script, shader_data, opts, GetDelegate());
// Hold the executor result until the extractions are complete. This will let
// us dump any buffers requested even on failure.
@@ -180,38 +182,37 @@ amber::Result Amber::ExecuteWithShaderData(const amber::Recipe* recipe,
return {};
}
- // TODO(dsinclair): Figure out how extractions work with multiple pipelines.
- auto* pipeline = script->GetPipelines()[0].get();
-
- // The dump process holds onto the results and terminates the loop if any dump
- // fails. This will allow us to validate |extractor_result| first as if the
- // extractor fails before running the pipeline that will trigger the dumps
- // to almost always fail.
+ // Try to perform each extraction, copying the buffer data into |buffer_info|.
+ // We do not overwrite |executor_result| if extraction fails.
for (BufferInfo& buffer_info : opts->extractions) {
if (buffer_info.is_image_buffer) {
auto* buffer = script->GetBuffer(buffer_info.buffer_name);
if (!buffer)
- break;
+ continue;
buffer_info.width = buffer->GetWidth();
buffer_info.height = buffer->GetHeight();
- r = GetFrameBuffer(buffer, &(buffer_info.values));
- if (!r.IsSuccess())
- break;
-
+ GetFrameBuffer(buffer, &(buffer_info.values));
continue;
}
- DescriptorSetAndBindingParser desc_set_and_binding_parser;
- r = desc_set_and_binding_parser.Parse(buffer_info.buffer_name);
+ DescriptorSetAndBindingParser p;
+ r = p.Parse(buffer_info.buffer_name);
if (!r.IsSuccess())
- break;
+ continue;
- const auto* buffer = pipeline->GetBufferForBinding(
- desc_set_and_binding_parser.GetDescriptorSet(),
- desc_set_and_binding_parser.GetBinding());
+ // Extract the named pipeline from the request, otherwise use the
+ // first pipeline which was parsed.
+ Pipeline* pipeline = nullptr;
+ if (p.HasPipelineName())
+ pipeline = script->GetPipeline(p.PipelineName());
+ else
+ pipeline = script->GetPipelines()[0].get();
+
+ const auto* buffer =
+ pipeline->GetBufferForBinding(p.GetDescriptorSet(), p.GetBinding());
if (!buffer)
- break;
+ continue;
const uint8_t* ptr = buffer->ValuePtr()->data();
auto& values = buffer_info.values;
diff --git a/src/amberscript/parser.cc b/src/amberscript/parser.cc
index fbe703c..50760f0 100644
--- a/src/amberscript/parser.cc
+++ b/src/amberscript/parser.cc
@@ -14,6 +14,7 @@
#include "src/amberscript/parser.h"
+#include <algorithm>
#include <cassert>
#include <limits>
#include <map>
@@ -21,7 +22,9 @@
#include <utility>
#include <vector>
+#include "src/image.h"
#include "src/make_unique.h"
+#include "src/sampler.h"
#include "src/shader_data.h"
#include "src/tokenizer.h"
#include "src/type_parser.h"
@@ -51,30 +54,41 @@ ProbeSSBOCommand::Comparator ToComparator(const std::string& in) {
return ProbeSSBOCommand::Comparator::kLessOrEqual;
}
-std::unique_ptr<type::Type> ToType(const std::string& str) {
+std::unique_ptr<type::Type> ToType(const std::string& str_in) {
+ std::string str = str_in;
+
+ bool is_array = false;
+ if (str.length() > 2 && str[str.length() - 2] == '[' &&
+ str[str.length() - 1] == ']') {
+ is_array = true;
+ str = str.substr(0, str.length() - 2);
+ }
+
TypeParser parser;
- if (str == "int8")
- return parser.Parse("R8_SINT");
- if (str == "int16")
- return parser.Parse("R16_SINT");
- if (str == "int32")
- return parser.Parse("R32_SINT");
- if (str == "int64")
- return parser.Parse("R64_SINT");
- if (str == "uint8")
- return parser.Parse("R8_UINT");
- if (str == "uint16")
- return parser.Parse("R16_UINT");
- if (str == "uint32")
- return parser.Parse("R32_UINT");
- if (str == "uint64")
- return parser.Parse("R64_UINT");
- if (str == "float")
- return parser.Parse("R32_SFLOAT");
- if (str == "double")
- return parser.Parse("R64_SFLOAT");
-
- if (str.length() > 7 && str.substr(0, 3) == "vec") {
+ std::unique_ptr<type::Type> type;
+ if (str == "int8") {
+ type = parser.Parse("R8_SINT");
+ } else if (str == "int16") {
+ type = parser.Parse("R16_SINT");
+ } else if (str == "int32") {
+ type = parser.Parse("R32_SINT");
+ } else if (str == "int64") {
+ type = parser.Parse("R64_SINT");
+ } else if (str == "uint8") {
+ type = parser.Parse("R8_UINT");
+ } else if (str == "uint16") {
+ type = parser.Parse("R16_UINT");
+ } else if (str == "uint32") {
+ type = parser.Parse("R32_UINT");
+ } else if (str == "uint64") {
+ type = parser.Parse("R64_UINT");
+ } else if (str == "float16") {
+ type = parser.Parse("R16_SFLOAT");
+ } else if (str == "float") {
+ type = parser.Parse("R32_SFLOAT");
+ } else if (str == "double") {
+ type = parser.Parse("R64_SFLOAT");
+ } else if (str.length() > 7 && str.substr(0, 3) == "vec") {
if (str[4] != '<' || str[str.length() - 1] != '>')
return nullptr;
@@ -82,7 +96,7 @@ std::unique_ptr<type::Type> ToType(const std::string& str) {
if (component_count < 2 || component_count > 4)
return nullptr;
- auto type = ToType(str.substr(5, str.length() - 6));
+ type = ToType(str.substr(5, str.length() - 6));
if (!type)
return nullptr;
@@ -92,10 +106,7 @@ std::unique_ptr<type::Type> ToType(const std::string& str) {
}
type->SetRowCount(static_cast<uint32_t>(component_count));
- return type;
- }
-
- if (str.length() > 9 && str.substr(0, 3) == "mat") {
+ } else if (str.length() > 9 && str.substr(0, 3) == "mat") {
if (str[4] != 'x' || str[6] != '<' || str[str.length() - 1] != '>')
return nullptr;
@@ -107,7 +118,7 @@ std::unique_ptr<type::Type> ToType(const std::string& str) {
if (row_count < 2 || row_count > 4)
return nullptr;
- auto type = ToType(str.substr(7, str.length() - 8));
+ type = ToType(str.substr(7, str.length() - 8));
if (!type)
return nullptr;
if (!type->IsNumber() || type->IsArray() || type->IsVec() ||
@@ -117,14 +128,153 @@ std::unique_ptr<type::Type> ToType(const std::string& str) {
type->SetRowCount(static_cast<uint32_t>(row_count));
type->SetColumnCount(static_cast<uint32_t>(column_count));
- return type;
}
- return nullptr;
+
+ if (!type)
+ return nullptr;
+ if (is_array)
+ type->SetIsRuntimeArray();
+
+ return type;
+}
+
+AddressMode StrToAddressMode(std::string str) {
+ if (str == "repeat")
+ return AddressMode::kRepeat;
+ if (str == "mirrored_repeat")
+ return AddressMode::kMirroredRepeat;
+ if (str == "clamp_to_edge")
+ return AddressMode::kClampToEdge;
+ if (str == "clamp_to_border")
+ return AddressMode::kClampToBorder;
+ if (str == "mirror_clamp_to_edge")
+ return AddressMode::kMirrorClampToEdge;
+
+ return AddressMode::kUnknown;
+}
+
+CompareOp StrToCompareOp(const std::string& str) {
+ if (str == "never")
+ return CompareOp::kNever;
+ if (str == "less")
+ return CompareOp::kLess;
+ if (str == "equal")
+ return CompareOp::kEqual;
+ if (str == "less_or_equal")
+ return CompareOp::kLessOrEqual;
+ if (str == "greater")
+ return CompareOp::kGreater;
+ if (str == "not_equal")
+ return CompareOp::kNotEqual;
+ if (str == "greater_or_equal")
+ return CompareOp::kGreaterOrEqual;
+ if (str == "always")
+ return CompareOp::kAlways;
+
+ return CompareOp::kUnknown;
+}
+
+StencilOp StrToStencilOp(const std::string& str) {
+ if (str == "keep")
+ return StencilOp::kKeep;
+ if (str == "zero")
+ return StencilOp::kZero;
+ if (str == "replace")
+ return StencilOp::kReplace;
+ if (str == "increment_and_clamp")
+ return StencilOp::kIncrementAndClamp;
+ if (str == "decrement_and_clamp")
+ return StencilOp::kDecrementAndClamp;
+ if (str == "invert")
+ return StencilOp::kInvert;
+ if (str == "increment_and_wrap")
+ return StencilOp::kIncrementAndWrap;
+ if (str == "decrement_and_wrap")
+ return StencilOp::kDecrementAndWrap;
+
+ return StencilOp::kUnknown;
+}
+
+Result ParseBufferData(Buffer* buffer,
+ Tokenizer* tokenizer,
+ bool from_data_file) {
+ auto fmt = buffer->GetFormat();
+ const auto& segs = fmt->GetSegments();
+ size_t seg_idx = 0;
+ uint32_t value_count = 0;
+
+ std::vector<Value> values;
+ for (auto token = tokenizer->NextToken();; token = tokenizer->NextToken()) {
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS()) {
+ if (from_data_file) {
+ break;
+ } else {
+ return Result("missing BUFFER END command");
+ }
+ }
+ if (token->IsIdentifier() && token->AsString() == "END")
+ break;
+ if (!token->IsInteger() && !token->IsDouble() && !token->IsHex())
+ return Result("invalid BUFFER data value: " + token->ToOriginalString());
+
+ while (segs[seg_idx].IsPadding()) {
+ ++seg_idx;
+ if (seg_idx >= segs.size())
+ seg_idx = 0;
+ }
+
+ Value v;
+ if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
+ token->ConvertToDouble();
+
+ double val = token->IsHex() ? static_cast<double>(token->AsHex())
+ : token->AsDouble();
+ v.SetDoubleValue(val);
+ ++value_count;
+ } else {
+ if (token->IsDouble()) {
+ return Result("invalid BUFFER data value: " +
+ token->ToOriginalString());
+ }
+
+ uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
+ v.SetIntValue(val);
+ ++value_count;
+ }
+ ++seg_idx;
+ if (seg_idx >= segs.size())
+ seg_idx = 0;
+
+ values.emplace_back(v);
+ }
+ // Write final padding bytes
+ while (segs[seg_idx].IsPadding()) {
+ ++seg_idx;
+ if (seg_idx >= segs.size())
+ break;
+ }
+
+ buffer->SetValueCount(value_count);
+ Result r = buffer->SetData(std::move(values));
+ if (!r.IsSuccess())
+ return r;
+
+ return {};
+}
+
+constexpr uint32_t valid_samples[] = {1, 2, 4, 8, 16, 32, 64};
+
+bool IsValidSampleCount(uint32_t samples) {
+ return (std::find(std::begin(valid_samples), std::end(valid_samples),
+ samples) != std::end(valid_samples));
}
} // namespace
-Parser::Parser() : amber::Parser() {}
+Parser::Parser() : amber::Parser(nullptr) {}
+Parser::Parser(Delegate* delegate) : amber::Parser(delegate) {}
Parser::~Parser() = default;
@@ -139,8 +289,8 @@ Result Parser::Parse(const std::string& data) {
token = tokenizer_->NextToken()) {
if (token->IsEOL())
continue;
- if (!token->IsString())
- return Result(make_error("expected string"));
+ if (!token->IsIdentifier())
+ return Result(make_error("expected identifier"));
Result r;
std::string tok = token->AsString();
@@ -154,6 +304,8 @@ Result Parser::Parse(const std::string& data) {
r = ParseDeviceFeature();
} else if (tok == "DEVICE_EXTENSION") {
r = ParseDeviceExtension();
+ } else if (tok == "IMAGE") {
+ r = ParseImage();
} else if (tok == "INSTANCE_EXTENSION") {
r = ParseInstanceExtension();
} else if (tok == "PIPELINE") {
@@ -166,6 +318,10 @@ Result Parser::Parse(const std::string& data) {
r = ParseShaderBlock();
} else if (tok == "STRUCT") {
r = ParseStruct();
+ } else if (tok == "SAMPLER") {
+ r = ParseSampler();
+ } else if (tok == "VIRTUAL_FILE") {
+ r = ParseVirtualFile();
} else {
r = Result("unknown token: " + tok);
}
@@ -189,7 +345,7 @@ Result Parser::Parse(const std::string& data) {
if (!r.IsSuccess())
return r;
}
- Result r = pipeline->AddColorAttachment(buf, 0);
+ Result r = pipeline->AddColorAttachment(buf, 0, 0);
if (!r.IsSuccess())
return r;
}
@@ -207,8 +363,9 @@ Result Parser::Parse(const std::string& data) {
}
bool Parser::IsRepeatable(const std::string& name) const {
- return name == "CLEAR" || name == "CLEAR_COLOR" || name == "COPY" ||
- name == "EXPECT" || name == "RUN";
+ return name == "CLEAR" || name == "CLEAR_COLOR" || name == "CLEAR_DEPTH" ||
+ name == "CLEAR_STENCIL" || name == "COPY" || name == "EXPECT" ||
+ name == "RUN" || name == "DEBUG";
}
// The given |name| must be one of the repeatable commands or this method
@@ -218,12 +375,18 @@ Result Parser::ParseRepeatableCommand(const std::string& name) {
return ParseClear();
if (name == "CLEAR_COLOR")
return ParseClearColor();
+ if (name == "CLEAR_DEPTH")
+ return ParseClearDepth();
+ if (name == "CLEAR_STENCIL")
+ return ParseClearStencil();
if (name == "COPY")
return ParseCopy();
if (name == "EXPECT")
return ParseExpect();
if (name == "RUN")
return ParseRun();
+ if (name == "DEBUG")
+ return ParseDebug();
return Result("invalid repeatable command: " + name);
}
@@ -284,12 +447,13 @@ Result Parser::ValidateEndOfStatement(const std::string& name) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return {};
- return Result("extra parameters after " + name);
+ return Result("extra parameters after " + name + ": " +
+ token->ToOriginalString());
}
Result Parser::ParseShaderBlock() {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid token when looking for shader type");
ShaderType type = kShaderTypeVertex;
@@ -300,13 +464,13 @@ Result Parser::ParseShaderBlock() {
auto shader = MakeUnique<Shader>(type);
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid token when looking for shader name");
shader->SetName(token->AsString());
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid token when looking for shader format");
std::string fmt = token->AsString();
@@ -318,6 +482,7 @@ Result Parser::ParseShaderBlock() {
}
shader->SetFormat(kShaderFormatSpirvAsm);
shader->SetData(kPassThroughShader);
+ shader->SetTargetEnv("spv1.0");
r = script_->AddShader(std::move(shader));
if (!r.IsSuccess())
@@ -333,6 +498,40 @@ Result Parser::ParseShaderBlock() {
shader->SetFormat(format);
+ token = tokenizer_->PeekNextToken();
+ if (token->IsIdentifier() && token->AsString() == "TARGET_ENV") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() && !token->IsString())
+ return Result("expected target environment after TARGET_ENV");
+ shader->SetTargetEnv(token->AsString());
+ }
+
+ token = tokenizer_->PeekNextToken();
+ if (token->IsIdentifier() && token->AsString() == "VIRTUAL_FILE") {
+ tokenizer_->NextToken(); // Skip VIRTUAL_FILE
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() && !token->IsString())
+ return Result("expected virtual file path after VIRTUAL_FILE");
+
+ auto path = token->AsString();
+
+ std::string data;
+ r = script_->GetVirtualFile(path, &data);
+ if (!r.IsSuccess())
+ return r;
+
+ shader->SetData(data);
+ shader->SetFilePath(path);
+
+ r = script_->AddShader(std::move(shader));
+ if (!r.IsSuccess())
+ return r;
+
+ return ValidateEndOfStatement("SHADER command");
+ }
+
r = ValidateEndOfStatement("SHADER command");
if (!r.IsSuccess())
return r;
@@ -343,8 +542,12 @@ Result Parser::ParseShaderBlock() {
shader->SetData(data);
+ auto path = "embedded-shaders/" + shader->GetName();
+ script_->AddVirtualFile(path, data);
+ shader->SetFilePath(path);
+
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "END")
+ if (!token->IsIdentifier() || token->AsString() != "END")
return Result("SHADER missing END command");
r = script_->AddShader(std::move(shader));
@@ -356,7 +559,7 @@ Result Parser::ParseShaderBlock() {
Result Parser::ParsePipelineBlock() {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid token when looking for pipeline type");
PipelineType type = PipelineType::kCompute;
@@ -367,7 +570,7 @@ Result Parser::ParsePipelineBlock() {
auto pipeline = MakeUnique<Pipeline>(type);
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid token when looking for pipeline name");
pipeline->SetName(token->AsString());
@@ -386,8 +589,8 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name,
token = tokenizer_->NextToken()) {
if (token->IsEOL())
continue;
- if (!token->IsString())
- return Result("expected string");
+ if (!token->IsIdentifier())
+ return Result("expected identifier");
Result r;
std::string tok = token->AsString();
@@ -409,6 +612,14 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name,
r = ParsePipelineSet(pipeline.get());
} else if (tok == "COMPILE_OPTIONS") {
r = ParsePipelineShaderCompileOptions(pipeline.get());
+ } else if (tok == "POLYGON_MODE") {
+ r = ParsePipelinePolygonMode(pipeline.get());
+ } else if (tok == "DEPTH") {
+ r = ParsePipelineDepth(pipeline.get());
+ } else if (tok == "STENCIL") {
+ r = ParsePipelineStencil(pipeline.get());
+ } else if (tok == "SUBGROUP") {
+ r = ParsePipelineSubgroup(pipeline.get());
} else {
r = Result("unknown token in pipeline block: " + tok);
}
@@ -416,7 +627,7 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name,
return r;
}
- if (!token->IsString() || token->AsString() != "END")
+ if (!token->IsIdentifier() || token->AsString() != "END")
return Result(cmd_name + " missing END command");
Result r = script_->AddPipeline(std::move(pipeline));
@@ -428,7 +639,7 @@ Result Parser::ParsePipelineBody(const std::string& cmd_name,
Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid token in ATTACH command");
auto* shader = script_->GetShader(token->AsString());
@@ -445,7 +656,7 @@ Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
return r;
return {};
}
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid token after ATTACH");
bool set_shader_type = false;
@@ -453,7 +664,7 @@ Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
auto type = token->AsString();
if (type == "TYPE") {
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid type in ATTACH");
Result r = ToShaderType(token->AsString(), &shader_type);
@@ -463,7 +674,7 @@ Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
set_shader_type = true;
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("ATTACH TYPE requires an ENTRY_POINT");
type = token->AsString();
@@ -480,7 +691,7 @@ Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
if (type == "ENTRY_POINT") {
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing shader name in ATTACH ENTRY_POINT command");
r = pipeline->SetShaderEntryPoint(shader, token->AsString());
@@ -491,7 +702,7 @@ Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
}
while (true) {
- if (token->IsString() && token->AsString() == "SPECIALIZE") {
+ if (token->IsIdentifier() && token->AsString() == "SPECIALIZE") {
r = ParseShaderSpecialization(pipeline);
if (!r.IsSuccess())
return r;
@@ -500,9 +711,10 @@ Result Parser::ParsePipelineAttach(Pipeline* pipeline) {
} else {
if (token->IsEOL() || token->IsEOS())
return {};
- if (token->IsString())
+ if (token->IsIdentifier())
return Result("unknown ATTACH parameter: " + token->AsString());
- return Result("extra parameters after ATTACH command");
+ return Result("extra parameters after ATTACH command: " +
+ token->ToOriginalString());
}
}
}
@@ -515,11 +727,11 @@ Result Parser::ParseShaderSpecialization(Pipeline* pipeline) {
auto spec_id = token->AsUint32();
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "AS")
+ if (!token->IsIdentifier() || token->AsString() != "AS")
return Result("expected AS as next token");
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("expected data type in SPECIALIZE subcommand");
auto type = ToType(token->AsString());
@@ -558,7 +770,7 @@ Result Parser::ParseShaderSpecialization(Pipeline* pipeline) {
Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing shader name in SHADER_OPTIMIZATION command");
auto* shader = script_->GetShader(token->AsString());
@@ -567,7 +779,8 @@ Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) {
token = tokenizer_->NextToken();
if (!token->IsEOL())
- return Result("extra parameters after SHADER_OPTIMIZATION command");
+ return Result("extra parameters after SHADER_OPTIMIZATION command: " +
+ token->ToOriginalString());
std::vector<std::string> optimizations;
while (true) {
@@ -576,8 +789,8 @@ Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) {
continue;
if (token->IsEOS())
return Result("SHADER_OPTIMIZATION missing END command");
- if (!token->IsString())
- return Result("SHADER_OPTIMIZATION options must be strings");
+ if (!token->IsIdentifier())
+ return Result("SHADER_OPTIMIZATION options must be identifiers");
if (token->AsString() == "END")
break;
@@ -593,7 +806,7 @@ Result Parser::ParsePipelineShaderOptimizations(Pipeline* pipeline) {
Result Parser::ParsePipelineShaderCompileOptions(Pipeline* pipeline) {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing shader name in COMPILE_OPTIONS command");
auto* shader = script_->GetShader(token->AsString());
@@ -606,7 +819,8 @@ Result Parser::ParsePipelineShaderCompileOptions(Pipeline* pipeline) {
token = tokenizer_->NextToken();
if (!token->IsEOL())
- return Result("extra parameters after COMPILE_OPTIONS command");
+ return Result("extra parameters after COMPILE_OPTIONS command: " +
+ token->ToOriginalString());
std::vector<std::string> options;
while (true) {
@@ -628,6 +842,93 @@ Result Parser::ParsePipelineShaderCompileOptions(Pipeline* pipeline) {
return ValidateEndOfStatement("COMPILE_OPTIONS command");
}
+Result Parser::ParsePipelineSubgroup(Pipeline* pipeline) {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("missing shader name in SUBGROUP command");
+
+ auto* shader = script_->GetShader(token->AsString());
+ if (!shader)
+ return Result("unknown shader in SUBGROUP command");
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS())
+ return Result("SUBGROUP missing END command");
+ if (!token->IsIdentifier())
+ return Result("SUBGROUP options must be identifiers");
+ if (token->AsString() == "END")
+ break;
+
+ if (token->AsString() == "FULLY_POPULATED") {
+ if (!script_->IsRequiredFeature(
+ "SubgroupSizeControl.computeFullSubgroups"))
+ return Result(
+ "missing DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups");
+ token = tokenizer_->NextToken();
+ if (token->IsEOL() || token->IsEOS())
+ return Result("missing value for FULLY_POPULATED command");
+ bool isOn = false;
+ if (token->AsString() == "on") {
+ isOn = true;
+ } else if (token->AsString() == "off") {
+ isOn = false;
+ } else {
+ return Result("invalid value for FULLY_POPULATED command");
+ }
+ Result r = pipeline->SetShaderRequireFullSubgroups(shader, isOn);
+ if (!r.IsSuccess())
+ return r;
+
+ } else if (token->AsString() == "VARYING_SIZE") {
+ if (!script_->IsRequiredFeature(
+ "SubgroupSizeControl.subgroupSizeControl"))
+ return Result(
+ "missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl");
+ token = tokenizer_->NextToken();
+ if (token->IsEOL() || token->IsEOS())
+ return Result("missing value for VARYING_SIZE command");
+ bool isOn = false;
+ if (token->AsString() == "on") {
+ isOn = true;
+ } else if (token->AsString() == "off") {
+ isOn = false;
+ } else {
+ return Result("invalid value for VARYING_SIZE command");
+ }
+ Result r = pipeline->SetShaderVaryingSubgroupSize(shader, isOn);
+ if (!r.IsSuccess())
+ return r;
+ } else if (token->AsString() == "REQUIRED_SIZE") {
+ if (!script_->IsRequiredFeature(
+ "SubgroupSizeControl.subgroupSizeControl"))
+ return Result(
+ "missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl");
+ token = tokenizer_->NextToken();
+ if (token->IsEOL() || token->IsEOS())
+ return Result("missing size for REQUIRED_SIZE command");
+ Result r;
+ if (token->IsInteger()) {
+ r = pipeline->SetShaderRequiredSubgroupSize(shader, token->AsUint32());
+ } else if (token->AsString() == "MIN") {
+ r = pipeline->SetShaderRequiredSubgroupSizeToMinimum(shader);
+ } else if (token->AsString() == "MAX") {
+ r = pipeline->SetShaderRequiredSubgroupSizeToMaximum(shader);
+ } else {
+ return Result("invalid size for REQUIRED_SIZE command");
+ }
+ if (!r.IsSuccess())
+ return r;
+ } else {
+ return Result("SUBGROUP invalid value for SUBGROUP " + token->AsString());
+ }
+ }
+
+ return ValidateEndOfStatement("SUBGROUP command");
+}
+
Result Parser::ParsePipelineFramebufferSize(Pipeline* pipeline) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
@@ -650,10 +951,30 @@ Result Parser::ParsePipelineFramebufferSize(Pipeline* pipeline) {
Result Parser::ToBufferType(const std::string& name, BufferType* type) {
assert(type);
- if (name == "uniform")
+ if (name == "color")
+ *type = BufferType::kColor;
+ else if (name == "depth_stencil")
+ *type = BufferType::kDepthStencil;
+ else if (name == "push_constant")
+ *type = BufferType::kPushConstant;
+ else if (name == "uniform")
*type = BufferType::kUniform;
+ else if (name == "uniform_dynamic")
+ *type = BufferType::kUniformDynamic;
else if (name == "storage")
*type = BufferType::kStorage;
+ else if (name == "storage_dynamic")
+ *type = BufferType::kStorageDynamic;
+ else if (name == "storage_image")
+ *type = BufferType::kStorageImage;
+ else if (name == "sampled_image")
+ *type = BufferType::kSampledImage;
+ else if (name == "combined_image_sampler")
+ *type = BufferType::kCombinedImageSampler;
+ else if (name == "uniform_texel_buffer")
+ *type = BufferType::kUniformTexelBuffer;
+ else if (name == "storage_texel_buffer")
+ *type = BufferType::kStorageTexelBuffer;
else
return Result("unknown buffer_type: " + name);
@@ -662,107 +983,300 @@ Result Parser::ToBufferType(const std::string& name, BufferType* type) {
Result Parser::ParsePipelineBind(Pipeline* pipeline) {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
- return Result("missing BUFFER in BIND command");
- if (token->AsString() != "BUFFER")
- return Result("missing BUFFER in BIND command");
- token = tokenizer_->NextToken();
- if (!token->IsString())
- return Result("missing buffer name in BIND command");
+ if (!token->IsIdentifier()) {
+ return Result(
+ "missing BUFFER, BUFFER_ARRAY, SAMPLER, or SAMPLER_ARRAY in BIND "
+ "command");
+ }
- auto* buffer = script_->GetBuffer(token->AsString());
- if (!buffer)
- return Result("unknown buffer: " + token->AsString());
+ auto object_type = token->AsString();
- token = tokenizer_->NextToken();
- if (token->IsString() && token->AsString() == "AS") {
+ if (object_type == "BUFFER" || object_type == "BUFFER_ARRAY") {
+ bool is_buffer_array = object_type == "BUFFER_ARRAY";
token = tokenizer_->NextToken();
- if (!token->IsString())
- return Result("invalid token for BUFFER type");
+ if (!token->IsIdentifier())
+ return Result("missing buffer name in BIND command");
+
+ auto* buffer = script_->GetBuffer(token->AsString());
+ if (!buffer)
+ return Result("unknown buffer: " + token->AsString());
+ std::vector<Buffer*> buffers = {buffer};
+
+ if (is_buffer_array) {
+ // Check for additional buffer names
+ token = tokenizer_->PeekNextToken();
+ while (token->IsIdentifier() && token->AsString() != "AS" &&
+ token->AsString() != "KERNEL" &&
+ token->AsString() != "DESCRIPTOR_SET") {
+ tokenizer_->NextToken();
+ buffer = script_->GetBuffer(token->AsString());
+ if (!buffer)
+ return Result("unknown buffer: " + token->AsString());
+ buffers.push_back(buffer);
+ token = tokenizer_->PeekNextToken();
+ }
- if (token->AsString() == "color") {
- token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "LOCATION")
- return Result("BIND missing LOCATION");
+ if (buffers.size() < 2)
+ return Result("expecting multiple buffer names for BUFFER_ARRAY");
+ }
+ BufferType buffer_type = BufferType::kUnknown;
+ token = tokenizer_->NextToken();
+ if (token->IsIdentifier() && token->AsString() == "AS") {
token = tokenizer_->NextToken();
- if (!token->IsInteger())
- return Result("invalid value for BIND LOCATION");
+ if (!token->IsIdentifier())
+ return Result("invalid token for BUFFER type");
- buffer->SetBufferType(BufferType::kColor);
-
- Result r = pipeline->AddColorAttachment(buffer, token->AsUint32());
- if (!r.IsSuccess())
- return r;
- } else if (token->AsString() == "depth_stencil") {
- buffer->SetBufferType(BufferType::kDepth);
- Result r = pipeline->SetDepthBuffer(buffer);
- if (!r.IsSuccess())
- return r;
- } else if (token->AsString() == "push_constant") {
- buffer->SetBufferType(BufferType::kPushConstant);
- Result r = pipeline->SetPushConstantBuffer(buffer);
- if (!r.IsSuccess())
- return r;
- } else {
- BufferType type = BufferType::kColor;
- Result r = ToBufferType(token->AsString(), &type);
+ Result r = ToBufferType(token->AsString(), &buffer_type);
if (!r.IsSuccess())
return r;
- if (buffer->GetBufferType() == BufferType::kUnknown)
- buffer->SetBufferType(type);
- else if (buffer->GetBufferType() != type)
- return Result("buffer type does not match intended usage");
+ if (buffer_type == BufferType::kColor) {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "LOCATION")
+ return Result("BIND missing LOCATION");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("invalid value for BIND LOCATION");
+ auto location = token->AsUint32();
+
+ uint32_t base_mip_level = 0;
+ token = tokenizer_->PeekNextToken();
+ if (token->IsIdentifier() && token->AsString() == "BASE_MIP_LEVEL") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("invalid value for BASE_MIP_LEVEL");
+
+ base_mip_level = token->AsUint32();
+
+ if (base_mip_level >= buffer->GetMipLevels())
+ return Result(
+ "base mip level (now " + token->AsString() +
+ ") needs to be larger than the number of buffer mip maps (" +
+ std::to_string(buffer->GetMipLevels()) + ")");
+ }
+
+ r = pipeline->AddColorAttachment(buffer, location, base_mip_level);
+ if (!r.IsSuccess())
+ return r;
+
+ } else if (buffer_type == BufferType::kDepthStencil) {
+ r = pipeline->SetDepthStencilBuffer(buffer);
+ if (!r.IsSuccess())
+ return r;
+
+ } else if (buffer_type == BufferType::kPushConstant) {
+ r = pipeline->SetPushConstantBuffer(buffer);
+ if (!r.IsSuccess())
+ return r;
+
+ } else if (buffer_type == BufferType::kCombinedImageSampler) {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "SAMPLER")
+ return Result("expecting SAMPLER for combined image sampler");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("missing sampler name in BIND command");
+
+ auto* sampler = script_->GetSampler(token->AsString());
+ if (!sampler)
+ return Result("unknown sampler: " + token->AsString());
+
+ for (auto& buf : buffers)
+ buf->SetSampler(sampler);
+ }
}
- }
- if (buffer->GetBufferType() == BufferType::kUnknown ||
- buffer->GetBufferType() == BufferType::kStorage ||
- buffer->GetBufferType() == BufferType::kUniform) {
- // If AS was parsed above consume the next token.
- if (buffer->GetBufferType() != BufferType::kUnknown)
- token = tokenizer_->NextToken();
- // DESCRIPTOR_SET requires a buffer type to have been specified.
- if (buffer->GetBufferType() != BufferType::kUnknown && token->IsString() &&
- token->AsString() == "DESCRIPTOR_SET") {
+ // The OpenCL bindings can be typeless which allows for the kUnknown
+ // buffer type.
+ if (buffer_type == BufferType::kUnknown ||
+ buffer_type == BufferType::kStorage ||
+ buffer_type == BufferType::kUniform ||
+ buffer_type == BufferType::kStorageDynamic ||
+ buffer_type == BufferType::kUniformDynamic ||
+ buffer_type == BufferType::kStorageImage ||
+ buffer_type == BufferType::kSampledImage ||
+ buffer_type == BufferType::kCombinedImageSampler ||
+ buffer_type == BufferType::kUniformTexelBuffer ||
+ buffer_type == BufferType::kStorageTexelBuffer) {
+ // If the buffer type is known, then we proccessed the AS block above
+ // and have to advance to the next token. Otherwise, we're already on
+ // the next token and don't want to advance.
+ if (buffer_type != BufferType::kUnknown)
+ token = tokenizer_->NextToken();
+
+ // DESCRIPTOR_SET requires a buffer type to have been specified.
+ if (token->IsIdentifier() && token->AsString() == "DESCRIPTOR_SET") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("invalid value for DESCRIPTOR_SET in BIND command");
+ uint32_t descriptor_set = token->AsUint32();
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "BINDING")
+ return Result("missing BINDING for BIND command");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("invalid value for BINDING in BIND command");
+
+ auto binding = token->AsUint32();
+ uint32_t base_mip_level = 0;
+
+ if (buffer_type == BufferType::kStorageImage ||
+ buffer_type == BufferType::kSampledImage ||
+ buffer_type == BufferType::kCombinedImageSampler) {
+ token = tokenizer_->PeekNextToken();
+ if (token->IsIdentifier() && token->AsString() == "BASE_MIP_LEVEL") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("invalid value for BASE_MIP_LEVEL");
+
+ base_mip_level = token->AsUint32();
+
+ if (base_mip_level >= buffer->GetMipLevels())
+ return Result("base mip level (now " + token->AsString() +
+ ") needs to be larger than the number of buffer "
+ "mip maps (" +
+ std::to_string(buffer->GetMipLevels()) + ")");
+ }
+ }
+
+ std::vector<uint32_t> dynamic_offsets(buffers.size(), 0);
+ if (buffer_type == BufferType::kUniformDynamic ||
+ buffer_type == BufferType::kStorageDynamic) {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "OFFSET")
+ return Result("expecting an OFFSET for dynamic buffer type");
+
+ for (size_t i = 0; i < buffers.size(); i++) {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger()) {
+ if (i > 0) {
+ return Result(
+ "expecting an OFFSET value for each buffer in the array");
+ } else {
+ return Result("expecting an integer value for OFFSET");
+ }
+ }
+
+ dynamic_offsets[i] = token->AsUint32();
+ }
+ }
+
+ pipeline->ClearBuffers(descriptor_set, binding);
+ for (size_t i = 0; i < buffers.size(); i++) {
+ pipeline->AddBuffer(buffers[i], buffer_type, descriptor_set, binding,
+ base_mip_level, dynamic_offsets[i]);
+ }
+ } else if (token->IsIdentifier() && token->AsString() == "KERNEL") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("missing kernel arg identifier");
+
+ if (token->AsString() == "ARG_NAME") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("expected argument identifier");
+
+ pipeline->AddBuffer(buffer, buffer_type, token->AsString());
+ } else if (token->AsString() == "ARG_NUMBER") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected argument number");
+
+ pipeline->AddBuffer(buffer, buffer_type, token->AsUint32());
+ } else {
+ return Result("missing ARG_NAME or ARG_NUMBER keyword");
+ }
+ } else {
+ return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
+ }
+ }
+ } else if (object_type == "SAMPLER" || object_type == "SAMPLER_ARRAY") {
+ bool is_sampler_array = object_type == "SAMPLER_ARRAY";
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("missing sampler name in BIND command");
+
+ auto* sampler = script_->GetSampler(token->AsString());
+ if (!sampler)
+ return Result("unknown sampler: " + token->AsString());
+ std::vector<Sampler*> samplers = {sampler};
+
+ if (is_sampler_array) {
+ // Check for additional sampler names
+ token = tokenizer_->PeekNextToken();
+ while (token->IsIdentifier() && token->AsString() != "KERNEL" &&
+ token->AsString() != "DESCRIPTOR_SET") {
+ tokenizer_->NextToken();
+ sampler = script_->GetSampler(token->AsString());
+ if (!sampler)
+ return Result("unknown sampler: " + token->AsString());
+ samplers.push_back(sampler);
+ token = tokenizer_->PeekNextToken();
+ }
+
+ if (samplers.size() < 2)
+ return Result("expecting multiple sampler names for SAMPLER_ARRAY");
+ }
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("expected a string token for BIND command");
+
+ if (token->AsString() == "DESCRIPTOR_SET") {
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("invalid value for DESCRIPTOR_SET in BIND command");
uint32_t descriptor_set = token->AsUint32();
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "BINDING")
+ if (!token->IsIdentifier() || token->AsString() != "BINDING")
return Result("missing BINDING for BIND command");
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("invalid value for BINDING in BIND command");
- pipeline->AddBuffer(buffer, descriptor_set, token->AsUint32());
- } else if (token->IsString() && token->AsString() == "KERNEL") {
+
+ uint32_t binding = token->AsUint32();
+ pipeline->ClearSamplers(descriptor_set, binding);
+ for (const auto& s : samplers) {
+ pipeline->AddSampler(s, descriptor_set, binding);
+ }
+ } else if (token->AsString() == "KERNEL") {
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing kernel arg identifier");
if (token->AsString() == "ARG_NAME") {
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("expected argument identifier");
- pipeline->AddBuffer(buffer, token->AsString());
+ pipeline->AddSampler(sampler, token->AsString());
} else if (token->AsString() == "ARG_NUMBER") {
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("expected argument number");
- pipeline->AddBuffer(buffer, token->AsUint32());
+ pipeline->AddSampler(sampler, token->AsUint32());
} else {
return Result("missing ARG_NAME or ARG_NUMBER keyword");
}
} else {
return Result("missing DESCRIPTOR_SET or KERNEL for BIND command");
}
+ } else {
+ return Result("missing BUFFER or SAMPLER in BIND command");
}
return ValidateEndOfStatement("BIND command");
@@ -770,7 +1284,7 @@ Result Parser::ParsePipelineBind(Pipeline* pipeline) {
Result Parser::ParsePipelineVertexData(Pipeline* pipeline) {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing buffer name in VERTEX_DATA command");
auto* buffer = script_->GetBuffer(token->AsString());
@@ -778,15 +1292,69 @@ Result Parser::ParsePipelineVertexData(Pipeline* pipeline) {
return Result("unknown buffer: " + token->AsString());
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "LOCATION")
+ if (!token->IsIdentifier() || token->AsString() != "LOCATION")
return Result("VERTEX_DATA missing LOCATION");
token = tokenizer_->NextToken();
if (!token->IsInteger())
return Result("invalid value for VERTEX_DATA LOCATION");
+ const uint32_t location = token->AsUint32();
+
+ InputRate rate = InputRate::kVertex;
+ uint32_t offset = 0;
+ Format* format = buffer->GetFormat();
+ uint32_t stride = 0;
+
+ token = tokenizer_->PeekNextToken();
+ while (token->IsIdentifier()) {
+ if (token->AsString() == "RATE") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("missing input rate value for RATE");
+ if (token->AsString() == "instance") {
+ rate = InputRate::kInstance;
+ } else if (token->AsString() != "vertex") {
+ return Result("expecting 'vertex' or 'instance' for RATE value");
+ }
+ } else if (token->AsString() == "OFFSET") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected unsigned integer for OFFSET");
+ offset = token->AsUint32();
+ } else if (token->AsString() == "STRIDE") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected unsigned integer for STRIDE");
+ stride = token->AsUint32();
+ if (stride == 0)
+ return Result("STRIDE needs to be larger than zero");
+ } else if (token->AsString() == "FORMAT") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("vertex data FORMAT must be an identifier");
+ auto type = script_->ParseType(token->AsString());
+ if (!type)
+ return Result("invalid vertex data FORMAT");
+ auto fmt = MakeUnique<Format>(type);
+ format = fmt.get();
+ script_->RegisterFormat(std::move(fmt));
+ } else {
+ return Result("unexpected identifier for VERTEX_DATA command: " +
+ token->ToOriginalString());
+ }
- buffer->SetBufferType(BufferType::kVertex);
- Result r = pipeline->AddVertexBuffer(buffer, token->AsUint32());
+ token = tokenizer_->PeekNextToken();
+ }
+
+ if (stride == 0)
+ stride = format->SizeInBytes();
+
+ Result r =
+ pipeline->AddVertexBuffer(buffer, location, rate, format, offset, stride);
if (!r.IsSuccess())
return r;
@@ -795,14 +1363,13 @@ Result Parser::ParsePipelineVertexData(Pipeline* pipeline) {
Result Parser::ParsePipelineIndexData(Pipeline* pipeline) {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing buffer name in INDEX_DATA command");
auto* buffer = script_->GetBuffer(token->AsString());
if (!buffer)
return Result("unknown buffer: " + token->AsString());
- buffer->SetBufferType(BufferType::kIndex);
Result r = pipeline->SetIndexBuffer(buffer);
if (!r.IsSuccess())
return r;
@@ -818,18 +1385,18 @@ Result Parser::ParsePipelineSet(Pipeline* pipeline) {
}
auto token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "KERNEL")
+ if (!token->IsIdentifier() || token->AsString() != "KERNEL")
return Result("missing KERNEL in SET command");
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("expected ARG_NAME or ARG_NUMBER");
std::string arg_name = "";
uint32_t arg_no = std::numeric_limits<uint32_t>::max();
if (token->AsString() == "ARG_NAME") {
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("expected argument identifier");
arg_name = token->AsString();
@@ -844,17 +1411,20 @@ Result Parser::ParsePipelineSet(Pipeline* pipeline) {
}
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "AS")
+ if (!token->IsIdentifier() || token->AsString() != "AS")
return Result("missing AS in SET command");
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("expected data type");
auto type = ToType(token->AsString());
if (!type)
return Result("invalid data type '" + token->AsString() + "' provided");
+ if (type->IsVec() || type->IsMatrix() || type->IsArray() || type->IsStruct())
+ return Result("data type must be a scalar type");
+
token = tokenizer_->NextToken();
if (!token->IsInteger() && !token->IsDouble())
return Result("expected data value");
@@ -878,9 +1448,283 @@ Result Parser::ParsePipelineSet(Pipeline* pipeline) {
return ValidateEndOfStatement("SET command");
}
+Result Parser::ParsePipelinePolygonMode(Pipeline* pipeline) {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("missing mode in POLYGON_MODE command");
+
+ auto mode = token->AsString();
+
+ if (mode == "fill")
+ pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kFill);
+ else if (mode == "line")
+ pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kLine);
+ else if (mode == "point")
+ pipeline->GetPipelineData()->SetPolygonMode(PolygonMode::kPoint);
+ else
+ return Result("invalid polygon mode: " + mode);
+
+ return ValidateEndOfStatement("POLYGON_MODE command");
+}
+
+Result Parser::ParsePipelineDepth(Pipeline* pipeline) {
+ while (true) {
+ auto token = tokenizer_->NextToken();
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS())
+ return Result("DEPTH missing END command");
+ if (!token->IsIdentifier())
+ return Result("DEPTH options must be identifiers");
+ if (token->AsString() == "END")
+ break;
+
+ if (token->AsString() == "TEST") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid value for TEST");
+
+ if (token->AsString() == "on")
+ pipeline->GetPipelineData()->SetEnableDepthTest(true);
+ else if (token->AsString() == "off")
+ pipeline->GetPipelineData()->SetEnableDepthTest(false);
+ else
+ return Result("invalid value for TEST: " + token->AsString());
+ } else if (token->AsString() == "CLAMP") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid value for CLAMP");
+
+ if (token->AsString() == "on")
+ pipeline->GetPipelineData()->SetEnableDepthClamp(true);
+ else if (token->AsString() == "off")
+ pipeline->GetPipelineData()->SetEnableDepthClamp(false);
+ else
+ return Result("invalid value for CLAMP: " + token->AsString());
+ } else if (token->AsString() == "WRITE") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid value for WRITE");
+
+ if (token->AsString() == "on")
+ pipeline->GetPipelineData()->SetEnableDepthWrite(true);
+ else if (token->AsString() == "off")
+ pipeline->GetPipelineData()->SetEnableDepthWrite(false);
+ else
+ return Result("invalid value for WRITE: " + token->AsString());
+ } else if (token->AsString() == "COMPARE_OP") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid value for COMPARE_OP");
+
+ CompareOp compare_op = StrToCompareOp(token->AsString());
+ if (compare_op != CompareOp::kUnknown) {
+ pipeline->GetPipelineData()->SetDepthCompareOp(compare_op);
+ } else {
+ return Result("invalid value for COMPARE_OP: " + token->AsString());
+ }
+ } else if (token->AsString() == "BOUNDS") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "min")
+ return Result("BOUNDS expecting min");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsDouble())
+ return Result("BOUNDS invalid value for min");
+ pipeline->GetPipelineData()->SetMinDepthBounds(token->AsFloat());
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "max")
+ return Result("BOUNDS expecting max");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsDouble())
+ return Result("BOUNDS invalid value for max");
+ pipeline->GetPipelineData()->SetMaxDepthBounds(token->AsFloat());
+ } else if (token->AsString() == "BIAS") {
+ pipeline->GetPipelineData()->SetEnableDepthBias(true);
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "constant")
+ return Result("BIAS expecting constant");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsDouble())
+ return Result("BIAS invalid value for constant");
+ pipeline->GetPipelineData()->SetDepthBiasConstantFactor(token->AsFloat());
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "clamp")
+ return Result("BIAS expecting clamp");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsDouble())
+ return Result("BIAS invalid value for clamp");
+ pipeline->GetPipelineData()->SetDepthBiasClamp(token->AsFloat());
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "slope")
+ return Result("BIAS expecting slope");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsDouble())
+ return Result("BIAS invalid value for slope");
+ pipeline->GetPipelineData()->SetDepthBiasSlopeFactor(token->AsFloat());
+ } else {
+ return Result("invalid value for DEPTH: " + token->AsString());
+ }
+ }
+
+ return ValidateEndOfStatement("DEPTH command");
+}
+
+Result Parser::ParsePipelineStencil(Pipeline* pipeline) {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("STENCIL missing face");
+
+ bool setFront = false;
+ bool setBack = false;
+
+ if (token->AsString() == "front") {
+ setFront = true;
+ } else if (token->AsString() == "back") {
+ setBack = true;
+ } else if (token->AsString() == "front_and_back") {
+ setFront = true;
+ setBack = true;
+ } else {
+ return Result("STENCIL invalid face: " + token->AsString());
+ }
+
+ while (true) {
+ token = tokenizer_->NextToken();
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS())
+ return Result("STENCIL missing END command");
+ if (!token->IsIdentifier())
+ return Result("STENCIL options must be identifiers");
+ if (token->AsString() == "END")
+ break;
+
+ if (token->AsString() == "TEST") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("STENCIL invalid value for TEST");
+
+ if (token->AsString() == "on")
+ pipeline->GetPipelineData()->SetEnableStencilTest(true);
+ else if (token->AsString() == "off")
+ pipeline->GetPipelineData()->SetEnableStencilTest(false);
+ else
+ return Result("STENCIL invalid value for TEST: " + token->AsString());
+ } else if (token->AsString() == "FAIL_OP") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("STENCIL invalid value for FAIL_OP");
+
+ StencilOp stencil_op = StrToStencilOp(token->AsString());
+ if (stencil_op == StencilOp::kUnknown) {
+ return Result("STENCIL invalid value for FAIL_OP: " +
+ token->AsString());
+ }
+ if (setFront)
+ pipeline->GetPipelineData()->SetFrontFailOp(stencil_op);
+ if (setBack)
+ pipeline->GetPipelineData()->SetBackFailOp(stencil_op);
+ } else if (token->AsString() == "PASS_OP") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("STENCIL invalid value for PASS_OP");
+
+ StencilOp stencil_op = StrToStencilOp(token->AsString());
+ if (stencil_op == StencilOp::kUnknown) {
+ return Result("STENCIL invalid value for PASS_OP: " +
+ token->AsString());
+ }
+ if (setFront)
+ pipeline->GetPipelineData()->SetFrontPassOp(stencil_op);
+ if (setBack)
+ pipeline->GetPipelineData()->SetBackPassOp(stencil_op);
+ } else if (token->AsString() == "DEPTH_FAIL_OP") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("STENCIL invalid value for DEPTH_FAIL_OP");
+
+ StencilOp stencil_op = StrToStencilOp(token->AsString());
+ if (stencil_op == StencilOp::kUnknown) {
+ return Result("STENCIL invalid value for DEPTH_FAIL_OP: " +
+ token->AsString());
+ }
+ if (setFront)
+ pipeline->GetPipelineData()->SetFrontDepthFailOp(stencil_op);
+ if (setBack)
+ pipeline->GetPipelineData()->SetBackDepthFailOp(stencil_op);
+ } else if (token->AsString() == "COMPARE_OP") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("STENCIL invalid value for COMPARE_OP");
+
+ CompareOp compare_op = StrToCompareOp(token->AsString());
+ if (compare_op == CompareOp::kUnknown) {
+ return Result("STENCIL invalid value for COMPARE_OP: " +
+ token->AsString());
+ }
+ if (setFront)
+ pipeline->GetPipelineData()->SetFrontCompareOp(compare_op);
+ if (setBack)
+ pipeline->GetPipelineData()->SetBackCompareOp(compare_op);
+ } else if (token->AsString() == "COMPARE_MASK") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("STENCIL invalid value for COMPARE_MASK");
+
+ if (setFront)
+ pipeline->GetPipelineData()->SetFrontCompareMask(token->AsUint32());
+ if (setBack)
+ pipeline->GetPipelineData()->SetBackCompareMask(token->AsUint32());
+ } else if (token->AsString() == "WRITE_MASK") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("STENCIL invalid value for WRITE_MASK");
+
+ if (setFront)
+ pipeline->GetPipelineData()->SetFrontWriteMask(token->AsUint32());
+ if (setBack)
+ pipeline->GetPipelineData()->SetBackWriteMask(token->AsUint32());
+ } else if (token->AsString() == "REFERENCE") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("STENCIL invalid value for REFERENCE");
+
+ if (setFront)
+ pipeline->GetPipelineData()->SetFrontReference(token->AsUint32());
+ if (setBack)
+ pipeline->GetPipelineData()->SetBackReference(token->AsUint32());
+ } else {
+ return Result("STENCIL invalid value for STENCIL: " + token->AsString());
+ }
+ }
+
+ return ValidateEndOfStatement("STENCIL command");
+}
+
Result Parser::ParseStruct() {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid STRUCT name provided");
auto struct_name = token->AsString();
@@ -895,7 +1739,7 @@ Result Parser::ParseStruct() {
return r;
token = tokenizer_->NextToken();
- if (token->IsString()) {
+ if (token->IsIdentifier()) {
if (token->AsString() != "STRIDE")
return Result("invalid token in STRUCT definition");
@@ -916,7 +1760,7 @@ Result Parser::ParseStruct() {
std::map<std::string, bool> seen;
for (;;) {
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid type for STRUCT member");
if (token->AsString() == "END")
break;
@@ -939,7 +1783,7 @@ Result Parser::ParseStruct() {
token = tokenizer_->NextToken();
if (token->IsEOL())
return Result("missing name for STRUCT member");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid name for STRUCT member");
auto member_name = token->AsString();
@@ -952,7 +1796,7 @@ Result Parser::ParseStruct() {
m->name = member_name;
token = tokenizer_->NextToken();
- while (token->IsString()) {
+ while (token->IsIdentifier()) {
if (token->AsString() == "OFFSET") {
token = tokenizer_->NextToken();
if (token->IsEOL())
@@ -998,7 +1842,7 @@ Result Parser::ParseStruct() {
Result Parser::ParseBuffer() {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid BUFFER name provided");
auto name = token->AsString();
@@ -1006,7 +1850,7 @@ Result Parser::ParseBuffer() {
return Result("missing BUFFER name");
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid BUFFER command provided");
std::unique_ptr<Buffer> buffer;
@@ -1019,8 +1863,8 @@ Result Parser::ParseBuffer() {
return r;
} else if (cmd == "FORMAT") {
token = tokenizer_->NextToken();
- if (!token->IsString())
- return Result("BUFFER FORMAT must be a string");
+ if (!token->IsIdentifier())
+ return Result("BUFFER FORMAT must be an identifier");
buffer = MakeUnique<Buffer>();
@@ -1031,6 +1875,39 @@ Result Parser::ParseBuffer() {
auto fmt = MakeUnique<Format>(type);
buffer->SetFormat(fmt.get());
script_->RegisterFormat(std::move(fmt));
+
+ token = tokenizer_->PeekNextToken();
+ while (token->IsIdentifier()) {
+ if (token->AsString() == "MIP_LEVELS") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("invalid value for MIP_LEVELS");
+
+ buffer->SetMipLevels(token->AsUint32());
+ } else if (token->AsString() == "FILE") {
+ tokenizer_->NextToken();
+ Result r = ParseBufferInitializerFile(buffer.get());
+
+ if (!r.IsSuccess())
+ return r;
+ } else if (token->AsString() == "SAMPLES") {
+ tokenizer_->NextToken();
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected integer value for SAMPLES");
+
+ const uint32_t samples = token->AsUint32();
+ if (!IsValidSampleCount(samples))
+ return Result("invalid sample count: " + token->ToOriginalString());
+
+ buffer->SetSamples(samples);
+ } else {
+ break;
+ }
+ token = tokenizer_->PeekNextToken();
+ }
} else {
return Result("unknown BUFFER command provided: " + cmd);
}
@@ -1043,9 +1920,169 @@ Result Parser::ParseBuffer() {
return {};
}
+Result Parser::ParseImage() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("invalid IMAGE name provided");
+
+ auto name = token->AsString();
+ if (name == "DATA_TYPE" || name == "FORMAT")
+ return Result("missing IMAGE name");
+
+ std::unique_ptr<Buffer> buffer = MakeUnique<Buffer>();
+ buffer->SetName(name);
+ bool width_set = false;
+ bool height_set = false;
+ bool depth_set = false;
+
+ token = tokenizer_->PeekNextToken();
+ while (token->IsIdentifier()) {
+ if (token->AsString() == "FILL" || token->AsString() == "SERIES_FROM" ||
+ token->AsString() == "DATA") {
+ break;
+ }
+
+ tokenizer_->NextToken();
+
+ if (token->AsString() == "DATA_TYPE") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("IMAGE invalid data type");
+
+ auto type = script_->ParseType(token->AsString());
+ std::unique_ptr<Format> fmt;
+ if (type != nullptr) {
+ fmt = MakeUnique<Format>(type);
+ buffer->SetFormat(fmt.get());
+ } else {
+ auto new_type = ToType(token->AsString());
+ if (!new_type) {
+ return Result("invalid data type '" + token->AsString() +
+ "' provided");
+ }
+
+ fmt = MakeUnique<Format>(new_type.get());
+ buffer->SetFormat(fmt.get());
+ script_->RegisterType(std::move(new_type));
+ }
+ script_->RegisterFormat(std::move(fmt));
+ } else if (token->AsString() == "FORMAT") {
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("IMAGE FORMAT must be an identifier");
+
+ auto type = script_->ParseType(token->AsString());
+ if (!type)
+ return Result("invalid IMAGE FORMAT");
+
+ auto fmt = MakeUnique<Format>(type);
+ buffer->SetFormat(fmt.get());
+ script_->RegisterFormat(std::move(fmt));
+ } else if (token->AsString() == "MIP_LEVELS") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsInteger())
+ return Result("invalid value for MIP_LEVELS");
+
+ buffer->SetMipLevels(token->AsUint32());
+ } else if (token->AsString() == "DIM_1D") {
+ buffer->SetImageDimension(ImageDimension::k1D);
+ } else if (token->AsString() == "DIM_2D") {
+ buffer->SetImageDimension(ImageDimension::k2D);
+ } else if (token->AsString() == "DIM_3D") {
+ buffer->SetImageDimension(ImageDimension::k3D);
+ } else if (token->AsString() == "WIDTH") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger() || token->AsUint32() == 0)
+ return Result("expected positive IMAGE WIDTH");
+
+ buffer->SetWidth(token->AsUint32());
+ width_set = true;
+ } else if (token->AsString() == "HEIGHT") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger() || token->AsUint32() == 0)
+ return Result("expected positive IMAGE HEIGHT");
+
+ buffer->SetHeight(token->AsUint32());
+ height_set = true;
+ } else if (token->AsString() == "DEPTH") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger() || token->AsUint32() == 0)
+ return Result("expected positive IMAGE DEPTH");
+
+ buffer->SetDepth(token->AsUint32());
+ depth_set = true;
+ } else if (token->AsString() == "SAMPLES") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected integer value for SAMPLES");
+
+ const uint32_t samples = token->AsUint32();
+ if (!IsValidSampleCount(samples))
+ return Result("invalid sample count: " + token->ToOriginalString());
+
+ buffer->SetSamples(samples);
+ } else {
+ return Result("unknown IMAGE command provided: " +
+ token->ToOriginalString());
+ }
+ token = tokenizer_->PeekNextToken();
+ }
+
+ if (buffer->GetImageDimension() == ImageDimension::k3D && !depth_set)
+ return Result("expected IMAGE DEPTH");
+
+ if ((buffer->GetImageDimension() == ImageDimension::k3D ||
+ buffer->GetImageDimension() == ImageDimension::k2D) &&
+ !height_set) {
+ return Result("expected IMAGE HEIGHT");
+ }
+ if (!width_set)
+ return Result("expected IMAGE WIDTH");
+
+ const uint32_t size_in_items =
+ buffer->GetWidth() * buffer->GetHeight() * buffer->GetDepth();
+ buffer->SetElementCount(size_in_items);
+
+ // Parse initializers.
+ token = tokenizer_->NextToken();
+ if (token->IsIdentifier()) {
+ if (token->AsString() == "DATA") {
+ Result r = ParseBufferInitializerData(buffer.get());
+ if (!r.IsSuccess())
+ return r;
+
+ if (size_in_items != buffer->ElementCount()) {
+ return Result(
+ "Elements provided in data does not match size specified: " +
+ std::to_string(size_in_items) + " specified vs " +
+ std::to_string(buffer->ElementCount()) + " provided");
+ }
+ } else if (token->AsString() == "FILL") {
+ Result r = ParseBufferInitializerFill(buffer.get(), size_in_items);
+ if (!r.IsSuccess())
+ return r;
+ } else if (token->AsString() == "SERIES_FROM") {
+ Result r = ParseBufferInitializerSeries(buffer.get(), size_in_items);
+ if (!r.IsSuccess())
+ return r;
+ } else {
+ return Result("unexpected IMAGE token: " + token->AsString());
+ }
+ } else if (!token->IsEOL() && !token->IsEOS()) {
+ return Result("unexpected IMAGE token: " + token->ToOriginalString());
+ }
+
+ Result r = script_->AddBuffer(std::move(buffer));
+ if (!r.IsSuccess())
+ return r;
+
+ return {};
+}
+
Result Parser::ParseBufferInitializer(Buffer* buffer) {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("BUFFER invalid data type");
auto type = script_->ParseType(token->AsString());
@@ -1066,7 +2103,7 @@ Result Parser::ParseBufferInitializer(Buffer* buffer) {
script_->RegisterFormat(std::move(fmt));
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("BUFFER missing initializer");
if (token->AsString() == "STD140") {
@@ -1077,11 +2114,41 @@ Result Parser::ParseBufferInitializer(Buffer* buffer) {
token = tokenizer_->NextToken();
}
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("BUFFER missing initializer");
if (token->AsString() == "SIZE")
return ParseBufferInitializerSize(buffer);
+ if (token->AsString() == "WIDTH") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected an integer for WIDTH");
+ const uint32_t width = token->AsUint32();
+ if (width == 0)
+ return Result("expected WIDTH to be positive");
+ buffer->SetWidth(width);
+ buffer->SetImageDimension(ImageDimension::k2D);
+
+ token = tokenizer_->NextToken();
+ if (token->AsString() != "HEIGHT")
+ return Result("BUFFER HEIGHT missing");
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected an integer for HEIGHT");
+ const uint32_t height = token->AsUint32();
+ if (height == 0)
+ return Result("expected HEIGHT to be positive");
+ buffer->SetHeight(height);
+
+ token = tokenizer_->NextToken();
+ uint32_t size_in_items = width * height;
+ buffer->SetElementCount(size_in_items);
+ if (token->AsString() == "FILL")
+ return ParseBufferInitializerFill(buffer, size_in_items);
+ if (token->AsString() == "SERIES_FROM")
+ return ParseBufferInitializerSeries(buffer, size_in_items);
+ return {};
+ }
if (token->AsString() == "DATA")
return ParseBufferInitializerData(buffer);
@@ -1099,13 +2166,15 @@ Result Parser::ParseBufferInitializerSize(Buffer* buffer) {
buffer->SetElementCount(size_in_items);
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("BUFFER invalid initializer");
if (token->AsString() == "FILL")
return ParseBufferInitializerFill(buffer, size_in_items);
if (token->AsString() == "SERIES_FROM")
return ParseBufferInitializerSeries(buffer, size_in_items);
+ if (token->AsString() == "FILE")
+ return ParseBufferInitializerFile(buffer);
return Result("invalid BUFFER initializer provided");
}
@@ -1164,7 +2233,7 @@ Result Parser::ParseBufferInitializerSeries(Buffer* buffer,
}
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing BUFFER series_from inc_by");
if (token->AsString() != "INC_BY")
return Result("BUFFER series_from invalid command");
@@ -1197,70 +2266,71 @@ Result Parser::ParseBufferInitializerSeries(Buffer* buffer,
}
Result Parser::ParseBufferInitializerData(Buffer* buffer) {
- auto fmt = buffer->GetFormat();
- const auto& segs = fmt->GetSegments();
- size_t seg_idx = 0;
- uint32_t value_count = 0;
+ Result r = ParseBufferData(buffer, tokenizer_.get(), false);
- std::vector<Value> values;
- for (auto token = tokenizer_->NextToken();; token = tokenizer_->NextToken()) {
- if (token->IsEOL())
- continue;
- if (token->IsEOS())
- return Result("missing BUFFER END command");
- if (token->IsString() && token->AsString() == "END")
- break;
- if (!token->IsInteger() && !token->IsDouble() && !token->IsHex())
- return Result("invalid BUFFER data value: " + token->ToOriginalString());
+ if (!r.IsSuccess())
+ return r;
- while (segs[seg_idx].IsPadding()) {
- ++seg_idx;
- if (seg_idx >= segs.size())
- seg_idx = 0;
- }
+ return ValidateEndOfStatement("BUFFER data command");
+}
- Value v;
- if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
- token->ConvertToDouble();
+Result Parser::ParseBufferInitializerFile(Buffer* buffer) {
+ auto token = tokenizer_->NextToken();
- double val = token->IsHex() ? static_cast<double>(token->AsHex())
- : token->AsDouble();
- v.SetDoubleValue(val);
- ++value_count;
- } else {
- if (token->IsDouble()) {
- return Result("invalid BUFFER data value: " +
- token->ToOriginalString());
- }
+ if (!token->IsIdentifier())
+ return Result("invalid value for FILE");
- uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
- v.SetIntValue(val);
- ++value_count;
- }
- ++seg_idx;
- if (seg_idx >= segs.size())
- seg_idx = 0;
+ BufferDataFileType file_type = BufferDataFileType::kPng;
- values.emplace_back(v);
- }
- // Write final padding bytes
- while (segs[seg_idx].IsPadding()) {
- ++seg_idx;
- if (seg_idx >= segs.size())
- break;
+ if (token->AsString() == "TEXT") {
+ file_type = BufferDataFileType::kText;
+ token = tokenizer_->NextToken();
+ } else if (token->AsString() == "BINARY") {
+ file_type = BufferDataFileType::kBinary;
+ token = tokenizer_->NextToken();
+ } else if (token->AsString() == "PNG") {
+ token = tokenizer_->NextToken();
}
- buffer->SetValueCount(value_count);
- Result r = buffer->SetData(std::move(values));
+ if (!token->IsIdentifier())
+ return Result("missing file name for FILE");
+
+ if (!delegate_)
+ return Result("missing delegate");
+
+ BufferInfo info;
+ Result r = delegate_->LoadBufferData(token->AsString(), file_type, &info);
+
if (!r.IsSuccess())
return r;
- return ValidateEndOfStatement("BUFFER data command");
+ std::vector<uint8_t>* data = buffer->ValuePtr();
+
+ data->clear();
+ data->reserve(info.values.size());
+ for (auto v : info.values) {
+ data->push_back(v.AsUint8());
+ }
+
+ if (file_type == BufferDataFileType::kText) {
+ auto s = std::string(data->begin(), data->end());
+ Tokenizer tok(s);
+ r = ParseBufferData(buffer, &tok, true);
+ if (!r.IsSuccess())
+ return r;
+ } else {
+ buffer->SetElementCount(static_cast<uint32_t>(data->size()) /
+ buffer->GetFormat()->SizeInBytes());
+ buffer->SetWidth(info.width);
+ buffer->SetHeight(info.height);
+ }
+
+ return {};
}
Result Parser::ParseRun() {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing pipeline name for RUN command");
size_t line = tokenizer_->GetCurrentLine();
@@ -1298,7 +2368,8 @@ Result Parser::ParseRun() {
command_list_.push_back(std::move(cmd));
return ValidateEndOfStatement("RUN command");
}
- if (!token->IsString())
+
+ if (!token->IsIdentifier())
return Result("invalid token in RUN command: " + token->ToOriginalString());
if (token->AsString() == "DRAW_RECT") {
@@ -1315,7 +2386,7 @@ Result Parser::ParseRun() {
if (token->IsEOS() || token->IsEOL())
return Result("RUN DRAW_RECT command requires parameters");
- if (!token->IsString() || token->AsString() != "POS") {
+ if (!token->IsIdentifier() || token->AsString() != "POS") {
return Result("invalid token in RUN command: " +
token->ToOriginalString() + "; expected POS");
}
@@ -1324,7 +2395,8 @@ Result Parser::ParseRun() {
if (!token->IsInteger())
return Result("missing X position for RUN command");
- auto cmd = MakeUnique<DrawRectCommand>(pipeline, PipelineData{});
+ auto cmd =
+ MakeUnique<DrawRectCommand>(pipeline, *pipeline->GetPipelineData());
cmd->SetLine(line);
cmd->EnableOrtho();
@@ -1343,7 +2415,7 @@ Result Parser::ParseRun() {
cmd->SetY(token->AsFloat());
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "SIZE") {
+ if (!token->IsIdentifier() || token->AsString() != "SIZE") {
return Result("invalid token in RUN command: " +
token->ToOriginalString() + "; expected SIZE");
}
@@ -1370,6 +2442,93 @@ Result Parser::ParseRun() {
return ValidateEndOfStatement("RUN command");
}
+ if (token->AsString() == "DRAW_GRID") {
+ if (!pipeline->IsGraphics())
+ return Result("RUN command requires graphics pipeline");
+
+ if (pipeline->GetVertexBuffers().size() > 0) {
+ return Result(
+ "RUN DRAW_GRID is not supported in a pipeline with "
+ "vertex buffers attached");
+ }
+
+ token = tokenizer_->NextToken();
+ if (token->IsEOS() || token->IsEOL())
+ return Result("RUN DRAW_GRID command requires parameters");
+
+ if (!token->IsIdentifier() || token->AsString() != "POS") {
+ return Result("invalid token in RUN command: " +
+ token->ToOriginalString() + "; expected POS");
+ }
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("missing X position for RUN command");
+
+ auto cmd =
+ MakeUnique<DrawGridCommand>(pipeline, *pipeline->GetPipelineData());
+ cmd->SetLine(line);
+
+ Result r = token->ConvertToDouble();
+ if (!r.IsSuccess())
+ return r;
+ cmd->SetX(token->AsFloat());
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("missing Y position for RUN command");
+
+ r = token->ConvertToDouble();
+ if (!r.IsSuccess())
+ return r;
+ cmd->SetY(token->AsFloat());
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "SIZE") {
+ return Result("invalid token in RUN command: " +
+ token->ToOriginalString() + "; expected SIZE");
+ }
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("missing width value for RUN command");
+
+ r = token->ConvertToDouble();
+ if (!r.IsSuccess())
+ return r;
+ cmd->SetWidth(token->AsFloat());
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("missing height value for RUN command");
+
+ r = token->ConvertToDouble();
+ if (!r.IsSuccess())
+ return r;
+ cmd->SetHeight(token->AsFloat());
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "CELLS") {
+ return Result("invalid token in RUN command: " +
+ token->ToOriginalString() + "; expected CELLS");
+ }
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("missing columns value for RUN command");
+
+ cmd->SetColumns(token->AsUint32());
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("missing rows value for RUN command");
+
+ cmd->SetRows(token->AsUint32());
+
+ command_list_.push_back(std::move(cmd));
+ return ValidateEndOfStatement("RUN command");
+ }
+
if (token->AsString() == "DRAW_ARRAY") {
if (!pipeline->IsGraphics())
return Result("RUN command requires graphics pipeline");
@@ -1378,11 +2537,11 @@ Result Parser::ParseRun() {
return Result("RUN DRAW_ARRAY requires attached vertex buffer");
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "AS")
+ if (!token->IsIdentifier() || token->AsString() != "AS")
return Result("missing AS for RUN command");
token = tokenizer_->NextToken();
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result("invalid topology for RUN command: " +
token->ToOriginalString());
}
@@ -1391,37 +2550,37 @@ Result Parser::ParseRun() {
if (topo == Topology::kUnknown)
return Result("invalid topology for RUN command: " + token->AsString());
- token = tokenizer_->NextToken();
bool indexed = false;
- if (token->IsString() && token->AsString() == "INDEXED") {
- if (!pipeline->GetIndexBuffer())
- return Result("RUN DRAW_ARRAYS INDEXED requires attached index buffer");
-
- indexed = true;
- token = tokenizer_->NextToken();
- }
-
uint32_t start_idx = 0;
uint32_t count = 0;
- if (!token->IsEOS() && !token->IsEOL()) {
- if (!token->IsString() || token->AsString() != "START_IDX")
- return Result("missing START_IDX for RUN command");
+ uint32_t start_instance = 0;
+ uint32_t instance_count = 1;
- token = tokenizer_->NextToken();
- if (!token->IsInteger()) {
- return Result("invalid START_IDX value for RUN command: " +
- token->ToOriginalString());
- }
- if (token->AsInt32() < 0)
- return Result("START_IDX value must be >= 0 for RUN command");
- start_idx = token->AsUint32();
+ token = tokenizer_->PeekNextToken();
+ while (!token->IsEOS() && !token->IsEOL()) {
token = tokenizer_->NextToken();
- if (!token->IsEOS() && !token->IsEOL()) {
- if (!token->IsString() || token->AsString() != "COUNT")
- return Result("missing COUNT for RUN command");
+ if (!token->IsIdentifier())
+ return Result("expecting identifier for RUN command");
+ if (token->AsString() == "INDEXED") {
+ if (!pipeline->GetIndexBuffer()) {
+ return Result(
+ "RUN DRAW_ARRAYS INDEXED requires attached index buffer");
+ }
+
+ indexed = true;
+ } else if (token->AsString() == "START_IDX") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger()) {
+ return Result("invalid START_IDX value for RUN command: " +
+ token->ToOriginalString());
+ }
+ if (token->AsInt32() < 0)
+ return Result("START_IDX value must be >= 0 for RUN command");
+ start_idx = token->AsUint32();
+ } else if (token->AsString() == "COUNT") {
token = tokenizer_->NextToken();
if (!token->IsInteger()) {
return Result("invalid COUNT value for RUN command: " +
@@ -1431,25 +2590,57 @@ Result Parser::ParseRun() {
return Result("COUNT value must be > 0 for RUN command");
count = token->AsUint32();
+ } else if (token->AsString() == "INSTANCE_COUNT") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger()) {
+ return Result("invalid INSTANCE_COUNT value for RUN command: " +
+ token->ToOriginalString());
+ }
+ if (token->AsInt32() <= 0)
+ return Result("INSTANCE_COUNT value must be > 0 for RUN command");
+
+ instance_count = token->AsUint32();
+ } else if (token->AsString() == "START_INSTANCE") {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger()) {
+ return Result("invalid START_INSTANCE value for RUN command: " +
+ token->ToOriginalString());
+ }
+ if (token->AsInt32() < 0)
+ return Result("START_INSTANCE value must be >= 0 for RUN command");
+ start_instance = token->AsUint32();
+ } else {
+ return Result("Unexpected identifier for RUN command: " +
+ token->ToOriginalString());
}
+
+ token = tokenizer_->PeekNextToken();
}
+
+ uint32_t vertex_count =
+ indexed ? pipeline->GetIndexBuffer()->ElementCount()
+ : pipeline->GetVertexBuffers()[0].buffer->ElementCount();
+
// If we get here then we never set count, as if count was set it must
// be > 0.
- if (count == 0) {
- count =
- pipeline->GetVertexBuffers()[0].buffer->ElementCount() - start_idx;
- }
-
- if (start_idx + count >
- pipeline->GetVertexBuffers()[0].buffer->ElementCount()) {
- return Result("START_IDX plus COUNT exceeds vertex buffer data size");
+ if (count == 0)
+ count = vertex_count - start_idx;
+
+ if (start_idx + count > vertex_count) {
+ if (indexed)
+ return Result("START_IDX plus COUNT exceeds index buffer data size");
+ else
+ return Result("START_IDX plus COUNT exceeds vertex buffer data size");
}
- auto cmd = MakeUnique<DrawArraysCommand>(pipeline, PipelineData{});
+ auto cmd =
+ MakeUnique<DrawArraysCommand>(pipeline, *pipeline->GetPipelineData());
cmd->SetLine(line);
cmd->SetTopology(topo);
cmd->SetFirstVertexIndex(start_idx);
cmd->SetVertexCount(count);
+ cmd->SetInstanceCount(instance_count);
+ cmd->SetFirstInstance(start_instance);
if (indexed)
cmd->EnableIndexed();
@@ -1461,9 +2652,226 @@ Result Parser::ParseRun() {
return Result("invalid token in RUN command: " + token->AsString());
}
+Result Parser::ParseDebug() {
+ // DEBUG extends a RUN with debugger test cases
+ auto res = ParseRun();
+ if (!res.IsSuccess()) {
+ return res;
+ }
+
+ // As ParseRun() succeeded, we know it emplaced a run command at the back of
+ // the command_list_.
+ auto cmd = command_list_.back().get();
+
+ // We also know this command must derive from PipelineCommand, as it is
+ // runnable.
+ auto pipeline_cmd = static_cast<PipelineCommand*>(cmd);
+ auto pipeline = pipeline_cmd->GetPipeline();
+
+ auto dbg = debug::Script::Create();
+ for (auto token = tokenizer_->NextToken();; token = tokenizer_->NextToken()) {
+ if (token->IsEOL())
+ continue;
+ if (token->IsEOS())
+ return Result("missing DEBUG END command");
+ if (token->IsIdentifier() && token->AsString() == "END")
+ break;
+
+ if (token->AsString() == "THREAD") {
+ res = ParseDebugThread(dbg.get(), pipeline);
+ if (!res.IsSuccess()) {
+ return res;
+ }
+ } else {
+ return Result("invalid token in DEBUG command: " + token->AsString());
+ }
+ }
+
+ cmd->SetDebugScript(std::move(dbg));
+
+ return Result();
+}
+
+Result Parser::ParseDebugThread(debug::Events* dbg, Pipeline* pipeline) {
+ auto token = tokenizer_->NextToken();
+ if (token->AsString() == "GLOBAL_INVOCATION_ID") {
+ for (auto& shader : pipeline->GetShaders()) {
+ shader.SetEmitDebugInfo(true);
+ }
+
+ uint32_t invocation[3] = {};
+ for (int i = 0; i < 3; i++) {
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected invocation index");
+ invocation[i] = token->AsUint32();
+ }
+
+ auto thread = debug::ThreadScript::Create();
+ auto result = ParseDebugThreadBody(thread.get());
+ if (!result.IsSuccess()) {
+ return result;
+ }
+
+ dbg->BreakOnComputeGlobalInvocation(invocation[0], invocation[1],
+ invocation[2], thread);
+ } else if (token->AsString() == "VERTEX_INDEX") {
+ for (auto& shader : pipeline->GetShaders()) {
+ if (shader.GetShaderType() == kShaderTypeVertex) {
+ shader.SetEmitDebugInfo(true);
+ }
+ }
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected vertex index");
+ auto vertex_index = token->AsUint32();
+
+ auto thread = debug::ThreadScript::Create();
+ auto result = ParseDebugThreadBody(thread.get());
+ if (!result.IsSuccess()) {
+ return result;
+ }
+
+ dbg->BreakOnVertexIndex(vertex_index, thread);
+ } else if (token->AsString() == "FRAGMENT_WINDOW_SPACE_POSITION") {
+ for (auto& shader : pipeline->GetShaders()) {
+ if (shader.GetShaderType() == kShaderTypeFragment) {
+ shader.SetEmitDebugInfo(true);
+ }
+ }
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected x unsigned integer coordinate");
+ auto x = token->AsUint32();
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger())
+ return Result("expected y unsigned integer coordinate");
+ auto y = token->AsUint32();
+
+ auto thread = debug::ThreadScript::Create();
+ auto result = ParseDebugThreadBody(thread.get());
+ if (!result.IsSuccess()) {
+ return result;
+ }
+
+ dbg->BreakOnFragmentWindowSpacePosition(x, y, thread);
+ } else {
+ return Result("expected GLOBAL_INVOCATION_ID or VERTEX_INDEX");
+ }
+
+ return Result();
+}
+
+Result Parser::ParseDebugThreadBody(debug::Thread* thread) {
+ for (auto token = tokenizer_->NextToken();; token = tokenizer_->NextToken()) {
+ if (token->IsEOL()) {
+ continue;
+ }
+ if (token->IsEOS()) {
+ return Result("missing THREAD END command");
+ }
+ if (token->IsIdentifier() && token->AsString() == "END") {
+ break;
+ }
+
+ if (token->AsString() == "EXPECT") {
+ token = tokenizer_->NextToken();
+ if (token->AsString() == "LOCATION") {
+ debug::Location location;
+ token = tokenizer_->NextToken();
+ if (!token->IsString()) {
+ return Result("expected file name string");
+ }
+ location.file = token->AsString();
+
+ token = tokenizer_->NextToken();
+ if (!token->IsInteger()) {
+ return Result("expected line number");
+ }
+ location.line = token->AsUint32();
+
+ std::string line_source;
+ token = tokenizer_->NextToken();
+ if (token->IsString()) {
+ line_source = token->AsString();
+ }
+
+ thread->ExpectLocation(location, line_source);
+ } else if (token->AsString() == "LOCAL") {
+ auto name = tokenizer_->NextToken();
+ if (!name->IsString()) {
+ return Result("expected variable name");
+ }
+
+ if (tokenizer_->NextToken()->AsString() != "EQ") {
+ return Result("expected EQ");
+ }
+
+ auto value = tokenizer_->NextToken();
+ if (value->IsHex() || value->IsInteger()) {
+ thread->ExpectLocal(name->AsString(), value->AsInt64());
+ } else if (value->IsDouble()) {
+ thread->ExpectLocal(name->AsString(), value->AsDouble());
+ } else if (value->IsString()) {
+ thread->ExpectLocal(name->AsString(), value->AsString());
+ } else {
+ return Result("expected variable value");
+ }
+ } else if (token->AsString() == "CALLSTACK") {
+ std::vector<debug::StackFrame> stack;
+ for (auto tok = tokenizer_->NextToken(); tok->AsString() != "END";
+ tok = tokenizer_->NextToken()) {
+ if (tok->IsEOL()) {
+ continue;
+ }
+ debug::StackFrame frame;
+ if (!tok->IsString()) {
+ return Result("expected stack frame name");
+ }
+ frame.name = tok->AsString();
+
+ tok = tokenizer_->NextToken();
+ if (tok->IsString()) {
+ frame.location.file = tok->AsString();
+ tok = tokenizer_->NextToken();
+ if (tok->IsInteger()) {
+ frame.location.line = tok->AsUint32();
+ } else if (!tok->IsEOL()) {
+ return Result(
+ "expected end of line or stack frame location number");
+ }
+ } else if (!tok->IsEOL()) {
+ return Result(
+ "expected end of line or stack frame location file name");
+ }
+
+ stack.emplace_back(frame);
+ }
+ thread->ExpectCallstack(stack);
+ } else {
+ return Result("expected LOCATION or LOCAL");
+ }
+ } else if (token->AsString() == "STEP_IN") {
+ thread->StepIn();
+ } else if (token->AsString() == "STEP_OUT") {
+ thread->StepOut();
+ } else if (token->AsString() == "STEP_OVER") {
+ thread->StepOver();
+ } else if (token->AsString() == "CONTINUE") {
+ thread->Continue();
+ } else {
+ return Result("invalid token in THREAD block: " + token->AsString());
+ }
+ }
+ return Result();
+}
+
Result Parser::ParseClear() {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing pipeline name for CLEAR command");
size_t line = tokenizer_->GetCurrentLine();
@@ -1499,7 +2907,7 @@ Result Parser::ParseValues(const std::string& name,
}
if (type::Type::IsFloat(segs[seg_idx].GetFormatMode())) {
- if (!token->IsInteger() && !token->IsDouble()) {
+ if (!token->IsInteger() && !token->IsDouble() && !token->IsHex()) {
return Result(std::string("Invalid value provided to ") + name +
" command: " + token->ToOriginalString());
}
@@ -1510,12 +2918,13 @@ Result Parser::ParseValues(const std::string& name,
v.SetDoubleValue(token->AsDouble());
} else {
- if (!token->IsInteger()) {
+ if (!token->IsInteger() && !token->IsHex()) {
return Result(std::string("Invalid value provided to ") + name +
" command: " + token->ToOriginalString());
}
- v.SetIntValue(token->AsUint64());
+ uint64_t val = token->IsHex() ? token->AsHex() : token->AsUint64();
+ v.SetIntValue(val);
}
++seg_idx;
if (seg_idx >= segs.size())
@@ -1529,7 +2938,7 @@ Result Parser::ParseValues(const std::string& name,
Result Parser::ParseExpect() {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid buffer name in EXPECT command");
if (token->AsString() == "IDX")
@@ -1551,7 +2960,7 @@ Result Parser::ParseExpect() {
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid comparator in EXPECT command");
if (token->AsString() == "EQ_BUFFER" || token->AsString() == "RMSE_BUFFER" ||
@@ -1559,7 +2968,7 @@ Result Parser::ParseExpect() {
auto type = token->AsString();
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid buffer name in EXPECT " + type + " command");
auto* buffer_2 = script_->GetBuffer(token->AsString());
@@ -1592,7 +3001,7 @@ Result Parser::ParseExpect() {
cmd->SetComparator(CompareBufferCommand::Comparator::kRmse);
token = tokenizer_->NextToken();
- if (!token->IsString() && token->AsString() == "TOLERANCE")
+ if (!token->IsIdentifier() && token->AsString() == "TOLERANCE")
return Result("missing TOLERANCE for EXPECT RMSE_BUFFER");
token = tokenizer_->NextToken();
@@ -1608,7 +3017,7 @@ Result Parser::ParseExpect() {
cmd->SetComparator(CompareBufferCommand::Comparator::kHistogramEmd);
token = tokenizer_->NextToken();
- if (!token->IsString() && token->AsString() == "TOLERANCE")
+ if (!token->IsIdentifier() && token->AsString() == "TOLERANCE")
return Result("missing TOLERANCE for EXPECT EQ_HISTOGRAM_EMD_BUFFER");
token = tokenizer_->NextToken();
@@ -1651,7 +3060,7 @@ Result Parser::ParseExpect() {
token = tokenizer_->NextToken();
}
- if (token->IsString() && token->AsString() == "SIZE") {
+ if (token->IsIdentifier() && token->AsString() == "SIZE") {
if (!has_y_val)
return Result("invalid Y value in EXPECT command");
@@ -1674,7 +3083,7 @@ Result Parser::ParseExpect() {
probe->SetHeight(token->AsFloat());
token = tokenizer_->NextToken();
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result("invalid token in EXPECT command:" +
token->ToOriginalString());
}
@@ -1712,43 +3121,63 @@ Result Parser::ParseExpect() {
probe->SetA(token->AsFloat() / 255.f);
}
+ token = tokenizer_->NextToken();
+ if (token->IsIdentifier() && token->AsString() == "TOLERANCE") {
+ std::vector<Probe::Tolerance> tolerances;
+
+ Result r = ParseTolerances(&tolerances);
+
+ if (!r.IsSuccess())
+ return r;
+
+ if (tolerances.empty())
+ return Result("TOLERANCE specified but no tolerances provided");
+
+ if (!probe->IsRGBA() && tolerances.size() > 3) {
+ return Result(
+ "TOLERANCE for an RGB comparison has a maximum of 3 values");
+ }
+
+ if (tolerances.size() > 4) {
+ return Result(
+ "TOLERANCE for an RGBA comparison has a maximum of 4 values");
+ }
+
+ probe->SetTolerances(std::move(tolerances));
+ token = tokenizer_->NextToken();
+ }
+
+ if (!token->IsEOL() && !token->IsEOS()) {
+ return Result("extra parameters after EXPECT command: " +
+ token->ToOriginalString());
+ }
+
command_list_.push_back(std::move(probe));
- return ValidateEndOfStatement("EXPECT command");
+
+ return {};
}
auto probe = MakeUnique<ProbeSSBOCommand>(buffer);
probe->SetLine(line);
- if (token->IsString() && token->AsString() == "TOLERANCE") {
+ if (token->IsIdentifier() && token->AsString() == "TOLERANCE") {
std::vector<Probe::Tolerance> tolerances;
- token = tokenizer_->NextToken();
- while (!token->IsEOL() && !token->IsEOS()) {
- if (!token->IsInteger() && !token->IsDouble())
- break;
+ Result r = ParseTolerances(&tolerances);
- Result r = token->ConvertToDouble();
- if (!r.IsSuccess())
- return r;
+ if (!r.IsSuccess())
+ return r;
- double value = token->AsDouble();
- token = tokenizer_->NextToken();
- if (token->IsString() && token->AsString() == "%") {
- tolerances.push_back(Probe::Tolerance{true, value});
- token = tokenizer_->NextToken();
- } else {
- tolerances.push_back(Probe::Tolerance{false, value});
- }
- }
if (tolerances.empty())
return Result("TOLERANCE specified but no tolerances provided");
if (tolerances.size() > 4)
return Result("TOLERANCE has a maximum of 4 values");
probe->SetTolerances(std::move(tolerances));
+ token = tokenizer_->NextToken();
}
- if (!token->IsString() || !IsComparator(token->AsString())) {
+ if (!token->IsIdentifier() || !IsComparator(token->AsString())) {
return Result("unexpected token in EXPECT command: " +
token->ToOriginalString());
}
@@ -1786,7 +3215,7 @@ Result Parser::ParseCopy() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("missing buffer name after COPY");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid buffer name after COPY");
size_t line = tokenizer_->GetCurrentLine();
@@ -1802,7 +3231,7 @@ Result Parser::ParseCopy() {
token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("missing 'TO' after COPY and buffer name");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("expected 'TO' after COPY and buffer name");
name = token->AsString();
@@ -1812,7 +3241,7 @@ Result Parser::ParseCopy() {
token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("missing buffer name after TO");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid buffer name after TO");
name = token->AsString();
@@ -1820,16 +3249,11 @@ Result Parser::ParseCopy() {
if (!buffer_to)
return Result("COPY destination buffer was not declared");
- if (buffer_to->GetBufferType() == amber::BufferType::kUnknown) {
- // Set destination buffer to mirror origin buffer
- buffer_to->SetBufferType(buffer_from->GetBufferType());
- buffer_to->SetWidth(buffer_from->GetWidth());
- buffer_to->SetHeight(buffer_from->GetHeight());
- buffer_to->SetElementCount(buffer_from->ElementCount());
- }
+ // Set destination buffer to mirror origin buffer
+ buffer_to->SetWidth(buffer_from->GetWidth());
+ buffer_to->SetHeight(buffer_from->GetHeight());
+ buffer_to->SetElementCount(buffer_from->ElementCount());
- if (buffer_from->GetBufferType() != buffer_to->GetBufferType())
- return Result("cannot COPY between buffers of different types");
if (buffer_from == buffer_to)
return Result("COPY origin and destination buffers are identical");
@@ -1842,7 +3266,7 @@ Result Parser::ParseCopy() {
Result Parser::ParseClearColor() {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing pipeline name for CLEAR_COLOR command");
size_t line = tokenizer_->GetCurrentLine();
@@ -1903,11 +3327,75 @@ Result Parser::ParseClearColor() {
return ValidateEndOfStatement("CLEAR_COLOR command");
}
+Result Parser::ParseClearDepth() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("missing pipeline name for CLEAR_DEPTH command");
+
+ size_t line = tokenizer_->GetCurrentLine();
+
+ auto* pipeline = script_->GetPipeline(token->AsString());
+ if (!pipeline) {
+ return Result("unknown pipeline for CLEAR_DEPTH command: " +
+ token->AsString());
+ }
+ if (!pipeline->IsGraphics()) {
+ return Result("CLEAR_DEPTH command requires graphics pipeline");
+ }
+
+ auto cmd = MakeUnique<ClearDepthCommand>(pipeline);
+ cmd->SetLine(line);
+
+ token = tokenizer_->NextToken();
+ if (token->IsEOL() || token->IsEOS())
+ return Result("missing value for CLEAR_DEPTH command");
+ if (!token->IsDouble()) {
+ return Result("invalid value for CLEAR_DEPTH command: " +
+ token->ToOriginalString());
+ }
+ cmd->SetValue(token->AsFloat());
+
+ command_list_.push_back(std::move(cmd));
+ return ValidateEndOfStatement("CLEAR_DEPTH command");
+}
+
+Result Parser::ParseClearStencil() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("missing pipeline name for CLEAR_STENCIL command");
+
+ size_t line = tokenizer_->GetCurrentLine();
+
+ auto* pipeline = script_->GetPipeline(token->AsString());
+ if (!pipeline) {
+ return Result("unknown pipeline for CLEAR_STENCIL command: " +
+ token->AsString());
+ }
+ if (!pipeline->IsGraphics()) {
+ return Result("CLEAR_STENCIL command requires graphics pipeline");
+ }
+
+ auto cmd = MakeUnique<ClearStencilCommand>(pipeline);
+ cmd->SetLine(line);
+
+ token = tokenizer_->NextToken();
+ if (token->IsEOL() || token->IsEOS())
+ return Result("missing value for CLEAR_STENCIL command");
+ if (!token->IsInteger() || token->AsInt32() < 0 || token->AsInt32() > 255) {
+ return Result("invalid value for CLEAR_STENCIL command: " +
+ token->ToOriginalString());
+ }
+ cmd->SetValue(token->AsUint32());
+
+ command_list_.push_back(std::move(cmd));
+ return ValidateEndOfStatement("CLEAR_STENCIL command");
+}
+
Result Parser::ParseDeviceFeature() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("missing feature name for DEVICE_FEATURE command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("invalid feature name for DEVICE_FEATURE command");
if (!script_->IsKnownFeature(token->AsString()))
return Result("unknown feature name for DEVICE_FEATURE command");
@@ -1937,8 +3425,8 @@ Result Parser::ParseRepeat() {
token = tokenizer_->NextToken()) {
if (token->IsEOL())
continue;
- if (!token->IsString())
- return Result("expected string");
+ if (!token->IsIdentifier())
+ return Result("expected identifier");
std::string tok = token->AsString();
if (tok == "END")
@@ -1950,7 +3438,7 @@ Result Parser::ParseRepeat() {
if (!r.IsSuccess())
return r;
}
- if (!token->IsString() || token->AsString() != "END")
+ if (!token->IsIdentifier() || token->AsString() != "END")
return Result("missing END for REPEAT command");
auto cmd = MakeUnique<RepeatCommand>(count);
@@ -1964,7 +3452,7 @@ Result Parser::ParseRepeat() {
Result Parser::ParseDerivePipelineBlock() {
auto token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() == "FROM")
+ if (!token->IsIdentifier() || token->AsString() == "FROM")
return Result("missing pipeline name for DERIVE_PIPELINE command");
std::string name = token->AsString();
@@ -1972,11 +3460,11 @@ Result Parser::ParseDerivePipelineBlock() {
return Result("duplicate pipeline name for DERIVE_PIPELINE command");
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "FROM")
+ if (!token->IsIdentifier() || token->AsString() != "FROM")
return Result("missing FROM in DERIVE_PIPELINE command");
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("missing parent pipeline name in DERIVE_PIPELINE command");
Pipeline* parent = script_->GetPipeline(token->AsString());
@@ -1997,7 +3485,7 @@ Result Parser::ParseDeviceExtension() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("DEVICE_EXTENSION missing name");
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result("DEVICE_EXTENSION invalid name: " +
token->ToOriginalString());
}
@@ -2011,7 +3499,7 @@ Result Parser::ParseInstanceExtension() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("INSTANCE_EXTENSION missing name");
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result("INSTANCE_EXTENSION invalid name: " +
token->ToOriginalString());
}
@@ -2023,14 +3511,14 @@ Result Parser::ParseInstanceExtension() {
Result Parser::ParseSet() {
auto token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "ENGINE_DATA")
+ if (!token->IsIdentifier() || token->AsString() != "ENGINE_DATA")
return Result("SET missing ENGINE_DATA");
token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("SET missing variable to be set");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("SET invalid variable to set: " + token->ToOriginalString());
if (token->AsString() != "fence_timeout_ms")
@@ -2047,5 +3535,187 @@ Result Parser::ParseSet() {
return ValidateEndOfStatement("SET command");
}
+Result Parser::ParseSampler() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier())
+ return Result("invalid token when looking for sampler name");
+
+ auto sampler = MakeUnique<Sampler>();
+ sampler->SetName(token->AsString());
+
+ token = tokenizer_->NextToken();
+ while (!token->IsEOS() && !token->IsEOL()) {
+ if (!token->IsIdentifier())
+ return Result("invalid token when looking for sampler parameters");
+
+ auto param = token->AsString();
+ if (param == "MAG_FILTER") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid token when looking for MAG_FILTER value");
+
+ auto filter = token->AsString();
+
+ if (filter == "linear")
+ sampler->SetMagFilter(FilterType::kLinear);
+ else if (filter == "nearest")
+ sampler->SetMagFilter(FilterType::kNearest);
+ else
+ return Result("invalid MAG_FILTER value " + filter);
+ } else if (param == "MIN_FILTER") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid token when looking for MIN_FILTER value");
+
+ auto filter = token->AsString();
+
+ if (filter == "linear")
+ sampler->SetMinFilter(FilterType::kLinear);
+ else if (filter == "nearest")
+ sampler->SetMinFilter(FilterType::kNearest);
+ else
+ return Result("invalid MIN_FILTER value " + filter);
+ } else if (param == "ADDRESS_MODE_U") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid token when looking for ADDRESS_MODE_U value");
+
+ auto mode_str = token->AsString();
+ auto mode = StrToAddressMode(mode_str);
+
+ if (mode == AddressMode::kUnknown)
+ return Result("invalid ADDRESS_MODE_U value " + mode_str);
+
+ sampler->SetAddressModeU(mode);
+ } else if (param == "ADDRESS_MODE_V") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid token when looking for ADDRESS_MODE_V value");
+
+ auto mode_str = token->AsString();
+ auto mode = StrToAddressMode(mode_str);
+
+ if (mode == AddressMode::kUnknown)
+ return Result("invalid ADDRESS_MODE_V value " + mode_str);
+
+ sampler->SetAddressModeV(mode);
+ } else if (param == "ADDRESS_MODE_W") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid token when looking for ADDRESS_MODE_W value");
+
+ auto mode_str = token->AsString();
+ auto mode = StrToAddressMode(mode_str);
+
+ if (mode == AddressMode::kUnknown)
+ return Result("invalid ADDRESS_MODE_W value " + mode_str);
+
+ sampler->SetAddressModeW(mode);
+ } else if (param == "BORDER_COLOR") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsIdentifier())
+ return Result("invalid token when looking for BORDER_COLOR value");
+
+ auto color_str = token->AsString();
+
+ if (color_str == "float_transparent_black")
+ sampler->SetBorderColor(BorderColor::kFloatTransparentBlack);
+ else if (color_str == "int_transparent_black")
+ sampler->SetBorderColor(BorderColor::kIntTransparentBlack);
+ else if (color_str == "float_opaque_black")
+ sampler->SetBorderColor(BorderColor::kFloatOpaqueBlack);
+ else if (color_str == "int_opaque_black")
+ sampler->SetBorderColor(BorderColor::kIntOpaqueBlack);
+ else if (color_str == "float_opaque_white")
+ sampler->SetBorderColor(BorderColor::kFloatOpaqueWhite);
+ else if (color_str == "int_opaque_white")
+ sampler->SetBorderColor(BorderColor::kIntOpaqueWhite);
+ else
+ return Result("invalid BORDER_COLOR value " + color_str);
+ } else if (param == "MIN_LOD") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsDouble())
+ return Result("invalid token when looking for MIN_LOD value");
+
+ sampler->SetMinLOD(token->AsFloat());
+ } else if (param == "MAX_LOD") {
+ token = tokenizer_->NextToken();
+
+ if (!token->IsDouble())
+ return Result("invalid token when looking for MAX_LOD value");
+
+ sampler->SetMaxLOD(token->AsFloat());
+ } else if (param == "NORMALIZED_COORDS") {
+ sampler->SetNormalizedCoords(true);
+ } else if (param == "UNNORMALIZED_COORDS") {
+ sampler->SetNormalizedCoords(false);
+ sampler->SetMinLOD(0.0f);
+ sampler->SetMaxLOD(0.0f);
+ } else {
+ return Result("unexpected sampler parameter " + param);
+ }
+
+ token = tokenizer_->NextToken();
+ }
+
+ if (sampler->GetMaxLOD() < sampler->GetMinLOD()) {
+ return Result("max LOD needs to be greater than or equal to min LOD");
+ }
+
+ return script_->AddSampler(std::move(sampler));
+}
+
+Result Parser::ParseTolerances(std::vector<Probe::Tolerance>* tolerances) {
+ auto token = tokenizer_->PeekNextToken();
+ while (!token->IsEOL() && !token->IsEOS()) {
+ if (!token->IsInteger() && !token->IsDouble())
+ break;
+
+ token = tokenizer_->NextToken();
+ Result r = token->ConvertToDouble();
+ if (!r.IsSuccess())
+ return r;
+
+ double value = token->AsDouble();
+ token = tokenizer_->PeekNextToken();
+ if (token->IsIdentifier() && token->AsString() == "%") {
+ tolerances->push_back(Probe::Tolerance{true, value});
+ tokenizer_->NextToken();
+ token = tokenizer_->PeekNextToken();
+ } else {
+ tolerances->push_back(Probe::Tolerance{false, value});
+ }
+ }
+
+ return {};
+}
+
+Result Parser::ParseVirtualFile() {
+ auto token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() && !token->IsString())
+ return Result("invalid virtual file path");
+
+ auto path = token->AsString();
+
+ auto r = ValidateEndOfStatement("VIRTUAL_FILE command");
+ if (!r.IsSuccess())
+ return r;
+
+ auto data = tokenizer_->ExtractToNext("END");
+
+ token = tokenizer_->NextToken();
+ if (!token->IsIdentifier() || token->AsString() != "END")
+ return Result("VIRTUAL_FILE missing END command");
+
+ return script_->AddVirtualFile(path, data);
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/amberscript/parser.h b/src/amberscript/parser.h
index 2412208..e8cb835 100644
--- a/src/amberscript/parser.h
+++ b/src/amberscript/parser.h
@@ -27,6 +27,7 @@
namespace amber {
class Tokenizer;
+class Token;
namespace amberscript {
@@ -34,6 +35,7 @@ namespace amberscript {
class Parser : public amber::Parser {
public:
Parser();
+ explicit Parser(Delegate* delegate);
~Parser() override;
// amber::Parser
@@ -49,24 +51,35 @@ class Parser : public amber::Parser {
Result ParseStruct();
Result ParseBuffer();
+ Result ParseImage();
Result ParseBufferInitializer(Buffer*);
Result ParseBufferInitializerSize(Buffer*);
Result ParseBufferInitializerFill(Buffer*, uint32_t);
Result ParseBufferInitializerSeries(Buffer*, uint32_t);
Result ParseBufferInitializerData(Buffer*);
+ Result ParseBufferInitializerFile(Buffer*);
Result ParseShaderBlock();
Result ParsePipelineBlock();
Result ParsePipelineAttach(Pipeline*);
Result ParsePipelineShaderOptimizations(Pipeline*);
Result ParsePipelineShaderCompileOptions(Pipeline*);
+ Result ParsePipelineSubgroup(Pipeline* pipeline);
Result ParsePipelineFramebufferSize(Pipeline*);
Result ParsePipelineBind(Pipeline*);
Result ParsePipelineVertexData(Pipeline*);
Result ParsePipelineIndexData(Pipeline*);
Result ParsePipelineSet(Pipeline*);
+ Result ParsePipelinePolygonMode(Pipeline*);
+ Result ParsePipelineDepth(Pipeline* pipeline);
+ Result ParsePipelineStencil(Pipeline* pipeline);
Result ParseRun();
+ Result ParseDebug();
+ Result ParseDebugThread(debug::Events*, Pipeline* pipeline);
+ Result ParseDebugThreadBody(debug::Thread* thread);
Result ParseClear();
Result ParseClearColor();
+ Result ParseClearDepth();
+ Result ParseClearStencil();
Result ParseExpect();
Result ParseCopy();
Result ParseDeviceFeature();
@@ -80,6 +93,8 @@ class Parser : public amber::Parser {
Result ParsePipelineBody(const std::string& cmd_name,
std::unique_ptr<Pipeline> pipeline);
Result ParseShaderSpecialization(Pipeline* pipeline);
+ Result ParseSampler();
+ Result ParseTolerances(std::vector<Probe::Tolerance>* tolerances);
/// Parses a set of values out of the token stream. |name| is the name of the
/// current command we're parsing for error purposes. The |type| is the type
@@ -89,6 +104,8 @@ class Parser : public amber::Parser {
Format* fmt,
std::vector<Value>* values);
+ Result ParseVirtualFile();
+
std::unique_ptr<Tokenizer> tokenizer_;
std::vector<std::unique_ptr<Command>> command_list_;
};
diff --git a/src/amberscript/parser_attach_test.cc b/src/amberscript/parser_attach_test.cc
index 1f515a2..6ab92be 100644
--- a/src/amberscript/parser_attach_test.cc
+++ b/src/amberscript/parser_attach_test.cc
@@ -329,8 +329,8 @@ END)";
const auto& shaders = pipeline->GetShaders();
ASSERT_EQ(1U, shaders.size());
- EXPECT_EQ(1, shaders[0].GetSpecialization().size());
- EXPECT_EQ(4, shaders[0].GetSpecialization().at(1));
+ EXPECT_EQ(1u, shaders[0].GetSpecialization().size());
+ EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1));
}
TEST_F(AmberScriptParserTest, PipelineSpecializationInt32) {
@@ -354,8 +354,8 @@ END)";
const auto& shaders = pipeline->GetShaders();
ASSERT_EQ(1U, shaders.size());
- EXPECT_EQ(1, shaders[0].GetSpecialization().size());
- EXPECT_EQ(0xffffffff, shaders[0].GetSpecialization().at(2));
+ EXPECT_EQ(1u, shaders[0].GetSpecialization().size());
+ EXPECT_EQ(0xffffffffu, shaders[0].GetSpecialization().at(2));
}
TEST_F(AmberScriptParserTest, PipelineSpecializationFloat) {
@@ -379,8 +379,8 @@ END)";
const auto& shaders = pipeline->GetShaders();
ASSERT_EQ(1U, shaders.size());
- EXPECT_EQ(1, shaders[0].GetSpecialization().size());
- EXPECT_EQ(0x3f8ccccd, shaders[0].GetSpecialization().at(3));
+ EXPECT_EQ(1u, shaders[0].GetSpecialization().size());
+ EXPECT_EQ(0x3f8ccccdu, shaders[0].GetSpecialization().at(3));
}
TEST_F(AmberScriptParserTest, PipelineSpecializationIDIsString) {
@@ -469,10 +469,10 @@ END)";
const auto& shaders = pipeline->GetShaders();
ASSERT_EQ(1U, shaders.size());
- EXPECT_EQ(3, shaders[0].GetSpecialization().size());
- EXPECT_EQ(4, shaders[0].GetSpecialization().at(1));
- EXPECT_EQ(5, shaders[0].GetSpecialization().at(2));
- EXPECT_EQ(1, shaders[0].GetSpecialization().at(5));
+ EXPECT_EQ(3u, shaders[0].GetSpecialization().size());
+ EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1));
+ EXPECT_EQ(5u, shaders[0].GetSpecialization().at(2));
+ EXPECT_EQ(1u, shaders[0].GetSpecialization().at(5));
}
TEST_F(AmberScriptParserTest, PipelineSpecializationNoType) {
@@ -496,8 +496,8 @@ END)";
const auto& shaders = pipeline->GetShaders();
ASSERT_EQ(1U, shaders.size());
- EXPECT_EQ(1, shaders[0].GetSpecialization().size());
- EXPECT_EQ(4, shaders[0].GetSpecialization().at(1));
+ EXPECT_EQ(1u, shaders[0].GetSpecialization().size());
+ EXPECT_EQ(4u, shaders[0].GetSpecialization().at(1));
}
} // namespace amberscript
diff --git a/src/amberscript/parser_bind_test.cc b/src/amberscript/parser_bind_test.cc
index 5e6d87c..64f3948 100644
--- a/src/amberscript/parser_bind_test.cc
+++ b/src/amberscript/parser_bind_test.cc
@@ -49,10 +49,11 @@ END)";
const auto& buf_info = color_buffers[0];
ASSERT_TRUE(buf_info.buffer != nullptr);
- EXPECT_EQ(0, buf_info.location);
- EXPECT_EQ(250 * 250, buf_info.buffer->ElementCount());
- EXPECT_EQ(250 * 250 * 4, buf_info.buffer->ValueCount());
- EXPECT_EQ(250 * 250 * 4 * sizeof(float), buf_info.buffer->GetSizeInBytes());
+ EXPECT_EQ(0u, buf_info.location);
+ EXPECT_EQ(250u * 250u, buf_info.buffer->ElementCount());
+ EXPECT_EQ(250u * 250u * 4u, buf_info.buffer->ValueCount());
+ EXPECT_EQ(250u * 250u * 4u * sizeof(float),
+ buf_info.buffer->GetSizeInBytes());
}
TEST_F(AmberScriptParserTest, BindColorBufferTwice) {
@@ -199,7 +200,7 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: extra parameters after BIND command", r.Error());
+ EXPECT_EQ("12: extra parameters after BIND command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, BindColorBufferDuplicateLocation) {
@@ -297,19 +298,19 @@ END)";
const auto& buf1 = color_buffers1[0];
ASSERT_TRUE(buf1.buffer != nullptr);
- EXPECT_EQ(0, buf1.location);
- EXPECT_EQ(90 * 180, buf1.buffer->ElementCount());
- EXPECT_EQ(90 * 180 * 4, buf1.buffer->ValueCount());
- EXPECT_EQ(90 * 180 * 4 * sizeof(float), buf1.buffer->GetSizeInBytes());
+ EXPECT_EQ(0u, buf1.location);
+ EXPECT_EQ(90u * 180u, buf1.buffer->ElementCount());
+ EXPECT_EQ(90u * 180u * 4u, buf1.buffer->ValueCount());
+ EXPECT_EQ(90u * 180u * 4u * sizeof(float), buf1.buffer->GetSizeInBytes());
pipeline = pipelines[1].get();
const auto& color_buffers2 = pipeline->GetColorAttachments();
const auto& buf2 = color_buffers2[0];
ASSERT_TRUE(buf2.buffer != nullptr);
- EXPECT_EQ(9, buf2.location);
- EXPECT_EQ(256 * 300, buf2.buffer->ElementCount());
- EXPECT_EQ(256 * 300 * 4, buf2.buffer->ValueCount());
- EXPECT_EQ(256 * 300 * 4 * sizeof(uint8_t), buf2.buffer->GetSizeInBytes());
+ EXPECT_EQ(9u, buf2.location);
+ EXPECT_EQ(256u * 300u, buf2.buffer->ElementCount());
+ EXPECT_EQ(256u * 300u * 4u, buf2.buffer->ValueCount());
+ EXPECT_EQ(256u * 300u * 4u * sizeof(uint8_t), buf2.buffer->GetSizeInBytes());
}
TEST_F(AmberScriptParserTest, BindColorFBSizeSetBeforeBuffer) {
@@ -342,10 +343,10 @@ END)";
const auto& buf1 = color_buffers1[0];
ASSERT_TRUE(buf1.buffer != nullptr);
- EXPECT_EQ(0, buf1.location);
- EXPECT_EQ(90 * 180, buf1.buffer->ElementCount());
- EXPECT_EQ(90 * 180 * 4, buf1.buffer->ValueCount());
- EXPECT_EQ(90 * 180 * 4 * sizeof(float), buf1.buffer->GetSizeInBytes());
+ EXPECT_EQ(0u, buf1.location);
+ EXPECT_EQ(90u * 180u, buf1.buffer->ElementCount());
+ EXPECT_EQ(90u * 180u * 4u, buf1.buffer->ValueCount());
+ EXPECT_EQ(90u * 180u * 4u * sizeof(float), buf1.buffer->GetSizeInBytes());
}
TEST_F(AmberScriptParserTest, BindColorFBSizeSetAfterBuffer) {
@@ -378,10 +379,114 @@ END)";
const auto& buf1 = color_buffers1[0];
ASSERT_TRUE(buf1.buffer != nullptr);
- EXPECT_EQ(0, buf1.location);
- EXPECT_EQ(90 * 180, buf1.buffer->ElementCount());
- EXPECT_EQ(90 * 180 * 4, buf1.buffer->ValueCount());
- EXPECT_EQ(90 * 180 * 4 * sizeof(float), buf1.buffer->GetSizeInBytes());
+ EXPECT_EQ(0u, buf1.location);
+ EXPECT_EQ(90u * 180u, buf1.buffer->ElementCount());
+ EXPECT_EQ(90u * 180u * 4u, buf1.buffer->ValueCount());
+ EXPECT_EQ(90u * 180u * 4u * sizeof(float), buf1.buffer->GetSizeInBytes());
+}
+
+TEST_F(AmberScriptParserTest, BindColorBaseMipLevel) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT MIP_LEVELS 2
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER my_fb AS color LOCATION 0 BASE_MIP_LEVEL 1
+ FRAMEBUFFER_SIZE 90 180
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& color_buffers1 = pipeline->GetColorAttachments();
+ ASSERT_EQ(1U, color_buffers1.size());
+
+ const auto& buf1 = color_buffers1[0];
+ ASSERT_TRUE(buf1.buffer != nullptr);
+ EXPECT_EQ(1u, buf1.base_mip_level);
+}
+
+TEST_F(AmberScriptParserTest, BindColorMissingBaseMipLevel) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT MIP_LEVELS 2
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER my_fb AS color LOCATION 0 BASE_MIP_LEVEL
+ FRAMEBUFFER_SIZE 90 180
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: invalid value for BASE_MIP_LEVEL", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindColorBaseMipLevelTooLarge) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT MIP_LEVELS 2
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER my_fb AS color LOCATION 0 BASE_MIP_LEVEL 2
+ FRAMEBUFFER_SIZE 90 180
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "12: base mip level (now 2) needs to be larger than the number of buffer "
+ "mip maps (2)",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindColorTooManyMipLevels) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT MIP_LEVELS 20
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER my_fb AS color LOCATION 0
+ FRAMEBUFFER_SIZE 90 180
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "color attachment with 20 mip levels would have zero width for level 7",
+ r.Error());
}
TEST_F(AmberScriptParserTest, BindDepthBuffer) {
@@ -409,11 +514,11 @@ END)";
ASSERT_EQ(1U, pipelines.size());
const auto* pipeline = pipelines[0].get();
- const auto& buf = pipeline->GetDepthBuffer();
+ const auto& buf = pipeline->GetDepthStencilBuffer();
ASSERT_TRUE(buf.buffer != nullptr);
- EXPECT_EQ(90 * 180, buf.buffer->ElementCount());
- EXPECT_EQ(90 * 180 * 4, buf.buffer->ValueCount());
- EXPECT_EQ(90 * 180 * 4 * sizeof(float), buf.buffer->GetSizeInBytes());
+ EXPECT_EQ(90u * 180u, buf.buffer->ElementCount());
+ EXPECT_EQ(90u * 180u * 4u, buf.buffer->ValueCount());
+ EXPECT_EQ(90u * 180u * 4u * sizeof(float), buf.buffer->GetSizeInBytes());
}
TEST_F(AmberScriptParserTest, BindDepthBufferExtraParams) {
@@ -435,7 +540,7 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: extra parameters after BIND command", r.Error());
+ EXPECT_EQ("12: extra parameters after BIND command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, BindBufferMissingBufferName) {
@@ -545,7 +650,8 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("14: can only bind one depth buffer in a PIPELINE", r.Error());
+ EXPECT_EQ("14: can only bind one depth/stencil buffer in a PIPELINE",
+ r.Error());
}
TEST_F(AmberScriptParserTest, BindVertexData) {
@@ -575,15 +681,17 @@ END)";
const auto* pipeline = pipelines[0].get();
const auto& vertex_buffers = pipeline->GetVertexBuffers();
- ASSERT_EQ(2, vertex_buffers.size());
+ ASSERT_EQ(2u, vertex_buffers.size());
const auto& info1 = vertex_buffers[0];
ASSERT_TRUE(info1.buffer != nullptr);
- EXPECT_EQ(0, info1.location);
+ EXPECT_EQ(0u, info1.location);
+ EXPECT_EQ(InputRate::kVertex, info1.input_rate);
const auto& info2 = vertex_buffers[1];
ASSERT_TRUE(info2.buffer != nullptr);
- EXPECT_EQ(1, info2.location);
+ EXPECT_EQ(1u, info2.location);
+ EXPECT_EQ(InputRate::kVertex, info2.input_rate);
}
TEST_F(AmberScriptParserTest, BindVertexDataDuplicateLocation) {
@@ -623,14 +731,30 @@ PIPELINE graphics my_pipeline
ATTACH my_fragment
VERTEX_DATA my_buf LOCATION 0
- VERTEX_DATA my_buf LOCATION 1
+ VERTEX_DATA my_buf LOCATION 1 OFFSET 10
END)";
Parser parser;
Result r = parser.Parse(in);
- ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("13: vertex buffer may only be bound to a PIPELINE once",
- r.Error());
+ ASSERT_TRUE(r.IsSuccess());
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& vertex_buffers = pipeline->GetVertexBuffers();
+ ASSERT_EQ(2u, vertex_buffers.size());
+
+ const auto& info1 = vertex_buffers[0];
+ ASSERT_TRUE(info1.buffer != nullptr);
+ EXPECT_EQ(0u, info1.location);
+ EXPECT_EQ(0u, info1.offset);
+
+ const auto& info2 = vertex_buffers[1];
+ ASSERT_TRUE(info2.buffer != nullptr);
+ EXPECT_EQ(1u, info2.location);
+ EXPECT_EQ(10u, info2.offset);
}
TEST_F(AmberScriptParserTest, BindVertexDataMissingBuffer) {
@@ -734,7 +858,397 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: extra parameters after VERTEX_DATA command", r.Error());
+ EXPECT_EQ("12: unexpected identifier for VERTEX_DATA command: EXTRA",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataInputRate) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+BUFFER my_buf2 DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 RATE vertex
+ VERTEX_DATA my_buf2 LOCATION 1 RATE instance
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& vertex_buffers = pipeline->GetVertexBuffers();
+ ASSERT_EQ(2u, vertex_buffers.size());
+
+ const auto& info1 = vertex_buffers[0];
+ ASSERT_TRUE(info1.buffer != nullptr);
+ EXPECT_EQ(0u, info1.location);
+ EXPECT_EQ(InputRate::kVertex, info1.input_rate);
+
+ const auto& info2 = vertex_buffers[1];
+ ASSERT_TRUE(info2.buffer != nullptr);
+ EXPECT_EQ(1u, info2.location);
+ EXPECT_EQ(InputRate::kInstance, info2.input_rate);
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataInputRateMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 RATE
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: missing input rate value for RATE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataInputRateInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 RATE foo
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: expecting 'vertex' or 'instance' for RATE value", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataOffset) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+BUFFER my_buf2 DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 OFFSET 5
+ VERTEX_DATA my_buf2 LOCATION 1 OFFSET 10
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& vertex_buffers = pipeline->GetVertexBuffers();
+ ASSERT_EQ(2u, vertex_buffers.size());
+
+ const auto& info1 = vertex_buffers[0];
+ ASSERT_TRUE(info1.buffer != nullptr);
+ EXPECT_EQ(0u, info1.location);
+ EXPECT_EQ(5u, info1.offset);
+
+ const auto& info2 = vertex_buffers[1];
+ ASSERT_TRUE(info2.buffer != nullptr);
+ EXPECT_EQ(1u, info2.location);
+ EXPECT_EQ(10u, info2.offset);
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataOffsetMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 OFFSET
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: expected unsigned integer for OFFSET", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataOffsetIncorrectValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 OFFSET foo
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: expected unsigned integer for OFFSET", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataStride) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+BUFFER my_buf2 DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 STRIDE 5
+ VERTEX_DATA my_buf2 LOCATION 1 STRIDE 10
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& vertex_buffers = pipeline->GetVertexBuffers();
+ ASSERT_EQ(2u, vertex_buffers.size());
+
+ const auto& info1 = vertex_buffers[0];
+ ASSERT_TRUE(info1.buffer != nullptr);
+ EXPECT_EQ(0u, info1.location);
+ EXPECT_EQ(5u, info1.stride);
+
+ const auto& info2 = vertex_buffers[1];
+ ASSERT_TRUE(info2.buffer != nullptr);
+ EXPECT_EQ(1u, info2.location);
+ EXPECT_EQ(10u, info2.stride);
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataStrideFromFormat) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+BUFFER my_buf2 DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0
+ VERTEX_DATA my_buf2 LOCATION 1 FORMAT R16_UINT
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& vertex_buffers = pipeline->GetVertexBuffers();
+ ASSERT_EQ(2u, vertex_buffers.size());
+
+ const auto& info1 = vertex_buffers[0];
+ ASSERT_TRUE(info1.buffer != nullptr);
+ EXPECT_EQ(0u, info1.location);
+ EXPECT_EQ(1u, info1.stride);
+
+ const auto& info2 = vertex_buffers[1];
+ ASSERT_TRUE(info2.buffer != nullptr);
+ EXPECT_EQ(1u, info2.location);
+ EXPECT_EQ(2u, info2.stride);
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataStrideMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 STRIDE
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: expected unsigned integer for STRIDE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataStrideIncorrectValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 STRIDE foo
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: expected unsigned integer for STRIDE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataStrideZero) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 STRIDE 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: STRIDE needs to be larger than zero", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataFormat) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+BUFFER my_buf2 DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 FORMAT R8G8_UNORM
+ VERTEX_DATA my_buf2 LOCATION 1 FORMAT R8_SRGB
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& vertex_buffers = pipeline->GetVertexBuffers();
+ ASSERT_EQ(2u, vertex_buffers.size());
+
+ const auto& info1 = vertex_buffers[0];
+ ASSERT_TRUE(info1.buffer != nullptr);
+ EXPECT_EQ(0u, info1.location);
+ EXPECT_EQ(FormatType::kR8G8_UNORM, info1.format->GetFormatType());
+
+ const auto& info2 = vertex_buffers[1];
+ ASSERT_TRUE(info2.buffer != nullptr);
+ EXPECT_EQ(1u, info2.location);
+ EXPECT_EQ(FormatType::kR8_SRGB, info2.format->GetFormatType());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataFormatMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 FORMAT
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: vertex data FORMAT must be an identifier", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindVertexDataFormatIncorrectValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf DATA_TYPE int8 SIZE 5 FILL 5
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ VERTEX_DATA my_buf LOCATION 0 FORMAT foo
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: invalid vertex data FORMAT", r.Error());
}
TEST_F(AmberScriptParserTest, BindIndexData) {
@@ -824,7 +1338,7 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: extra parameters after INDEX_DATA command", r.Error());
+ EXPECT_EQ("12: extra parameters after INDEX_DATA command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, BindIndexDataMultiple) {
@@ -876,7 +1390,7 @@ END
const auto* pipeline = pipelines[0].get();
const auto& bufs = pipeline->GetBuffers();
ASSERT_EQ(1U, bufs.size());
- EXPECT_EQ(BufferType::kUniform, bufs[0].buffer->GetBufferType());
+ EXPECT_EQ(BufferType::kUniform, bufs[0].type);
EXPECT_EQ(1U, bufs[0].descriptor_set);
EXPECT_EQ(2U, bufs[0].binding);
EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location);
@@ -986,7 +1500,7 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: extra parameters after BIND command", r.Error());
+ EXPECT_EQ("12: extra parameters after BIND command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, BindingBufferInvalidBindingValue) {
@@ -1087,7 +1601,7 @@ PIPELINE graphics my_pipeline
const auto* pipeline = pipelines[0].get();
const auto& bufs = pipeline->GetBuffers();
ASSERT_EQ(1U, bufs.size());
- EXPECT_EQ(test_data.type, bufs[0].buffer->GetBufferType());
+ EXPECT_EQ(test_data.type, bufs[0].type);
}
INSTANTIATE_TEST_SUITE_P(
AmberScriptParserBufferTypeTestSamples,
@@ -1123,9 +1637,9 @@ END)";
const auto* pipeline = pipelines[0].get();
const auto& buf = pipeline->GetPushConstantBuffer();
ASSERT_TRUE(buf.buffer != nullptr);
- EXPECT_EQ(20, buf.buffer->ElementCount());
- EXPECT_EQ(20, buf.buffer->ValueCount());
- EXPECT_EQ(20 * sizeof(float), buf.buffer->GetSizeInBytes());
+ EXPECT_EQ(20u, buf.buffer->ElementCount());
+ EXPECT_EQ(20u, buf.buffer->ValueCount());
+ EXPECT_EQ(20u * sizeof(float), buf.buffer->GetSizeInBytes());
}
TEST_F(AmberScriptParserTest, BindPushConstantsExtraParams) {
@@ -1146,7 +1660,7 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: extra parameters after BIND command", r.Error());
+ EXPECT_EQ("12: extra parameters after BIND command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, BindBufferOpenCLArgName) {
@@ -1325,5 +1839,1417 @@ END)";
EXPECT_EQ("9: expected argument number", r.Error());
}
+TEST_F(AmberScriptParserTest, BindSamplerOpenCLMissingKernel) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+SAMPLER s
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ BIND SAMPLER s
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: expected a string token for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerOpenCLInvalidKernel) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+SAMPLER s
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ BIND SAMPLER s INVALID
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: missing DESCRIPTOR_SET or KERNEL for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerOpenCLMissingArgument) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+SAMPLER s
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ BIND SAMPLER s KERNEL
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: missing kernel arg identifier", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerOpenCLMissingArgumentName) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+SAMPLER s
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ BIND SAMPLER s KERNEL ARG_NAME
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: expected argument identifier", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerOpenCLArgumentNameNotString) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+SAMPLER s
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ BIND SAMPLER s KERNEL ARG_NAME 0
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: expected argument identifier", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerOpenCLMissingArgumentNumber) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+SAMPLER s
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ BIND SAMPLER s KERNEL ARG_NUMBER
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: expected argument number", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerOpenCLArgumentNumberNotInteger) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+SAMPLER s
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ BIND SAMPLER s KERNEL ARG_NUMBER a
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: expected argument number", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageImageCompute) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET 0 BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kStorageImage, bufs[0].type);
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageImageGraphics) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kStorageImage, bufs[0].type);
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageImageMissingDescriptorSetValue) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: invalid value for DESCRIPTOR_SET in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageImageMissingDescriptorSet) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_image BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: missing DESCRIPTOR_SET or KERNEL for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageImageMissingBindingValue) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET 0 BINDING
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("11: invalid value for BINDING in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageImageMissingBinding) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("11: missing BINDING for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferSampledImage) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kSampledImage, bufs[0].type);
+}
+
+TEST_F(AmberScriptParserTest, BindBufferSampledImageMissingDescriptorSetValue) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: invalid value for DESCRIPTOR_SET in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferSampledImageMissingDescriptorSet) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS sampled_image BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: missing DESCRIPTOR_SET or KERNEL for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferSampledImageMissingBindingValue) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: invalid value for BINDING in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferSampledImageMissingBinding) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: missing BINDING for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferCombinedImageSampler) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kCombinedImageSampler, bufs[0].type);
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferCombinedImageSamplerMissingDescriptorSetValue) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: invalid value for DESCRIPTOR_SET in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferCombinedImageSamplerMissingDescriptorSet) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: missing DESCRIPTOR_SET or KERNEL for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferCombinedImageSamplerMissingBindingValue) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: invalid value for BINDING in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferCombinedImageSamplerMissingBinding) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("14: missing BINDING for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferCombinedImageSamplerMissingSampler) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler DESCRIPTOR_SET 0 BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: expecting SAMPLER for combined image sampler", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferCombinedImageSamplerUnknownSampler) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER foo DESCRIPTOR_SET 0 BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: unknown sampler: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferCombinedImageSamplerBaseMipLevel) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM MIP_LEVELS 4
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+SAMPLER sampler MAX_LOD 4.0
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0 BASE_MIP_LEVEL 2
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(2U, bufs[0].base_mip_level);
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferCombinedImageSamplerMissingBaseMipLevel) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM MIP_LEVELS 4
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+SAMPLER sampler MAX_LOD 4.0
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0 BASE_MIP_LEVEL
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("15: invalid value for BASE_MIP_LEVEL", r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferCombinedImageSamplerBaseMipLevelTooLarge) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM MIP_LEVELS 2
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+SAMPLER sampler MAX_LOD 2.0
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0 BASE_MIP_LEVEL 3
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "14: base mip level (now 3) needs to be larger than the number of buffer "
+ "mip maps (2)",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferUniformTexelBufferCompute) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS uniform_texel_buffer DESCRIPTOR_SET 0 BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kUniformTexelBuffer, bufs[0].type);
+}
+
+TEST_F(AmberScriptParserTest, BindBufferUniformTexelBufferGraphics) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS uniform_texel_buffer DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kUniformTexelBuffer, bufs[0].type);
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferUniformTexelBufferMissingDescriptorSetValue) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS uniform_texel_buffer DESCRIPTOR_SET BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: invalid value for DESCRIPTOR_SET in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferUniformTexelBufferMissingDescriptorSet) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS uniform_texel_buffer BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: missing DESCRIPTOR_SET or KERNEL for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferUniformTexelBufferMissingBindingValue) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS uniform_texel_buffer DESCRIPTOR_SET 0 BINDING
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("11: invalid value for BINDING in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferUniformTexelBufferMissingBinding) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS uniform_texel_buffer DESCRIPTOR_SET 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("11: missing BINDING for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageTexelBufferCompute) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kStorageTexelBuffer, bufs[0].type);
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageTexelBufferGraphics) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kStorageTexelBuffer, bufs[0].type);
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferStorageTexelBufferMissingDescriptorSetValue) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_texel_buffer DESCRIPTOR_SET BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: invalid value for DESCRIPTOR_SET in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ BindBufferStorageTexelBufferMissingDescriptorSet) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_texel_buffer BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: missing DESCRIPTOR_SET or KERNEL for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageTexelBufferMissingBindingValue) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("11: invalid value for BINDING in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferStorageTexelBufferMissingBinding) {
+ std::string in = R"(
+SHADER compute compute_shader GLSL
+# GLSL Shader
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_texel_buffer DESCRIPTOR_SET 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("11: missing BINDING for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSampler) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+SAMPLER sampler
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& samplers = pipeline->GetSamplers();
+ ASSERT_EQ(1U, samplers.size());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerMissingDescriptorSetValue) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND SAMPLER sampler DESCRIPTOR_SET BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: invalid value for DESCRIPTOR_SET in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerMissingDescriptorSet) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND SAMPLER sampler BINDING 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: missing DESCRIPTOR_SET or KERNEL for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerMissingBindingValue) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: invalid value for BINDING in BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerMissingBinding) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+SAMPLER sampler
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND SAMPLER sampler DESCRIPTOR_SET 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("13: missing BINDING for BIND command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindBufferArray) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf1 FORMAT R32G32B32A32_SFLOAT
+BUFFER my_buf2 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER_ARRAY my_buf1 my_buf2 AS uniform DESCRIPTOR_SET 1 BINDING 2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
+ for (size_t i = 0; i < 2; i++) {
+ EXPECT_EQ(BufferType::kUniform, bufs[i].type);
+ EXPECT_EQ(1U, bufs[i].descriptor_set);
+ EXPECT_EQ(2U, bufs[i].binding);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[i].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[i].buffer->GetFormat()->GetFormatType());
+ }
+}
+
+TEST_F(AmberScriptParserTest, BindBufferArrayOnlyOneBuffer) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER_ARRAY my_buf AS uniform DESCRIPTOR_SET 1 BINDING 2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: expecting multiple buffer names for BUFFER_ARRAY", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerArray) {
+ std::string in = R"(
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+# GLSL Shader
+END
+
+SAMPLER sampler1
+SAMPLER sampler2
+BUFFER framebuffer FORMAT R8G8B8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND SAMPLER_ARRAY sampler1 sampler2 DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& samplers = pipeline->GetSamplers();
+ ASSERT_EQ(2U, samplers.size());
+}
+
+TEST_F(AmberScriptParserTest, BindSamplerArrayOnlyOneSampler) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+SAMPLER sampler
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND SAMPLER_ARRAY sampler DESCRIPTOR_SET 1 BINDING 2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("12: expecting multiple sampler names for SAMPLER_ARRAY",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferDynamic) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER my_buf AS uniform_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET 8
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kUniformDynamic, bufs[0].type);
+ EXPECT_EQ(1U, bufs[0].descriptor_set);
+ EXPECT_EQ(2U, bufs[0].binding);
+ EXPECT_EQ(8u, bufs[0].dynamic_offset);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[0].buffer->GetFormat()->GetFormatType());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferArrayDynamic) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER buf0 FORMAT R32G32B32A32_SFLOAT
+BUFFER buf1 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER_ARRAY buf0 buf1 AS uniform_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET 8 16
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
+ EXPECT_EQ(BufferType::kUniformDynamic, bufs[0].type);
+ EXPECT_EQ(1U, bufs[0].descriptor_set);
+ EXPECT_EQ(2U, bufs[0].binding);
+ EXPECT_EQ(8u, bufs[0].dynamic_offset);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[0].buffer->GetFormat()->GetFormatType());
+ EXPECT_EQ(1U, bufs[1].descriptor_set);
+ EXPECT_EQ(2U, bufs[1].binding);
+ EXPECT_EQ(16u, bufs[1].dynamic_offset);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[1].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[1].buffer->GetFormat()->GetFormatType());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferDynamicMissingOffset) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS uniform_dynamic DESCRIPTOR_SET 1 BINDING 2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: expecting an OFFSET for dynamic buffer type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferDynamicEmptyOffset) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS uniform_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: expecting an integer value for OFFSET", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferDynamicInvalidOffset) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS uniform_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET foo
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: expecting an integer value for OFFSET", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindUniformBufferArrayDynamicNotEnoughOffsets) {
+ std::string in = R"(
+BUFFER buf0 FORMAT R32G32B32A32_SFLOAT
+BUFFER buf1 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ BIND BUFFER_ARRAY buf0 buf1 AS uniform_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET 8
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expecting an OFFSET value for each buffer in the array",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindStorageBufferDynamic) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER my_buf AS storage_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET 8
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
+ EXPECT_EQ(BufferType::kStorageDynamic, bufs[0].type);
+ EXPECT_EQ(1U, bufs[0].descriptor_set);
+ EXPECT_EQ(2U, bufs[0].binding);
+ EXPECT_EQ(8u, bufs[0].dynamic_offset);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[0].buffer->GetFormat()->GetFormatType());
+}
+
+TEST_F(AmberScriptParserTest, BindStorageBufferArrayDynamic) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER buf0 FORMAT R32G32B32A32_SFLOAT
+BUFFER buf1 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+
+ BIND BUFFER_ARRAY buf0 buf1 AS storage_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET 8 16
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& bufs = pipeline->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
+ EXPECT_EQ(BufferType::kStorageDynamic, bufs[0].type);
+ EXPECT_EQ(1U, bufs[0].descriptor_set);
+ EXPECT_EQ(2U, bufs[0].binding);
+ EXPECT_EQ(8u, bufs[0].dynamic_offset);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[0].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[0].buffer->GetFormat()->GetFormatType());
+ EXPECT_EQ(1U, bufs[1].descriptor_set);
+ EXPECT_EQ(2U, bufs[1].binding);
+ EXPECT_EQ(16u, bufs[1].dynamic_offset);
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[1].location);
+ EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
+ bufs[1].buffer->GetFormat()->GetFormatType());
+}
+
+TEST_F(AmberScriptParserTest, BindStorageBufferDynamicMissingOffset) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS storage_dynamic DESCRIPTOR_SET 1 BINDING 2
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: expecting an OFFSET for dynamic buffer type", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindStorageBufferDynamicEmptyOffset) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS storage_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("5: expecting an integer value for OFFSET", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindStorageBufferDynamicInvalidOffset) {
+ std::string in = R"(
+BUFFER my_buf FORMAT R32G32B32A32_SFLOAT
+PIPELINE graphics my_pipeline
+ BIND BUFFER my_buf AS storage_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET foo
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("4: expecting an integer value for OFFSET", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BindStorageBufferArrayDynamicNotEnoughOffsets) {
+ std::string in = R"(
+BUFFER buf0 FORMAT R32G32B32A32_SFLOAT
+BUFFER buf1 FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ BIND BUFFER_ARRAY buf0 buf1 AS storage_dynamic DESCRIPTOR_SET 1 BINDING 2 OFFSET 8
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: expecting an OFFSET value for each buffer in the array",
+ r.Error());
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/amberscript/parser_buffer_test.cc b/src/amberscript/parser_buffer_test.cc
index 1381ec8..aeac2cb 100644
--- a/src/amberscript/parser_buffer_test.cc
+++ b/src/amberscript/parser_buffer_test.cc
@@ -20,6 +20,34 @@ namespace amberscript {
using AmberScriptParserTest = testing::Test;
+class DummyDelegate : public amber::Delegate {
+ public:
+ DummyDelegate() = default;
+ ~DummyDelegate() override = default;
+
+ void Log(const std::string&) override {}
+ bool LogGraphicsCalls() const override { return false; }
+ void SetLogGraphicsCalls(bool) {}
+ bool LogExecuteCalls() const override { return false; }
+ void SetLogExecuteCalls(bool) {}
+ bool LogGraphicsCallsTime() const override { return false; }
+ void SetLogGraphicsCallsTime(bool) {}
+ uint64_t GetTimestampNs() const override { return 0; }
+ void SetScriptPath(std::string) {}
+
+ amber::Result LoadBufferData(const std::string,
+ amber::BufferDataFileType type,
+ amber::BufferInfo* buffer) const override {
+ amber::Value v;
+ v.SetIntValue(static_cast<uint64_t>(type));
+ buffer->values.push_back(v);
+ buffer->width = 1;
+ buffer->height = 1;
+
+ return {};
+ }
+};
+
TEST_F(AmberScriptParserTest, BufferData) {
std::string in = R"(
BUFFER my_buffer DATA_TYPE uint32 DATA
@@ -184,6 +212,72 @@ END)";
}
}
+TEST_F(AmberScriptParserTest, BufferDataArrayStd140) {
+ std::string in = R"(
+BUFFER my_buffer DATA_TYPE uint32[] STD140 DATA
+1 2 3 4
+55 99 1234
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("my_buffer", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_TRUE(buffer->GetFormat()->IsUint32());
+ EXPECT_EQ(Format::Layout::kStd140, buffer->GetFormat()->GetLayout());
+ EXPECT_EQ(7U, buffer->ElementCount());
+ EXPECT_EQ(28U * sizeof(uint32_t), buffer->GetSizeInBytes());
+
+ std::vector<uint32_t> results = {1, 0, 0, 0, 2, 0, 0, 0, 3, 0,
+ 0, 0, 4, 0, 0, 0, 55, 0, 0, 0,
+ 99, 0, 0, 0, 1234, 0, 0, 0};
+ const auto* data = buffer->GetValues<uint32_t>();
+ for (size_t i = 0; i < results.size(); ++i) {
+ EXPECT_EQ(results[i], data[i]);
+ }
+}
+
+TEST_F(AmberScriptParserTest, BufferDataArrayStd430) {
+ std::string in = R"(
+BUFFER my_buffer DATA_TYPE uint32[] STD430 DATA
+1 2 3 4
+55 99 1234
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("my_buffer", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_TRUE(buffer->GetFormat()->IsUint32());
+ EXPECT_EQ(Format::Layout::kStd430, buffer->GetFormat()->GetLayout());
+ EXPECT_EQ(7U, buffer->ElementCount());
+ EXPECT_EQ(7U, buffer->ValueCount());
+ EXPECT_EQ(7U * sizeof(uint32_t), buffer->GetSizeInBytes());
+
+ std::vector<uint32_t> results = {1, 2, 3, 4, 55, 99, 1234};
+ const auto* data = buffer->GetValues<uint32_t>();
+ ASSERT_EQ(results.size(), buffer->ValueCount());
+ for (size_t i = 0; i < results.size(); ++i) {
+ EXPECT_EQ(results[i], data[i]);
+ }
+}
+
TEST_F(AmberScriptParserTest, BufferDataOneLine) {
std::string in = "BUFFER my_buffer DATA_TYPE uint32 DATA 1 2 3 4 END";
@@ -568,12 +662,29 @@ TEST_F(AmberScriptParserTest, BufferFormat) {
ASSERT_EQ(4U, segs.size());
for (size_t i = 0; i < 4; ++i) {
- EXPECT_EQ(segs[i].GetNumBits(), 32);
+ EXPECT_EQ(segs[i].GetNumBits(), 32u);
EXPECT_EQ(segs[i].GetFormatMode(), FormatMode::kSInt);
EXPECT_EQ(segs[i].GetName(), static_cast<FormatComponentType>(i));
}
}
+TEST_F(AmberScriptParserTest, BufferSamples) {
+ std::string in = "BUFFER my_buf FORMAT R8G8B8A8_UNORM SAMPLES 2";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ auto* buffer = buffers[0].get();
+ EXPECT_EQ("my_buf", buffer->GetName());
+ EXPECT_EQ(2u, buffer->GetSamples());
+}
+
struct BufferParseError {
const char* in;
const char* err;
@@ -594,11 +705,11 @@ INSTANTIATE_TEST_SUITE_P(
AmberScriptParserBufferParseErrorTest,
testing::Values(
BufferParseError{"BUFFER my_buf FORMAT 123",
- "1: BUFFER FORMAT must be a string"},
+ "1: BUFFER FORMAT must be an identifier"},
BufferParseError{"BUFFER my_buf FORMAT A23A32",
"1: invalid BUFFER FORMAT"},
BufferParseError{"BUFFER my_buf FORMAT",
- "1: BUFFER FORMAT must be a string"},
+ "1: BUFFER FORMAT must be an identifier"},
BufferParseError{"BUFFER my_buffer FORMAT R32G32B32A32_SFLOAT EXTRA",
"1: unknown token: EXTRA"},
BufferParseError{"BUFFER 1234 DATA_TYPE uint8 SIZE 5 FILL 5",
@@ -650,18 +761,23 @@ INSTANTIATE_TEST_SUITE_P(
BufferParseError{
"BUFFER my_index_buffer DATA_TYPE int32 DATA INVALID\n123\nEND",
"1: invalid BUFFER data value: INVALID"},
- BufferParseError{"BUFFER my_index_buffer DATA_TYPE int32 SIZE 256 FILL "
- "5 INVALID\n123\nEND",
- "1: extra parameters after BUFFER fill command"},
+ BufferParseError{
+ "BUFFER my_index_buffer DATA_TYPE int32 SIZE 256 FILL "
+ "5 INVALID\n123\nEND",
+ "1: extra parameters after BUFFER fill command: INVALID"},
BufferParseError{
"BUFFER my_buffer DATA_TYPE int32 SIZE 256 SERIES_FROM 2 "
"INC_BY 5 "
"INVALID",
- "1: extra parameters after BUFFER series_from command"},
+ "1: extra parameters after BUFFER series_from command: INVALID"},
BufferParseError{"BUFFER my_buf DATA_TYPE int32 SIZE 5 FILL 5\nBUFFER "
"my_buf DATA_TYPE int16 SIZE 5 FILL 2",
// NOLINTNEXTLINE(whitespace/parens)
- "2: duplicate buffer name provided"}));
+ "2: duplicate buffer name provided"},
+ BufferParseError{"BUFFER my_buf FORMAT R8G8B8A8_UNORM SAMPLES 9",
+ "1: invalid sample count: 9"},
+ BufferParseError{"BUFFER my_buf FORMAT R8G8B8A8_UNORM SAMPLES foo",
+ "1: expected integer value for SAMPLES"}));
struct BufferData {
const char* name;
@@ -669,6 +785,7 @@ struct BufferData {
size_t num_bits;
size_t row_count;
size_t column_count;
+ bool is_array;
};
using AmberScriptParserBufferDataTypeTest = testing::TestWithParam<BufferData>;
@@ -693,30 +810,55 @@ TEST_P(AmberScriptParserBufferDataTypeTest, BufferTypes) {
EXPECT_EQ(test_data.row_count, fmt->GetType()->RowCount());
EXPECT_EQ(test_data.column_count, fmt->GetType()->ColumnCount());
- EXPECT_EQ(test_data.type, fmt->GetSegments()[0].GetFormatMode());
- EXPECT_EQ(test_data.num_bits, fmt->GetSegments()[0].GetNumBits());
+ auto& seg = fmt->GetSegments()[0];
+ EXPECT_EQ(test_data.type, seg.GetFormatMode());
+ EXPECT_EQ(test_data.num_bits, seg.GetNumBits());
+ EXPECT_EQ(test_data.is_array, fmt->GetType()->IsArray());
}
INSTANTIATE_TEST_SUITE_P(
AmberScriptParserTestsDataType,
AmberScriptParserBufferDataTypeTest,
- testing::Values(BufferData{"int8", FormatMode::kSInt, 8, 1, 1},
- BufferData{"int16", FormatMode::kSInt, 16, 1, 1},
- BufferData{"int32", FormatMode::kSInt, 32, 1, 1},
- BufferData{"int64", FormatMode::kSInt, 64, 1, 1},
- BufferData{"uint8", FormatMode::kUInt, 8, 1, 1},
- BufferData{"uint16", FormatMode::kUInt, 16, 1, 1},
- BufferData{"uint32", FormatMode::kUInt, 32, 1, 1},
- BufferData{"uint64", FormatMode::kUInt, 64, 1, 1},
- BufferData{"vec2<int8>", FormatMode::kSInt, 8, 2, 1},
- BufferData{"vec4<uint32>", FormatMode::kUInt, 32, 4, 1},
- BufferData{"mat2x4<int32>", FormatMode::kSInt, 32, 4, 2},
- BufferData{"mat4x2<uint16>", FormatMode::kUInt, 16, 2, 4},
- BufferData{"B8G8R8_UNORM", FormatMode::kUNorm, 8, 3, 1},
- BufferData{"float", FormatMode::kSFloat, 32, 1, 1},
- BufferData{"double", FormatMode::kSFloat, 64, 1, 1},
- BufferData{"vec3<float>", FormatMode::kSFloat, 32, 3, 1},
- BufferData{"mat3x3<float>", FormatMode::kSFloat, 32, 3,
- 3})); // NOLINT(whitespace/parens)
+ testing::Values(
+ BufferData{"int8", FormatMode::kSInt, 8, 1, 1, false},
+ BufferData{"int16", FormatMode::kSInt, 16, 1, 1, false},
+ BufferData{"int32", FormatMode::kSInt, 32, 1, 1, false},
+ BufferData{"int64", FormatMode::kSInt, 64, 1, 1, false},
+ BufferData{"uint8", FormatMode::kUInt, 8, 1, 1, false},
+ BufferData{"uint16", FormatMode::kUInt, 16, 1, 1, false},
+ BufferData{"uint32", FormatMode::kUInt, 32, 1, 1, false},
+ BufferData{"uint64", FormatMode::kUInt, 64, 1, 1, false},
+ BufferData{"float", FormatMode::kSFloat, 32, 1, 1, false},
+ BufferData{"double", FormatMode::kSFloat, 64, 1, 1, false},
+ BufferData{"vec2<int8>", FormatMode::kSInt, 8, 2, 1, false},
+ BufferData{"vec3<float>", FormatMode::kSFloat, 32, 3, 1, false},
+ BufferData{"vec4<uint32>", FormatMode::kUInt, 32, 4, 1, false},
+ BufferData{"mat2x4<int32>", FormatMode::kSInt, 32, 4, 2, false},
+ BufferData{"mat3x3<float>", FormatMode::kSFloat, 32, 3, 3, false},
+ BufferData{"mat4x2<uint16>", FormatMode::kUInt, 16, 2, 4, false},
+ BufferData{"B8G8R8_UNORM", FormatMode::kUNorm, 8, 3, 1,
+ false})); // NOLINT(whitespace/parens)
+
+INSTANTIATE_TEST_SUITE_P(
+ AmberScriptParserTestsDataType2,
+ AmberScriptParserBufferDataTypeTest,
+ testing::Values(
+ BufferData{"int8[]", FormatMode::kSInt, 8, 1, 1, true},
+ BufferData{"int16[]", FormatMode::kSInt, 16, 1, 1, true},
+ BufferData{"int32[]", FormatMode::kSInt, 32, 1, 1, true},
+ BufferData{"int64[]", FormatMode::kSInt, 64, 1, 1, true},
+ BufferData{"uint8[]", FormatMode::kUInt, 8, 1, 1, true},
+ BufferData{"uint16[]", FormatMode::kUInt, 16, 1, 1, true},
+ BufferData{"uint32[]", FormatMode::kUInt, 32, 1, 1, true},
+ BufferData{"uint64[]", FormatMode::kUInt, 64, 1, 1, true},
+ BufferData{"float[]", FormatMode::kSFloat, 32, 1, 1, true},
+ BufferData{"double[]", FormatMode::kSFloat, 64, 1, 1, true},
+ BufferData{"vec2<int8>[]", FormatMode::kSInt, 8, 2, 1, true},
+ BufferData{"vec3<float>[]", FormatMode::kSFloat, 32, 3, 1, true},
+ BufferData{"vec4<uint32>[]", FormatMode::kUInt, 32, 4, 1, true},
+ BufferData{"mat2x4<int32>[]", FormatMode::kSInt, 32, 4, 2, true},
+ BufferData{"mat3x3<float>[]", FormatMode::kSFloat, 32, 3, 3, true},
+ BufferData{"mat4x2<uint16>[]", FormatMode::kUInt, 16, 2, 4,
+ true})); // NOLINT(whitespace/parens)
struct NameData {
const char* name;
@@ -742,6 +884,9 @@ INSTANTIATE_TEST_SUITE_P(
AmberScriptParserBufferDataTypeInvalidTestSamples,
AmberScriptParserBufferDataTypeInvalidTest,
testing::Values(NameData{"int17"},
+ NameData{"int["},
+ NameData{"int]"},
+ NameData{"B8G8R8_UNORM[]"},
NameData{"uintt0"},
NameData{"vec7<uint8>"},
NameData{"vec27<uint8>"},
@@ -805,12 +950,12 @@ END)";
const auto* data = buffer->GetValues<uint8_t>();
EXPECT_FLOAT_EQ(1.f, *reinterpret_cast<const float*>(data + 0));
- EXPECT_EQ(64,
+ EXPECT_EQ(64u,
*reinterpret_cast<const uint32_t*>(data + 4 /* sizeof(float) */));
- EXPECT_EQ(128,
+ EXPECT_EQ(128u,
*reinterpret_cast<const uint32_t*>(data + 16 /* 8 round -> 16 */));
- EXPECT_EQ(220, *reinterpret_cast<const uint32_t*>(
- data + 20 /* 8 round -> 16 + 4 */));
+ EXPECT_EQ(220u, *reinterpret_cast<const uint32_t*>(
+ data + 20 /* 8 round -> 16 + 4 */));
}
TEST_F(AmberScriptParserTest, BufferWithStructStd430) {
@@ -853,9 +998,9 @@ END)";
const auto* data = buffer->GetValues<uint8_t>();
EXPECT_FLOAT_EQ(1.f, *reinterpret_cast<const float*>(data + 0));
- EXPECT_EQ(64, *reinterpret_cast<const uint32_t*>(data + 4));
- EXPECT_EQ(128, *reinterpret_cast<const uint32_t*>(data + 8));
- EXPECT_EQ(220, *reinterpret_cast<const uint32_t*>(data + 12));
+ EXPECT_EQ(64u, *reinterpret_cast<const uint32_t*>(data + 4));
+ EXPECT_EQ(128u, *reinterpret_cast<const uint32_t*>(data + 8));
+ EXPECT_EQ(220u, *reinterpret_cast<const uint32_t*>(data + 12));
}
TEST_F(AmberScriptParserTest, BufferWithStructAndPaddingStd430) {
@@ -898,9 +1043,9 @@ END)";
const auto* data = buffer->GetValues<uint8_t>();
EXPECT_FLOAT_EQ(1.f, *reinterpret_cast<const float*>(data + 8));
- EXPECT_EQ(64, *reinterpret_cast<const uint32_t*>(data + 16));
- EXPECT_EQ(128, *reinterpret_cast<const uint32_t*>(data + 28));
- EXPECT_EQ(220, *reinterpret_cast<const uint32_t*>(data + 36));
+ EXPECT_EQ(64u, *reinterpret_cast<const uint32_t*>(data + 16));
+ EXPECT_EQ(128u, *reinterpret_cast<const uint32_t*>(data + 28));
+ EXPECT_EQ(220u, *reinterpret_cast<const uint32_t*>(data + 36));
}
TEST_F(AmberScriptParserTest, BufferWithStructPartialInitialization) {
@@ -956,9 +1101,201 @@ END)";
const auto* data = buffer->GetValues<uint8_t>();
EXPECT_FLOAT_EQ(1.f, *reinterpret_cast<const float*>(data + 0));
- EXPECT_FLOAT_EQ(64, *reinterpret_cast<const float*>(data + 16));
- EXPECT_FLOAT_EQ(128, *reinterpret_cast<const float*>(data + 20));
- EXPECT_FLOAT_EQ(220, *reinterpret_cast<const float*>(data + 24));
+ EXPECT_FLOAT_EQ(64u, *reinterpret_cast<const float*>(data + 16));
+ EXPECT_FLOAT_EQ(128u, *reinterpret_cast<const float*>(data + 20));
+ EXPECT_FLOAT_EQ(220u, *reinterpret_cast<const float*>(data + 24));
+}
+
+TEST_F(AmberScriptParserTest, InvalidBufferWidth) {
+ std::string in = R"(
+BUFFER buf DATA_TYPE vec4<float> WIDTH a
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected an integer for WIDTH", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ZeroBufferWidth) {
+ std::string in = R"(
+BUFFER buf DATA_TYPE vec4<float> WIDTH 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected WIDTH to be positive", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, MissingBufferHeight) {
+ std::string in = R"(
+BUFFER buf DATA_TYPE vec4<float> WIDTH 1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: BUFFER HEIGHT missing", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, InvalidBufferHeight) {
+ std::string in = R"(
+BUFFER buf DATA_TYPE vec4<float> WIDTH 1 HEIGHT 1.0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected an integer for HEIGHT", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ZeroBufferHeight) {
+ std::string in = R"(
+BUFFER buf DATA_TYPE vec4<float> WIDTH 1 HEIGHT 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("2: expected HEIGHT to be positive", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BufferWidthAndHeight) {
+ std::string in = R"(
+BUFFER buf DATA_TYPE vec4<float> WIDTH 2 HEIGHT 3
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("buf", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_EQ(2u, buffer->GetWidth());
+ EXPECT_EQ(3u, buffer->GetHeight());
+ EXPECT_EQ(6u, buffer->ElementCount());
+}
+
+TEST_F(AmberScriptParserTest, BufferMipLevels) {
+ std::string in = "BUFFER my_buffer FORMAT R8G8B8A8_UNORM MIP_LEVELS 3";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("my_buffer", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_EQ(3U, buffer->GetMipLevels());
+}
+
+TEST_F(AmberScriptParserTest, BufferMissingMipLevels) {
+ std::string in = "BUFFER my_buffer FORMAT R8G8B8A8_UNORM MIP_LEVELS";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("1: invalid value for MIP_LEVELS", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BufferMissingDataFile) {
+ std::string in = "BUFFER my_buffer FORMAT R8G8B8A8_UNORM FILE";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("1: invalid value for FILE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BufferMissingDataFilePng) {
+ std::string in = "BUFFER my_buffer FORMAT R8G8B8A8_UNORM FILE PNG";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("1: missing file name for FILE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BufferDataFilePng) {
+ std::string in = "BUFFER my_buffer FORMAT R8G8B8A8_UNORM FILE PNG foo.png";
+
+ DummyDelegate delegate;
+ Parser parser(&delegate);
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+ ASSERT_TRUE(buffers[0] != nullptr);
+ ASSERT_EQ(static_cast<uint8_t>(amber::BufferDataFileType::kPng),
+ buffers[0]->GetValues<uint8_t>()[0]);
+}
+
+TEST_F(AmberScriptParserTest, BufferMissingDataFileBinary) {
+ std::string in = "BUFFER my_buffer DATA_TYPE float SIZE 10 FILE BINARY";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("1: missing file name for FILE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BufferDataFileBinary) {
+ std::string in =
+ "BUFFER my_buffer DATA_TYPE int32 SIZE 10 FILE BINARY data.bin";
+
+ DummyDelegate delegate;
+ Parser parser(&delegate);
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+ ASSERT_TRUE(buffers[0] != nullptr);
+ ASSERT_EQ(static_cast<uint8_t>(amber::BufferDataFileType::kBinary),
+ buffers[0]->GetValues<uint8_t>()[0]);
+}
+
+TEST_F(AmberScriptParserTest, BufferMissingDataFileText) {
+ std::string in = "BUFFER my_buffer DATA_TYPE float SIZE 10 FILE TEXT";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("1: missing file name for FILE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, BufferDataFileText) {
+ std::string in =
+ "BUFFER my_buffer DATA_TYPE int32 SIZE 10 FILE TEXT data.txt";
+
+ DummyDelegate delegate;
+ Parser parser(&delegate);
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+ ASSERT_TRUE(buffers[0] != nullptr);
+ ASSERT_EQ(static_cast<uint8_t>(amber::BufferDataFileType::kText),
+ buffers[0]->GetValues<uint8_t>()[0]);
}
} // namespace amberscript
diff --git a/src/amberscript/parser_clear_color_test.cc b/src/amberscript/parser_clear_color_test.cc
index 95d6e46..685d67b 100644
--- a/src/amberscript/parser_clear_color_test.cc
+++ b/src/amberscript/parser_clear_color_test.cc
@@ -140,7 +140,7 @@ INSTANTIATE_TEST_SUITE_P(
ClearColorTestData{"255 255 255 INVALID",
"invalid A value for CLEAR_COLOR command: INVALID"},
ClearColorTestData{"255 255 255 255 EXTRA",
- "extra parameters after CLEAR_COLOR command"},
+ "extra parameters after CLEAR_COLOR command: EXTRA"},
ClearColorTestData{"-1 255 255 255",
"invalid R value for CLEAR_COLOR command: -1"},
ClearColorTestData{"5.2 255 255 255",
diff --git a/src/amberscript/parser_clear_depth_test.cc b/src/amberscript/parser_clear_depth_test.cc
new file mode 100644
index 0000000..d2615fe
--- /dev/null
+++ b/src/amberscript/parser_clear_depth_test.cc
@@ -0,0 +1,134 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, ClearDepth) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+CLEAR_DEPTH my_pipeline 1.5)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsClearDepth());
+
+ auto* clr = cmd->AsClearDepth();
+ EXPECT_FLOAT_EQ(1.5, clr->GetValue());
+}
+
+TEST_F(AmberScriptParserTest, ClearDepthWithComputePipeline) {
+ std::string in = R"(
+SHADER compute my_shader GLSL
+# shader
+END
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+END
+
+CLEAR_DEPTH my_pipeline 0.0)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: CLEAR_DEPTH command requires graphics pipeline", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ClearDepthMissingPipeline) {
+ std::string in = "CLEAR_DEPTH 0.0";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: missing pipeline name for CLEAR_DEPTH command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ClearDepthInvalidPipeline) {
+ std::string in = "CLEAR_DEPTH unknown_pipeline 0.0";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("1: unknown pipeline for CLEAR_DEPTH command: unknown_pipeline",
+ r.Error());
+}
+
+struct ClearDepthTestData {
+ std::string data;
+ std::string error;
+};
+using AmberScriptParserClearDepthTest =
+ testing::TestWithParam<ClearDepthTestData>;
+TEST_P(AmberScriptParserClearDepthTest, InvalidParams) {
+ auto test_data = GetParam();
+
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+CLEAR_DEPTH my_pipeline )" +
+ test_data.data;
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << test_data.data;
+ EXPECT_EQ(std::string("13: ") + test_data.error, r.Error()) << test_data.data;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AmberScriptParserClearDepthTests,
+ AmberScriptParserClearDepthTest,
+ testing::Values(
+ ClearDepthTestData{"", "missing value for CLEAR_DEPTH command"},
+ ClearDepthTestData{"INVALID",
+ "invalid value for CLEAR_DEPTH command: INVALID"},
+ ClearDepthTestData{"5", "invalid value for CLEAR_DEPTH command: 5"},
+ ClearDepthTestData{"1.0 EXTRA",
+ "extra parameters after CLEAR_DEPTH command: "
+ "EXTRA"})); // NOLINT(whitespace/parens)
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_clear_stencil_test.cc b/src/amberscript/parser_clear_stencil_test.cc
new file mode 100644
index 0000000..eed7f14
--- /dev/null
+++ b/src/amberscript/parser_clear_stencil_test.cc
@@ -0,0 +1,137 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, ClearStencil) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+CLEAR_STENCIL my_pipeline 15)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsClearStencil());
+
+ auto* clr = cmd->AsClearStencil();
+ EXPECT_EQ(15u, clr->GetValue());
+}
+
+TEST_F(AmberScriptParserTest, ClearStencilWithComputePipeline) {
+ std::string in = R"(
+SHADER compute my_shader GLSL
+# shader
+END
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+END
+
+CLEAR_STENCIL my_pipeline 0.0)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("10: CLEAR_STENCIL command requires graphics pipeline", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ClearStencilMissingPipeline) {
+ std::string in = "CLEAR_STENCIL 0";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: missing pipeline name for CLEAR_STENCIL command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ClearStencilInvalidPipeline) {
+ std::string in = "CLEAR_STENCIL unknown_pipeline 0";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("1: unknown pipeline for CLEAR_STENCIL command: unknown_pipeline",
+ r.Error());
+}
+
+struct ClearStencilTestData {
+ std::string data;
+ std::string error;
+};
+using AmberScriptParserClearStencilTest =
+ testing::TestWithParam<ClearStencilTestData>;
+TEST_P(AmberScriptParserClearStencilTest, InvalidParams) {
+ auto test_data = GetParam();
+
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+CLEAR_STENCIL my_pipeline )" +
+ test_data.data;
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << test_data.data;
+ EXPECT_EQ(std::string("13: ") + test_data.error, r.Error()) << test_data.data;
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ AmberScriptParserClearStencilTests,
+ AmberScriptParserClearStencilTest,
+ testing::Values(
+ ClearStencilTestData{"", "missing value for CLEAR_STENCIL command"},
+ ClearStencilTestData{
+ "INVALID", "invalid value for CLEAR_STENCIL command: INVALID"},
+ ClearStencilTestData{"-5",
+ "invalid value for CLEAR_STENCIL command: -5"},
+ ClearStencilTestData{"256",
+ "invalid value for CLEAR_STENCIL command: 256"},
+ ClearStencilTestData{"10 EXTRA",
+ "extra parameters after CLEAR_STENCIL command: "
+ "EXTRA"})); // NOLINT(whitespace/parens)
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_clear_test.cc b/src/amberscript/parser_clear_test.cc
index 859ee2d..1eb812d 100644
--- a/src/amberscript/parser_clear_test.cc
+++ b/src/amberscript/parser_clear_test.cc
@@ -112,7 +112,7 @@ CLEAR my_pipeline EXTRA)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("12: extra parameters after CLEAR command", r.Error());
+ EXPECT_EQ("12: extra parameters after CLEAR command: EXTRA", r.Error());
}
} // namespace amberscript
diff --git a/src/amberscript/parser_compile_options_test.cc b/src/amberscript/parser_compile_options_test.cc
index aa181dc..fa45545 100644
--- a/src/amberscript/parser_compile_options_test.cc
+++ b/src/amberscript/parser_compile_options_test.cc
@@ -124,7 +124,8 @@ END
Parser parser;
auto r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("7: extra parameters after COMPILE_OPTIONS command", r.Error());
+ EXPECT_EQ("7: extra parameters after COMPILE_OPTIONS command: extra",
+ r.Error());
}
TEST_F(AmberScriptParserTest, PipelineShaderCompileOptionsExtraTokenEnd) {
@@ -142,7 +143,8 @@ END
Parser parser;
auto r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("8: extra parameters after COMPILE_OPTIONS command", r.Error());
+ EXPECT_EQ("8: extra parameters after COMPILE_OPTIONS command: token",
+ r.Error());
}
TEST_F(AmberScriptParserTest, PipelineShaderCompileOptionsNotOpenCL) {
diff --git a/src/amberscript/parser_debug_test.cc b/src/amberscript/parser_debug_test.cc
new file mode 100644
index 0000000..8ce8577
--- /dev/null
+++ b/src/amberscript/parser_debug_test.cc
@@ -0,0 +1,296 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <sstream>
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+#include "src/shader_data.h"
+
+namespace amber {
+namespace amberscript {
+
+namespace {
+class ThreadEventRecorder : public debug::Thread {
+ std::stringstream& events;
+ std::string indent = " ";
+
+ public:
+ explicit ThreadEventRecorder(std::stringstream& ev) : events(ev) {}
+
+ void StepOver() override { events << indent << "STEP_OVER" << std::endl; }
+ void StepIn() override { events << indent << "STEP_IN" << std::endl; }
+ void StepOut() override { events << indent << "STEP_OUT" << std::endl; }
+ void Continue() override { events << indent << "CONTINUE" << std::endl; }
+ void ExpectLocation(const debug::Location& location,
+ const std::string& line) override {
+ events << indent << "EXPECT LOCATION \"" << location.file << "\" "
+ << location.line;
+ if (!line.empty()) {
+ events << " \"" << line << "\"";
+ }
+ events << std::endl;
+ }
+ void ExpectCallstack(
+ const std::vector<debug::StackFrame>& callstack) override {
+ events << indent << "EXPECT CALLSTACK";
+ for (auto& frame : callstack) {
+ events << indent << " " << frame.name << " " << frame.location.file
+ << ":" << frame.location.line << " " << std::endl;
+ }
+ events << std::endl;
+ }
+ void ExpectLocal(const std::string& name, int64_t value) override {
+ events << indent << "EXPECT LOCAL \"" << name << "\" EQ " << value
+ << std::endl;
+ }
+ void ExpectLocal(const std::string& name, double value) override {
+ events << indent << "EXPECT LOCAL \"" << name << "\" EQ " << value
+ << std::endl;
+ }
+ void ExpectLocal(const std::string& name, const std::string& value) override {
+ events << indent << "EXPECT LOCAL \"" << name << "\" EQ \"" << value << "\""
+ << std::endl;
+ }
+};
+
+class EventRecorder : public debug::Events {
+ public:
+ std::stringstream events;
+
+ void record(const std::shared_ptr<const debug::ThreadScript>& script) {
+ ThreadEventRecorder thread{events};
+ script->Run(&thread);
+ }
+ void BreakOnComputeGlobalInvocation(
+ uint32_t x,
+ uint32_t y,
+ uint32_t z,
+ const std::shared_ptr<const debug::ThreadScript>& script) override {
+ events << "THREAD GLOBAL_INVOCATION_ID " << x << " " << y << " " << z
+ << std::endl;
+ record(script);
+ events << "END" << std::endl;
+ }
+ void BreakOnVertexIndex(
+ uint32_t index,
+ const std::shared_ptr<const debug::ThreadScript>& script) override {
+ events << "THREAD VERTEX_INDEX " << index << std::endl;
+ record(script);
+ events << "END" << std::endl;
+ }
+ void BreakOnFragmentWindowSpacePosition(
+ uint32_t x,
+ uint32_t y,
+ const std::shared_ptr<const debug::ThreadScript>& script) override {
+ events << "THREAD FRAGMENT_WINDOW_SPACE_POSITION " << x << " " << y
+ << std::endl;
+ record(script);
+ events << "END" << std::endl;
+ }
+};
+} // namespace
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, DebugEventsScript) {
+ std::string dbg = R"(THREAD GLOBAL_INVOCATION_ID 1 2 3
+ EXPECT LOCATION "compute.hlsl" 2
+ STEP_IN
+ EXPECT LOCAL "one" EQ 1
+ STEP_OUT
+ EXPECT LOCAL "pi" EQ 3.14
+ STEP_OVER
+ EXPECT LOCAL "cat" EQ "meow"
+ CONTINUE
+END
+THREAD VERTEX_INDEX 2
+ EXPECT LOCATION "vertex.hlsl" 2 " dog:woof cat:meow duck:quack"
+END
+THREAD FRAGMENT_WINDOW_SPACE_POSITION 4 5
+ EXPECT LOCATION "fragment.hlsl" 42
+ CONTINUE
+END
+)";
+
+ std::string in = R"(
+SHADER compute dbg_compute GLSL
+void main() {}
+END
+
+PIPELINE compute my_pipeline
+ ATTACH dbg_compute
+END
+
+DEBUG my_pipeline 2 4 5
+)" + dbg + "END";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsCompute());
+ auto* compute = cmd->AsCompute();
+ EXPECT_EQ(2U, compute->GetX());
+ EXPECT_EQ(4U, compute->GetY());
+ EXPECT_EQ(5U, compute->GetZ());
+
+ EventRecorder event_recorder;
+ compute->GetDebugScript()->Run(&event_recorder);
+ EXPECT_EQ(dbg, event_recorder.events.str());
+
+ auto& shaders = compute->GetPipeline()->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+
+ EXPECT_EQ(true, shaders[0].GetEmitDebugInfo());
+}
+
+TEST_F(AmberScriptParserTest, DebugEmitDebugInfoVertex) {
+ std::string dbg = R"()";
+
+ std::string in = R"(
+SHADER vertex dbg_vertex GLSL
+void main() {}
+END
+
+SHADER fragment dbg_fragment GLSL
+void main() {}
+END
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+ 1 1 2 2 3 3
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH dbg_vertex
+ ATTACH dbg_fragment
+ VERTEX_DATA position_buf LOCATION 0
+END
+
+DEBUG my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 1
+ THREAD VERTEX_INDEX 100
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsDrawArrays());
+ auto* draw = cmd->AsDrawArrays();
+
+ for (auto& shader : draw->GetPipeline()->GetShaders()) {
+ bool expect_debug_info = shader.GetShaderType() == kShaderTypeVertex;
+ EXPECT_EQ(expect_debug_info, shader.GetEmitDebugInfo())
+ << "Emit debug info for shader type " << shader.GetShaderType();
+ }
+}
+
+TEST_F(AmberScriptParserTest, DebugEmitDebugInfoFragment) {
+ std::string dbg = R"()";
+
+ std::string in = R"(
+SHADER vertex dbg_vertex GLSL
+void main() {}
+END
+
+SHADER fragment dbg_fragment GLSL
+void main() {}
+END
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+ 1 1 2 2 3 3
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH dbg_vertex
+ ATTACH dbg_fragment
+ VERTEX_DATA position_buf LOCATION 0
+END
+
+DEBUG my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 1
+ THREAD FRAGMENT_WINDOW_SPACE_POSITION 1 2
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsDrawArrays());
+ auto* draw = cmd->AsDrawArrays();
+
+ for (auto& shader : draw->GetPipeline()->GetShaders()) {
+ bool expect_debug_info = shader.GetShaderType() == kShaderTypeFragment;
+ EXPECT_EQ(expect_debug_info, shader.GetEmitDebugInfo())
+ << "Emit debug info for shader type " << shader.GetShaderType();
+ }
+}
+
+TEST_F(AmberScriptParserTest, DebugEmitNoDebugInfo) {
+ std::string dbg = R"()";
+
+ std::string in = R"(
+SHADER vertex dbg_vertex GLSL
+void main() {}
+END
+
+SHADER fragment dbg_fragment GLSL
+void main() {}
+END
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+ 1 1 2 2 3 3
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH dbg_vertex
+ ATTACH dbg_fragment
+ VERTEX_DATA position_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsDrawArrays());
+ auto* draw = cmd->AsDrawArrays();
+
+ for (auto& shader : draw->GetPipeline()->GetShaders()) {
+ EXPECT_EQ(false, shader.GetEmitDebugInfo());
+ }
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_depth_test.cc b/src/amberscript/parser_depth_test.cc
new file mode 100644
index 0000000..68ddf4b
--- /dev/null
+++ b/src/amberscript/parser_depth_test.cc
@@ -0,0 +1,557 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, DepthAllValues) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ WRITE on
+ COMPARE_OP less_or_equal
+ CLAMP on
+ BOUNDS min 1.5 max 6.7
+ BIAS constant 2.1 clamp 3.5 slope 5.5
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ auto* pipeline = pipelines[0].get();
+ ASSERT_NE(nullptr, pipeline->GetDepthStencilBuffer().buffer);
+
+ ASSERT_TRUE(pipeline->GetPipelineData()->GetEnableDepthTest());
+ ASSERT_TRUE(pipeline->GetPipelineData()->GetEnableDepthWrite());
+ ASSERT_TRUE(pipeline->GetPipelineData()->GetEnableDepthClamp());
+ ASSERT_FLOAT_EQ(1.5f, pipeline->GetPipelineData()->GetMinDepthBounds());
+ ASSERT_FLOAT_EQ(6.7f, pipeline->GetPipelineData()->GetMaxDepthBounds());
+ ASSERT_FLOAT_EQ(2.1f,
+ pipeline->GetPipelineData()->GetDepthBiasConstantFactor());
+ ASSERT_FLOAT_EQ(3.5f, pipeline->GetPipelineData()->GetDepthBiasClamp());
+ ASSERT_FLOAT_EQ(5.5f, pipeline->GetPipelineData()->GetDepthBiasSlopeFactor());
+}
+
+TEST_F(AmberScriptParserTest, DepthTestMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST
+ WRITE on
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: invalid value for TEST", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthTestInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST foo
+ WRITE on
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("16: invalid value for TEST: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthWriteMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ WRITE
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: invalid value for WRITE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthWriteInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ WRITE foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: invalid value for WRITE: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthClampMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ CLAMP
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: invalid value for CLAMP", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthClampInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ CLAMP foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: invalid value for CLAMP: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthCompareMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ COMPARE_OP
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: invalid value for COMPARE_OP", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthCompareInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ COMPARE_OP foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: invalid value for COMPARE_OP: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBoundsExpectingMin) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BOUNDS
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: BOUNDS expecting min", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBoundsMinInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BOUNDS min foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: BOUNDS invalid value for min", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBoundsExpectingMax) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BOUNDS min 0.0 foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: BOUNDS expecting max", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBoundsMaxInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BOUNDS min 0.0 max foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: BOUNDS invalid value for max", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBiasExpectingConstant) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BIAS
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: BIAS expecting constant", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBiasConstantInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BIAS constant foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: BIAS invalid value for constant", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBiasExpectingClamp) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BIAS constant 0.0 foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: BIAS expecting clamp", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBiasClampInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BIAS constant 0.0 clamp foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: BIAS invalid value for clamp", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBiasExpectingSlope) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BIAS constant 0.0 clamp 0.0
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: BIAS expecting slope", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, DepthBiasSlopeInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ DEPTH
+ TEST on
+ BIAS constant 0.0 clamp 0.0 slope foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: BIAS invalid value for slope", r.Error());
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_device_feature_test.cc b/src/amberscript/parser_device_feature_test.cc
index e7a567a..1020f2f 100644
--- a/src/amberscript/parser_device_feature_test.cc
+++ b/src/amberscript/parser_device_feature_test.cc
@@ -23,7 +23,18 @@ using AmberScriptParserTest = testing::Test;
TEST_F(AmberScriptParserTest, DeviceFeature) {
std::string in = R"(
DEVICE_FEATURE vertexPipelineStoresAndAtomics
-DEVICE_FEATURE VariablePointerFeatures.variablePointersStorageBuffer)";
+DEVICE_FEATURE VariablePointerFeatures.variablePointersStorageBuffer
+DEVICE_FEATURE Float16Int8Features.shaderFloat16
+DEVICE_FEATURE Float16Int8Features.shaderInt8
+DEVICE_FEATURE Storage8BitFeatures.storageBuffer8BitAccess
+DEVICE_FEATURE Storage8BitFeatures.uniformAndStorageBuffer8BitAccess
+DEVICE_FEATURE Storage8BitFeatures.storagePushConstant8
+DEVICE_FEATURE Storage16BitFeatures.storageBuffer16BitAccess
+DEVICE_FEATURE Storage16BitFeatures.uniformAndStorageBuffer16BitAccess
+DEVICE_FEATURE Storage16BitFeatures.storagePushConstant16
+DEVICE_FEATURE Storage16BitFeatures.storageInputOutput16
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups)";
Parser parser;
Result r = parser.Parse(in);
@@ -31,10 +42,23 @@ DEVICE_FEATURE VariablePointerFeatures.variablePointersStorageBuffer)";
auto script = parser.GetScript();
const auto& features = script->GetRequiredFeatures();
- ASSERT_EQ(2U, features.size());
+ ASSERT_EQ(13U, features.size());
EXPECT_EQ("vertexPipelineStoresAndAtomics", features[0]);
EXPECT_EQ("VariablePointerFeatures.variablePointersStorageBuffer",
features[1]);
+ EXPECT_EQ("Float16Int8Features.shaderFloat16", features[2]);
+ EXPECT_EQ("Float16Int8Features.shaderInt8", features[3]);
+ EXPECT_EQ("Storage8BitFeatures.storageBuffer8BitAccess", features[4]);
+ EXPECT_EQ("Storage8BitFeatures.uniformAndStorageBuffer8BitAccess",
+ features[5]);
+ EXPECT_EQ("Storage8BitFeatures.storagePushConstant8", features[6]);
+ EXPECT_EQ("Storage16BitFeatures.storageBuffer16BitAccess", features[7]);
+ EXPECT_EQ("Storage16BitFeatures.uniformAndStorageBuffer16BitAccess",
+ features[8]);
+ EXPECT_EQ("Storage16BitFeatures.storagePushConstant16", features[9]);
+ EXPECT_EQ("Storage16BitFeatures.storageInputOutput16", features[10]);
+ EXPECT_EQ("SubgroupSizeControl.subgroupSizeControl", features[11]);
+ EXPECT_EQ("SubgroupSizeControl.computeFullSubgroups", features[12]);
}
TEST_F(AmberScriptParserTest, DeviceFeatureMissingFeature) {
@@ -70,7 +94,8 @@ TEST_F(AmberScriptParserTest, DeviceFeatureExtraParams) {
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("1: extra parameters after DEVICE_FEATURE command", r.Error());
+ EXPECT_EQ("1: extra parameters after DEVICE_FEATURE command: EXTRA",
+ r.Error());
}
} // namespace amberscript
diff --git a/src/amberscript/parser_expect_test.cc b/src/amberscript/parser_expect_test.cc
index dc81ca8..b7e8c59 100644
--- a/src/amberscript/parser_expect_test.cc
+++ b/src/amberscript/parser_expect_test.cc
@@ -609,7 +609,7 @@ EXPECT my_fb IDX 0 0 SIZE 250 250 EQ_RGB 0 128 255 EXTRA)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("15: extra parameters after EXPECT command", r.Error());
+ EXPECT_EQ("15: extra parameters after EXPECT command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, ExpectRGBAExtraParam) {
@@ -632,7 +632,7 @@ EXPECT my_fb IDX 0 0 SIZE 250 250 EQ_RGBA 0 128 255 99 EXTRA)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("15: extra parameters after EXPECT command", r.Error());
+ EXPECT_EQ("15: extra parameters after EXPECT command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, ExpectEQ) {
@@ -656,7 +656,7 @@ EXPECT orig_buf IDX 5 EQ 11)";
EXPECT_EQ(5U, probe->GetOffset());
EXPECT_TRUE(probe->GetFormat()->IsInt32());
ASSERT_EQ(1U, probe->GetValues().size());
- EXPECT_EQ(11U, probe->GetValues()[0].AsInt32());
+ EXPECT_EQ(11, probe->GetValues()[0].AsInt32());
}
TEST_F(AmberScriptParserTest, ExpectEQStruct) {
@@ -874,7 +874,7 @@ EXPECT orig_buf IDX 5 TOLERANCE 1 EQ 11)";
EXPECT_EQ(5U, probe->GetOffset());
EXPECT_TRUE(probe->GetFormat()->IsInt32());
ASSERT_EQ(1U, probe->GetValues().size());
- EXPECT_EQ(11U, probe->GetValues()[0].AsInt32());
+ EXPECT_EQ(11, probe->GetValues()[0].AsInt32());
EXPECT_TRUE(probe->HasTolerances());
auto& tolerances = probe->GetTolerances();
@@ -904,7 +904,7 @@ EXPECT orig_buf IDX 5 TOLERANCE 1% EQ 11)";
EXPECT_EQ(5U, probe->GetOffset());
EXPECT_TRUE(probe->GetFormat()->IsInt32());
ASSERT_EQ(1U, probe->GetValues().size());
- EXPECT_EQ(11U, probe->GetValues()[0].AsInt32());
+ EXPECT_EQ(11, probe->GetValues()[0].AsInt32());
EXPECT_TRUE(probe->HasTolerances());
auto& tolerances = probe->GetTolerances();
@@ -934,7 +934,7 @@ EXPECT orig_buf IDX 5 TOLERANCE 1% .2 3.7% 4 EQ 11)";
EXPECT_EQ(5U, probe->GetOffset());
EXPECT_TRUE(probe->GetFormat()->IsInt32());
ASSERT_EQ(1U, probe->GetValues().size());
- EXPECT_EQ(11U, probe->GetValues()[0].AsInt32());
+ EXPECT_EQ(11, probe->GetValues()[0].AsInt32());
EXPECT_TRUE(probe->HasTolerances());
auto& tolerances = probe->GetTolerances();
@@ -986,6 +986,175 @@ EXPECT orig_buf IDX 5 TOLERANCE 1 2 3 4 NE 11)";
EXPECT_EQ("3: TOLERANCE only available with EQ probes", r.Error());
}
+TEST_F(AmberScriptParserTest, ExpectEqRgbaToleranceOneValue) {
+ std::string in = R"(
+BUFFER buf FORMAT R8G8B8A8_UNORM
+EXPECT buf IDX 80 80 SIZE 5 8 EQ_RGBA 128 0 128 255 TOLERANCE 3)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsProbe());
+
+ auto* probe = cmd->AsProbe();
+ EXPECT_TRUE(probe->IsRGBA());
+ EXPECT_TRUE(probe->HasTolerances());
+
+ auto& tolerances = probe->GetTolerances();
+ ASSERT_EQ(1U, tolerances.size());
+
+ EXPECT_FALSE(tolerances[0].is_percent);
+ EXPECT_FLOAT_EQ(3.f, static_cast<float>(tolerances[0].value));
+}
+
+TEST_F(AmberScriptParserTest, ExpectEqRgbaToleranceMultiValue) {
+ std::string in = R"(
+BUFFER buf FORMAT R8G8B8A8_UNORM
+EXPECT buf IDX 80 80 SIZE 5 8 EQ_RGBA 128 0 128 255 TOLERANCE 5.2 2% 4 1.5)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsProbe());
+
+ auto* probe = cmd->AsProbe();
+ EXPECT_TRUE(probe->IsRGBA());
+ EXPECT_TRUE(probe->HasTolerances());
+
+ auto& tolerances = probe->GetTolerances();
+ ASSERT_EQ(4U, tolerances.size());
+
+ EXPECT_FALSE(tolerances[0].is_percent);
+ EXPECT_FLOAT_EQ(5.2f, static_cast<float>(tolerances[0].value));
+
+ EXPECT_TRUE(tolerances[1].is_percent);
+ EXPECT_FLOAT_EQ(2.0f, static_cast<float>(tolerances[1].value));
+
+ EXPECT_FALSE(tolerances[2].is_percent);
+ EXPECT_FLOAT_EQ(4.0f, static_cast<float>(tolerances[2].value));
+
+ EXPECT_FALSE(tolerances[3].is_percent);
+ EXPECT_FLOAT_EQ(1.5f, static_cast<float>(tolerances[3].value));
+}
+
+TEST_F(AmberScriptParserTest, ExpectEqRgbaToleranceTooManyValues) {
+ std::string in = R"(
+BUFFER buf FORMAT R8G8B8A8_UNORM
+EXPECT buf IDX 80 80 SIZE 5 8 EQ_RGBA 128 0 128 255 TOLERANCE 5.2 2% 4 1.5 6)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: TOLERANCE for an RGBA comparison has a maximum of 4 values",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectEqRgbaToleranceExtraParameters) {
+ std::string in = R"(
+BUFFER buf FORMAT R8G8B8A8_UNORM
+EXPECT buf IDX 80 80 SIZE 5 8 EQ_RGBA 128 0 128 255 TOLERANCE 3 FOO)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: extra parameters after EXPECT command: FOO", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectEqRgbToleranceOneValue) {
+ std::string in = R"(
+BUFFER buf FORMAT R8G8B8_UNORM
+EXPECT buf IDX 80 80 SIZE 5 8 EQ_RGB 128 0 128 TOLERANCE 3)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsProbe());
+
+ auto* probe = cmd->AsProbe();
+ EXPECT_FALSE(probe->IsRGBA());
+ EXPECT_TRUE(probe->HasTolerances());
+
+ auto& tolerances = probe->GetTolerances();
+ ASSERT_EQ(1U, tolerances.size());
+
+ EXPECT_FALSE(tolerances[0].is_percent);
+ EXPECT_FLOAT_EQ(3.f, static_cast<float>(tolerances[0].value));
+}
+
+TEST_F(AmberScriptParserTest, ExpectEqRgbToleranceMultiValue) {
+ std::string in = R"(
+BUFFER buf FORMAT R8G8B8_UNORM
+EXPECT buf IDX 80 80 SIZE 5 8 EQ_RGB 128 0 128 TOLERANCE 5.2 2% 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsProbe());
+
+ auto* probe = cmd->AsProbe();
+ EXPECT_FALSE(probe->IsRGBA());
+ EXPECT_TRUE(probe->HasTolerances());
+
+ auto& tolerances = probe->GetTolerances();
+ ASSERT_EQ(3U, tolerances.size());
+
+ EXPECT_FALSE(tolerances[0].is_percent);
+ EXPECT_FLOAT_EQ(5.2f, static_cast<float>(tolerances[0].value));
+
+ EXPECT_TRUE(tolerances[1].is_percent);
+ EXPECT_FLOAT_EQ(2.0f, static_cast<float>(tolerances[1].value));
+
+ EXPECT_FALSE(tolerances[2].is_percent);
+ EXPECT_FLOAT_EQ(4.0f, static_cast<float>(tolerances[2].value));
+}
+
+TEST_F(AmberScriptParserTest, ExpectEqRgbToleranceTooManyValues) {
+ std::string in = R"(
+BUFFER buf FORMAT R8G8B8_UNORM
+EXPECT buf IDX 80 80 SIZE 5 8 EQ_RGB 128 0 128 TOLERANCE 5.2 2% 4 1.5)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: TOLERANCE for an RGB comparison has a maximum of 3 values",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ExpectEqRgbToleranceExtraParameters) {
+ std::string in = R"(
+BUFFER buf FORMAT R8G8B8_UNORM
+EXPECT buf IDX 80 80 SIZE 5 8 EQ_RGB 128 0 128 TOLERANCE 3 FOO)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: extra parameters after EXPECT command: FOO", r.Error());
+}
+
TEST_F(AmberScriptParserTest, ExpectRMSEBuffer) {
std::string in = R"(
BUFFER buf_1 DATA_TYPE int32 SIZE 10 FILL 11
@@ -1109,5 +1278,59 @@ EXPECT buf_1 RMSE_BUFFER buf_2)";
r.Error());
}
+TEST_F(AmberScriptParserTest, ExpectAllowIntegerHexValue) {
+ std::string in = R"(
+BUFFER b1 DATA_TYPE uint32 SIZE 4 FILL 0
+EXPECT b1 IDX 0 EQ 0x0 0x1 0x2 0x3
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsProbeSSBO());
+
+ auto* probe = cmd->AsProbeSSBO();
+ EXPECT_EQ(probe->GetComparator(), ProbeSSBOCommand::Comparator::kEqual);
+
+ EXPECT_EQ(4, probe->GetValues().size());
+ EXPECT_EQ(0, probe->GetValues()[0].AsUint64());
+ EXPECT_EQ(1, probe->GetValues()[1].AsUint64());
+ EXPECT_EQ(2, probe->GetValues()[2].AsUint64());
+ EXPECT_EQ(3, probe->GetValues()[3].AsUint64());
+}
+
+TEST_F(AmberScriptParserTest, ExpectAllowFloatHexValue) {
+ std::string in = R"(
+BUFFER b1 DATA_TYPE float SIZE 4 FILL 0
+EXPECT b1 IDX 0 EQ 0x0 0x1 0x2 0x3
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsProbeSSBO());
+
+ auto* probe = cmd->AsProbeSSBO();
+ EXPECT_EQ(probe->GetComparator(), ProbeSSBOCommand::Comparator::kEqual);
+
+ EXPECT_EQ(4, probe->GetValues().size());
+ EXPECT_EQ(static_cast<double>(0), probe->GetValues()[0].AsDouble());
+ EXPECT_EQ(static_cast<double>(1), probe->GetValues()[1].AsDouble());
+ EXPECT_EQ(static_cast<double>(2), probe->GetValues()[2].AsDouble());
+ EXPECT_EQ(static_cast<double>(3), probe->GetValues()[3].AsDouble());
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/amberscript/parser_extension_test.cc b/src/amberscript/parser_extension_test.cc
index 28354f5..0a1c8f3 100644
--- a/src/amberscript/parser_extension_test.cc
+++ b/src/amberscript/parser_extension_test.cc
@@ -76,7 +76,8 @@ TEST_F(AmberScriptParserTest, ExtensionInstanceExtraParams) {
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("1: extra parameters after INSTANCE_EXTENSION command", r.Error());
+ EXPECT_EQ("1: extra parameters after INSTANCE_EXTENSION command: EXTRA",
+ r.Error());
}
TEST_F(AmberScriptParserTest, ExtensionDevice) {
@@ -132,7 +133,8 @@ TEST_F(AmberScriptParserTest, ExtensionDeviceExtraParams) {
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("1: extra parameters after DEVICE_EXTENSION command", r.Error());
+ EXPECT_EQ("1: extra parameters after DEVICE_EXTENSION command: EXTRA",
+ r.Error());
}
} // namespace amberscript
diff --git a/src/amberscript/parser_framebuffer_test.cc b/src/amberscript/parser_framebuffer_test.cc
index 5707473..f0cf96f 100644
--- a/src/amberscript/parser_framebuffer_test.cc
+++ b/src/amberscript/parser_framebuffer_test.cc
@@ -41,8 +41,8 @@ END
ASSERT_EQ(1U, pipelines.size());
const auto* pipeline = pipelines[0].get();
- EXPECT_EQ(250, pipeline->GetFramebufferWidth());
- EXPECT_EQ(250, pipeline->GetFramebufferHeight());
+ EXPECT_EQ(250u, pipeline->GetFramebufferWidth());
+ EXPECT_EQ(250u, pipeline->GetFramebufferHeight());
}
TEST_F(AmberScriptParserTest, FramebufferSize) {
@@ -67,8 +67,8 @@ END
ASSERT_EQ(1U, pipelines.size());
const auto* pipeline = pipelines[0].get();
- EXPECT_EQ(256, pipeline->GetFramebufferWidth());
- EXPECT_EQ(246, pipeline->GetFramebufferHeight());
+ EXPECT_EQ(256u, pipeline->GetFramebufferWidth());
+ EXPECT_EQ(246u, pipeline->GetFramebufferHeight());
}
TEST_F(AmberScriptParserTest, FramebufferSizeMissingSize) {
@@ -128,7 +128,8 @@ END
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("9: extra parameters after FRAMEBUFFER_SIZE command", r.Error());
+ EXPECT_EQ("9: extra parameters after FRAMEBUFFER_SIZE command: INVALID",
+ r.Error());
}
TEST_F(AmberScriptParserTest, FramebufferInvalidWidth) {
diff --git a/src/amberscript/parser_image_test.cc b/src/amberscript/parser_image_test.cc
new file mode 100644
index 0000000..53c8ad7
--- /dev/null
+++ b/src/amberscript/parser_image_test.cc
@@ -0,0 +1,369 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, ImageNameMissing1) {
+ std::string in = R"(
+IMAGE
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: invalid IMAGE name provided", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageNameMissing2) {
+ std::string in = R"(
+IMAGE DATA_TYPE
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: missing IMAGE name", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageNameMissing3) {
+ std::string in = R"(
+IMAGE FORMAT
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: missing IMAGE name", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageNameInvalid) {
+ std::string in = R"(
+IMAGE 1
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: invalid IMAGE name provided", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageDataTypeInvalid) {
+ std::string in = R"(
+IMAGE image DATA_TYPE blah
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: invalid data type 'blah' provided", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageFormatInvalid) {
+ std::string in = R"(
+IMAGE image FORMAT blah
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: invalid IMAGE FORMAT", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageMipLevelsInvalid) {
+ std::string in = R"(
+IMAGE image FORMAT R32G32B32A32_SFLOAT MIP_LEVELS mips
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: invalid value for MIP_LEVELS", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageMissingDataTypeCommand) {
+ std::string in = R"(
+IMAGE image OTHER
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: unknown IMAGE command provided: OTHER", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageDimensionalityInvalid) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_WRONG
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: unknown IMAGE command provided: DIM_WRONG", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageDimensionalityInvalid2) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 4
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected IMAGE WIDTH", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageWidthMissing) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_3D HEIGHT 2 DEPTH 2 FILL 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected IMAGE WIDTH", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageHeightMissing) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_3D WIDTH 2 DEPTH 2 FILL 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected IMAGE HEIGHT", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageDepthMissing) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_3D WIDTH 2 HEIGHT 2 FILL 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected IMAGE DEPTH", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageWidthMissingNumber) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_3D WIDTH HEIGHT 2 DEPTH 2 FILL 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected positive IMAGE WIDTH", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageHeightMissingNumber) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_3D WIDTH 2 HEIGHT DEPTH 2 FILL 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected positive IMAGE HEIGHT", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ImageDepthMissingNumber) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_3D WIDTH 2 HEIGHT 2 DEPTH FILL 0
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected positive IMAGE DEPTH", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, Image1D) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_1D WIDTH 4
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("image", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_TRUE(buffer->GetFormat()->IsUint32());
+ EXPECT_EQ(ImageDimension::k1D, buffer->GetImageDimension());
+ EXPECT_EQ(4u, buffer->GetWidth());
+ EXPECT_EQ(1u, buffer->GetHeight());
+ EXPECT_EQ(1u, buffer->GetDepth());
+ EXPECT_EQ(4u, buffer->ElementCount());
+}
+
+TEST_F(AmberScriptParserTest, Image2D) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_2D WIDTH 3 HEIGHT 4
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("image", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_TRUE(buffer->GetFormat()->IsUint32());
+ EXPECT_EQ(ImageDimension::k2D, buffer->GetImageDimension());
+ EXPECT_EQ(3u, buffer->GetWidth());
+ EXPECT_EQ(4u, buffer->GetHeight());
+ EXPECT_EQ(1u, buffer->GetDepth());
+ EXPECT_EQ(12u, buffer->ElementCount());
+}
+
+TEST_F(AmberScriptParserTest, Image2DMultiSample) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_2D WIDTH 3 HEIGHT 4 SAMPLES 4
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("image", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_EQ(4u, buffer->GetSamples());
+}
+
+TEST_F(AmberScriptParserTest, Image2DInvalidSampleValue) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_2D WIDTH 3 HEIGHT 4 SAMPLES foo
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected integer value for SAMPLES", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, Image2DInvalidSampleCount) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_2D WIDTH 3 HEIGHT 4 SAMPLES 5
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: invalid sample count: 5", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, Image3D) {
+ std::string in = R"(
+IMAGE image DATA_TYPE uint32 DIM_3D WIDTH 3 HEIGHT 4 DEPTH 5
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("image", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_TRUE(buffer->GetFormat()->IsUint32());
+ EXPECT_EQ(ImageDimension::k3D, buffer->GetImageDimension());
+ EXPECT_EQ(3u, buffer->GetWidth());
+ EXPECT_EQ(4u, buffer->GetHeight());
+ EXPECT_EQ(5u, buffer->GetDepth());
+ EXPECT_EQ(60u, buffer->ElementCount());
+}
+
+TEST_F(AmberScriptParserTest, ImageWithData) {
+ std::string in = R"(
+IMAGE image DATA_TYPE float DIM_3D HEIGHT 2 WIDTH 2 DEPTH 2 DATA
+ 0.11 0.12
+ 0.21 0.22
+
+ 0.31 0.32
+ 0.41 0.42
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ auto script = parser.GetScript();
+ const auto& buffers = script->GetBuffers();
+ ASSERT_EQ(1U, buffers.size());
+
+ ASSERT_TRUE(buffers[0] != nullptr);
+ EXPECT_EQ("image", buffers[0]->GetName());
+
+ auto* buffer = buffers[0].get();
+ EXPECT_TRUE(buffer->GetFormat()->IsFloat32());
+ EXPECT_EQ(ImageDimension::k3D, buffer->GetImageDimension());
+ EXPECT_EQ(2u, buffer->GetWidth());
+ EXPECT_EQ(2u, buffer->GetHeight());
+ EXPECT_EQ(2u, buffer->GetDepth());
+ EXPECT_EQ(8u, buffer->ElementCount());
+
+ auto* values = buffer->GetValues<float>();
+ std::vector<float> result = {0.11f, 0.12f, 0.21f, 0.22f,
+ 0.31f, 0.32f, 0.41f, 0.42f};
+
+ EXPECT_EQ((*buffer->ValuePtr()).size(), 8u * sizeof(float));
+ for (size_t i = 0; i < result.size(); ++i) {
+ EXPECT_FLOAT_EQ(result[i], values[i]);
+ }
+}
+
+TEST_F(AmberScriptParserTest, ImageDataSizeIncorrect) {
+ std::string in = R"(
+IMAGE image DATA_TYPE float DIM_3D HEIGHT 2 WIDTH 2 DEPTH 2 DATA
+ 0.11 0.12
+ 0.21 0.22
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "6: Elements provided in data does not match size specified: 8 specified "
+ "vs 4 provided",
+ r.Error());
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_pipeline_set_test.cc b/src/amberscript/parser_pipeline_set_test.cc
index b35b35e..46f0dd4 100644
--- a/src/amberscript/parser_pipeline_set_test.cc
+++ b/src/amberscript/parser_pipeline_set_test.cc
@@ -153,7 +153,7 @@ END
Parser parser;
auto r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("7: extra parameters after SET command", r.Error());
+ EXPECT_EQ("7: extra parameters after SET command: BLAH", r.Error());
}
TEST_F(AmberScriptParserTest, OpenCLSetArgNameNotString) {
@@ -241,5 +241,22 @@ END
EXPECT_EQ("7: SET can only be used with OPENCL-C shaders", r.Error());
}
+TEST_F(AmberScriptParserTest, OpenCLSetNonScalarDataType) {
+ std::string in = R"(
+SHADER compute my_shader OPENCL-C
+#shader
+END
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+ SET KERNEL ARG_NAME arg_a AS vec4<uint32> 0 0 0 0
+END
+)";
+
+ Parser parser;
+ auto r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("7: data type must be a scalar type", r.Error());
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/amberscript/parser_pipeline_test.cc b/src/amberscript/parser_pipeline_test.cc
index ebd2ddd..2b56e2e 100644
--- a/src/amberscript/parser_pipeline_test.cc
+++ b/src/amberscript/parser_pipeline_test.cc
@@ -86,7 +86,7 @@ END
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("2: extra parameters after PIPELINE command", r.Error());
+ EXPECT_EQ("2: extra parameters after PIPELINE command: INVALID", r.Error());
}
TEST_F(AmberScriptParserTest, PipelineInvalidType) {
@@ -198,21 +198,21 @@ END)";
Buffer* buffer1 = buf1.buffer;
EXPECT_EQ(FormatType::kB8G8R8A8_UNORM, buffer1->GetFormat()->GetFormatType());
- EXPECT_EQ(0, buf1.location);
- EXPECT_EQ(250 * 250, buffer1->ElementCount());
- EXPECT_EQ(250 * 250 * 4, buffer1->ValueCount());
- EXPECT_EQ(250 * 250 * 4 * sizeof(uint8_t), buffer1->GetSizeInBytes());
+ EXPECT_EQ(0u, buf1.location);
+ EXPECT_EQ(250u * 250u, buffer1->ElementCount());
+ EXPECT_EQ(250u * 250u * 4u, buffer1->ValueCount());
+ EXPECT_EQ(250u * 250u * 4u * sizeof(uint8_t), buffer1->GetSizeInBytes());
ASSERT_EQ(1U, pipelines[1]->GetColorAttachments().size());
const auto& buf2 = pipelines[1]->GetColorAttachments()[0];
ASSERT_TRUE(buf2.buffer != nullptr);
ASSERT_EQ(buffer1, buf2.buffer);
- EXPECT_EQ(0, buf2.location);
+ EXPECT_EQ(0u, buf2.location);
EXPECT_EQ(FormatType::kB8G8R8A8_UNORM,
buf2.buffer->GetFormat()->GetFormatType());
- EXPECT_EQ(250 * 250, buf2.buffer->ElementCount());
- EXPECT_EQ(250 * 250 * 4, buf2.buffer->ValueCount());
- EXPECT_EQ(250 * 250 * 4 * sizeof(uint8_t), buf2.buffer->GetSizeInBytes());
+ EXPECT_EQ(250u * 250u, buf2.buffer->ElementCount());
+ EXPECT_EQ(250u * 250u * 4u, buf2.buffer->ValueCount());
+ EXPECT_EQ(250u * 250u * 4u * sizeof(uint8_t), buf2.buffer->GetSizeInBytes());
}
TEST_F(AmberScriptParserTest, PipelineDefaultColorBufferMismatchSize) {
@@ -240,6 +240,97 @@ END)";
r.Error());
}
+TEST_F(AmberScriptParserTest, PipelinePolygonMode) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline_default
+ ATTACH my_shader
+ ATTACH my_fragment
+ FRAMEBUFFER_SIZE 256 256
+END
+PIPELINE graphics my_pipeline_fill
+ ATTACH my_shader
+ ATTACH my_fragment
+ POLYGON_MODE fill
+ FRAMEBUFFER_SIZE 256 256
+END
+PIPELINE graphics my_pipeline_line
+ ATTACH my_shader
+ ATTACH my_fragment
+ POLYGON_MODE line
+ FRAMEBUFFER_SIZE 256 256
+END
+PIPELINE graphics my_pipeline_point
+ ATTACH my_shader
+ ATTACH my_fragment
+ POLYGON_MODE point
+ FRAMEBUFFER_SIZE 256 256
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(4U, pipelines.size());
+
+ auto mode0 = pipelines[0]->GetPipelineData()->GetPolygonMode();
+ ASSERT_EQ(mode0, PolygonMode::kFill);
+ auto mode1 = pipelines[1]->GetPipelineData()->GetPolygonMode();
+ ASSERT_EQ(mode1, PolygonMode::kFill);
+ auto mode2 = pipelines[2]->GetPipelineData()->GetPolygonMode();
+ ASSERT_EQ(mode2, PolygonMode::kLine);
+ auto mode3 = pipelines[3]->GetPipelineData()->GetPolygonMode();
+ ASSERT_EQ(mode3, PolygonMode::kPoint);
+}
+
+TEST_F(AmberScriptParserTest, PipelineMissingPolygonMode) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ POLYGON_MODE
+ FRAMEBUFFER_SIZE 256 256
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("11: missing mode in POLYGON_MODE command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, PipelineInvalidPolygonMode) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ POLYGON_MODE foo
+ FRAMEBUFFER_SIZE 256 256
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+
+ EXPECT_EQ("10: invalid polygon mode: foo", r.Error());
+}
+
TEST_F(AmberScriptParserTest, DerivePipeline) {
std::string in = R"(
SHADER vertex my_shader PASSTHROUGH
@@ -276,8 +367,8 @@ END
auto buffers1 = pipeline1->GetBuffers();
ASSERT_EQ(1U, buffers1.size());
EXPECT_EQ("buf1", buffers1[0].buffer->GetName());
- EXPECT_EQ(1, buffers1[0].descriptor_set);
- EXPECT_EQ(3, buffers1[0].binding);
+ EXPECT_EQ(1u, buffers1[0].descriptor_set);
+ EXPECT_EQ(3u, buffers1[0].binding);
auto shaders1 = pipeline1->GetShaders();
ASSERT_EQ(2U, shaders1.size());
@@ -290,8 +381,8 @@ END
auto buffers2 = pipeline2->GetBuffers();
ASSERT_EQ(1U, buffers2.size());
EXPECT_EQ("buf2", buffers2[0].buffer->GetName());
- EXPECT_EQ(1, buffers2[0].descriptor_set);
- EXPECT_EQ(3, buffers2[0].binding);
+ EXPECT_EQ(1u, buffers2[0].descriptor_set);
+ EXPECT_EQ(3u, buffers2[0].binding);
auto shaders2 = pipeline2->GetShaders();
ASSERT_EQ(2U, shaders2.size());
@@ -439,15 +530,15 @@ END
const auto& s1 = p1->GetShaders();
ASSERT_EQ(1U, s1.size());
- EXPECT_EQ(1, s1[0].GetSpecialization().size());
- EXPECT_EQ(4, s1[0].GetSpecialization().at(3));
+ EXPECT_EQ(1u, s1[0].GetSpecialization().size());
+ EXPECT_EQ(4u, s1[0].GetSpecialization().at(3));
const auto* p2 = pipelines[1].get();
const auto& s2 = p2->GetShaders();
ASSERT_EQ(1U, s2.size());
- EXPECT_EQ(1, s2[0].GetSpecialization().size());
- EXPECT_EQ(4, s2[0].GetSpecialization().at(3));
+ EXPECT_EQ(1u, s2[0].GetSpecialization().size());
+ EXPECT_EQ(4u, s2[0].GetSpecialization().at(3));
}
} // namespace amberscript
diff --git a/src/amberscript/parser_run_test.cc b/src/amberscript/parser_run_test.cc
index e4f0bd5..0f6b23f 100644
--- a/src/amberscript/parser_run_test.cc
+++ b/src/amberscript/parser_run_test.cc
@@ -125,7 +125,7 @@ RUN my_pipeline 2 4 5 EXTRA)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- ASSERT_EQ("12: extra parameters after RUN command", r.Error());
+ ASSERT_EQ("12: extra parameters after RUN command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, RunComputeInvalidZ) {
@@ -486,7 +486,368 @@ RUN my_pipeline DRAW_RECT POS 2 4 SIZE 10 20 EXTRA)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- ASSERT_EQ("12: extra parameters after RUN command", r.Error());
+ ASSERT_EQ("12: extra parameters after RUN command: EXTRA", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGrid) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 20 CELLS 4 5)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ auto* cmd = commands[0].get();
+ ASSERT_TRUE(cmd->IsDrawGrid());
+ EXPECT_FLOAT_EQ(2.f, cmd->AsDrawGrid()->GetX());
+ EXPECT_FLOAT_EQ(4.f, cmd->AsDrawGrid()->GetY());
+ EXPECT_FLOAT_EQ(10.f, cmd->AsDrawGrid()->GetWidth());
+ EXPECT_FLOAT_EQ(20.f, cmd->AsDrawGrid()->GetHeight());
+ EXPECT_EQ(4u, cmd->AsDrawGrid()->GetColumns());
+ EXPECT_EQ(5u, cmd->AsDrawGrid()->GetRows());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridWithComputePipelineInvalid) {
+ std::string in = R"(
+SHADER compute my_shader GLSL
+void main() {
+ gl_FragColor = vec3(2, 3, 4);
+}
+END
+
+PIPELINE compute my_pipeline
+ ATTACH my_shader
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 20 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: RUN command requires graphics pipeline", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridWithMissingPipeline) {
+ std::string in = R"(RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 20 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("1: unknown pipeline for RUN command: my_pipeline", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingValues) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: RUN DRAW_GRID command requires parameters", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingPOS) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID 2 4 SIZE 10 20 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: invalid token in RUN command: 2; expected POS", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridPOSMissingValues) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS SIZE 10 20 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing X position for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingPOSY) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 SIZE 10 20 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing Y position for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridInvalidPOSX) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS INVALID 4 SIZE 10 20 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing X position for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridInavlidPOSY) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 INVALID SIZE 10 20 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing Y position for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingSize) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 10 20 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: invalid token in RUN command: 10; expected SIZE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingSizeValues) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing width value for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingSizeHeight) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 CELLS 4 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing height value for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingCells) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 20)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: invalid token in RUN command: ; expected CELLS", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingCellParams) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 20 CELLS)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing columns value for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridMissingCellRows) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 20 CELLS 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing rows value for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridInvalidSizeWidth) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE INVALID 20)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing width value for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridInvalidSizeHeight) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 INVALID)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: missing height value for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawGridExtraCommands) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+END
+
+RUN my_pipeline DRAW_GRID POS 2 4 SIZE 10 20 CELLS 4 4 EXTRA)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ ASSERT_EQ("12: extra parameters after RUN command: EXTRA", r.Error());
}
TEST_F(AmberScriptParserTest, RunDrawArrays) {
@@ -521,8 +882,47 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 2)";
auto* cmd = commands[0]->AsDrawArrays();
EXPECT_FALSE(cmd->IsIndexed());
- EXPECT_FALSE(cmd->IsInstanced());
- EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(1U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetFirstInstance());
+ EXPECT_EQ(Topology::kTriangleList, cmd->GetTopology());
+ EXPECT_EQ(1U, cmd->GetFirstVertexIndex());
+ EXPECT_EQ(2U, cmd->GetVertexCount());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysInstanced) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 2 START_INSTANCE 2 INSTANCE_COUNT 10)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& commands = script->GetCommands();
+ ASSERT_EQ(1U, commands.size());
+
+ ASSERT_TRUE(commands[0]->IsDrawArrays());
+
+ auto* cmd = commands[0]->AsDrawArrays();
+ EXPECT_FALSE(cmd->IsIndexed());
+ EXPECT_EQ(static_cast<uint32_t>(10U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(2U), cmd->GetFirstInstance());
EXPECT_EQ(Topology::kTriangleList, cmd->GetTopology());
EXPECT_EQ(1U, cmd->GetFirstVertexIndex());
EXPECT_EQ(2U, cmd->GetVertexCount());
@@ -560,8 +960,8 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1)";
auto* cmd = commands[0]->AsDrawArrays();
EXPECT_FALSE(cmd->IsIndexed());
- EXPECT_FALSE(cmd->IsInstanced());
- EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(1U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetFirstInstance());
EXPECT_EQ(Topology::kTriangleList, cmd->GetTopology());
EXPECT_EQ(1U, cmd->GetFirstVertexIndex());
// There are 3 elements in the vertex buffer, but we start at element 1.
@@ -600,8 +1000,8 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST)";
auto* cmd = commands[0]->AsDrawArrays();
EXPECT_FALSE(cmd->IsIndexed());
- EXPECT_FALSE(cmd->IsInstanced());
- EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(1U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetFirstInstance());
EXPECT_EQ(Topology::kTriangleList, cmd->GetTopology());
EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetFirstVertexIndex());
// There are 3 elements in the vertex buffer.
@@ -646,8 +1046,8 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST INDEXED)";
auto* cmd = commands[0]->AsDrawArrays();
EXPECT_TRUE(cmd->IsIndexed());
- EXPECT_FALSE(cmd->IsInstanced());
- EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(1U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetFirstInstance());
EXPECT_EQ(Topology::kTriangleList, cmd->GetTopology());
EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetFirstVertexIndex());
// There are 3 elements in the vertex buffer.
@@ -830,7 +1230,7 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST 1 COUNT 2)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("18: missing START_IDX for RUN command", r.Error());
+ EXPECT_EQ("18: expecting identifier for RUN command", r.Error());
}
TEST_F(AmberScriptParserTest, RunDrawArraysMissingStartIdxValue) {
@@ -937,6 +1337,112 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX -1 COUNT 2)";
EXPECT_EQ("18: START_IDX value must be >= 0 for RUN command", r.Error());
}
+TEST_F(AmberScriptParserTest, RunDrawArraysMissingStartInstanceValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 2 START_INSTANCE INSTANCE_COUNT)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: invalid START_INSTANCE value for RUN command: INSTANCE_COUNT",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysInvalidStartInstanceValueFormat) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 2 START_INSTANCE INVALID INSTANCE_COUNT 4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: invalid START_INSTANCE value for RUN command: INVALID",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysInvalidStartInstanceValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 2 START_INSTANCE 1.3 INSTANCE_COUNT 5)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: invalid START_INSTANCE value for RUN command: 1.3", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysNegativeStartInstanceValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 2 START_INSTANCE -1 INSTANCE_COUNT 2)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: START_INSTANCE value must be >= 0 for RUN command", r.Error());
+}
+
TEST_F(AmberScriptParserTest, RunDrawArraysMissingCount) {
std::string in = R"(
SHADER vertex my_shader PASSTHROUGH
@@ -960,7 +1466,7 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 2)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("18: missing COUNT for RUN command", r.Error());
+ EXPECT_EQ("18: expecting identifier for RUN command", r.Error());
}
TEST_F(AmberScriptParserTest, RunDrawArraysMissingCountValue) {
@@ -1043,6 +1549,64 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 9)";
r.Error());
}
+TEST_F(AmberScriptParserTest, RunDrawArraysIndexedStartIdxTooLarge) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+END
+BUFFER indices DATA_TYPE int32 DATA
+0 1 2 1 2 0
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+ INDEX_DATA indices
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST INDEXED START_IDX 6 COUNT 1)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("20: START_IDX plus COUNT exceeds index buffer data size",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysIndexedCountTooLarge) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+END
+BUFFER indices DATA_TYPE int32 DATA
+0 1 2 1 2 0
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+ INDEX_DATA indices
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST INDEXED START_IDX 1 COUNT 6)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("20: START_IDX plus COUNT exceeds index buffer data size",
+ r.Error());
+}
+
TEST_F(AmberScriptParserTest, RunDrawArraysInvalidCountValueFormat) {
std::string in = R"(
SHADER vertex my_shader PASSTHROUGH
@@ -1147,5 +1711,136 @@ RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 0)";
EXPECT_EQ("18: COUNT value must be > 0 for RUN command", r.Error());
}
+TEST_F(AmberScriptParserTest, RunDrawArraysMissingInstanceCountValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 2 START_INSTANCE 0 INSTANCE_COUNT)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: invalid INSTANCE_COUNT value for RUN command: ", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysInvalidInstanceCountValueFormat) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 2 START_INSTANCE 0 INSTANCE_COUNT INVALID)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: invalid INSTANCE_COUNT value for RUN command: INVALID",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysInvalidInstanceCountValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 2 START_INSTANCE 0 INSTANCE_COUNT 2.4)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: invalid INSTANCE_COUNT value for RUN command: 2.4", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysNegativeInstanceCountValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 2 START_INSTANCE 0 INSTANCE_COUNT -2)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: INSTANCE_COUNT value must be > 0 for RUN command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, RunDrawArraysZeroInstanceCountValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER vtex_buf DATA_TYPE vec3<float> DATA
+1 2 3
+4 5 6
+7 8 9
+END
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ VERTEX_DATA vtex_buf LOCATION 0
+END
+
+RUN my_pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 1 COUNT 2 START_INSTANCE 0 INSTANCE_COUNT 0)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("18: INSTANCE_COUNT value must be > 0 for RUN command", r.Error());
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/amberscript/parser_sampler_test.cc b/src/amberscript/parser_sampler_test.cc
new file mode 100644
index 0000000..6cba69d
--- /dev/null
+++ b/src/amberscript/parser_sampler_test.cc
@@ -0,0 +1,195 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, SamplerDefaultValues) {
+ std::string in = "SAMPLER sampler";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& samplers = script->GetSamplers();
+ ASSERT_EQ(1U, samplers.size());
+
+ ASSERT_TRUE(samplers[0] != nullptr);
+ EXPECT_EQ("sampler", samplers[0]->GetName());
+
+ auto* sampler = samplers[0].get();
+ EXPECT_EQ(FilterType::kNearest, sampler->GetMagFilter());
+ EXPECT_EQ(FilterType::kNearest, sampler->GetMinFilter());
+ EXPECT_EQ(FilterType::kNearest, sampler->GetMipmapMode());
+ EXPECT_EQ(AddressMode::kRepeat, sampler->GetAddressModeU());
+ EXPECT_EQ(AddressMode::kRepeat, sampler->GetAddressModeV());
+ EXPECT_EQ(AddressMode::kRepeat, sampler->GetAddressModeW());
+ EXPECT_EQ(BorderColor::kFloatTransparentBlack, sampler->GetBorderColor());
+ EXPECT_EQ(0.0, sampler->GetMinLOD());
+ EXPECT_EQ(1.0, sampler->GetMaxLOD());
+ EXPECT_EQ(true, sampler->GetNormalizedCoords());
+}
+
+TEST_F(AmberScriptParserTest, SamplerCustomValues) {
+ std::string in = R"(
+SAMPLER sampler MAG_FILTER linear \
+ MIN_FILTER linear \
+ ADDRESS_MODE_U clamp_to_edge \
+ ADDRESS_MODE_V clamp_to_border \
+ ADDRESS_MODE_W mirrored_repeat \
+ BORDER_COLOR float_opaque_white \
+ MIN_LOD 2.5 \
+ MAX_LOD 5.0 \
+ NORMALIZED_COORDS)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& samplers = script->GetSamplers();
+ ASSERT_EQ(1U, samplers.size());
+
+ ASSERT_TRUE(samplers[0] != nullptr);
+ EXPECT_EQ("sampler", samplers[0]->GetName());
+
+ auto* sampler = samplers[0].get();
+ EXPECT_EQ(FilterType::kLinear, sampler->GetMagFilter());
+ EXPECT_EQ(FilterType::kLinear, sampler->GetMinFilter());
+ EXPECT_EQ(FilterType::kNearest, sampler->GetMipmapMode());
+ EXPECT_EQ(AddressMode::kClampToEdge, sampler->GetAddressModeU());
+ EXPECT_EQ(AddressMode::kClampToBorder, sampler->GetAddressModeV());
+ EXPECT_EQ(AddressMode::kMirroredRepeat, sampler->GetAddressModeW());
+ EXPECT_EQ(BorderColor::kFloatOpaqueWhite, sampler->GetBorderColor());
+ EXPECT_EQ(2.5, sampler->GetMinLOD());
+ EXPECT_EQ(5.0, sampler->GetMaxLOD());
+ EXPECT_EQ(true, sampler->GetNormalizedCoords());
+}
+
+TEST_F(AmberScriptParserTest, SamplerUnexpectedParameter) {
+ std::string in = R"(
+SAMPLER sampler MAG_FILTER linear \
+ FOO \
+ ADDRESS_MODE_U clamp_to_edge)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: unexpected sampler parameter FOO", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerInvalidMagFilter) {
+ std::string in = "SAMPLER sampler MAG_FILTER foo";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid MAG_FILTER value foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerInvalidMinFilter) {
+ std::string in = "SAMPLER sampler MIN_FILTER foo";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid MIN_FILTER value foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerInvalidAddressModeU) {
+ std::string in = "SAMPLER sampler ADDRESS_MODE_U foo";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid ADDRESS_MODE_U value foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerInvalidAddressModeV) {
+ std::string in = "SAMPLER sampler ADDRESS_MODE_V foo";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid ADDRESS_MODE_V value foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerInvalidBorderColor) {
+ std::string in = "SAMPLER sampler BORDER_COLOR foo";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid BORDER_COLOR value foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerInvalidMinLod) {
+ std::string in = "SAMPLER sampler MIN_LOD foo";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid token when looking for MIN_LOD value", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerInvalidMaxLod) {
+ std::string in = "SAMPLER sampler MAX_LOD foo";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: invalid token when looking for MAX_LOD value", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerMaxLodSmallerThanMinLod) {
+ std::string in = "SAMPLER sampler MIN_LOD 2.0 MAX_LOD 1.0";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("1: max LOD needs to be greater than or equal to min LOD",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SamplerUnnormalizedCoordsSetsLod) {
+ std::string in = R"(
+SAMPLER sampler \
+ MIN_LOD 2.0 \
+ MAX_LOD 3.0 \
+ UNNORMALIZED_COORDS
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess());
+ auto script = parser.GetScript();
+ const auto& samplers = script->GetSamplers();
+ ASSERT_EQ(1U, samplers.size());
+
+ ASSERT_TRUE(samplers[0] != nullptr);
+ EXPECT_EQ("sampler", samplers[0]->GetName());
+
+ auto* sampler = samplers[0].get();
+ EXPECT_EQ(0.0f, sampler->GetMinLOD());
+ EXPECT_EQ(0.0f, sampler->GetMaxLOD());
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_set_test.cc b/src/amberscript/parser_set_test.cc
index 1796402..f6068a2 100644
--- a/src/amberscript/parser_set_test.cc
+++ b/src/amberscript/parser_set_test.cc
@@ -94,7 +94,7 @@ TEST_F(AmberScriptParserTest, SetFenceTimeoutExtraParams) {
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("1: extra parameters after SET command", r.Error());
+ EXPECT_EQ("1: extra parameters after SET command: EXTRA", r.Error());
}
} // namespace amberscript
diff --git a/src/amberscript/parser_shader_opt_test.cc b/src/amberscript/parser_shader_opt_test.cc
index 5aff3e4..9bb7eb4 100644
--- a/src/amberscript/parser_shader_opt_test.cc
+++ b/src/amberscript/parser_shader_opt_test.cc
@@ -150,7 +150,8 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("5: extra parameters after SHADER_OPTIMIZATION command", r.Error());
+ EXPECT_EQ("5: extra parameters after SHADER_OPTIMIZATION command: EXTRA",
+ r.Error());
}
TEST_F(AmberScriptParserTest, PipelineShaderOptimizationNonStringParam) {
@@ -167,7 +168,7 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("6: SHADER_OPTIMIZATION options must be strings", r.Error());
+ EXPECT_EQ("6: SHADER_OPTIMIZATION options must be identifiers", r.Error());
}
} // namespace amberscript
diff --git a/src/amberscript/parser_shader_test.cc b/src/amberscript/parser_shader_test.cc
index 4858568..657cc67 100644
--- a/src/amberscript/parser_shader_test.cc
+++ b/src/amberscript/parser_shader_test.cc
@@ -119,7 +119,7 @@ TEST_F(AmberScriptParserTest, ShaderPassThroughExtraParameters) {
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("1: extra parameters after SHADER PASSTHROUGH", r.Error());
+ EXPECT_EQ("1: extra parameters after SHADER PASSTHROUGH: INVALID", r.Error());
}
TEST_F(AmberScriptParserTest, Shader) {
@@ -232,7 +232,104 @@ END)";
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("2: extra parameters after SHADER command", r.Error());
+ EXPECT_EQ("2: extra parameters after SHADER command: INVALID", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ShaderTargetEnv) {
+ std::string in = R"(#!amber
+SHADER geometry shader_name GLSL TARGET_ENV spv1.4
+void main() {
+ gl_FragColor = vec3(2, 3, 4);
+}
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& shaders = script->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+
+ const auto* shader = shaders[0].get();
+ EXPECT_EQ("spv1.4", shader->GetTargetEnv());
+}
+
+TEST_F(AmberScriptParserTest, ShaderTargetEnvMissingEnv) {
+ std::string in = R"(#!amber
+SHADER geometry shader_name GLSL TARGET_ENV
+void main() {
+ gl_FragColor = vec3(2, 3, 4);
+}
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("3: expected target environment after TARGET_ENV", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ShaderTargetEnvInvalidEnv) {
+ std::string in = R"(#!amber
+SHADER geometry shader_name GLSL TARGET_ENV 12345
+void main() {
+ gl_FragColor = vec3(2, 3, 4);
+}
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("2: expected target environment after TARGET_ENV", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, ShaderVirtualFile) {
+ std::string in = R"(#!amber
+VIRTUAL_FILE my_shader.hlsl
+My shader source
+END
+
+SHADER vertex my_shader HLSL VIRTUAL_FILE my_shader.hlsl
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_EQ(r.Error(), "");
+
+ auto script = parser.GetScript();
+ auto shader = script->GetShader("my_shader");
+ ASSERT_TRUE(shader != nullptr);
+ auto source = shader->GetData();
+ ASSERT_EQ("My shader source\n", shader->GetData());
+}
+
+TEST_F(AmberScriptParserTest, VirtualFileDuplicatePath) {
+ std::string in = R"(#!amber
+VIRTUAL_FILE my.file
+Blah
+END
+
+VIRTUAL_FILE my.file
+Blah
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_EQ(r.Error(), "8: Virtual file 'my.file' already declared");
+}
+
+TEST_F(AmberScriptParserTest, VirtualFileEmptyPath) {
+ std::string in = R"(#!amber
+VIRTUAL_FILE ""
+Blah
+END
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_EQ(r.Error(), "4: Virtual file path was empty");
}
struct ShaderTypeData {
@@ -315,6 +412,7 @@ TEST_P(AmberScriptParserShaderFormatTest, ShaderFormats) {
EXPECT_EQ(test_data.format, shader->GetFormat());
EXPECT_EQ(shader_result, shader->GetData());
}
+
INSTANTIATE_TEST_SUITE_P(
AmberScriptParserTestsShaderFormat,
AmberScriptParserShaderFormatTest,
@@ -363,5 +461,42 @@ END
ASSERT_TRUE(r.IsSuccess());
}
+TEST_F(AmberScriptParserTest, ShaderDefaultFilePath) {
+ std::string in = R"(#!amber
+SHADER fragment shader_name GLSL
+void main() {
+ gl_FragColor = vec3(2, 3, 4);
+}
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ auto shader = script->GetShader("shader_name");
+ EXPECT_EQ("embedded-shaders/shader_name", shader->GetFilePath());
+}
+
+TEST_F(AmberScriptParserTest, ShaderVirtualFilePath) {
+ std::string in = R"(#!amber
+VIRTUAL_FILE my_fragment_shader
+void main() {
+ gl_FragColor = vec3(2, 3, 4);
+}
+END
+
+SHADER fragment shader_name GLSL VIRTUAL_FILE my_fragment_shader
+)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ auto shader = script->GetShader("shader_name");
+ EXPECT_EQ("my_fragment_shader", shader->GetFilePath());
+}
+
} // namespace amberscript
} // namespace amber
diff --git a/src/amberscript/parser_stencil_test.cc b/src/amberscript/parser_stencil_test.cc
new file mode 100644
index 0000000..194761f
--- /dev/null
+++ b/src/amberscript/parser_stencil_test.cc
@@ -0,0 +1,578 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest, StencilAllValues) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ FAIL_OP increment_and_clamp
+ PASS_OP invert
+ DEPTH_FAIL_OP keep
+ COMPARE_OP equal
+ COMPARE_MASK 1
+ WRITE_MASK 2
+ REFERENCE 3
+ END
+ STENCIL back
+ TEST on
+ FAIL_OP zero
+ PASS_OP increment_and_wrap
+ DEPTH_FAIL_OP replace
+ COMPARE_OP greater
+ COMPARE_MASK 4
+ WRITE_MASK 5
+ REFERENCE 6
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ auto* pipeline = pipelines[0].get();
+ ASSERT_NE(nullptr, pipeline->GetDepthStencilBuffer().buffer);
+
+ ASSERT_TRUE(pipeline->GetPipelineData()->GetEnableStencilTest());
+ ASSERT_EQ(StencilOp::kIncrementAndClamp,
+ pipeline->GetPipelineData()->GetFrontFailOp());
+ ASSERT_EQ(StencilOp::kZero, pipeline->GetPipelineData()->GetBackFailOp());
+ ASSERT_EQ(StencilOp::kInvert, pipeline->GetPipelineData()->GetFrontPassOp());
+ ASSERT_EQ(StencilOp::kIncrementAndWrap,
+ pipeline->GetPipelineData()->GetBackPassOp());
+ ASSERT_EQ(StencilOp::kKeep,
+ pipeline->GetPipelineData()->GetFrontDepthFailOp());
+ ASSERT_EQ(StencilOp::kReplace,
+ pipeline->GetPipelineData()->GetBackDepthFailOp());
+ ASSERT_EQ(CompareOp::kEqual,
+ pipeline->GetPipelineData()->GetFrontCompareOp());
+ ASSERT_EQ(CompareOp::kGreater,
+ pipeline->GetPipelineData()->GetBackCompareOp());
+
+ ASSERT_EQ(1u, pipeline->GetPipelineData()->GetFrontCompareMask());
+ ASSERT_EQ(4u, pipeline->GetPipelineData()->GetBackCompareMask());
+ ASSERT_EQ(2u, pipeline->GetPipelineData()->GetFrontWriteMask());
+ ASSERT_EQ(5u, pipeline->GetPipelineData()->GetBackWriteMask());
+ ASSERT_EQ(3u, pipeline->GetPipelineData()->GetFrontReference());
+ ASSERT_EQ(6u, pipeline->GetPipelineData()->GetBackReference());
+}
+
+TEST_F(AmberScriptParserTest, StencilMissingFace) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL
+ TEST on
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("16: STENCIL missing face", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilInvalidFaceValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL foo
+ TEST on
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("15: STENCIL invalid face: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilTestMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: STENCIL invalid value for TEST", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilTestInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("16: STENCIL invalid value for TEST: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilFailMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ FAIL_OP
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: STENCIL invalid value for FAIL_OP", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilFailInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ FAIL_OP foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: STENCIL invalid value for FAIL_OP: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilPassMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ PASS_OP
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: STENCIL invalid value for PASS_OP", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilPassInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ PASS_OP foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: STENCIL invalid value for PASS_OP: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilDepthFailMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ DEPTH_FAIL_OP
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: STENCIL invalid value for DEPTH_FAIL_OP", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilDepthFailInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ DEPTH_FAIL_OP foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: STENCIL invalid value for DEPTH_FAIL_OP: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilCompareMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ COMPARE_OP
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: STENCIL invalid value for COMPARE_OP", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilCompareInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ COMPARE_OP foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: STENCIL invalid value for COMPARE_OP: foo", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilCompareMaskMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ COMPARE_MASK
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: STENCIL invalid value for COMPARE_MASK", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilCompareMaskInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ COMPARE_MASK foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: STENCIL invalid value for COMPARE_MASK", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilWriteMaskMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ WRITE_MASK
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: STENCIL invalid value for WRITE_MASK", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilWriteMaskInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ WRITE_MASK foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: STENCIL invalid value for WRITE_MASK", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilReferenceMissingValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ REFERENCE
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("18: STENCIL invalid value for REFERENCE", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, StencilReferenceInvalidValue) {
+ std::string in = R"(
+SHADER vertex my_shader PASSTHROUGH
+SHADER fragment my_fragment GLSL
+# GLSL Shader
+END
+BUFFER my_fb FORMAT R32G32B32A32_SFLOAT
+BUFFER my_ds FORMAT D32_SFLOAT_S8_UINT
+
+PIPELINE graphics my_pipeline
+ ATTACH my_shader
+ ATTACH my_fragment
+ BIND BUFFER my_fb AS color LOCATION 0
+ BIND BUFFER my_ds AS depth_stencil
+
+ STENCIL front
+ TEST on
+ REFERENCE foo
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("17: STENCIL invalid value for REFERENCE", r.Error());
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_subgroup_size_control_test.cc b/src/amberscript/parser_subgroup_size_control_test.cc
new file mode 100644
index 0000000..4358b4b
--- /dev/null
+++ b/src/amberscript/parser_subgroup_size_control_test.cc
@@ -0,0 +1,489 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or parseried.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "gtest/gtest.h"
+#include "src/amberscript/parser.h"
+
+namespace amber {
+namespace amberscript {
+
+using AmberScriptParserTest = testing::Test;
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlMissingRequiredFeaturecomputeFullSubgroups) {
+ std::string in = R"(
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ FULLY_POPULATED on
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "8: missing DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlMissingRequiredFeaturesubgroupSizeControl) {
+ std::string in = R"(
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ VARYING_SIZE on
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("8: missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlMissingRequiredFeaturesubgroupSizeControl2) {
+ std::string in = R"(
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE 32
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("8: missing DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlNoShader) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: missing shader name in SUBGROUP command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlUnknownShader) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP unused
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("8: unknown shader in SUBGROUP command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlUnknownIdentifier) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ UNKNOWN_SETTING
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: SUBGROUP invalid value for SUBGROUP UNKNOWN_SETTING",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlUnknownIdentifier2) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE 2 UNKNOWN_SETTING
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: SUBGROUP invalid value for SUBGROUP UNKNOWN_SETTING",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlRequiredSubgroupSizeInvalidSubgroupSize) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE unused
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: invalid size for REQUIRED_SIZE command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlRequiredSubgroupSizeInvalidSubgroupSize2) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE 0
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "9: invalid required subgroup size 0 specified for shader name "
+ "test_shader",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlRequiredSubgroupSizeInvalidSubgroupSize3) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE 256
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "9: invalid required subgroup size 256 specified for shader name "
+ "test_shader",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlRequiredSubgroupSizeInvalidSubgroupSize4) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE 7
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ(
+ "9: invalid required subgroup size 7 specified for shader name "
+ "test_shader",
+ r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlRequireFullSubgroupsInvalidValue) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ FULLY_POPULATED unused
+ END
+END)";
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: invalid value for FULLY_POPULATED command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlRequireVaryingSubgroupsInvalidValue) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ VARYING_SIZE unused
+ END
+END)";
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_FALSE(r.IsSuccess());
+ EXPECT_EQ("9: invalid value for VARYING_SIZE command", r.Error());
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlRequireFullSubgroupsSet) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ FULLY_POPULATED on
+ END
+END)";
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& shaders = pipeline->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+ ASSERT_TRUE(shaders[0].GetShader() != nullptr);
+ ASSERT_TRUE(shaders[0].GetRequireFullSubgroups() == true);
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlVaryingSubgroupsSet) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ VARYING_SIZE on
+ END
+END)";
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& shaders = pipeline->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+ ASSERT_TRUE(shaders[0].GetShader() != nullptr);
+ ASSERT_TRUE(shaders[0].GetVaryingSubgroupSize() == true);
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlRequiredSubgroupSizeSetTo8) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE 8
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& shaders = pipeline->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+ ASSERT_TRUE(shaders[0].GetShader() != nullptr);
+ ASSERT_TRUE(shaders[0].GetRequiredSubgroupSizeSetting() ==
+ amber::Pipeline::ShaderInfo::RequiredSubgroupSizeSetting::
+ kSetToSpecificSize);
+ ASSERT_EQ(8U, shaders[0].GetRequiredSubgroupSize());
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlRequiredSubgroupSizeSetToMax) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE MAX
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& shaders = pipeline->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+ ASSERT_TRUE(shaders[0].GetShader() != nullptr);
+ ASSERT_TRUE(shaders[0].GetRequiredSubgroupSizeSetting() ==
+ amber::Pipeline::ShaderInfo::RequiredSubgroupSizeSetting::
+ kSetToMaximumSize);
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlRequiredSubgroupSizeSetToMin) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ REQUIRED_SIZE MIN
+ END
+END)";
+
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& shaders = pipeline->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+ ASSERT_TRUE(shaders[0].GetShader() != nullptr);
+ ASSERT_TRUE(shaders[0].GetRequiredSubgroupSizeSetting() ==
+ amber::Pipeline::ShaderInfo::RequiredSubgroupSizeSetting::
+ kSetToMinimumSize);
+}
+
+TEST_F(AmberScriptParserTest,
+ SubgroupSizeControlRequireFullAndVaryingSubgroups) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ FULLY_POPULATED on
+ VARYING_SIZE on
+ END
+END)";
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& shaders = pipeline->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+ ASSERT_TRUE(shaders[0].GetShader() != nullptr);
+ ASSERT_TRUE(shaders[0].GetRequireFullSubgroups() == true);
+ ASSERT_TRUE(shaders[0].GetVaryingSubgroupSize() == true);
+}
+
+TEST_F(AmberScriptParserTest, SubgroupSizeControlRequireFullAndMinSubgroups) {
+ std::string in = R"(
+DEVICE_FEATURE SubgroupSizeControl.computeFullSubgroups
+DEVICE_FEATURE SubgroupSizeControl.subgroupSizeControl
+SHADER compute test_shader GLSL
+# GLSL
+END
+PIPELINE compute pipeline
+ ATTACH test_shader
+ SUBGROUP test_shader
+ FULLY_POPULATED on
+ REQUIRED_SIZE MIN
+ END
+END)";
+ Parser parser;
+ Result r = parser.Parse(in);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ auto script = parser.GetScript();
+ const auto& pipelines = script->GetPipelines();
+ ASSERT_EQ(1U, pipelines.size());
+
+ const auto* pipeline = pipelines[0].get();
+ const auto& shaders = pipeline->GetShaders();
+ ASSERT_EQ(1U, shaders.size());
+ ASSERT_TRUE(shaders[0].GetShader() != nullptr);
+ ASSERT_TRUE(shaders[0].GetRequireFullSubgroups() == true);
+ ASSERT_TRUE(shaders[0].GetRequiredSubgroupSizeSetting() ==
+ amber::Pipeline::ShaderInfo::RequiredSubgroupSizeSetting::
+ kSetToMinimumSize);
+}
+
+} // namespace amberscript
+} // namespace amber
diff --git a/src/amberscript/parser_test.cc b/src/amberscript/parser_test.cc
index 37b61af..c7390a4 100644
--- a/src/amberscript/parser_test.cc
+++ b/src/amberscript/parser_test.cc
@@ -40,7 +40,7 @@ TEST_F(AmberScriptParserTest, InvalidStartToken) {
Parser parser;
Result r = parser.Parse(in);
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("3: expected string", r.Error());
+ EXPECT_EQ("3: expected identifier", r.Error());
}
TEST_F(AmberScriptParserTest, UnknownStartToken) {
diff --git a/src/buffer.cc b/src/buffer.cc
index f5d2270..71f295d 100644
--- a/src/buffer.cc
+++ b/src/buffer.cc
@@ -19,37 +19,11 @@
#include <cmath>
#include <cstring>
+#include "src/float16_helper.h"
+
namespace amber {
namespace {
-// Return sign value of 32 bits float.
-uint16_t FloatSign(const uint32_t hex_float) {
- return static_cast<uint16_t>(hex_float >> 31U);
-}
-
-// Return exponent value of 32 bits float.
-uint16_t FloatExponent(const uint32_t hex_float) {
- uint32_t exponent = ((hex_float >> 23U) & ((1U << 8U) - 1U)) - 112U;
- const uint32_t half_exponent_mask = (1U << 5U) - 1U;
- assert(((exponent & ~half_exponent_mask) == 0U) && "Float exponent overflow");
- return static_cast<uint16_t>(exponent & half_exponent_mask);
-}
-
-// Return mantissa value of 32 bits float. Note that mantissa for 32
-// bits float is 23 bits and this method must return uint32_t.
-uint32_t FloatMantissa(const uint32_t hex_float) {
- return static_cast<uint32_t>(hex_float & ((1U << 23U) - 1U));
-}
-
-// Convert 32 bits float |value| to 16 bits float based on IEEE-754.
-uint16_t FloatToHexFloat16(const float value) {
- const uint32_t* hex = reinterpret_cast<const uint32_t*>(&value);
- return static_cast<uint16_t>(
- static_cast<uint16_t>(FloatSign(*hex) << 15U) |
- static_cast<uint16_t>(FloatExponent(*hex) << 10U) |
- static_cast<uint16_t>(FloatMantissa(*hex) >> 13U));
-}
-
template <typename T>
T* ValuesAs(uint8_t* values) {
return reinterpret_cast<T*>(values);
@@ -82,10 +56,10 @@ double CalculateDiff(const Format::Segment* seg,
return Sub<uint32_t>(buf1, buf2);
if (type::Type::IsUint64(mode, num_bits))
return Sub<uint64_t>(buf1, buf2);
- // TODO(dsinclair): Handle float16 ...
if (type::Type::IsFloat16(mode, num_bits)) {
- assert(false && "Float16 suppport not implemented");
- return 0.0;
+ float val1 = float16::HexFloatToFloat(buf1, 16);
+ float val2 = float16::HexFloatToFloat(buf2, 16);
+ return static_cast<double>(val1 - val2);
}
if (type::Type::IsFloat32(mode, num_bits))
return Sub<float>(buf1, buf2);
@@ -100,8 +74,6 @@ double CalculateDiff(const Format::Segment* seg,
Buffer::Buffer() = default;
-Buffer::Buffer(BufferType type) : buffer_type_(type) {}
-
Buffer::~Buffer() = default;
Result Buffer::CopyTo(Buffer* buffer) const {
@@ -401,7 +373,7 @@ uint32_t Buffer::WriteValueFromComponent(const Value& value,
return sizeof(uint64_t);
}
if (type::Type::IsFloat16(mode, num_bits)) {
- *(ValuesAs<uint16_t>(ptr)) = FloatToHexFloat16(value.AsFloat());
+ *(ValuesAs<uint16_t>(ptr)) = float16::FloatToHexFloat16(value.AsFloat());
return sizeof(uint16_t);
}
if (type::Type::IsFloat32(mode, num_bits)) {
diff --git a/src/buffer.h b/src/buffer.h
index c209a3f..18f7fed 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -21,12 +21,16 @@
#include <utility>
#include <vector>
+#include "amber/amber.h"
#include "amber/result.h"
#include "amber/value.h"
#include "src/format.h"
+#include "src/image.h"
namespace amber {
+class Sampler;
+
/// Types of buffers which can be created.
enum class BufferType : int8_t {
/// Unknown buffer type
@@ -34,19 +38,36 @@ enum class BufferType : int8_t {
/// A color buffer.
kColor = 0,
/// A depth/stencil buffer.
- kDepth,
+ kDepthStencil,
/// An index buffer.
kIndex,
- /// A sampled buffer.
- kSampled,
+ /// A sampled image.
+ kSampledImage,
+ /// A combined image sampler.
+ kCombinedImageSampler,
/// A storage buffer.
kStorage,
+ /// A dynamic storage buffer.
+ kStorageDynamic,
/// A uniform buffer.
kUniform,
+ /// A dynamic uniform buffer.
+ kUniformDynamic,
/// A push constant buffer.
kPushConstant,
/// A vertex buffer.
- kVertex
+ kVertex,
+ /// A storage image.
+ kStorageImage,
+ /// A uniform texel buffer.
+ kUniformTexelBuffer,
+ /// A storage texel buffer.
+ kStorageTexelBuffer
+};
+
+enum class InputRate : int8_t {
+ kVertex = 0,
+ kInstance,
};
/// A buffer stores data. The buffer maybe provided from the input script, or
@@ -55,16 +76,9 @@ class Buffer {
public:
/// Create a buffer of unknown type.
Buffer();
- /// Create a buffer of |type_|.
- explicit Buffer(BufferType type);
~Buffer();
- /// Returns the BufferType of this buffer.
- BufferType GetBufferType() const { return buffer_type_; }
- /// Sets the BufferType for this buffer.
- void SetBufferType(BufferType type) { buffer_type_ = type; }
-
/// Sets the Format of the buffer to |format|.
void SetFormat(Format* format) {
format_is_default_ = false;
@@ -73,6 +87,11 @@ class Buffer {
/// Returns the Format describing the buffer data.
Format* GetFormat() const { return format_; }
+ /// Sets the sampler used with buffer of combined image sampler type.
+ void SetSampler(Sampler* sampler) { sampler_ = sampler; }
+ /// Returns the sampler of combined image sampler buffer.
+ Sampler* GetSampler() const { return sampler_; }
+
void SetFormatIsDefault(bool val) { format_is_default_ = val; }
bool FormatIsDefault() const { return format_is_default_; }
@@ -89,6 +108,15 @@ class Buffer {
uint32_t GetHeight() const { return height_; }
/// Set the number of elements high for the buffer.
void SetHeight(uint32_t height) { height_ = height; }
+ /// Get the number of elements this buffer is deep.
+ uint32_t GetDepth() const { return depth_; }
+ /// Set the number of elements this buffer is deep.
+ void SetDepth(uint32_t depth) { depth_ = depth; }
+
+ /// Get the image dimensionality.
+ ImageDimension GetImageDimension() const { return image_dim_; }
+ /// Set the image dimensionality.
+ void SetImageDimension(ImageDimension dim) { image_dim_ = dim; }
// | ---------- Element ---------- | ElementCount == 1
// | Value | Value | Value | Value | ValueCount == 4
@@ -175,6 +203,19 @@ class Buffer {
/// Writes |src| data into buffer at |offset|.
Result SetDataFromBuffer(const Buffer* src, uint32_t offset);
+ /// Sets the number of mip levels for a buffer used as a color buffer
+ /// or a texture.
+ void SetMipLevels(uint32_t mip_levels) { mip_levels_ = mip_levels; }
+
+ /// Returns the number of mip levels.
+ uint32_t GetMipLevels() const { return mip_levels_; }
+
+ /// Sets the number of samples.
+ void SetSamples(uint32_t samples) { samples_ = samples; }
+
+ /// Returns the number of samples.
+ uint32_t GetSamples() const { return samples_; }
+
/// Returns a pointer to the internal storage of the buffer.
std::vector<uint8_t>* ValuePtr() { return &bytes_; }
/// Returns a pointer to the internal storage of the buffer.
@@ -217,17 +258,21 @@ class Buffer {
// those stored in |buffer| and returns all the values.
std::vector<double> CalculateDiffs(const Buffer* buffer) const;
- BufferType buffer_type_ = BufferType::kUnknown;
std::string name_;
/// max_size_in_bytes_ is the total size in bytes needed to hold the buffer
/// over all ubo, ssbo size and ssbo subdata size calls.
uint32_t max_size_in_bytes_ = 0;
uint32_t element_count_ = 0;
- uint32_t width_ = 0;
- uint32_t height_ = 0;
+ uint32_t width_ = 1;
+ uint32_t height_ = 1;
+ uint32_t depth_ = 1;
+ uint32_t mip_levels_ = 1;
+ uint32_t samples_ = 1;
bool format_is_default_ = false;
std::vector<uint8_t> bytes_;
Format* format_ = nullptr;
+ Sampler* sampler_ = nullptr;
+ ImageDimension image_dim_ = ImageDimension::kUnknown;
};
} // namespace amber
diff --git a/src/buffer_test.cc b/src/buffer_test.cc
index a869f7e..1868b95 100644
--- a/src/buffer_test.cc
+++ b/src/buffer_test.cc
@@ -14,9 +14,11 @@
#include "src/buffer.h"
+#include <limits>
#include <utility>
#include "gtest/gtest.h"
+#include "src/float16_helper.h"
#include "src/type_parser.h"
namespace amber {
@@ -24,7 +26,7 @@ namespace amber {
using BufferTest = testing::Test;
TEST_F(BufferTest, EmptyByDefault) {
- Buffer b(BufferType::kColor);
+ Buffer b;
EXPECT_EQ(static_cast<size_t>(0U), b.ElementCount());
EXPECT_EQ(static_cast<size_t>(0U), b.ValueCount());
EXPECT_EQ(static_cast<size_t>(0U), b.GetSizeInBytes());
@@ -35,12 +37,12 @@ TEST_F(BufferTest, Size) {
auto type = parser.Parse("R16_SINT");
Format fmt(type.get());
- Buffer b(BufferType::kColor);
+ Buffer b;
b.SetFormat(&fmt);
b.SetElementCount(10);
- EXPECT_EQ(10, b.ElementCount());
- EXPECT_EQ(10, b.ValueCount());
- EXPECT_EQ(10 * sizeof(int16_t), b.GetSizeInBytes());
+ EXPECT_EQ(10u, b.ElementCount());
+ EXPECT_EQ(10u, b.ValueCount());
+ EXPECT_EQ(10u * sizeof(int16_t), b.GetSizeInBytes());
}
TEST_F(BufferTest, SizeFromData) {
@@ -51,13 +53,13 @@ TEST_F(BufferTest, SizeFromData) {
auto type = parser.Parse("R32_SFLOAT");
Format fmt(type.get());
- Buffer b(BufferType::kColor);
+ Buffer b;
b.SetFormat(&fmt);
b.SetData(std::move(values));
- EXPECT_EQ(5, b.ElementCount());
- EXPECT_EQ(5, b.ValueCount());
- EXPECT_EQ(5 * sizeof(float), b.GetSizeInBytes());
+ EXPECT_EQ(5u, b.ElementCount());
+ EXPECT_EQ(5u, b.ValueCount());
+ EXPECT_EQ(5u * sizeof(float), b.GetSizeInBytes());
}
TEST_F(BufferTest, SizeFromDataDoesNotOverrideSize) {
@@ -68,14 +70,14 @@ TEST_F(BufferTest, SizeFromDataDoesNotOverrideSize) {
auto type = parser.Parse("R32_SFLOAT");
Format fmt(type.get());
- Buffer b(BufferType::kColor);
+ Buffer b;
b.SetFormat(&fmt);
b.SetElementCount(20);
b.SetData(std::move(values));
- EXPECT_EQ(20, b.ElementCount());
- EXPECT_EQ(20, b.ValueCount());
- EXPECT_EQ(20 * sizeof(float), b.GetSizeInBytes());
+ EXPECT_EQ(20u, b.ElementCount());
+ EXPECT_EQ(20u, b.ValueCount());
+ EXPECT_EQ(20u * sizeof(float), b.GetSizeInBytes());
}
TEST_F(BufferTest, SizeMatrixStd430) {
@@ -84,13 +86,13 @@ TEST_F(BufferTest, SizeMatrixStd430) {
type->SetColumnCount(3);
Format fmt(type.get());
- Buffer b(BufferType::kColor);
+ Buffer b;
b.SetFormat(&fmt);
b.SetElementCount(10);
- EXPECT_EQ(10, b.ElementCount());
- EXPECT_EQ(60, b.ValueCount());
- EXPECT_EQ(60 * sizeof(int16_t), b.GetSizeInBytes());
+ EXPECT_EQ(10u, b.ElementCount());
+ EXPECT_EQ(60u, b.ValueCount());
+ EXPECT_EQ(60u * sizeof(int16_t), b.GetSizeInBytes());
}
TEST_F(BufferTest, SizeMatrixStd140) {
@@ -100,13 +102,13 @@ TEST_F(BufferTest, SizeMatrixStd140) {
Format fmt(type.get());
fmt.SetLayout(Format::Layout::kStd140);
- Buffer b(BufferType::kColor);
+ Buffer b;
b.SetFormat(&fmt);
b.SetElementCount(10);
- EXPECT_EQ(10, b.ElementCount());
- EXPECT_EQ(10 * 2 * 3, b.ValueCount());
- EXPECT_EQ(120 * sizeof(int16_t), b.GetSizeInBytes());
+ EXPECT_EQ(10u, b.ElementCount());
+ EXPECT_EQ(10u * 2u * 3u, b.ValueCount());
+ EXPECT_EQ(120u * sizeof(int16_t), b.GetSizeInBytes());
}
TEST_F(BufferTest, SizeMatrixPaddedStd430) {
@@ -115,7 +117,7 @@ TEST_F(BufferTest, SizeMatrixPaddedStd430) {
type->SetColumnCount(3);
Format fmt(type.get());
- Buffer b(BufferType::kColor);
+ Buffer b;
b.SetFormat(&fmt);
b.SetValueCount(9);
@@ -124,4 +126,196 @@ TEST_F(BufferTest, SizeMatrixPaddedStd430) {
EXPECT_EQ(12U * sizeof(int32_t), b.GetSizeInBytes());
}
+// Creates 10 RGBA pixel values, with the blue channels ranging from 0 to 255,
+// and checks that the bin for each blue channel value contains 1, as expected.
+TEST_F(BufferTest, GetHistogramForChannelGradient) {
+ TypeParser parser;
+ auto type = parser.Parse("R8G8B8A8_UINT");
+ Format fmt(type.get());
+
+ // Creates 10 RBGA pixel values with the blue channels ranging from 0 to 255.
+ // Every value gets multiplied by 25 to create a gradient
+ std::vector<Value> values(40);
+ for (uint32_t i = 0; i < values.size(); i += 4)
+ values[i + 2].SetIntValue(i / 4 * 25);
+
+ Buffer b;
+ b.SetFormat(&fmt);
+ b.SetData(values);
+
+ std::vector<uint64_t> bins = b.GetHistogramForChannel(2, 256);
+ for (uint32_t i = 0; i < values.size(); i += 4)
+ EXPECT_EQ(1u, bins[i / 4 * 25]);
+}
+
+// Creates 10 RGBA pixel values, with all channels being 0, and checks that all
+// channels have a count of 10 (all pixels) in the 0 bin.
+TEST_F(BufferTest, GetHistogramForChannelAllBlack) {
+ TypeParser parser;
+ auto type = parser.Parse("R8G8B8A8_UINT");
+ Format fmt(type.get());
+
+ std::vector<Value> values(40);
+ for (uint32_t i = 0; i < values.size(); i++)
+ values[i].SetIntValue(0);
+
+ Buffer b;
+ b.SetFormat(&fmt);
+ b.SetData(values);
+
+ for (uint8_t i = 0; i < 4; i++) {
+ std::vector<uint64_t> bins = b.GetHistogramForChannel(i, 256);
+ for (uint32_t y = 0; y < values.size(); y++)
+ EXPECT_EQ(10u, bins[0]);
+ }
+}
+
+// Creates 10 RGBA pixel values, with all channels being the maximum value of 8
+// bit uint, and checks that all channels have a count of 10 (all pixels) in the
+// 255 (max uint8_t) bin.
+TEST_F(BufferTest, GetHistogramForChannelAllWhite) {
+ TypeParser parser;
+ auto type = parser.Parse("R8G8B8A8_UINT");
+ Format fmt(type.get());
+
+ std::vector<Value> values(40);
+ for (uint32_t i = 0; i < values.size(); i++)
+ values[i].SetIntValue(std::numeric_limits<uint8_t>::max());
+
+ Buffer b;
+ b.SetFormat(&fmt);
+ b.SetData(values);
+
+ for (uint8_t i = 0; i < 4; i++) {
+ std::vector<uint64_t> bins = b.GetHistogramForChannel(i, 256);
+ for (uint32_t y = 0; y < values.size(); y++)
+ EXPECT_EQ(10u, bins[255]);
+ }
+}
+
+// Creates two sets of equal pixel values, except for one pixel that has +50 in
+// its red channel. Compares the histograms to see if they are equal with a low
+// threshold, which we expect to fail.
+TEST_F(BufferTest, CompareHistogramEMDToleranceFalse) {
+ TypeParser parser;
+ auto type = parser.Parse("R8G8B8A8_UINT");
+ Format fmt(type.get());
+
+ // Every value gets multiplied by 25 to create a gradient
+ std::vector<Value> values1(40);
+ for (uint32_t i = 0; i < values1.size(); i += 4)
+ values1[i].SetIntValue(i / 4 * 25);
+
+ std::vector<Value> values2 = values1;
+ values2[4].SetIntValue(values2[4].AsUint8() + 50);
+
+ Buffer b1;
+ b1.SetFormat(&fmt);
+ b1.SetData(values1);
+
+ Buffer b2;
+ b2.SetFormat(&fmt);
+ b2.SetData(values2);
+
+ EXPECT_FALSE(b1.CompareHistogramEMD(&b2, 0.001f).IsSuccess());
+}
+
+// Creates two sets of equal pixel values, except for one pixel that has +50 in
+// its red channel. Compares the histograms to see if they are equal with a high
+// threshold, which we expect to succeed.
+TEST_F(BufferTest, CompareHistogramEMDToleranceTrue) {
+ TypeParser parser;
+ auto type = parser.Parse("R8G8B8A8_UINT");
+ Format fmt(type.get());
+
+ // Every value gets multiplied by 25 to create a gradient
+ std::vector<Value> values1(40);
+ for (uint32_t i = 0; i < values1.size(); i += 4)
+ values1[i].SetIntValue(i / 4 * 25);
+
+ std::vector<Value> values2 = values1;
+ values2[4].SetIntValue(values2[4].AsUint8() + 50);
+
+ Buffer b1;
+ b1.SetFormat(&fmt);
+ b1.SetData(values1);
+
+ Buffer b2;
+ b2.SetFormat(&fmt);
+ b2.SetData(values2);
+
+ EXPECT_TRUE(b1.CompareHistogramEMD(&b2, 0.02f).IsSuccess());
+}
+
+// Creates two identical sets of RGBA pixel values and checks that the
+// histograms are equal.
+TEST_F(BufferTest, CompareHistogramEMDToleranceAllBlack) {
+ TypeParser parser;
+ auto type = parser.Parse("R8G8B8A8_UINT");
+ Format fmt(type.get());
+
+ std::vector<Value> values1(40);
+ for (uint32_t i = 0; i < values1.size(); i++)
+ values1[i].SetIntValue(0);
+
+ std::vector<Value> values2 = values1;
+
+ Buffer b1;
+ b1.SetFormat(&fmt);
+ b1.SetData(values1);
+
+ Buffer b2;
+ b2.SetFormat(&fmt);
+ b2.SetData(values2);
+
+ EXPECT_TRUE(b1.CompareHistogramEMD(&b2, 0.0f).IsSuccess());
+}
+
+// Creates two identical sets of RGBA pixel values and checks that the
+// histograms are equal.
+TEST_F(BufferTest, CompareHistogramEMDToleranceAllWhite) {
+ TypeParser parser;
+ auto type = parser.Parse("R8G8B8A8_UINT");
+ Format fmt(type.get());
+
+ std::vector<Value> values1(40);
+ for (uint32_t i = 0; i < values1.size(); i++)
+ values1[i].SetIntValue(std::numeric_limits<uint8_t>().max());
+
+ std::vector<Value> values2 = values1;
+
+ Buffer b1;
+ b1.SetFormat(&fmt);
+ b1.SetData(values1);
+
+ Buffer b2;
+ b2.SetFormat(&fmt);
+ b2.SetData(values2);
+
+ EXPECT_TRUE(b1.CompareHistogramEMD(&b2, 0.0f).IsSuccess());
+}
+
+TEST_F(BufferTest, SetFloat16) {
+ std::vector<Value> values;
+ values.resize(2);
+ values[0].SetDoubleValue(2.8);
+ values[1].SetDoubleValue(1234.567);
+
+ TypeParser parser;
+ auto type = parser.Parse("R16_SFLOAT");
+
+ Format fmt(type.get());
+ Buffer b;
+ b.SetFormat(&fmt);
+ b.SetData(std::move(values));
+
+ EXPECT_EQ(2u, b.ElementCount());
+ EXPECT_EQ(2u, b.ValueCount());
+ EXPECT_EQ(4u, b.GetSizeInBytes());
+
+ auto v = b.GetValues<uint16_t>();
+ EXPECT_EQ(float16::FloatToHexFloat16(2.8f), v[0]);
+ EXPECT_EQ(float16::FloatToHexFloat16(1234.567f), v[1]);
+}
+
} // namespace amber
diff --git a/src/cast_hash.h b/src/cast_hash.h
index e02c148..4d9d812 100644
--- a/src/cast_hash.h
+++ b/src/cast_hash.h
@@ -15,6 +15,8 @@
#ifndef SRC_CAST_HASH_H_
#define SRC_CAST_HASH_H_
+#include <cstddef>
+
namespace amber {
/// A hash implementation for types that can trivially be up-cast to a size_t.
diff --git a/src/clspv_helper.cc b/src/clspv_helper.cc
index d0e9f49..59b8737 100644
--- a/src/clspv_helper.cc
+++ b/src/clspv_helper.cc
@@ -14,74 +14,275 @@
#include "src/clspv_helper.h"
+#include <unordered_map>
#include <utility>
#include "clspv/ArgKind.h"
#include "clspv/Compiler.h"
+#include "clspv/Sampler.h"
+#include "spirv-tools/libspirv.hpp"
+#include "spirv-tools/optimizer.hpp"
+#include "spirv/unified1/NonSemanticClspvReflection.h"
+#include "spirv/unified1/spirv.hpp"
+
+using amber::Pipeline;
+
+namespace {
+
+struct ReflectionHelper {
+ Pipeline::ShaderInfo* shader_info = nullptr;
+ Pipeline* pipeline = nullptr;
+ uint32_t uint_id = 0;
+ std::unordered_map<uint32_t, std::string> strings;
+ std::unordered_map<uint32_t, uint32_t> constants;
+ std::string error_message;
+};
+
+Pipeline::ShaderInfo::DescriptorMapEntry::Kind GetArgKindFromExtInst(
+ uint32_t value) {
+ switch (static_cast<NonSemanticClspvReflectionInstructions>(value)) {
+ case NonSemanticClspvReflectionArgumentStorageBuffer:
+ case NonSemanticClspvReflectionConstantDataStorageBuffer:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
+ case NonSemanticClspvReflectionArgumentUniform:
+ case NonSemanticClspvReflectionConstantDataUniform:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO;
+ case NonSemanticClspvReflectionArgumentPodStorageBuffer:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
+ case NonSemanticClspvReflectionArgumentPodUniform:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO;
+ case NonSemanticClspvReflectionArgumentPodPushConstant:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT;
+ case NonSemanticClspvReflectionArgumentSampledImage:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE;
+ case NonSemanticClspvReflectionArgumentStorageImage:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE;
+ case NonSemanticClspvReflectionArgumentSampler:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER;
+ case NonSemanticClspvReflectionArgumentWorkgroup:
+ default:
+ return Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
+ }
+}
+
+spv_result_t ParseExtendedInst(ReflectionHelper* helper,
+ const spv_parsed_instruction_t* inst) {
+ auto ext_inst = inst->words[inst->operands[3].offset];
+ switch (ext_inst) {
+ case NonSemanticClspvReflectionKernel: {
+ // Remap the name string to the declaration's result id.
+ const auto& name = helper->strings[inst->words[inst->operands[5].offset]];
+ helper->strings[inst->result_id] = name;
+ break;
+ }
+ case NonSemanticClspvReflectionArgumentInfo: {
+ // Remap the name string to the info's result id.
+ const auto& name = helper->strings[inst->words[inst->operands[4].offset]];
+ helper->strings[inst->result_id] = name;
+ break;
+ }
+ case NonSemanticClspvReflectionArgumentStorageBuffer:
+ case NonSemanticClspvReflectionArgumentUniform:
+ case NonSemanticClspvReflectionArgumentSampledImage:
+ case NonSemanticClspvReflectionArgumentStorageImage:
+ case NonSemanticClspvReflectionArgumentSampler: {
+ // These arguments have descriptor set and binding.
+ auto kernel_id = inst->words[inst->operands[4].offset];
+ auto ordinal_id = inst->words[inst->operands[5].offset];
+ auto ds_id = inst->words[inst->operands[6].offset];
+ auto binding_id = inst->words[inst->operands[7].offset];
+ std::string arg_name;
+ if (inst->num_operands == 9) {
+ arg_name = helper->strings[inst->words[inst->operands[8].offset]];
+ }
+ auto kind = GetArgKindFromExtInst(ext_inst);
+ Pipeline::ShaderInfo::DescriptorMapEntry entry{
+ arg_name,
+ kind,
+ helper->constants[ds_id],
+ helper->constants[binding_id],
+ helper->constants[ordinal_id],
+ /* offset */ 0,
+ /* size */ 0};
+ helper->shader_info->AddDescriptorEntry(helper->strings[kernel_id],
+ std::move(entry));
+ break;
+ }
+ case NonSemanticClspvReflectionArgumentPodStorageBuffer:
+ case NonSemanticClspvReflectionArgumentPodUniform: {
+ // These arguments have descriptor set, binding, offset and size.
+ auto kernel_id = inst->words[inst->operands[4].offset];
+ auto ordinal_id = inst->words[inst->operands[5].offset];
+ auto ds_id = inst->words[inst->operands[6].offset];
+ auto binding_id = inst->words[inst->operands[7].offset];
+ auto offset_id = inst->words[inst->operands[8].offset];
+ auto size_id = inst->words[inst->operands[9].offset];
+ std::string arg_name;
+ if (inst->num_operands == 11) {
+ arg_name = helper->strings[inst->words[inst->operands[10].offset]];
+ }
+ auto kind = GetArgKindFromExtInst(ext_inst);
+ Pipeline::ShaderInfo::DescriptorMapEntry entry{
+ arg_name,
+ kind,
+ helper->constants[ds_id],
+ helper->constants[binding_id],
+ helper->constants[ordinal_id],
+ helper->constants[offset_id],
+ helper->constants[size_id]};
+ helper->shader_info->AddDescriptorEntry(helper->strings[kernel_id],
+ std::move(entry));
+ break;
+ }
+ case NonSemanticClspvReflectionArgumentPodPushConstant: {
+ // These arguments have offset and size.
+ auto kernel_id = inst->words[inst->operands[4].offset];
+ auto ordinal_id = inst->words[inst->operands[5].offset];
+ auto offset_id = inst->words[inst->operands[6].offset];
+ auto size_id = inst->words[inst->operands[7].offset];
+ std::string arg_name;
+ if (inst->num_operands == 9) {
+ arg_name = helper->strings[inst->words[inst->operands[8].offset]];
+ }
+ auto kind = GetArgKindFromExtInst(ext_inst);
+ Pipeline::ShaderInfo::DescriptorMapEntry entry{
+ arg_name,
+ kind,
+ /* descriptor set */ 0,
+ /* binding */ 0,
+ helper->constants[ordinal_id],
+ helper->constants[offset_id],
+ helper->constants[size_id]};
+ helper->shader_info->AddDescriptorEntry(helper->strings[kernel_id],
+ std::move(entry));
+ break;
+ }
+ case NonSemanticClspvReflectionArgumentWorkgroup:
+ helper->error_message = "Workgroup arguments are not currently supported";
+ return SPV_ERROR_INVALID_DATA;
+ case NonSemanticClspvReflectionConstantDataStorageBuffer:
+ case NonSemanticClspvReflectionConstantDataUniform:
+ helper->error_message =
+ "Constant descriptor entries are not currently supported";
+ return SPV_ERROR_INVALID_DATA;
+ case NonSemanticClspvReflectionSpecConstantWorkgroupSize:
+ case NonSemanticClspvReflectionSpecConstantGlobalOffset:
+ case NonSemanticClspvReflectionSpecConstantWorkDim:
+ // Nothing to do. Amber currently requires script authors to know the
+ // spec ids and use them directly.
+ break;
+ case NonSemanticClspvReflectionPushConstantGlobalOffset: {
+ auto offset_id = inst->words[inst->operands[4].offset];
+ auto size_id = inst->words[inst->operands[5].offset];
+ Pipeline::ShaderInfo::PushConstant push_constant{
+ Pipeline::ShaderInfo::PushConstant::PushConstantType::kGlobalOffset,
+ helper->constants[offset_id], helper->constants[size_id]};
+ helper->shader_info->AddPushConstant(std::move(push_constant));
+ break;
+ }
+ case NonSemanticClspvReflectionPushConstantRegionOffset: {
+ auto offset_id = inst->words[inst->operands[4].offset];
+ auto size_id = inst->words[inst->operands[5].offset];
+ Pipeline::ShaderInfo::PushConstant push_constant{
+ Pipeline::ShaderInfo::PushConstant::PushConstantType::kRegionOffset,
+ helper->constants[offset_id], helper->constants[size_id]};
+ helper->shader_info->AddPushConstant(std::move(push_constant));
+ break;
+ }
+ case NonSemanticClspvReflectionPushConstantEnqueuedLocalSize:
+ case NonSemanticClspvReflectionPushConstantGlobalSize:
+ case NonSemanticClspvReflectionPushConstantNumWorkgroups:
+ case NonSemanticClspvReflectionPushConstantRegionGroupOffset:
+ helper->error_message = "Unsupported push constant";
+ return SPV_ERROR_INVALID_DATA;
+ case NonSemanticClspvReflectionLiteralSampler: {
+ auto ds_id = inst->words[inst->operands[4].offset];
+ auto binding_id = inst->words[inst->operands[5].offset];
+ auto mask_id = inst->words[inst->operands[6].offset];
+ helper->pipeline->AddSampler(helper->constants[mask_id],
+ helper->constants[ds_id],
+ helper->constants[binding_id]);
+ break;
+ } break;
+ }
+
+ return SPV_SUCCESS;
+}
+
+spv_result_t ParseReflection(void* user_data,
+ const spv_parsed_instruction_t* inst) {
+ auto* helper = reinterpret_cast<ReflectionHelper*>(user_data);
+ switch (inst->opcode) {
+ case spv::OpTypeInt:
+ if (inst->words[inst->operands[1].offset] == 32 &&
+ inst->words[inst->operands[2].offset] == 0) {
+ // Track the result id of OpTypeInt 32 0.
+ helper->uint_id = inst->result_id;
+ }
+ break;
+ case spv::OpConstant:
+ if (inst->words[inst->operands[0].offset] == helper->uint_id) {
+ // Record the values for all uint32_t constants.
+ uint32_t value = inst->words[inst->operands[2].offset];
+ helper->constants[inst->result_id] = value;
+ }
+ break;
+ case spv::OpString: {
+ // Track all strings.
+ std::string value =
+ reinterpret_cast<const char*>(inst->words + inst->operands[1].offset);
+ helper->strings[inst->result_id] = value;
+ break;
+ }
+ case spv::OpExtInst:
+ if (inst->ext_inst_type ==
+ SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
+ return ParseExtendedInst(helper, inst);
+ }
+ break;
+ }
+ return SPV_SUCCESS;
+}
+
+} // anonymous namespace
namespace amber {
namespace clspvhelper {
Result Compile(Pipeline::ShaderInfo* shader_info,
+ Pipeline* pipeline,
+ spv_target_env env,
std::vector<uint32_t>* generated_binary) {
- std::vector<clspv::version0::DescriptorMapEntry> entries;
const auto& src_str = shader_info->GetShader()->GetData();
std::string options;
for (const auto& option : shader_info->GetCompileOptions()) {
options += option + " ";
}
if (clspv::CompileFromSourceString(src_str, /* sampler map */ "", options,
- generated_binary, &entries)) {
+ generated_binary)) {
return Result("Clspv compile failed");
}
- for (auto& entry : entries) {
- if (entry.kind != clspv::version0::DescriptorMapEntry::KernelArg) {
- return Result(
- "Only kernel argument descriptor entries are currently supported");
- }
-
- Pipeline::ShaderInfo::DescriptorMapEntry descriptor_entry;
- descriptor_entry.descriptor_set = entry.descriptor_set;
- descriptor_entry.binding = entry.binding;
- descriptor_entry.pod_offset = 0;
- descriptor_entry.pod_arg_size = 0;
- switch (entry.kernel_arg_data.arg_kind) {
- case clspv::ArgKind::Buffer:
- descriptor_entry.kind =
- Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO;
- break;
- case clspv::ArgKind::BufferUBO:
- descriptor_entry.kind =
- Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO;
- break;
- case clspv::ArgKind::Pod:
- descriptor_entry.kind =
- Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD;
- break;
- case clspv::ArgKind::PodUBO:
- descriptor_entry.kind =
- Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO;
- break;
- case clspv::ArgKind::Local:
- // Local arguments are handled via specialization constants.
- break;
- default:
- return Result("Unsupported kernel argument descriptor entry");
- }
-
- if (entry.kernel_arg_data.arg_kind == clspv::ArgKind::Pod ||
- entry.kernel_arg_data.arg_kind == clspv::ArgKind::PodUBO) {
- descriptor_entry.pod_offset = entry.kernel_arg_data.pod_offset;
- descriptor_entry.pod_arg_size = entry.kernel_arg_data.pod_arg_size;
- }
-
- descriptor_entry.arg_name = entry.kernel_arg_data.arg_name;
- descriptor_entry.arg_ordinal = entry.kernel_arg_data.arg_ordinal;
+ // Parse the reflection instructions.
+ ReflectionHelper helper;
+ helper.shader_info = shader_info;
+ helper.pipeline = pipeline;
+ spv_context context(spvContextCreate(env));
+ if (spvBinaryParse(context, &helper, generated_binary->data(),
+ generated_binary->size(), nullptr, ParseReflection,
+ nullptr)) {
+ return Result(helper.error_message);
+ }
- shader_info->AddDescriptorEntry(entry.kernel_arg_data.kernel_name,
- std::move(descriptor_entry));
+ // Strip the reflection instructions to avoid requiring the implementation to
+ // support VK_KHR_shader_non_semantic_info.
+ spvtools::Optimizer opt(env);
+ opt.RegisterPass(spvtools::CreateStripReflectInfoPass());
+ std::vector<uint32_t> stripped;
+ if (!opt.Run(generated_binary->data(), generated_binary->size(), &stripped)) {
+ return Result("failed to strip reflection instructions");
}
+ generated_binary->swap(stripped);
return Result();
}
diff --git a/src/clspv_helper.h b/src/clspv_helper.h
index 0df2d05..3a06c74 100644
--- a/src/clspv_helper.h
+++ b/src/clspv_helper.h
@@ -19,6 +19,7 @@
#include <vector>
#include "amber/result.h"
+#include "spirv-tools/libspirv.h"
#include "src/pipeline.h"
namespace amber {
@@ -27,6 +28,8 @@ namespace clspvhelper {
// Passes the OpenCL C source code to Clspv.
// Returns the generated SPIR-V binary via |generated_binary| argument.
Result Compile(Pipeline::ShaderInfo* shader_info,
+ Pipeline* pipeline,
+ spv_target_env env,
std::vector<uint32_t>* generated_binary);
} // namespace clspvhelper
diff --git a/src/command.cc b/src/command.cc
index 84db7fe..ea242b7 100644
--- a/src/command.cc
+++ b/src/command.cc
@@ -58,6 +58,10 @@ DrawRectCommand* Command::AsDrawRect() {
return static_cast<DrawRectCommand*>(this);
}
+DrawGridCommand* Command::AsDrawGrid() {
+ return static_cast<DrawGridCommand*>(this);
+}
+
EntryPointCommand* Command::AsEntryPoint() {
return static_cast<EntryPointCommand*>(this);
}
@@ -92,6 +96,11 @@ DrawRectCommand::DrawRectCommand(Pipeline* pipeline, PipelineData data)
DrawRectCommand::~DrawRectCommand() = default;
+DrawGridCommand::DrawGridCommand(Pipeline* pipeline, PipelineData data)
+ : PipelineCommand(Type::kDrawGrid, pipeline), data_(data) {}
+
+DrawGridCommand::~DrawGridCommand() = default;
+
DrawArraysCommand::DrawArraysCommand(Pipeline* pipeline, PipelineData data)
: PipelineCommand(Type::kDrawArrays, pipeline), data_(data) {}
@@ -120,11 +129,21 @@ ProbeSSBOCommand::ProbeSSBOCommand(Buffer* buffer)
ProbeSSBOCommand::~ProbeSSBOCommand() = default;
+BindableResourceCommand::BindableResourceCommand(Type type, Pipeline* pipeline)
+ : PipelineCommand(type, pipeline) {}
+
+BindableResourceCommand::~BindableResourceCommand() = default;
+
BufferCommand::BufferCommand(BufferType type, Pipeline* pipeline)
- : PipelineCommand(Type::kBuffer, pipeline), buffer_type_(type) {}
+ : BindableResourceCommand(Type::kBuffer, pipeline), buffer_type_(type) {}
BufferCommand::~BufferCommand() = default;
+SamplerCommand::SamplerCommand(Pipeline* pipeline)
+ : BindableResourceCommand(Type::kSampler, pipeline) {}
+
+SamplerCommand::~SamplerCommand() = default;
+
CopyCommand::CopyCommand(Buffer* buffer_from, Buffer* buffer_to)
: Command(Type::kCopy), buffer_from_(buffer_from), buffer_to_(buffer_to) {}
diff --git a/src/command.h b/src/command.h
index 8595637..207c336 100644
--- a/src/command.h
+++ b/src/command.h
@@ -25,7 +25,9 @@
#include "amber/value.h"
#include "src/buffer.h"
#include "src/command_data.h"
+#include "src/debug.h"
#include "src/pipeline_data.h"
+#include "src/sampler.h"
namespace amber {
@@ -39,6 +41,7 @@ class ComputeCommand;
class CopyCommand;
class DrawArraysCommand;
class DrawRectCommand;
+class DrawGridCommand;
class EntryPointCommand;
class PatchParameterVerticesCommand;
class Pipeline;
@@ -59,13 +62,15 @@ class Command {
kCopy,
kDrawArrays,
kDrawRect,
+ kDrawGrid,
kEntryPoint,
kPatchParameterVertices,
kPipelineProperties,
kProbe,
kProbeSSBO,
kBuffer,
- kRepeat
+ kRepeat,
+ kSampler
};
virtual ~Command();
@@ -73,6 +78,7 @@ class Command {
Command::Type GetType() const { return command_type_; }
bool IsDrawRect() const { return command_type_ == Type::kDrawRect; }
+ bool IsDrawGrid() const { return command_type_ == Type::kDrawGrid; }
bool IsDrawArrays() const { return command_type_ == Type::kDrawArrays; }
bool IsCompareBuffer() const { return command_type_ == Type::kCompareBuffer; }
bool IsCompute() const { return command_type_ == Type::kCompute; }
@@ -99,6 +105,7 @@ class Command {
CopyCommand* AsCopy();
DrawArraysCommand* AsDrawArrays();
DrawRectCommand* AsDrawRect();
+ DrawGridCommand* AsDrawGrid();
EntryPointCommand* AsEntryPoint();
PatchParameterVerticesCommand* AsPatchParameterVertices();
ProbeCommand* AsProbe();
@@ -113,11 +120,20 @@ class Command {
/// Returns the input file line this command was declared on.
size_t GetLine() const { return line_; }
+ /// Sets the debug script to run for this command.
+ void SetDebugScript(std::unique_ptr<debug::Script>&& debug) {
+ debug_ = std::move(debug);
+ }
+
+ /// Returns the optional debug script associated with this command.
+ debug::Script* GetDebugScript() { return debug_.get(); }
+
protected:
explicit Command(Type type);
Type command_type_;
size_t line_ = 1;
+ std::unique_ptr<debug::Script> debug_;
};
/// Base class for commands which contain a pipeline.
@@ -128,7 +144,7 @@ class PipelineCommand : public Command {
Pipeline* GetPipeline() const { return pipeline_; }
protected:
- explicit PipelineCommand(Type type, Pipeline* pipeline);
+ PipelineCommand(Type type, Pipeline* pipeline);
Pipeline* pipeline_ = nullptr;
};
@@ -136,7 +152,7 @@ class PipelineCommand : public Command {
/// Command to draw a rectangle on screen.
class DrawRectCommand : public PipelineCommand {
public:
- explicit DrawRectCommand(Pipeline* pipeline, PipelineData data);
+ DrawRectCommand(Pipeline* pipeline, PipelineData data);
~DrawRectCommand() override;
const PipelineData* GetPipelineData() const { return &data_; }
@@ -171,10 +187,48 @@ class DrawRectCommand : public PipelineCommand {
float height_ = 0.0;
};
+/// Command to draw a grid of recrangles on screen.
+class DrawGridCommand : public PipelineCommand {
+ public:
+ DrawGridCommand(Pipeline* pipeline, PipelineData data);
+ ~DrawGridCommand() override;
+
+ const PipelineData* GetPipelineData() const { return &data_; }
+
+ void SetX(float x) { x_ = x; }
+ float GetX() const { return x_; }
+
+ void SetY(float y) { y_ = y; }
+ float GetY() const { return y_; }
+
+ void SetWidth(float w) { width_ = w; }
+ float GetWidth() const { return width_; }
+
+ void SetHeight(float h) { height_ = h; }
+ float GetHeight() const { return height_; }
+
+ void SetColumns(uint32_t c) { columns_ = c; }
+ uint32_t GetColumns() const { return columns_; }
+
+ void SetRows(uint32_t r) { rows_ = r; }
+ uint32_t GetRows() const { return rows_; }
+
+ std::string ToString() const override { return "DrawGridCommand"; }
+
+ private:
+ PipelineData data_;
+ float x_ = 0.0;
+ float y_ = 0.0;
+ float width_ = 0.0;
+ float height_ = 0.0;
+ uint32_t columns_ = 0;
+ uint32_t rows_ = 0;
+};
+
/// Command to draw from a vertex and index buffer.
class DrawArraysCommand : public PipelineCommand {
public:
- explicit DrawArraysCommand(Pipeline* pipeline, PipelineData data);
+ DrawArraysCommand(Pipeline* pipeline, PipelineData data);
~DrawArraysCommand() override;
const PipelineData* GetPipelineData() const { return &data_; }
@@ -182,9 +236,6 @@ class DrawArraysCommand : public PipelineCommand {
void EnableIndexed() { is_indexed_ = true; }
bool IsIndexed() const { return is_indexed_; }
- void EnableInstanced() { is_instanced_ = true; }
- bool IsInstanced() const { return is_instanced_; }
-
void SetTopology(Topology topo) { topology_ = topo; }
Topology GetTopology() const { return topology_; }
@@ -194,6 +245,9 @@ class DrawArraysCommand : public PipelineCommand {
void SetVertexCount(uint32_t count) { vertex_count_ = count; }
uint32_t GetVertexCount() const { return vertex_count_; }
+ void SetFirstInstance(uint32_t idx) { first_instance_ = idx; }
+ uint32_t GetFirstInstance() const { return first_instance_; }
+
void SetInstanceCount(uint32_t count) { instance_count_ = count; }
uint32_t GetInstanceCount() const { return instance_count_; }
@@ -202,11 +256,11 @@ class DrawArraysCommand : public PipelineCommand {
private:
PipelineData data_;
bool is_indexed_ = false;
- bool is_instanced_ = false;
Topology topology_ = Topology::kUnknown;
uint32_t first_vertex_index_ = 0;
uint32_t vertex_count_ = 0;
- uint32_t instance_count_ = 0;
+ uint32_t first_instance_ = 0;
+ uint32_t instance_count_ = 1;
};
/// A command to compare two buffers.
@@ -294,7 +348,7 @@ class Probe : public Command {
const std::vector<Tolerance>& GetTolerances() const { return tolerances_; }
protected:
- explicit Probe(Type type, Buffer* buffer);
+ Probe(Type type, Buffer* buffer);
private:
Buffer* buffer_;
@@ -413,20 +467,65 @@ class ProbeSSBOCommand : public Probe {
std::vector<Value> values_;
};
+/// Base class for BufferCommand and SamplerCommand to handle binding.
+class BindableResourceCommand : public PipelineCommand {
+ public:
+ BindableResourceCommand(Type type, Pipeline* pipeline);
+ ~BindableResourceCommand() override;
+
+ void SetDescriptorSet(uint32_t set) { descriptor_set_ = set; }
+ uint32_t GetDescriptorSet() const { return descriptor_set_; }
+
+ void SetBinding(uint32_t num) { binding_num_ = num; }
+ uint32_t GetBinding() const { return binding_num_; }
+
+ private:
+ uint32_t descriptor_set_ = 0;
+ uint32_t binding_num_ = 0;
+};
+
/// Command to set the size of a buffer, or update a buffers contents.
-class BufferCommand : public PipelineCommand {
+class BufferCommand : public BindableResourceCommand {
public:
enum class BufferType {
kSSBO,
+ kSSBODynamic,
kUniform,
+ kUniformDynamic,
kPushConstant,
+ kStorageImage,
+ kSampledImage,
+ kCombinedImageSampler,
+ kUniformTexelBuffer,
+ kStorageTexelBuffer
};
- explicit BufferCommand(BufferType type, Pipeline* pipeline);
+ BufferCommand(BufferType type, Pipeline* pipeline);
~BufferCommand() override;
bool IsSSBO() const { return buffer_type_ == BufferType::kSSBO; }
+ bool IsSSBODynamic() const {
+ return buffer_type_ == BufferType::kSSBODynamic;
+ }
bool IsUniform() const { return buffer_type_ == BufferType::kUniform; }
+ bool IsUniformDynamic() const {
+ return buffer_type_ == BufferType::kUniformDynamic;
+ }
+ bool IsStorageImage() const {
+ return buffer_type_ == BufferType::kStorageImage;
+ }
+ bool IsSampledImage() const {
+ return buffer_type_ == BufferType::kSampledImage;
+ }
+ bool IsCombinedImageSampler() const {
+ return buffer_type_ == BufferType::kCombinedImageSampler;
+ }
+ bool IsUniformTexelBuffer() const {
+ return buffer_type_ == BufferType::kUniformTexelBuffer;
+ }
+ bool IsStorageTexelBuffer() const {
+ return buffer_type_ == BufferType::kStorageTexelBuffer;
+ }
bool IsPushConstant() const {
return buffer_type_ == BufferType::kPushConstant;
}
@@ -434,33 +533,56 @@ class BufferCommand : public PipelineCommand {
void SetIsSubdata() { is_subdata_ = true; }
bool IsSubdata() const { return is_subdata_; }
- void SetDescriptorSet(uint32_t set) { descriptor_set_ = set; }
- uint32_t GetDescriptorSet() const { return descriptor_set_; }
-
- void SetBinding(uint32_t num) { binding_num_ = num; }
- uint32_t GetBinding() const { return binding_num_; }
-
void SetOffset(uint32_t offset) { offset_ = offset; }
uint32_t GetOffset() const { return offset_; }
+ void SetBaseMipLevel(uint32_t base_mip_level) {
+ base_mip_level_ = base_mip_level;
+ }
+ uint32_t GetBaseMipLevel() const { return base_mip_level_; }
+
+ void SetDynamicOffset(uint32_t dynamic_offset) {
+ dynamic_offset_ = dynamic_offset;
+ }
+ uint32_t GetDynamicOffset() const { return dynamic_offset_; }
+
void SetValues(std::vector<Value>&& values) { values_ = std::move(values); }
const std::vector<Value>& GetValues() const { return values_; }
void SetBuffer(Buffer* buffer) { buffer_ = buffer; }
Buffer* GetBuffer() const { return buffer_; }
+ void SetSampler(Sampler* sampler) { sampler_ = sampler; }
+ Sampler* GetSampler() const { return sampler_; }
+
std::string ToString() const override { return "BufferCommand"; }
private:
Buffer* buffer_ = nullptr;
+ Sampler* sampler_ = nullptr;
BufferType buffer_type_;
bool is_subdata_ = false;
- uint32_t descriptor_set_ = 0;
- uint32_t binding_num_ = 0;
uint32_t offset_ = 0;
+ uint32_t base_mip_level_ = 0;
+ uint32_t dynamic_offset_ = 0;
std::vector<Value> values_;
};
+/// Command for setting sampler parameters and binding.
+class SamplerCommand : public BindableResourceCommand {
+ public:
+ explicit SamplerCommand(Pipeline* pipeline);
+ ~SamplerCommand() override;
+
+ void SetSampler(Sampler* sampler) { sampler_ = sampler; }
+ Sampler* GetSampler() const { return sampler_; }
+
+ std::string ToString() const override { return "SamplerCommand"; }
+
+ private:
+ Sampler* sampler_ = nullptr;
+};
+
/// Command to clear the colour attachments.
class ClearCommand : public PipelineCommand {
public:
diff --git a/src/command_data.h b/src/command_data.h
index 430533c..98ec405 100644
--- a/src/command_data.h
+++ b/src/command_data.h
@@ -61,7 +61,8 @@ enum ColorMask {
};
enum class CompareOp : uint8_t {
- kNever = 0,
+ kUnknown = 0,
+ kNever,
kLess,
kEqual,
kLessOrEqual,
@@ -72,7 +73,8 @@ enum class CompareOp : uint8_t {
};
enum class StencilOp : uint8_t {
- kKeep = 0,
+ kUnknown = 0,
+ kKeep,
kZero,
kReplace,
kIncrementAndClamp,
diff --git a/src/dawn/engine_dawn.cc b/src/dawn/engine_dawn.cc
index 868e979..1666e4a 100644
--- a/src/dawn/engine_dawn.cc
+++ b/src/dawn/engine_dawn.cc
@@ -1328,6 +1328,10 @@ Result EngineDawn::DoDrawRect(const DrawRectCommand* command) {
return result;
}
+Result EngineDawn::DoDrawGrid(const DrawGridCommand* command) {
+ return Result("DRAW_GRID not implemented on Dawn");
+}
+
Result EngineDawn::DoDrawArrays(const DrawArraysCommand* command) {
Result result;
@@ -1616,7 +1620,7 @@ Result EngineDawn::AttachBuffersAndTextures(
for (const auto& buf_info : render_pipeline->pipeline->GetBuffers()) {
::dawn::BufferUsage bufferUsage;
::dawn::BindingType bindingType;
- switch (buf_info.buffer->GetBufferType()) {
+ switch (buf_info.type) {
case BufferType::kStorage: {
bufferUsage = ::dawn::BufferUsage::Storage;
bindingType = ::dawn::BindingType::StorageBuffer;
@@ -1629,8 +1633,7 @@ Result EngineDawn::AttachBuffersAndTextures(
}
default: {
return Result("AttachBuffersAndTextures: unknown buffer type: " +
- std::to_string(static_cast<uint32_t>(
- buf_info.buffer->GetBufferType())));
+ std::to_string(static_cast<uint32_t>(buf_info.type)));
break;
}
}
@@ -1716,7 +1719,7 @@ Result EngineDawn::AttachBuffers(ComputePipelineInfo* compute_pipeline) {
for (const auto& buf_info : compute_pipeline->pipeline->GetBuffers()) {
::dawn::BufferUsage bufferUsage;
::dawn::BindingType bindingType;
- switch (buf_info.buffer->GetBufferType()) {
+ switch (buf_info.type) {
case BufferType::kStorage: {
bufferUsage = ::dawn::BufferUsage::Storage;
bindingType = ::dawn::BindingType::StorageBuffer;
@@ -1729,8 +1732,7 @@ Result EngineDawn::AttachBuffers(ComputePipelineInfo* compute_pipeline) {
}
default: {
return Result("AttachBuffers: unknown buffer type: " +
- std::to_string(static_cast<uint32_t>(
- buf_info.buffer->GetBufferType())));
+ std::to_string(static_cast<uint32_t>(buf_info.type)));
break;
}
}
diff --git a/src/dawn/engine_dawn.h b/src/dawn/engine_dawn.h
index 540a705..92c1312 100644
--- a/src/dawn/engine_dawn.h
+++ b/src/dawn/engine_dawn.h
@@ -18,6 +18,7 @@
#include <cstdint>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "dawn/dawncpp.h"
@@ -54,6 +55,7 @@ class EngineDawn : public Engine {
Result DoClearDepth(const ClearDepthCommand* cmd) override;
Result DoClear(const ClearCommand* cmd) override;
Result DoDrawRect(const DrawRectCommand* cmd) override;
+ Result DoDrawGrid(const DrawGridCommand* cmd) override;
Result DoDrawArrays(const DrawArraysCommand* cmd) override;
Result DoCompute(const ComputeCommand* cmd) override;
Result DoEntryPoint(const EntryPointCommand* cmd) override;
@@ -61,6 +63,10 @@ class EngineDawn : public Engine {
const PatchParameterVerticesCommand* cmd) override;
Result DoBuffer(const BufferCommand* cmd) override;
+ std::pair<Debugger*, Result> GetDebugger(VirtualFileStore*) override {
+ return {nullptr, Result("Dawn does not currently support a debugger")};
+ }
+
private:
// Returns the Dawn-specific render pipeline for the given command,
// if it exists. Returns nullptr otherwise.
diff --git a/src/debug.cc b/src/debug.cc
new file mode 100644
index 0000000..a8f07c0
--- /dev/null
+++ b/src/debug.cc
@@ -0,0 +1,136 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/debug.h"
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "src/make_unique.h"
+
+namespace amber {
+namespace debug {
+
+namespace {
+
+class ScriptImpl : public Script {
+ public:
+ void Run(Events* e) const override {
+ for (auto f : sequence_) {
+ f(e);
+ }
+ }
+
+ void BreakOnComputeGlobalInvocation(
+ uint32_t x,
+ uint32_t y,
+ uint32_t z,
+ const std::shared_ptr<const ThreadScript>& thread) override {
+ sequence_.emplace_back([=](Events* events) {
+ events->BreakOnComputeGlobalInvocation(x, y, z, thread);
+ });
+ }
+
+ void BreakOnVertexIndex(
+ uint32_t index,
+ const std::shared_ptr<const ThreadScript>& thread) override {
+ sequence_.emplace_back(
+ [=](Events* events) { events->BreakOnVertexIndex(index, thread); });
+ }
+
+ void BreakOnFragmentWindowSpacePosition(
+ uint32_t x,
+ uint32_t y,
+ const std::shared_ptr<const ThreadScript>& thread) override {
+ sequence_.emplace_back([=](Events* events) {
+ events->BreakOnFragmentWindowSpacePosition(x, y, thread);
+ });
+ }
+
+ private:
+ using Event = std::function<void(Events*)>;
+ std::vector<Event> sequence_;
+};
+
+class ThreadScriptImpl : public ThreadScript {
+ public:
+ void Run(Thread* thread) const override {
+ for (auto f : sequence_) {
+ f(thread);
+ }
+ }
+
+ // Thread compliance
+ void StepOver() override {
+ sequence_.emplace_back([](Thread* t) { t->StepOver(); });
+ }
+
+ void StepIn() override {
+ sequence_.emplace_back([](Thread* t) { t->StepIn(); });
+ }
+
+ void StepOut() override {
+ sequence_.emplace_back([](Thread* t) { t->StepOut(); });
+ }
+
+ void Continue() override {
+ sequence_.emplace_back([](Thread* t) { t->Continue(); });
+ }
+
+ void ExpectLocation(const Location& location,
+ const std::string& line) override {
+ sequence_.emplace_back(
+ [=](Thread* t) { t->ExpectLocation(location, line); });
+ }
+
+ void ExpectCallstack(const std::vector<StackFrame>& callstack) override {
+ sequence_.emplace_back([=](Thread* t) { t->ExpectCallstack(callstack); });
+ }
+
+ void ExpectLocal(const std::string& name, int64_t value) override {
+ sequence_.emplace_back([=](Thread* t) { t->ExpectLocal(name, value); });
+ }
+
+ void ExpectLocal(const std::string& name, double value) override {
+ sequence_.emplace_back([=](Thread* t) { t->ExpectLocal(name, value); });
+ }
+
+ void ExpectLocal(const std::string& name, const std::string& value) override {
+ sequence_.emplace_back([=](Thread* t) { t->ExpectLocal(name, value); });
+ }
+
+ private:
+ using Event = std::function<void(Thread*)>;
+ std::vector<Event> sequence_;
+};
+
+} // namespace
+
+Thread::~Thread() = default;
+Events::~Events() = default;
+ThreadScript::~ThreadScript() = default;
+Script::~Script() = default;
+
+std::shared_ptr<ThreadScript> ThreadScript::Create() {
+ return std::make_shared<ThreadScriptImpl>();
+}
+
+std::unique_ptr<Script> Script::Create() {
+ return MakeUnique<ScriptImpl>();
+}
+
+} // namespace debug
+} // namespace amber
diff --git a/src/debug.h b/src/debug.h
new file mode 100644
index 0000000..4da0d91
--- /dev/null
+++ b/src/debug.h
@@ -0,0 +1,156 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_DEBUG_H_
+#define SRC_DEBUG_H_
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "amber/result.h"
+
+/// amber::debug holds the types used for testing a graphics debugger.
+namespace amber {
+namespace debug {
+
+// Forward declaration.
+class ThreadScript;
+
+/// Location holds a file path and a 1-based line number.
+struct Location {
+ std::string file; // empty represents unspecified.
+ uint32_t line = 0; // 0 represents unspecified.
+};
+
+// StackFrame holds name and location of a stack frame.
+struct StackFrame {
+ std::string name;
+ Location location;
+};
+
+/// Thread is the interface used to control a single debugger thread of
+/// execution.
+class Thread {
+ public:
+ virtual ~Thread();
+
+ /// StepOver instructs the debugger to perform a single line step on the given
+ /// thread of execution, stepping over any function call instructions.
+ virtual void StepOver() = 0;
+
+ /// StepIn instructs the debugger to perform a single line step on the given
+ /// thread of execution, stepping into any function call instructions.
+ virtual void StepIn() = 0;
+
+ /// StepOut instructs the debugger to resume execution of the given thread of
+ /// execution. If the current function is not the top most of the call stack,
+ /// then the debugger will pause at the next line after the call to the
+ /// current function.
+ virtual void StepOut() = 0;
+
+ /// Continue instructs the debugger to resume execution of the given thread of
+ /// execution.
+ virtual void Continue() = 0;
+
+ /// ExpectLocation verifies that the debugger is currently suspended for the
+ /// given thread of execution at the specified source location. If |line| is
+ /// non-empty, then the line's textual source will also be verified.
+ virtual void ExpectLocation(const Location& location,
+ const std::string& line) = 0;
+
+ /// ExpectCallstack verifies that the debugger is currently suspended for the
+ /// given thread of execution with the specified callstack.
+ /// callstack is ordered with the 0th element representing the most nested
+ /// call.
+ virtual void ExpectCallstack(const std::vector<StackFrame>& callstack) = 0;
+
+ /// ExpectLocal verifies that the local variable with the given name has the
+ /// expected value. |name| may contain `.` delimiters to index structure or
+ /// array types.
+ virtual void ExpectLocal(const std::string& name, int64_t value) = 0;
+ virtual void ExpectLocal(const std::string& name, double value) = 0;
+ virtual void ExpectLocal(const std::string& name,
+ const std::string& value) = 0;
+};
+
+/// Events is the interface used to control the debugger.
+class Events {
+ public:
+ virtual ~Events();
+
+ /// BreakOnComputeGlobalInvocation instructs the debugger to set a breakpoint
+ /// at the start of the compute shader program for the invocation with the
+ /// global invocation identifier [|x|, |y|, |z|], and run the |ThreadScript|
+ /// once the breakpoint is hit.
+ virtual void BreakOnComputeGlobalInvocation(
+ uint32_t x,
+ uint32_t y,
+ uint32_t z,
+ const std::shared_ptr<const ThreadScript>&) = 0;
+
+ /// BreakOnVertexIndex instructs the debugger to set a breakpoint at the start
+ /// of the vertex shader program for the invocation with the vertex index
+ /// |index|, and run the |ThreadScript| once the breakpoint is hit.
+ virtual void BreakOnVertexIndex(
+ uint32_t index,
+ const std::shared_ptr<const ThreadScript>&) = 0;
+
+ /// BreakOnFragmentWindowSpacePosition instructs the debugger to set a
+ /// breakpoint at the start of the fragment shader program for the invocation
+ /// with the window space coordinate [x, y], and run the |ThreadScript| once
+ /// the breakpoint is hit.
+ virtual void BreakOnFragmentWindowSpacePosition(
+ uint32_t x,
+ uint32_t y,
+ const std::shared_ptr<const ThreadScript>&) = 0;
+};
+
+/// ThreadScript is a specialization of the |amber::debug::Thread| interface,
+/// and is used to record all the calls made on it, which can be later replayed
+/// with |ThreadScript::Run|.
+class ThreadScript : public Thread {
+ public:
+ ~ThreadScript() override;
+
+ /// Run replays all the calls made to the |ThreadScript| on the given |Thread|
+ /// parameter.
+ virtual void Run(Thread*) const = 0;
+
+ // Create constructs and returns a new ThreadScript.
+ static std::shared_ptr<ThreadScript> Create();
+};
+
+/// Script is an specialization of the |amber::debug::Events| interface, and is
+/// used to record all the calls made on it, which can be later replayed with
+/// |Script::Run|.
+class Script : public Events {
+ public:
+ ~Script() override;
+
+ /// Run replays all the calls made to the |Script| on the given |Events|
+ /// parameter.
+ virtual void Run(Events*) const = 0;
+
+ // Create constructs and returns a new Script.
+ static std::unique_ptr<Script> Create();
+};
+
+} // namespace debug
+} // namespace amber
+
+#endif // SRC_DEBUG_H_
diff --git a/src/descriptor_set_and_binding_parser.cc b/src/descriptor_set_and_binding_parser.cc
index d563573..d351935 100644
--- a/src/descriptor_set_and_binding_parser.cc
+++ b/src/descriptor_set_and_binding_parser.cc
@@ -14,6 +14,7 @@
#include "src/descriptor_set_and_binding_parser.h"
+#include <cctype>
#include <iostream>
#include <limits>
@@ -26,7 +27,24 @@ DescriptorSetAndBindingParser::DescriptorSetAndBindingParser() = default;
DescriptorSetAndBindingParser::~DescriptorSetAndBindingParser() = default;
Result DescriptorSetAndBindingParser::Parse(const std::string& buffer_id) {
- Tokenizer t(buffer_id);
+ size_t idx = 0;
+
+ // Pipeline name
+ if (!std::isdigit(buffer_id[idx]) && std::isalpha(buffer_id[idx]) &&
+ buffer_id[idx] != ':' && buffer_id[idx] != '-') {
+ idx++;
+ while (idx < buffer_id.size() && buffer_id[idx] != ':')
+ idx++;
+
+ pipeline_name_ = buffer_id.substr(0, idx);
+
+ // Move past the :
+ idx += 1;
+ }
+ if (idx >= buffer_id.size())
+ return Result("Invalid buffer id: " + buffer_id);
+
+ Tokenizer t(buffer_id.substr(idx));
auto token = t.NextToken();
if (token->IsInteger()) {
if (token->AsInt32() < 0) {
@@ -45,11 +63,9 @@ Result DescriptorSetAndBindingParser::Parse(const std::string& buffer_id) {
}
descriptor_set_ = val;
- } else {
- descriptor_set_ = 0;
}
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid buffer id: " + buffer_id);
auto& str = token->AsString();
diff --git a/src/descriptor_set_and_binding_parser.h b/src/descriptor_set_and_binding_parser.h
index 44a412a..145aa42 100644
--- a/src/descriptor_set_and_binding_parser.h
+++ b/src/descriptor_set_and_binding_parser.h
@@ -32,9 +32,13 @@ class DescriptorSetAndBindingParser {
/// be a single non-negative integer or two those integers separated by
/// ':'. For example, ":0", "1", and "2:3" are valid strings for |buffer_id|,
/// but "", "-4", ":-5", ":", "a", and "b:c" are invalid strings for
- /// |buffer_id|.
+ /// |buffer_id|. The |buffer_id| can be prefixed by the name of a pipeline.
Result Parse(const std::string& buffer_id);
+ /// Returns true if a pipeline name was specified
+ bool HasPipelineName() const { return !pipeline_name_.empty(); }
+ std::string PipelineName() const { return pipeline_name_; }
+
/// Return descriptor set that is the result of Parse().
uint32_t GetDescriptorSet() const { return descriptor_set_; }
@@ -42,6 +46,7 @@ class DescriptorSetAndBindingParser {
uint32_t GetBinding() const { return binding_; }
private:
+ std::string pipeline_name_;
uint32_t descriptor_set_ = 0;
uint32_t binding_ = 0;
};
diff --git a/src/descriptor_set_and_binding_parser_test.cc b/src/descriptor_set_and_binding_parser_test.cc
index 6010130..b5b9c54 100644
--- a/src/descriptor_set_and_binding_parser_test.cc
+++ b/src/descriptor_set_and_binding_parser_test.cc
@@ -25,8 +25,9 @@ TEST_F(DescriptorSetAndBindingParserTest, CommaAndBinding) {
Result r = parser.Parse(":1234");
ASSERT_TRUE(r.IsSuccess()) << r.Error();
- EXPECT_EQ(0, parser.GetDescriptorSet());
- EXPECT_EQ(1234, parser.GetBinding());
+ EXPECT_FALSE(parser.HasPipelineName());
+ EXPECT_EQ(0u, parser.GetDescriptorSet());
+ EXPECT_EQ(1234u, parser.GetBinding());
}
TEST_F(DescriptorSetAndBindingParserTest, Binding) {
@@ -34,8 +35,8 @@ TEST_F(DescriptorSetAndBindingParserTest, Binding) {
Result r = parser.Parse("1234");
ASSERT_TRUE(r.IsSuccess()) << r.Error();
- EXPECT_EQ(0, parser.GetDescriptorSet());
- EXPECT_EQ(1234, parser.GetBinding());
+ EXPECT_EQ(0u, parser.GetDescriptorSet());
+ EXPECT_EQ(1234u, parser.GetBinding());
}
TEST_F(DescriptorSetAndBindingParserTest, DescSetAndBinding) {
@@ -43,8 +44,8 @@ TEST_F(DescriptorSetAndBindingParserTest, DescSetAndBinding) {
Result r = parser.Parse("1234:5678");
ASSERT_TRUE(r.IsSuccess()) << r.Error();
- EXPECT_EQ(1234, parser.GetDescriptorSet());
- EXPECT_EQ(5678, parser.GetBinding());
+ EXPECT_EQ(1234u, parser.GetDescriptorSet());
+ EXPECT_EQ(5678u, parser.GetBinding());
}
TEST_F(DescriptorSetAndBindingParserTest, EmptyBufferId) {
@@ -107,4 +108,15 @@ TEST_F(DescriptorSetAndBindingParserTest, DescSetAndNegativeBinding) {
r.Error());
}
+TEST_F(DescriptorSetAndBindingParserTest, WithPipelineName) {
+ DescriptorSetAndBindingParser parser;
+ Result r = parser.Parse("pipeline1:123:234");
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ EXPECT_TRUE(parser.HasPipelineName());
+ EXPECT_EQ("pipeline1", parser.PipelineName());
+ EXPECT_EQ(123u, parser.GetDescriptorSet());
+ EXPECT_EQ(234u, parser.GetBinding());
+}
+
} // namespace amber
diff --git a/src/dxc_helper.cc b/src/dxc_helper.cc
index d323b4d..8fa0482 100644
--- a/src/dxc_helper.cc
+++ b/src/dxc_helper.cc
@@ -18,6 +18,7 @@
#include <sstream>
#include "src/platform.h"
+#include "src/virtual_file_store.h"
#if AMBER_PLATFORM_WINDOWS
#pragma warning(push)
@@ -25,20 +26,39 @@
#pragma warning(disable : 4003)
#endif // AMBER_PLATFORM_WINDOWS
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreserved-id-macro"
+#pragma clang diagnostic ignored "-Wextra-semi"
+#pragma clang diagnostic ignored "-Wdeprecated-dynamic-exception-spec"
+#pragma clang diagnostic ignored "-Wold-style-cast"
+#pragma clang diagnostic ignored "-Wshadow-field-in-constructor"
+#pragma clang diagnostic ignored "-Wconversion"
+#pragma clang diagnostic ignored "-Wsign-conversion"
+#pragma clang diagnostic ignored "-Wshadow"
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
+#pragma clang diagnostic ignored "-Wundef"
+#pragma clang diagnostic ignored "-Wunused-function"
+#pragma clang diagnostic ignored "-Wunused-parameter"
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-function"
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS
+#endif // __STDC_LIMIT_MACROS
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif // __STDC_CONSTANT_MACROS
+
// clang-format off
// The order here matters, so don't reformat.
-#include "dxc/Support/WinAdapter.h"
-#include "dxc/Support/WinIncludes.h"
#include "dxc/Support/Global.h"
#include "dxc/Support/HLSLOptions.h"
-#include "dxc/Support/dxcapi.use.h"
#include "dxc/dxcapi.h"
+#include "dxc/Support/microcom.h"
// clang-format on
-#if AMBER_PLATFORM_WINDOWS
-#pragma warning(pop)
-#endif // AMBER_PLATFORM_WINDOWS
-
namespace amber {
namespace dxchelper {
namespace {
@@ -62,15 +82,63 @@ void ConvertIDxcBlobToUint32(IDxcBlob* blob,
memcpy(binaryWords->data(), binaryStr.data(), binaryStr.size());
}
+class IncludeHandler : public IDxcIncludeHandler {
+ public:
+ IncludeHandler(const VirtualFileStore* file_store,
+ IDxcLibrary* dxc_lib,
+ IDxcIncludeHandler* fallback)
+ : file_store_(file_store), dxc_lib_(dxc_lib), fallback_(fallback) {}
+
+ HRESULT STDMETHODCALLTYPE LoadSource(LPCWSTR pFilename,
+ IDxcBlob** ppIncludeSource) override {
+ std::wstring wide_path(pFilename);
+ std::string path = std::string(wide_path.begin(), wide_path.end());
+
+ std::string content;
+ Result r = file_store_->Get(path, &content);
+ if (r.IsSuccess()) {
+ IDxcBlobEncoding* source;
+ auto res = dxc_lib_->CreateBlobWithEncodingOnHeapCopy(
+ content.data(), static_cast<uint32_t>(content.size()), CP_UTF8,
+ &source);
+ if (res != S_OK) {
+ DxcCleanupThreadMalloc();
+ return res;
+ }
+ *ppIncludeSource = source;
+ return S_OK;
+ }
+
+ return fallback_->LoadSource(pFilename, ppIncludeSource);
+ }
+
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
+ void** ppvObject) override {
+ return DoBasicQueryInterface<IDxcIncludeHandler>(this, iid, ppvObject);
+ }
+
+ private:
+ const VirtualFileStore* const file_store_;
+ IDxcLibrary* const dxc_lib_;
+ IDxcIncludeHandler* const fallback_;
+};
+
+#pragma GCC diagnostic pop
+#pragma clang diagnostic pop
+#if AMBER_PLATFORM_WINDOWS
+#pragma warning(pop)
+#endif // AMBER_PLATFORM_WINDOWS
+
} // namespace
Result Compile(const std::string& src,
const std::string& entry,
const std::string& profile,
const std::string& spv_env,
+ const std::string& filename,
+ const VirtualFileStore* virtual_files,
+ bool emit_debug_info,
std::vector<uint32_t>* generated_binary) {
- DxcInitThreadMalloc();
-
if (hlsl::options::initHlslOptTable()) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: initHlslOptTable");
@@ -91,12 +159,15 @@ Result Compile(const std::string& src,
return Result("DXC compile failure: CreateBlobFromFile");
}
- IDxcIncludeHandler* include_handler;
- if (dxc_lib->CreateIncludeHandler(&include_handler) < 0) {
+ IDxcIncludeHandler* fallback_include_handler;
+ if (dxc_lib->CreateIncludeHandler(&fallback_include_handler) < 0) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: CreateIncludeHandler");
}
+ CComPtr<IDxcIncludeHandler> include_handler(
+ new IncludeHandler(virtual_files, dxc_lib, fallback_include_handler));
+
IDxcCompiler* compiler;
if (DxcCreateInstance(CLSID_DxcCompiler, __uuidof(IDxcCompiler),
reinterpret_cast<void**>(&compiler)) < 0) {
@@ -104,11 +175,13 @@ Result Compile(const std::string& src,
return Result("DXCCreateInstance for DXCCompiler failed");
}
- IDxcOperationResult* result;
- std::wstring src_filename =
- L"amber." + std::wstring(profile.begin(), profile.end());
+ std::string filepath = filename.empty() ? ("amber." + profile) : filename;
std::vector<const wchar_t*> dxc_flags(kDxcFlags, &kDxcFlags[kDxcFlagsCount]);
+ if (emit_debug_info) { // Enable debug info generation
+ dxc_flags.emplace_back(L"-fspv-debug=rich");
+ }
+
const wchar_t* target_env = nullptr;
if (!spv_env.compare("spv1.3") || !spv_env.compare("vulkan1.1")) {
target_env = L"-fspv-target-env=vulkan1.1";
@@ -122,18 +195,21 @@ Result Compile(const std::string& src,
if (target_env)
dxc_flags.push_back(target_env);
- if (compiler->Compile(source, /* source text */
- src_filename.c_str(), /* original file source */
- std::wstring(entry.begin(), entry.end())
- .c_str(), /* entry point name */
- std::wstring(profile.begin(), profile.end())
- .c_str(), /* shader profile to compile */
- dxc_flags.data(), /* arguments */
- dxc_flags.size(), /* argument count */
- nullptr, /* defines */
- 0, /* define count */
- include_handler, /* handler for #include */
- &result /* output status */) < 0) {
+ IDxcOperationResult* result;
+ if (compiler->Compile(
+ source, /* source text */
+ std::wstring(filepath.begin(), filepath.end())
+ .c_str(), /* original file source */
+ std::wstring(entry.begin(), entry.end())
+ .c_str(), /* entry point name */
+ std::wstring(profile.begin(), profile.end())
+ .c_str(), /* shader profile to compile */
+ dxc_flags.data(), /* arguments */
+ static_cast<uint32_t>(dxc_flags.size()), /* argument count */
+ nullptr, /* defines */
+ 0, /* define count */
+ include_handler, /* handler for #include */
+ &result /* output status */) < 0) {
DxcCleanupThreadMalloc();
return Result("DXC compile failure: Compile");
}
@@ -160,7 +236,7 @@ Result Compile(const std::string& src,
error_buffer->GetBufferSize());
bool success = true;
- if (SUCCEEDED(result_status)) {
+ if (static_cast<HRESULT>(result_status) >= 0) {
IDxcBlob* compiled_blob;
if (result->GetResult(&compiled_blob) < 0) {
DxcCleanupThreadMalloc();
diff --git a/src/dxc_helper.h b/src/dxc_helper.h
index 4edb55d..2cd4212 100644
--- a/src/dxc_helper.h
+++ b/src/dxc_helper.h
@@ -21,14 +21,20 @@
#include "amber/result.h"
namespace amber {
+
+class VirtualFileStore;
+
namespace dxchelper {
// Passes the HLSL source code to the DXC compiler with SPIR-V CodeGen.
// Returns the generated SPIR-V binary via |generated_binary| argument.
-Result Compile(const std::string& src_str,
+Result Compile(const std::string& src,
+ const std::string& filename,
const std::string& entry_str,
const std::string& profile_str,
const std::string& spv_env,
+ const VirtualFileStore* virtual_files,
+ bool emit_debug_info,
std::vector<uint32_t>* generated_binary);
} // namespace dxchelper
diff --git a/src/engine.cc b/src/engine.cc
index 4f65a8a..7577e43 100644
--- a/src/engine.cc
+++ b/src/engine.cc
@@ -51,4 +51,6 @@ Engine::Engine() = default;
Engine::~Engine() = default;
+Engine::Debugger::~Debugger() = default;
+
} // namespace amber
diff --git a/src/engine.h b/src/engine.h
index 7b1f10e..7819b07 100644
--- a/src/engine.h
+++ b/src/engine.h
@@ -17,6 +17,7 @@
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include "amber/amber.h"
@@ -28,10 +29,12 @@
namespace amber {
+class VirtualFileStore;
+
/// EngineData stores information used during engine execution.
struct EngineData {
/// The timeout to use for fences, in milliseconds.
- uint32_t fence_timeout_ms = 1000;
+ uint32_t fence_timeout_ms = 10000;
};
/// Abstract class which describes a backing engine for Amber.
@@ -53,6 +56,16 @@ struct EngineData {
/// 5. Engine destructor is called.
class Engine {
public:
+ /// Debugger is the interface to the engine's shader debugger.
+ class Debugger : public debug::Events {
+ public:
+ ~Debugger() override;
+
+ /// Flush waits for all the debugger commands issued to complete, and
+ /// returns a Result that includes any debugger test failure.
+ virtual Result Flush() = 0;
+ };
+
/// Creates a new engine of the requested |type|.
static std::unique_ptr<Engine> Create(EngineType type);
@@ -88,6 +101,9 @@ class Engine {
/// Execute the draw rect command
virtual Result DoDrawRect(const DrawRectCommand* cmd) = 0;
+ /// Execute the draw grid command
+ virtual Result DoDrawGrid(const DrawGridCommand* cmd) = 0;
+
/// Execute the draw arrays command
virtual Result DoDrawArrays(const DrawArraysCommand* cmd) = 0;
@@ -106,6 +122,11 @@ class Engine {
/// This covers both Vulkan buffers and images.
virtual Result DoBuffer(const BufferCommand* cmd) = 0;
+ /// GetDebugger returns the shader debugger from the engine.
+ /// If the engine does not support a shader debugger then the Result will be a
+ /// failure.
+ virtual std::pair<Debugger*, Result> GetDebugger(VirtualFileStore*) = 0;
+
/// Sets the engine data to use.
void SetEngineData(const EngineData& data) { engine_data_ = data; }
diff --git a/src/executor.cc b/src/executor.cc
index 8f089d7..e64451d 100644
--- a/src/executor.cc
+++ b/src/executor.cc
@@ -15,6 +15,7 @@
#include "src/executor.h"
#include <cassert>
+#include <string>
#include <utility>
#include <vector>
@@ -34,12 +35,16 @@ Result Executor::CompileShaders(const amber::Script* script,
Options* options) {
for (auto& pipeline : script->GetPipelines()) {
for (auto& shader_info : pipeline->GetShaders()) {
- ShaderCompiler sc(script->GetSpvTargetEnv(),
- options->disable_spirv_validation);
+ std::string target_env = shader_info.GetShader()->GetTargetEnv();
+ if (target_env.empty())
+ target_env = script->GetSpvTargetEnv();
+
+ ShaderCompiler sc(target_env, options->disable_spirv_validation,
+ script->GetVirtualFiles());
Result r;
std::vector<uint32_t> data;
- std::tie(r, data) = sc.Compile(&shader_info, shader_map);
+ std::tie(r, data) = sc.Compile(pipeline.get(), &shader_info, shader_map);
if (!r.IsSuccess())
return r;
@@ -52,7 +57,8 @@ Result Executor::CompileShaders(const amber::Script* script,
Result Executor::Execute(Engine* engine,
const amber::Script* script,
const ShaderMap& shader_map,
- Options* options) {
+ Options* options,
+ Delegate* delegate) {
engine->SetEngineData(script->GetEngineData());
if (!script->GetPipelines().empty()) {
@@ -68,6 +74,12 @@ Result Executor::Execute(Engine* engine,
r = pipeline->GenerateOpenCLPodBuffers();
if (!r.IsSuccess())
return r;
+ r = pipeline->GenerateOpenCLLiteralSamplers();
+ if (!r.IsSuccess())
+ return r;
+ r = pipeline->GenerateOpenCLPushConstants();
+ if (!r.IsSuccess())
+ return r;
}
for (auto& pipeline : script->GetPipelines()) {
@@ -80,16 +92,40 @@ Result Executor::Execute(Engine* engine,
if (options->execution_type == ExecutionType::kPipelineCreateOnly)
return {};
+ Engine::Debugger* debugger = nullptr;
+
// Process Commands
for (const auto& cmd : script->GetCommands()) {
- if (options->delegate && options->delegate->LogExecuteCalls()) {
- options->delegate->Log(std::to_string(cmd->GetLine()) + ": " +
- cmd->ToString());
+ if (delegate && delegate->LogExecuteCalls()) {
+ delegate->Log(std::to_string(cmd->GetLine()) + ": " + cmd->ToString());
+ }
+
+ auto dbg_script = cmd->GetDebugScript();
+ if (dbg_script != nullptr) {
+ if (debugger == nullptr) {
+ // Lazilly obtain the debugger from the engine.
+ Result res;
+ std::tie(debugger, res) =
+ engine->GetDebugger(script->GetVirtualFiles());
+ if (!res.IsSuccess()) {
+ return res;
+ }
+ }
+ // Run the debugger script on the debugger for this command.
+ // This will run concurrently with the command.
+ dbg_script->Run(debugger);
}
Result r = ExecuteCommand(engine, cmd.get());
if (!r.IsSuccess())
return r;
+
+ if (debugger != nullptr) {
+ // Collect the debugger test results.
+ r = debugger->Flush();
+ if (!r.IsSuccess())
+ return r;
+ }
}
return {};
}
@@ -142,6 +178,8 @@ Result Executor::ExecuteCommand(Engine* engine, Command* cmd) {
}
if (cmd->IsDrawRect())
return engine->DoDrawRect(cmd->AsDrawRect());
+ if (cmd->IsDrawGrid())
+ return engine->DoDrawGrid(cmd->AsDrawGrid());
if (cmd->IsDrawArrays())
return engine->DoDrawArrays(cmd->AsDrawArrays());
if (cmd->IsCompute())
diff --git a/src/executor.h b/src/executor.h
index de5c794..e918634 100644
--- a/src/executor.h
+++ b/src/executor.h
@@ -36,7 +36,8 @@ class Executor {
Result Execute(Engine* engine,
const Script* script,
const ShaderMap& map,
- Options* options);
+ Options* options,
+ Delegate* delegate);
private:
Result CompileShaders(const Script* script,
diff --git a/src/executor_test.cc b/src/executor_test.cc
index c0558b5..859c244 100644
--- a/src/executor_test.cc
+++ b/src/executor_test.cc
@@ -108,6 +108,14 @@ class EngineStub : public Engine {
return {};
}
+ Result DoDrawGrid(const DrawGridCommand*) override {
+ did_draw_grid_command_ = true;
+
+ if (fail_draw_grid_command_)
+ return Result("draw grid command failed");
+ return {};
+ }
+
void FailDrawArraysCommand() { fail_draw_arrays_command_ = true; }
bool DidDrawArraysCommand() const { return did_draw_arrays_command_; }
Result DoDrawArrays(const DrawArraysCommand*) override {
@@ -159,12 +167,18 @@ class EngineStub : public Engine {
return {};
}
+ std::pair<Debugger*, Result> GetDebugger(VirtualFileStore*) override {
+ return {nullptr,
+ Result("EngineStub does not currently support a debugger")};
+ }
+
private:
bool fail_clear_command_ = false;
bool fail_clear_color_command_ = false;
bool fail_clear_stencil_command_ = false;
bool fail_clear_depth_command_ = false;
bool fail_draw_rect_command_ = false;
+ bool fail_draw_grid_command_ = false;
bool fail_draw_arrays_command_ = false;
bool fail_compute_command_ = false;
bool fail_entry_point_command_ = false;
@@ -176,6 +190,7 @@ class EngineStub : public Engine {
bool did_clear_stencil_command_ = false;
bool did_clear_depth_command_ = false;
bool did_draw_rect_command_ = false;
+ bool did_draw_grid_command_ = false;
bool did_draw_arrays_command_ = false;
bool did_compute_command_ = false;
bool did_entry_point_command_ = false;
@@ -192,17 +207,17 @@ class EngineStub : public Engine {
class VkScriptExecutorTest : public testing::Test {
public:
VkScriptExecutorTest() = default;
- ~VkScriptExecutorTest() = default;
+ ~VkScriptExecutorTest() override = default;
std::unique_ptr<Engine> MakeEngine() { return MakeUnique<EngineStub>(); }
std::unique_ptr<Engine> MakeAndInitializeEngine(
const std::vector<std::string>& features,
const std::vector<std::string>& instance_extensions,
const std::vector<std::string>& device_extensions) {
- auto engine = MakeUnique<EngineStub>();
+ std::unique_ptr<Engine> engine = MakeUnique<EngineStub>();
engine->Initialize(nullptr, nullptr, features, instance_extensions,
device_extensions);
- return std::move(engine);
+ return engine;
}
EngineStub* ToStub(Engine* engine) {
return static_cast<EngineStub*>(engine);
@@ -228,7 +243,8 @@ logicOp)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
const auto& features = ToStub(engine.get())->GetFeatures();
@@ -257,7 +273,8 @@ VK_KHR_variable_pointers)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
const auto& features = ToStub(engine.get())->GetFeatures();
@@ -286,7 +303,8 @@ depthstencil D24_UNORM_S8_UINT)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
const auto& features = ToStub(engine.get())->GetFeatures();
@@ -312,7 +330,8 @@ fence_timeout 12345)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
const auto& features = ToStub(engine.get())->GetFeatures();
@@ -346,7 +365,8 @@ fence_timeout 12345)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
const auto& features = ToStub(engine.get())->GetFeatures();
@@ -376,7 +396,8 @@ clear)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
EXPECT_TRUE(ToStub(engine.get())->DidClearCommand());
}
@@ -396,7 +417,8 @@ clear)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("clear command failed", r.Error());
}
@@ -415,7 +437,8 @@ clear color 244 123 123 13)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidClearColorCommand());
@@ -444,7 +467,8 @@ clear color 123 123 123 123)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("clear color command failed", r.Error());
}
@@ -463,7 +487,8 @@ clear depth 24)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidClearDepthCommand());
}
@@ -483,7 +508,8 @@ clear depth 24)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("clear depth command failed", r.Error());
}
@@ -502,7 +528,8 @@ clear stencil 24)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidClearStencilCommand());
}
@@ -522,7 +549,8 @@ clear stencil 24)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("clear stencil command failed", r.Error());
}
@@ -541,7 +569,8 @@ draw rect 2 4 10 20)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidDrawRectCommand());
}
@@ -561,7 +590,8 @@ draw rect 2 4 10 20)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("draw rect command failed", r.Error());
}
@@ -580,7 +610,8 @@ draw arrays TRIANGLE_LIST 0 0)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidDrawArraysCommand());
}
@@ -600,7 +631,8 @@ draw arrays TRIANGLE_LIST 0 0)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("draw arrays command failed", r.Error());
}
@@ -619,7 +651,8 @@ compute 2 3 4)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidComputeCommand());
}
@@ -639,7 +672,8 @@ compute 2 3 4)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("compute command failed", r.Error());
}
@@ -658,7 +692,8 @@ vertex entrypoint main)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidEntryPointCommand());
}
@@ -678,7 +713,8 @@ vertex entrypoint main)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("entrypoint command failed", r.Error());
}
@@ -697,7 +733,8 @@ patch parameter vertices 10)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidPatchParameterVerticesCommand());
}
@@ -717,7 +754,8 @@ patch parameter vertices 10)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("patch command failed", r.Error());
}
@@ -735,7 +773,8 @@ probe rect rgba 2 3 40 40 0.2 0.4 0.4 0.3)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
// ASSERT_TRUE(ToStub(engine.get())->DidProbeCommand());
}
@@ -754,7 +793,8 @@ probe rect rgba 2 3 40 40 0.2 0.4 0.4 0.3)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("probe command failed", r.Error());
}
@@ -773,7 +813,8 @@ ssbo 0 24)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
ASSERT_TRUE(ToStub(engine.get())->DidBufferCommand());
}
@@ -793,7 +834,8 @@ ssbo 0 24)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("buffer command failed", r.Error());
}
@@ -811,7 +853,8 @@ probe ssbo vec3 0 2 <= 2 3 4)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_TRUE(r.IsSuccess());
// ASSERT_TRUE(ToStub(engine.get())->DidProbeSSBOCommand());
}
@@ -830,7 +873,8 @@ probe ssbo vec3 0 2 <= 2 3 4)";
Options options;
Executor ex;
- Result r = ex.Execute(engine.get(), script.get(), ShaderMap(), &options);
+ Result r =
+ ex.Execute(engine.get(), script.get(), ShaderMap(), &options, nullptr);
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("probe ssbo command failed", r.Error());
}
diff --git a/src/float16_helper.cc b/src/float16_helper.cc
new file mode 100644
index 0000000..617bd72
--- /dev/null
+++ b/src/float16_helper.cc
@@ -0,0 +1,138 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/float16_helper.h"
+
+#include <cassert>
+
+// Float10
+// | 9 8 7 6 5 | 4 3 2 1 0 |
+// | exponent | mantissa |
+//
+// Float11
+// | 10 9 8 7 6 | 5 4 3 2 1 0 |
+// | exponent | mantissa |
+//
+// Float16
+// | 15 | 14 13 12 11 10 | 9 8 7 6 5 4 3 2 1 0 |
+// | s | exponent | mantissa |
+//
+// Float32
+// | 31 | 30 ... 23 | 22 ... 0 |
+// | s | exponent | mantissa |
+
+namespace amber {
+namespace float16 {
+namespace {
+
+// Return sign value of 32 bits float.
+uint16_t FloatSign(const uint32_t hex_float) {
+ return static_cast<uint16_t>(hex_float >> 31U);
+}
+
+// Return exponent value of 32 bits float.
+uint16_t FloatExponent(const uint32_t hex_float) {
+ uint32_t exponent_bits = ((hex_float >> 23U) & ((1U << 8U) - 1U));
+ // Handle zero and denormals.
+ if (exponent_bits == 0U)
+ return 0;
+ uint32_t exponent = exponent_bits - 112U;
+ const uint32_t half_exponent_mask = (1U << 5U) - 1U;
+ assert(((exponent & ~half_exponent_mask) == 0U) && "Float exponent overflow");
+ return static_cast<uint16_t>(exponent & half_exponent_mask);
+}
+
+// Return mantissa value of 32 bits float. Note that mantissa for 32
+// bits float is 23 bits and this method must return uint32_t.
+uint32_t FloatMantissa(const uint32_t hex_float) {
+ return static_cast<uint32_t>(hex_float & ((1U << 23U) - 1U));
+}
+
+// Convert float |value| whose size is 16 bits to 32 bits float
+// based on IEEE-754.
+float HexFloat16ToFloat(const uint8_t* value) {
+ uint32_t sign = (static_cast<uint32_t>(value[1]) & 0x80) << 24U;
+ uint32_t exponent_bits = (static_cast<uint32_t>(value[1]) & 0x7c) >> 2U;
+ uint32_t exponent = 0U;
+ uint32_t mantissa = 0U;
+ // Handle zero and flush denormals to zero.
+ if (exponent_bits != 0U) {
+ exponent = (exponent_bits + 112U) << 23U;
+ mantissa = ((static_cast<uint32_t>(value[1]) & 0x3) << 8U |
+ static_cast<uint32_t>(value[0]))
+ << 13U;
+ }
+
+ uint32_t hex = sign | exponent | mantissa;
+ float* hex_float = reinterpret_cast<float*>(&hex);
+ return *hex_float;
+}
+
+// Convert float |value| whose size is 11 bits to 32 bits float
+// based on IEEE-754.
+float HexFloat11ToFloat(const uint8_t* value) {
+ uint32_t exponent = (((static_cast<uint32_t>(value[1]) << 2U) |
+ ((static_cast<uint32_t>(value[0]) & 0xc0) >> 6U)) +
+ 112U)
+ << 23U;
+ uint32_t mantissa = (static_cast<uint32_t>(value[0]) & 0x3f) << 17U;
+
+ uint32_t hex = exponent | mantissa;
+ float* hex_float = reinterpret_cast<float*>(&hex);
+ return *hex_float;
+}
+
+// Convert float |value| whose size is 10 bits to 32 bits float
+// based on IEEE-754.
+float HexFloat10ToFloat(const uint8_t* value) {
+ uint32_t exponent = (((static_cast<uint32_t>(value[1]) << 3U) |
+ ((static_cast<uint32_t>(value[0]) & 0xe0) >> 5U)) +
+ 112U)
+ << 23U;
+ uint32_t mantissa = (static_cast<uint32_t>(value[0]) & 0x1f) << 18U;
+
+ uint32_t hex = exponent | mantissa;
+ float* hex_float = reinterpret_cast<float*>(&hex);
+ return *hex_float;
+}
+
+} // namespace
+
+float HexFloatToFloat(const uint8_t* value, uint8_t bits) {
+ switch (bits) {
+ case 10:
+ return HexFloat10ToFloat(value);
+ case 11:
+ return HexFloat11ToFloat(value);
+ case 16:
+ return HexFloat16ToFloat(value);
+ }
+
+ assert(false && "Invalid bits");
+ return 0;
+}
+
+uint16_t FloatToHexFloat16(const float value) {
+ const uint32_t* hex = reinterpret_cast<const uint32_t*>(&value);
+ uint16_t sign = FloatSign(*hex);
+ uint16_t exponent = FloatExponent(*hex);
+ // Flush denormals.
+ uint32_t mantissa = ((exponent == 0) ? 0U : FloatMantissa(*hex));
+ return static_cast<uint16_t>(static_cast<uint16_t>(sign << 15U) |
+ static_cast<uint16_t>(exponent << 10U) |
+ static_cast<uint16_t>(mantissa >> 13U));
+}
+
+} // namespace float16
+} // namespace amber
diff --git a/src/float16_helper.h b/src/float16_helper.h
new file mode 100644
index 0000000..66ae634
--- /dev/null
+++ b/src/float16_helper.h
@@ -0,0 +1,45 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_FLOAT16_HELPER_H_
+#define SRC_FLOAT16_HELPER_H_
+
+#include <cstdint>
+
+namespace amber {
+namespace float16 {
+
+// Convert float |value| whose size is |bits| bits to 32 bits float
+// based on IEEE-754.
+//
+// See https://www.khronos.org/opengl/wiki/Small_Float_Formats
+// and https://en.wikipedia.org/wiki/IEEE_754.
+//
+// Sign Exponent Mantissa Exponent-Bias
+// 16 1 5 10 15
+// 11 0 5 6 15
+// 10 0 5 5 15
+// 32 1 8 23 127
+// 64 1 11 52 1023
+//
+// 11 and 10 bits floats are always positive.
+float HexFloatToFloat(const uint8_t* value, uint8_t bits);
+
+// Convert 32 bits float |value| to 16 bits float based on IEEE-754.
+uint16_t FloatToHexFloat16(const float value);
+
+} // namespace float16
+} // namespace amber
+
+#endif // SRC_FLOAT16_HELPER_H_
diff --git a/src/float16_helper_test.cc b/src/float16_helper_test.cc
new file mode 100644
index 0000000..99ea295
--- /dev/null
+++ b/src/float16_helper_test.cc
@@ -0,0 +1,33 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/float16_helper.h"
+
+#include "gtest/gtest.h"
+
+namespace amber {
+namespace float16 {
+
+using Float16HelperTest = testing::Test;
+
+TEST_F(Float16HelperTest, F32ToF16AndBack) {
+ float a = 2.5;
+
+ uint16_t half = float16::FloatToHexFloat16(a);
+ float b = float16::HexFloatToFloat(reinterpret_cast<uint8_t*>(&half), 16);
+ EXPECT_FLOAT_EQ(a, b);
+}
+
+} // namespace float16
+} // namespace amber
diff --git a/src/format.h b/src/format.h
index e6e68ba..bfef06d 100644
--- a/src/format.h
+++ b/src/format.h
@@ -114,6 +114,14 @@ class Format {
uint32_t SizeInBytes() const;
bool IsFormatKnown() const { return format_type_ != FormatType::kUnknown; }
+ bool HasDepthComponent() const {
+ return format_type_ == FormatType::kD16_UNORM ||
+ format_type_ == FormatType::kD16_UNORM_S8_UINT ||
+ format_type_ == FormatType::kD24_UNORM_S8_UINT ||
+ format_type_ == FormatType::kD32_SFLOAT ||
+ format_type_ == FormatType::kD32_SFLOAT_S8_UINT ||
+ format_type_ == FormatType::kX8_D24_UNORM_PACK32;
+ }
bool HasStencilComponent() const {
return format_type_ == FormatType::kD24_UNORM_S8_UINT ||
format_type_ == FormatType::kD16_UNORM_S8_UINT ||
diff --git a/src/format_test.cc b/src/format_test.cc
index 341faac..f454297 100644
--- a/src/format_test.cc
+++ b/src/format_test.cc
@@ -263,13 +263,13 @@ TEST_F(FormatTest, SegmentPackedList_Std430) {
auto type = parser.Parse("A8B8G8R8_SINT_PACK32");
Format fmt(type.get());
- EXPECT_EQ(4, fmt.SizeInBytes());
+ EXPECT_EQ(4u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(1U, segs.size());
// Always packs into a unsigned ...
EXPECT_EQ(FormatMode::kUInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
}
TEST_F(FormatTest, SegmentListR32G32_Std140) {
@@ -278,14 +278,14 @@ TEST_F(FormatTest, SegmentListR32G32_Std140) {
Format fmt(type.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(8, fmt.SizeInBytes());
+ EXPECT_EQ(8u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(2U, segs.size());
EXPECT_EQ(FormatMode::kUInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
}
TEST_F(FormatTest, SegmentListR32G32B32_Std140) {
@@ -294,18 +294,18 @@ TEST_F(FormatTest, SegmentListR32G32B32_Std140) {
Format fmt(type.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(16, fmt.SizeInBytes());
+ EXPECT_EQ(16u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(4U, segs.size());
EXPECT_EQ(FormatMode::kUInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_TRUE(segs[3].IsPadding());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
}
TEST_F(FormatTest, SegmentListR32G32B32_Std430) {
@@ -314,18 +314,18 @@ TEST_F(FormatTest, SegmentListR32G32B32_Std430) {
Format fmt(type.get());
fmt.SetLayout(Format::Format::Layout::kStd430);
- EXPECT_EQ(16, fmt.SizeInBytes());
+ EXPECT_EQ(16u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(4U, segs.size());
EXPECT_EQ(FormatMode::kUInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_TRUE(segs[3].IsPadding());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
}
TEST_F(FormatTest, SegmentMat2x2_Std140) {
@@ -335,23 +335,23 @@ TEST_F(FormatTest, SegmentMat2x2_Std140) {
Format fmt(type.get());
fmt.SetLayout(Format::Format::Layout::kStd140);
- EXPECT_EQ(32, fmt.SizeInBytes());
+ EXPECT_EQ(32u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(6U, segs.size());
EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_TRUE(segs[2].IsPadding());
- EXPECT_EQ(8, segs[2].SizeInBytes());
+ EXPECT_EQ(8u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
EXPECT_TRUE(segs[5].IsPadding());
- EXPECT_EQ(8, segs[5].SizeInBytes());
+ EXPECT_EQ(8u, segs[5].SizeInBytes());
}
TEST_F(FormatTest, SegmentMat2x2_Std430) {
@@ -361,19 +361,19 @@ TEST_F(FormatTest, SegmentMat2x2_Std430) {
Format fmt(type.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(16, fmt.SizeInBytes());
+ EXPECT_EQ(16u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(4U, segs.size());
EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
}
TEST_F(FormatTest, SegmentMat2x3_Std430) {
@@ -383,27 +383,27 @@ TEST_F(FormatTest, SegmentMat2x3_Std430) {
Format fmt(type.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(32, fmt.SizeInBytes());
+ EXPECT_EQ(32u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(8U, segs.size());
EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_TRUE(segs[3].IsPadding());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
- EXPECT_EQ(4, segs[6].SizeInBytes());
+ EXPECT_EQ(4u, segs[6].SizeInBytes());
EXPECT_TRUE(segs[7].IsPadding());
- EXPECT_EQ(4, segs[7].SizeInBytes());
+ EXPECT_EQ(4u, segs[7].SizeInBytes());
}
TEST_F(FormatTest, SegmentRuntimeArray_Std140) {
@@ -413,14 +413,14 @@ TEST_F(FormatTest, SegmentRuntimeArray_Std140) {
Format fmt(type.get());
fmt.SetLayout(Format::Format::Layout::kStd140);
- EXPECT_EQ(16, fmt.SizeInBytes());
+ EXPECT_EQ(16u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(2U, segs.size());
EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(12, segs[1].SizeInBytes());
+ EXPECT_EQ(12u, segs[1].SizeInBytes());
}
TEST_F(FormatTest, SegmentRuntimeArray_Std430) {
@@ -430,12 +430,12 @@ TEST_F(FormatTest, SegmentRuntimeArray_Std430) {
Format fmt(type.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(4, fmt.SizeInBytes());
+ EXPECT_EQ(4u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(1U, segs.size());
EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
}
// struct {
@@ -451,16 +451,16 @@ TEST_F(FormatTest, SegmentStruct_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(16, fmt.SizeInBytes());
+ EXPECT_EQ(16u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
- ASSERT_EQ(3, segs.size());
+ ASSERT_EQ(3u, segs.size());
EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_TRUE(segs[2].IsPadding());
- EXPECT_EQ(8, segs[2].SizeInBytes());
+ EXPECT_EQ(8u, segs[2].SizeInBytes());
}
TEST_F(FormatTest, SegmentStruct_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -471,14 +471,14 @@ TEST_F(FormatTest, SegmentStruct_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(8, fmt.SizeInBytes());
+ EXPECT_EQ(8u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(2U, segs.size());
EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
}
// struct STRIDE 20 {
@@ -496,16 +496,16 @@ TEST_F(FormatTest, SegmentStructWithStride_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(20, fmt.SizeInBytes());
+ EXPECT_EQ(20u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(3U, segs.size());
EXPECT_EQ(FormatMode::kSFloat, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_TRUE(segs[2].IsPadding());
- EXPECT_EQ((20 - sizeof(float) - sizeof(uint32_t)), segs[2].SizeInBytes());
+ EXPECT_EQ((20u - sizeof(float) - sizeof(uint32_t)), segs[2].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithStride_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -517,7 +517,7 @@ TEST_F(FormatTest, SegmentStructWithStride_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(20, fmt.SizeInBytes());
+ EXPECT_EQ(20u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(3U, segs.size());
@@ -526,7 +526,7 @@ TEST_F(FormatTest, SegmentStructWithStride_Std430) {
EXPECT_EQ(FormatMode::kUInt, segs[1].GetFormatMode());
EXPECT_EQ(32U, segs[1].GetNumBits());
EXPECT_TRUE(segs[2].IsPadding());
- EXPECT_EQ((20 - sizeof(float) - sizeof(uint32_t)) * 8, segs[2].GetNumBits());
+ EXPECT_EQ((20u - sizeof(float) - sizeof(uint32_t)) * 8, segs[2].GetNumBits());
}
// struct {
@@ -545,18 +545,18 @@ TEST_F(FormatTest, SegmentStructWithMemberOffset_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(16, fmt.SizeInBytes());
+ EXPECT_EQ(16u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(4U, segs.size());
EXPECT_TRUE(segs[0].IsPadding());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_TRUE(segs[3].IsPadding());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithMemberOffset_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -575,11 +575,11 @@ TEST_F(FormatTest, SegmentStructWithMemberOffset_Std430) {
const auto& segs = fmt.GetSegments();
ASSERT_EQ(3U, segs.size());
EXPECT_TRUE(segs[0].IsPadding());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_EQ(FormatMode::kUInt, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
}
// struct {
@@ -602,20 +602,20 @@ TEST_F(FormatTest, SegmentStructWithStruct_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(32, fmt.SizeInBytes());
+ EXPECT_EQ(32u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(5U, segs.size());
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_TRUE(segs[2].IsPadding());
- EXPECT_EQ(8, segs[2].SizeInBytes());
+ EXPECT_EQ(8u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
EXPECT_TRUE(segs[4].IsPadding());
- EXPECT_EQ(12, segs[4].SizeInBytes());
+ EXPECT_EQ(12u, segs[4].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithStruct_Std430) {
auto x = MakeUnique<type::Struct>();
@@ -630,16 +630,16 @@ TEST_F(FormatTest, SegmentStructWithStruct_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(12, fmt.SizeInBytes());
+ EXPECT_EQ(12u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(3U, segs.size());
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
}
// struct {
@@ -660,27 +660,27 @@ TEST_F(FormatTest, SegmentStructWithVec2_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(32, fmt.SizeInBytes());
+ EXPECT_EQ(32u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(6U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
/* vec2 */
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[5].IsPadding());
- EXPECT_EQ(12, segs[5].SizeInBytes());
+ EXPECT_EQ(12u, segs[5].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithVec2_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -695,27 +695,27 @@ TEST_F(FormatTest, SegmentStructWithVec2_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(24, fmt.SizeInBytes());
+ EXPECT_EQ(24u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(6U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
/* vec2 */
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[5].IsPadding());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
}
// struct {
@@ -736,26 +736,26 @@ TEST_F(FormatTest, SegmentStructWithFloatPackedToVec_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(32, fmt.SizeInBytes());
+ EXPECT_EQ(32u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(6U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(12, segs[1].SizeInBytes());
+ EXPECT_EQ(12u, segs[1].SizeInBytes());
/* vec2 */
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithFloatPackedToVec_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -770,26 +770,26 @@ TEST_F(FormatTest, SegmentStructWithFloatPackedToVec_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(32, fmt.SizeInBytes());
+ EXPECT_EQ(32u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(6U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(12, segs[1].SizeInBytes());
+ EXPECT_EQ(12u, segs[1].SizeInBytes());
/* vec2 */
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
}
// struct {
@@ -811,34 +811,34 @@ TEST_F(FormatTest, SegmentStructVec3Vec2_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(48, fmt.SizeInBytes());
+ EXPECT_EQ(48u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(9U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(12, segs[1].SizeInBytes());
+ EXPECT_EQ(12u, segs[1].SizeInBytes());
/* vec3 */
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[5].IsPadding());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
/* vec2 */
EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
- EXPECT_EQ(4, segs[6].SizeInBytes());
+ EXPECT_EQ(4u, segs[6].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[7].GetFormatMode());
- EXPECT_EQ(4, segs[7].SizeInBytes());
+ EXPECT_EQ(4u, segs[7].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[8].IsPadding());
- EXPECT_EQ(8, segs[8].SizeInBytes());
+ EXPECT_EQ(8u, segs[8].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructVec3Vec2_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -854,34 +854,34 @@ TEST_F(FormatTest, SegmentStructVec3Vec2_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(48, fmt.SizeInBytes());
+ EXPECT_EQ(48u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(9U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(12, segs[1].SizeInBytes());
+ EXPECT_EQ(12u, segs[1].SizeInBytes());
/* vec3 */
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[5].IsPadding());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
/* vec2 */
EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
- EXPECT_EQ(4, segs[6].SizeInBytes());
+ EXPECT_EQ(4u, segs[6].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[7].GetFormatMode());
- EXPECT_EQ(4, segs[7].SizeInBytes());
+ EXPECT_EQ(4u, segs[7].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[8].IsPadding());
- EXPECT_EQ(8, segs[8].SizeInBytes());
+ EXPECT_EQ(8u, segs[8].SizeInBytes());
}
// struct {
@@ -903,36 +903,36 @@ TEST_F(FormatTest, SegmentStructMat2x2_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(64, fmt.SizeInBytes());
+ EXPECT_EQ(64u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(10U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(12, segs[1].SizeInBytes());
+ EXPECT_EQ(12u, segs[1].SizeInBytes());
/* column 1 */
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
EXPECT_TRUE(segs[4].IsPadding());
- EXPECT_EQ(8, segs[4].SizeInBytes());
+ EXPECT_EQ(8u, segs[4].SizeInBytes());
/* column 2 */
EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
- EXPECT_EQ(4, segs[6].SizeInBytes());
+ EXPECT_EQ(4u, segs[6].SizeInBytes());
EXPECT_TRUE(segs[7].IsPadding());
- EXPECT_EQ(8, segs[7].SizeInBytes());
+ EXPECT_EQ(8u, segs[7].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[8].GetFormatMode());
- EXPECT_EQ(4, segs[8].SizeInBytes());
+ EXPECT_EQ(4u, segs[8].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[9].IsPadding());
- EXPECT_EQ(12, segs[9].SizeInBytes());
+ EXPECT_EQ(12u, segs[9].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructMat2x2_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -948,32 +948,32 @@ TEST_F(FormatTest, SegmentStructMat2x2_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(32, fmt.SizeInBytes());
+ EXPECT_EQ(32u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(8U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
/* column 1 */
EXPECT_EQ(FormatMode::kSFloat, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
/* column 2 */
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
- EXPECT_EQ(4, segs[6].SizeInBytes());
+ EXPECT_EQ(4u, segs[6].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[7].IsPadding());
- EXPECT_EQ(4, segs[7].SizeInBytes());
+ EXPECT_EQ(4u, segs[7].SizeInBytes());
}
// struct {
@@ -1000,34 +1000,34 @@ TEST_F(FormatTest, SegmentStructWithStructNoPack_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(48, fmt.SizeInBytes());
+ EXPECT_EQ(48u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(8U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(12, segs[1].SizeInBytes());
+ EXPECT_EQ(12u, segs[1].SizeInBytes());
/* a */
EXPECT_EQ(FormatMode::kSInt, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
/* b */
EXPECT_EQ(FormatMode::kSInt, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
/* c */
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[5].IsPadding());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
- EXPECT_EQ(4, segs[6].SizeInBytes());
+ EXPECT_EQ(4u, segs[6].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[7].IsPadding());
- EXPECT_EQ(12, segs[7].SizeInBytes());
+ EXPECT_EQ(12u, segs[7].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithStructNoPack_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -1044,25 +1044,25 @@ TEST_F(FormatTest, SegmentStructWithStructNoPack_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(20, fmt.SizeInBytes());
+ EXPECT_EQ(20u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(5U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* a */
EXPECT_EQ(FormatMode::kSInt, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
/* b */
EXPECT_EQ(FormatMode::kSInt, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
/* c */
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
}
// struct {
@@ -1092,49 +1092,49 @@ TEST_F(FormatTest, SegmentStructWithStructArray_Std140) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd140);
- EXPECT_EQ(96, fmt.SizeInBytes());
+ EXPECT_EQ(96u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(13U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[1].IsPadding());
- EXPECT_EQ(12, segs[1].SizeInBytes());
+ EXPECT_EQ(12u, segs[1].SizeInBytes());
/* a */
EXPECT_EQ(FormatMode::kSInt, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
/* b */
EXPECT_EQ(FormatMode::kSInt, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[4].IsPadding());
- EXPECT_EQ(8, segs[4].SizeInBytes());
+ EXPECT_EQ(8u, segs[4].SizeInBytes());
/* c[0] */
EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[6].IsPadding());
- EXPECT_EQ(12, segs[6].SizeInBytes());
+ EXPECT_EQ(12u, segs[6].SizeInBytes());
/* c[1] */
EXPECT_EQ(FormatMode::kSFloat, segs[7].GetFormatMode());
- EXPECT_EQ(4, segs[7].SizeInBytes());
+ EXPECT_EQ(4u, segs[7].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[8].IsPadding());
- EXPECT_EQ(12, segs[8].SizeInBytes());
+ EXPECT_EQ(12u, segs[8].SizeInBytes());
/* c[2] */
EXPECT_EQ(FormatMode::kSFloat, segs[9].GetFormatMode());
- EXPECT_EQ(4, segs[9].SizeInBytes());
+ EXPECT_EQ(4u, segs[9].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[10].IsPadding());
- EXPECT_EQ(12, segs[10].SizeInBytes());
+ EXPECT_EQ(12u, segs[10].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[11].GetFormatMode());
- EXPECT_EQ(4, segs[11].SizeInBytes());
+ EXPECT_EQ(4u, segs[11].SizeInBytes());
/* pad */
EXPECT_TRUE(segs[12].IsPadding());
- EXPECT_EQ(12, segs[12].SizeInBytes());
+ EXPECT_EQ(12u, segs[12].SizeInBytes());
}
TEST_F(FormatTest, SegmentStructWithStructArray_Std430) {
auto s = MakeUnique<type::Struct>();
@@ -1153,31 +1153,31 @@ TEST_F(FormatTest, SegmentStructWithStructArray_Std430) {
Format fmt(s.get());
fmt.SetLayout(Format::Layout::kStd430);
- EXPECT_EQ(28, fmt.SizeInBytes());
+ EXPECT_EQ(28u, fmt.SizeInBytes());
const auto& segs = fmt.GetSegments();
ASSERT_EQ(7U, segs.size());
/* w */
EXPECT_EQ(FormatMode::kSInt, segs[0].GetFormatMode());
- EXPECT_EQ(4, segs[0].SizeInBytes());
+ EXPECT_EQ(4u, segs[0].SizeInBytes());
/* a */
EXPECT_EQ(FormatMode::kSInt, segs[1].GetFormatMode());
- EXPECT_EQ(4, segs[1].SizeInBytes());
+ EXPECT_EQ(4u, segs[1].SizeInBytes());
/* b */
EXPECT_EQ(FormatMode::kSInt, segs[2].GetFormatMode());
- EXPECT_EQ(4, segs[2].SizeInBytes());
+ EXPECT_EQ(4u, segs[2].SizeInBytes());
/* c[0] */
EXPECT_EQ(FormatMode::kSFloat, segs[3].GetFormatMode());
- EXPECT_EQ(4, segs[3].SizeInBytes());
+ EXPECT_EQ(4u, segs[3].SizeInBytes());
/* c[1] */
EXPECT_EQ(FormatMode::kSFloat, segs[4].GetFormatMode());
- EXPECT_EQ(4, segs[4].SizeInBytes());
+ EXPECT_EQ(4u, segs[4].SizeInBytes());
/* c[2] */
EXPECT_EQ(FormatMode::kSFloat, segs[5].GetFormatMode());
- EXPECT_EQ(4, segs[5].SizeInBytes());
+ EXPECT_EQ(4u, segs[5].SizeInBytes());
/* y */
EXPECT_EQ(FormatMode::kSFloat, segs[6].GetFormatMode());
- EXPECT_EQ(4, segs[6].SizeInBytes());
+ EXPECT_EQ(4u, segs[6].SizeInBytes());
}
} // namespace amber
diff --git a/src/image.h b/src/image.h
new file mode 100644
index 0000000..c6a51a3
--- /dev/null
+++ b/src/image.h
@@ -0,0 +1,26 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_IMAGE_H_
+#define SRC_IMAGE_H_
+
+#include <cstdint>
+
+namespace amber {
+
+enum class ImageDimension : int8_t { kUnknown = -1, k1D = 0, k2D = 1, k3D = 2 };
+
+} // namespace amber
+
+#endif // SRC_IMAGE_H_
diff --git a/src/parser.cc b/src/parser.cc
index 7e0a6e3..df8ee98 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -18,7 +18,8 @@
namespace amber {
-Parser::Parser() : script_(MakeUnique<Script>()) {}
+Parser::Parser(Delegate* delegate)
+ : script_(MakeUnique<Script>()), delegate_(delegate) {}
Parser::~Parser() = default;
diff --git a/src/parser.h b/src/parser.h
index 20a1713..21ce8e0 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -37,9 +37,10 @@ class Parser {
std::unique_ptr<Script> GetScript() { return std::move(script_); }
protected:
- Parser();
+ explicit Parser(Delegate* delegate);
std::unique_ptr<Script> script_;
+ Delegate* delegate_ = nullptr;
};
} // namespace amber
diff --git a/src/pipeline.cc b/src/pipeline.cc
index 8f23ca0..55c6bae 100644
--- a/src/pipeline.cc
+++ b/src/pipeline.cc
@@ -15,6 +15,7 @@
#include "src/pipeline.h"
#include <algorithm>
+#include <cstring>
#include <limits>
#include <set>
@@ -27,13 +28,35 @@ namespace {
const char* kDefaultColorBufferFormat = "B8G8R8A8_UNORM";
const char* kDefaultDepthBufferFormat = "D32_SFLOAT_S8_UINT";
+// OpenCL coordinates mode is bit 0
+const uint32_t kOpenCLNormalizedCoordsBit = 1;
+// OpenCL address mode bits are bits 1,2,3.
+const uint32_t kOpenCLAddressModeBits = 0xe;
+// OpenCL address mode bit values.
+const uint32_t kOpenCLAddressModeNone = 0;
+const uint32_t kOpenCLAddressModeClampToEdge = 2;
+const uint32_t kOpenCLAddressModeClamp = 4;
+const uint32_t kOpenCLAddressModeRepeat = 6;
+const uint32_t kOpenCLAddressModeMirroredRepeat = 8;
+// OpenCL filter mode bits.
+const uint32_t kOpenCLFilterModeNearestBit = 0x10;
+const uint32_t kOpenCLFilterModeLinearBit = 0x20;
+
} // namespace
const char* Pipeline::kGeneratedColorBuffer = "framebuffer";
const char* Pipeline::kGeneratedDepthBuffer = "depth_buffer";
+const char* Pipeline::kGeneratedPushConstantBuffer = "push_constant_buffer";
Pipeline::ShaderInfo::ShaderInfo(Shader* shader, ShaderType type)
- : shader_(shader), shader_type_(type), entry_point_("main") {}
+ : shader_(shader),
+ shader_type_(type),
+ entry_point_("main"),
+ required_subgroup_size_setting_(RequiredSubgroupSizeSetting::kNotSet),
+ required_subgroup_size_(0),
+ varying_subgroup_size_(false),
+ require_full_subgroups_(false),
+ emit_debug_info_(false) {}
Pipeline::ShaderInfo::ShaderInfo(const ShaderInfo&) = default;
@@ -49,11 +72,12 @@ std::unique_ptr<Pipeline> Pipeline::Clone() const {
clone->color_attachments_ = color_attachments_;
clone->vertex_buffers_ = vertex_buffers_;
clone->buffers_ = buffers_;
- clone->depth_buffer_ = depth_buffer_;
+ clone->depth_stencil_buffer_ = depth_stencil_buffer_;
clone->index_buffer_ = index_buffer_;
clone->fb_width_ = fb_width_;
clone->fb_height_ = fb_height_;
clone->set_arg_values_ = set_arg_values_;
+ clone->pipeline_data_ = pipeline_data_;
if (!opencl_pod_buffers_.empty()) {
// Generate specific buffers for the clone.
@@ -132,6 +156,85 @@ Result Pipeline::SetShaderCompileOptions(const Shader* shader,
shader->GetName());
}
+Result Pipeline::SetShaderRequiredSubgroupSize(
+ const Shader* shader,
+ const ShaderInfo::RequiredSubgroupSizeSetting setting,
+ const uint32_t size) {
+ if (!shader)
+ return Result("invalid shader specified for required subgroup size");
+
+ for (auto& info : shaders_) {
+ const auto* is = info.GetShader();
+ if (is == shader) {
+ info.SetRequiredSubgroupSizeSetting(setting, size);
+ return {};
+ }
+ }
+
+ return Result("unknown shader specified for required subgroup size: " +
+ shader->GetName());
+}
+
+Result Pipeline::SetShaderRequiredSubgroupSize(const Shader* shader,
+ const uint32_t subgroupSize) {
+ const bool isPow2 =
+ subgroupSize > 0 && (subgroupSize & (subgroupSize - 1)) == 0;
+ if (subgroupSize == 0 || subgroupSize > 128 || !isPow2) {
+ return Result("invalid required subgroup size " +
+ std::to_string(subgroupSize) + " specified for shader name " +
+ shader->GetName());
+ }
+ const ShaderInfo::RequiredSubgroupSizeSetting setting =
+ ShaderInfo::RequiredSubgroupSizeSetting::kSetToSpecificSize;
+ return SetShaderRequiredSubgroupSize(shader, setting, subgroupSize);
+}
+
+Result Pipeline::SetShaderRequiredSubgroupSizeToMinimum(const Shader* shader) {
+ const ShaderInfo::RequiredSubgroupSizeSetting subgroupSizeSetting =
+ ShaderInfo::RequiredSubgroupSizeSetting::kSetToMinimumSize;
+ return SetShaderRequiredSubgroupSize(shader, subgroupSizeSetting, 0);
+}
+
+Result Pipeline::SetShaderRequiredSubgroupSizeToMaximum(const Shader* shader) {
+ const ShaderInfo::RequiredSubgroupSizeSetting subgroupSizeSetting =
+ ShaderInfo::RequiredSubgroupSizeSetting::kSetToMaximumSize;
+ return SetShaderRequiredSubgroupSize(shader, subgroupSizeSetting, 0);
+}
+
+Result Pipeline::SetShaderVaryingSubgroupSize(const Shader* shader,
+ const bool isSet) {
+ if (!shader)
+ return Result("invalid shader specified for varying subgroup size");
+
+ for (auto& info : shaders_) {
+ const auto* is = info.GetShader();
+ if (is == shader) {
+ info.SetVaryingSubgroupSize(isSet);
+ return {};
+ }
+ }
+
+ return Result("unknown shader specified for varying subgroup size: " +
+ shader->GetName());
+}
+
+Result Pipeline::SetShaderRequireFullSubgroups(const Shader* shader,
+ const bool isSet) {
+ if (!shader)
+ return Result("invalid shader specified for optimizations");
+
+ for (auto& info : shaders_) {
+ const auto* is = info.GetShader();
+ if (is == shader) {
+ info.SetRequireFullSubgroups(isSet);
+ return {};
+ }
+ }
+
+ return Result("unknown shader specified for optimizations: " +
+ shader->GetName());
+}
+
Result Pipeline::SetShaderEntryPoint(const Shader* shader,
const std::string& name) {
if (!shader)
@@ -169,16 +272,19 @@ Result Pipeline::SetShaderType(const Shader* shader, ShaderType type) {
}
Result Pipeline::Validate() const {
- size_t fb_size = fb_width_ * fb_height_;
for (const auto& attachment : color_attachments_) {
- if (attachment.buffer->ElementCount() != fb_size) {
+ if (attachment.buffer->ElementCount() !=
+ (fb_width_ << attachment.base_mip_level) *
+ (fb_height_ << attachment.base_mip_level)) {
return Result(
"shared framebuffer must have same size over all PIPELINES");
}
}
- if (depth_buffer_.buffer && depth_buffer_.buffer->ElementCount() != fb_size)
+ if (depth_stencil_buffer_.buffer &&
+ depth_stencil_buffer_.buffer->ElementCount() != fb_width_ * fb_height_) {
return Result("shared depth buffer must have same size over all PIPELINES");
+ }
for (auto& buf : GetBuffers()) {
if (buf.buffer->GetFormat() == nullptr) {
@@ -189,6 +295,7 @@ Result Pipeline::Validate() const {
if (pipeline_type_ == PipelineType::kGraphics)
return ValidateGraphics();
+
return ValidateCompute();
}
@@ -207,6 +314,26 @@ Result Pipeline::ValidateGraphics() const {
if (!found_vertex)
return Result("graphics pipeline requires a vertex shader");
+
+ for (const auto& att : color_attachments_) {
+ auto width = att.buffer->GetWidth();
+ auto height = att.buffer->GetHeight();
+ for (uint32_t level = 1; level < att.buffer->GetMipLevels(); level++) {
+ width >>= 1;
+ if (width == 0)
+ return Result("color attachment with " +
+ std::to_string(att.buffer->GetMipLevels()) +
+ " mip levels would have zero width for level " +
+ std::to_string(level));
+ height >>= 1;
+ if (height == 0)
+ return Result("color attachment with " +
+ std::to_string(att.buffer->GetMipLevels()) +
+ " mip levels would have zero height for level " +
+ std::to_string(level));
+ }
+ }
+
return {};
}
@@ -223,19 +350,23 @@ void Pipeline::UpdateFramebufferSizes() {
return;
for (auto& attachment : color_attachments_) {
- attachment.buffer->SetWidth(fb_width_);
- attachment.buffer->SetHeight(fb_height_);
- attachment.buffer->SetElementCount(size);
+ auto mip0_width = fb_width_ << attachment.base_mip_level;
+ auto mip0_height = fb_height_ << attachment.base_mip_level;
+ attachment.buffer->SetWidth(mip0_width);
+ attachment.buffer->SetHeight(mip0_height);
+ attachment.buffer->SetElementCount(mip0_width * mip0_height);
}
- if (depth_buffer_.buffer) {
- depth_buffer_.buffer->SetWidth(fb_width_);
- depth_buffer_.buffer->SetHeight(fb_height_);
- depth_buffer_.buffer->SetElementCount(size);
+ if (depth_stencil_buffer_.buffer) {
+ depth_stencil_buffer_.buffer->SetWidth(fb_width_);
+ depth_stencil_buffer_.buffer->SetHeight(fb_height_);
+ depth_stencil_buffer_.buffer->SetElementCount(size);
}
}
-Result Pipeline::AddColorAttachment(Buffer* buf, uint32_t location) {
+Result Pipeline::AddColorAttachment(Buffer* buf,
+ uint32_t location,
+ uint32_t base_mip_level) {
for (const auto& attachment : color_attachments_) {
if (attachment.location == location)
return Result("can not bind two color buffers to the same LOCATION");
@@ -247,9 +378,14 @@ Result Pipeline::AddColorAttachment(Buffer* buf, uint32_t location) {
auto& info = color_attachments_.back();
info.location = location;
- buf->SetWidth(fb_width_);
- buf->SetHeight(fb_height_);
- buf->SetElementCount(fb_width_ * fb_height_);
+ info.type = BufferType::kColor;
+ info.base_mip_level = base_mip_level;
+ auto mip0_width = fb_width_ << base_mip_level;
+ auto mip0_height = fb_height_ << base_mip_level;
+ buf->SetWidth(mip0_width);
+ buf->SetHeight(mip0_height);
+ buf->SetElementCount(mip0_width * mip0_height);
+
return {};
}
@@ -264,13 +400,13 @@ Result Pipeline::GetLocationForColorAttachment(Buffer* buf,
return Result("Unable to find requested buffer");
}
-Result Pipeline::SetDepthBuffer(Buffer* buf) {
- if (depth_buffer_.buffer != nullptr)
- return Result("can only bind one depth buffer in a PIPELINE");
- if (buf->GetBufferType() != BufferType::kDepth)
- return Result("expected a depth buffer");
+Result Pipeline::SetDepthStencilBuffer(Buffer* buf) {
+ if (depth_stencil_buffer_.buffer != nullptr)
+ return Result("can only bind one depth/stencil buffer in a PIPELINE");
+
+ depth_stencil_buffer_.buffer = buf;
+ depth_stencil_buffer_.type = BufferType::kDepthStencil;
- depth_buffer_.buffer = buf;
buf->SetWidth(fb_width_);
buf->SetHeight(fb_height_);
buf->SetElementCount(fb_width_ * fb_height_);
@@ -285,28 +421,55 @@ Result Pipeline::SetIndexBuffer(Buffer* buf) {
return {};
}
-Result Pipeline::AddVertexBuffer(Buffer* buf, uint32_t location) {
+Result Pipeline::AddVertexBuffer(Buffer* buf,
+ uint32_t location,
+ InputRate rate,
+ Format* format,
+ uint32_t offset,
+ uint32_t stride) {
for (const auto& vtex : vertex_buffers_) {
if (vtex.location == location)
return Result("can not bind two vertex buffers to the same LOCATION");
- if (vtex.buffer == buf)
- return Result("vertex buffer may only be bound to a PIPELINE once");
}
- if (buf->GetBufferType() != BufferType::kVertex)
- return Result("expected a vertex buffer");
vertex_buffers_.push_back(BufferInfo{buf});
vertex_buffers_.back().location = location;
+ vertex_buffers_.back().type = BufferType::kVertex;
+ vertex_buffers_.back().input_rate = rate;
+ vertex_buffers_.back().format = format;
+ vertex_buffers_.back().offset = offset;
+ vertex_buffers_.back().stride = stride;
return {};
}
Result Pipeline::SetPushConstantBuffer(Buffer* buf) {
if (push_constant_buffer_.buffer != nullptr)
return Result("can only bind one push constant buffer in a PIPELINE");
- if (buf->GetBufferType() != BufferType::kPushConstant)
- return Result("expected a push constant buffer");
push_constant_buffer_.buffer = buf;
+ push_constant_buffer_.type = BufferType::kPushConstant;
+ return {};
+}
+
+Result Pipeline::CreatePushConstantBuffer() {
+ if (push_constant_buffer_.buffer != nullptr)
+ return Result("can only bind one push constant buffer in a PIPELINE");
+
+ TypeParser parser;
+ auto type = parser.Parse("R8_UINT");
+ auto fmt = MakeUnique<Format>(type.get());
+
+ std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
+ buf->SetName(kGeneratedPushConstantBuffer);
+ buf->SetFormat(fmt.get());
+
+ push_constant_buffer_.buffer = buf.get();
+ push_constant_buffer_.type = BufferType::kPushConstant;
+
+ formats_.push_back(std::move(fmt));
+ types_.push_back(std::move(type));
+ opencl_push_constants_ = std::move(buf);
+
return {};
}
@@ -315,7 +478,7 @@ std::unique_ptr<Buffer> Pipeline::GenerateDefaultColorAttachmentBuffer() {
auto type = parser.Parse(kDefaultColorBufferFormat);
auto fmt = MakeUnique<Format>(type.get());
- std::unique_ptr<Buffer> buf = MakeUnique<Buffer>(BufferType::kColor);
+ std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
buf->SetName(kGeneratedColorBuffer);
buf->SetFormat(fmt.get());
@@ -324,12 +487,13 @@ std::unique_ptr<Buffer> Pipeline::GenerateDefaultColorAttachmentBuffer() {
return buf;
}
-std::unique_ptr<Buffer> Pipeline::GenerateDefaultDepthAttachmentBuffer() {
+std::unique_ptr<Buffer>
+Pipeline::GenerateDefaultDepthStencilAttachmentBuffer() {
TypeParser parser;
auto type = parser.Parse(kDefaultDepthBufferFormat);
auto fmt = MakeUnique<Format>(type.get());
- std::unique_ptr<Buffer> buf = MakeUnique<Buffer>(BufferType::kDepth);
+ std::unique_ptr<Buffer> buf = MakeUnique<Buffer>();
buf->SetName(kGeneratedDepthBuffer);
buf->SetFormat(fmt.get());
@@ -348,24 +512,25 @@ Buffer* Pipeline::GetBufferForBinding(uint32_t descriptor_set,
}
void Pipeline::AddBuffer(Buffer* buf,
+ BufferType type,
uint32_t descriptor_set,
- uint32_t binding) {
- // If this buffer binding already exists, overwrite with the new buffer.
- for (auto& info : buffers_) {
- if (info.descriptor_set == descriptor_set && info.binding == binding) {
- info.buffer = buf;
- return;
- }
- }
-
+ uint32_t binding,
+ uint32_t base_mip_level,
+ uint32_t dynamic_offset) {
buffers_.push_back(BufferInfo{buf});
auto& info = buffers_.back();
info.descriptor_set = descriptor_set;
info.binding = binding;
+ info.type = type;
+ info.base_mip_level = base_mip_level;
+ info.dynamic_offset = dynamic_offset;
+ info.sampler = buf->GetSampler();
}
-void Pipeline::AddBuffer(Buffer* buf, const std::string& arg_name) {
+void Pipeline::AddBuffer(Buffer* buf,
+ BufferType type,
+ const std::string& arg_name) {
// If this buffer binding already exists, overwrite with the new buffer.
for (auto& info : buffers_) {
if (info.arg_name == arg_name) {
@@ -377,13 +542,16 @@ void Pipeline::AddBuffer(Buffer* buf, const std::string& arg_name) {
buffers_.push_back(BufferInfo{buf});
auto& info = buffers_.back();
+ info.type = type;
info.arg_name = arg_name;
info.descriptor_set = std::numeric_limits<uint32_t>::max();
info.binding = std::numeric_limits<uint32_t>::max();
info.arg_no = std::numeric_limits<uint32_t>::max();
+ info.base_mip_level = 0;
+ info.dynamic_offset = 0;
}
-void Pipeline::AddBuffer(Buffer* buf, uint32_t arg_no) {
+void Pipeline::AddBuffer(Buffer* buf, BufferType type, uint32_t arg_no) {
// If this buffer binding already exists, overwrite with the new buffer.
for (auto& info : buffers_) {
if (info.arg_no == arg_no) {
@@ -395,15 +563,98 @@ void Pipeline::AddBuffer(Buffer* buf, uint32_t arg_no) {
buffers_.push_back(BufferInfo{buf});
auto& info = buffers_.back();
+ info.type = type;
info.arg_no = arg_no;
info.descriptor_set = std::numeric_limits<uint32_t>::max();
info.binding = std::numeric_limits<uint32_t>::max();
+ info.base_mip_level = 0;
+ info.dynamic_offset = 0;
+}
+
+void Pipeline::ClearBuffers(uint32_t descriptor_set, uint32_t binding) {
+ buffers_.erase(
+ std::remove_if(buffers_.begin(), buffers_.end(),
+ [descriptor_set, binding](BufferInfo& info) -> bool {
+ return (info.descriptor_set == descriptor_set &&
+ info.binding == binding);
+ }),
+ buffers_.end());
+}
+
+void Pipeline::AddSampler(Sampler* sampler,
+ uint32_t descriptor_set,
+ uint32_t binding) {
+ samplers_.push_back(SamplerInfo{sampler});
+
+ auto& info = samplers_.back();
+ info.descriptor_set = descriptor_set;
+ info.binding = binding;
+ info.mask = std::numeric_limits<uint32_t>::max();
+}
+
+void Pipeline::AddSampler(Sampler* sampler, const std::string& arg_name) {
+ for (auto& info : samplers_) {
+ if (info.arg_name == arg_name) {
+ info.sampler = sampler;
+ return;
+ }
+ }
+
+ samplers_.push_back(SamplerInfo{sampler});
+
+ auto& info = samplers_.back();
+ info.arg_name = arg_name;
+ info.descriptor_set = std::numeric_limits<uint32_t>::max();
+ info.binding = std::numeric_limits<uint32_t>::max();
+ info.arg_no = std::numeric_limits<uint32_t>::max();
+ info.mask = std::numeric_limits<uint32_t>::max();
+}
+
+void Pipeline::AddSampler(Sampler* sampler, uint32_t arg_no) {
+ for (auto& info : samplers_) {
+ if (info.arg_no == arg_no) {
+ info.sampler = sampler;
+ return;
+ }
+ }
+
+ samplers_.push_back(SamplerInfo{sampler});
+
+ auto& info = samplers_.back();
+ info.arg_no = arg_no;
+ info.descriptor_set = std::numeric_limits<uint32_t>::max();
+ info.binding = std::numeric_limits<uint32_t>::max();
+ info.mask = std::numeric_limits<uint32_t>::max();
+}
+
+void Pipeline::AddSampler(uint32_t mask,
+ uint32_t descriptor_set,
+ uint32_t binding) {
+ samplers_.push_back(SamplerInfo{nullptr});
+
+ auto& info = samplers_.back();
+ info.arg_name = "";
+ info.arg_no = std::numeric_limits<uint32_t>::max();
+ info.mask = mask;
+ info.descriptor_set = descriptor_set;
+ info.binding = binding;
+}
+
+void Pipeline::ClearSamplers(uint32_t descriptor_set, uint32_t binding) {
+ samplers_.erase(
+ std::remove_if(samplers_.begin(), samplers_.end(),
+ [descriptor_set, binding](SamplerInfo& info) -> bool {
+ return (info.descriptor_set == descriptor_set &&
+ info.binding == binding);
+ }),
+ samplers_.end());
}
Result Pipeline::UpdateOpenCLBufferBindings() {
if (!IsCompute() || GetShaders().empty() ||
- GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC)
+ GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
return {};
+ }
const auto& shader_info = GetShaders()[0];
const auto& descriptor_map = shader_info.GetDescriptorMap();
@@ -414,6 +665,23 @@ Result Pipeline::UpdateOpenCLBufferBindings() {
if (iter == descriptor_map.end())
return {};
+ for (auto& info : samplers_) {
+ if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
+ info.binding == std::numeric_limits<uint32_t>::max()) {
+ for (const auto& entry : iter->second) {
+ if (entry.arg_name == info.arg_name ||
+ entry.arg_ordinal == info.arg_no) {
+ if (entry.kind !=
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER) {
+ return Result("Sampler bound to non-sampler kernel arg");
+ }
+ info.descriptor_set = entry.descriptor_set;
+ info.binding = entry.binding;
+ }
+ }
+ }
+ }
+
for (auto& info : buffers_) {
if (info.descriptor_set == std::numeric_limits<uint32_t>::max() &&
info.binding == std::numeric_limits<uint32_t>::max()) {
@@ -421,29 +689,35 @@ Result Pipeline::UpdateOpenCLBufferBindings() {
if (entry.arg_name == info.arg_name ||
entry.arg_ordinal == info.arg_no) {
// Buffer storage class consistency checks.
- if (info.buffer->GetBufferType() == BufferType::kUnknown) {
+ if (info.type == BufferType::kUnknown) {
// Set the appropriate buffer type.
switch (entry.kind) {
case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO:
case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO:
- info.buffer->SetBufferType(BufferType::kUniform);
+ info.type = BufferType::kUniform;
break;
case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO:
case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD:
- info.buffer->SetBufferType(BufferType::kStorage);
+ info.type = BufferType::kStorage;
+ break;
+ case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE:
+ info.type = BufferType::kSampledImage;
+ break;
+ case Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE:
+ info.type = BufferType::kStorageImage;
break;
default:
return Result("Unhandled buffer type for OPENCL-C shader");
}
- } else if (info.buffer->GetBufferType() == BufferType::kUniform) {
+ } else if (info.type == BufferType::kUniform) {
if (entry.kind !=
Pipeline::ShaderInfo::DescriptorMapEntry::Kind::UBO &&
entry.kind !=
Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO) {
return Result("Buffer " + info.buffer->GetName() +
- " must be an uniform binding");
+ " must be a uniform binding");
}
- } else if (info.buffer->GetBufferType() == BufferType::kStorage) {
+ } else if (info.type == BufferType::kStorage) {
if (entry.kind !=
Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SSBO &&
entry.kind !=
@@ -451,6 +725,18 @@ Result Pipeline::UpdateOpenCLBufferBindings() {
return Result("Buffer " + info.buffer->GetName() +
" must be a storage binding");
}
+ } else if (info.type == BufferType::kSampledImage) {
+ if (entry.kind !=
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE) {
+ return Result("Buffer " + info.buffer->GetName() +
+ " must be a read-only image binding");
+ }
+ } else if (info.type == BufferType::kStorageImage) {
+ if (entry.kind !=
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE) {
+ return Result("Buffer " + info.buffer->GetName() +
+ " must be a write-only image binding");
+ }
} else {
return Result("Unhandled buffer type for OPENCL-C shader");
}
@@ -494,7 +780,9 @@ Result Pipeline::GenerateOpenCLPodBuffers() {
for (const auto& entry : iter->second) {
if (entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD &&
entry.kind !=
- Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO) {
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO &&
+ entry.kind != Pipeline::ShaderInfo::DescriptorMapEntry::Kind::
+ POD_PUSHCONSTANT) {
continue;
}
@@ -510,84 +798,121 @@ Result Pipeline::GenerateOpenCLPodBuffers() {
}
}
- if (descriptor_set == std::numeric_limits<uint32_t>::max() ||
- binding == std::numeric_limits<uint32_t>::max()) {
- std::string message =
- "could not find descriptor map entry for SET command: kernel " +
- shader_info.GetEntryPoint();
- if (uses_name) {
- message += ", name " + arg_info.name;
- } else {
- message += ", number " + std::to_string(arg_info.ordinal);
+ Buffer* buffer = nullptr;
+ if (kind ==
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT) {
+ if (GetPushConstantBuffer().buffer == nullptr) {
+ auto r = CreatePushConstantBuffer();
+ if (!r.IsSuccess())
+ return r;
+ }
+ buffer = GetPushConstantBuffer().buffer;
+ } else {
+ if (descriptor_set == std::numeric_limits<uint32_t>::max() ||
+ binding == std::numeric_limits<uint32_t>::max()) {
+ std::string message =
+ "could not find descriptor map entry for SET command: kernel " +
+ shader_info.GetEntryPoint();
+ if (uses_name) {
+ message += ", name " + arg_info.name;
+ } else {
+ message += ", number " + std::to_string(arg_info.ordinal);
+ }
+ return Result(message);
}
- return Result(message);
- }
- auto buf_iter = opencl_pod_buffer_map_.lower_bound(
- std::make_pair(descriptor_set, binding));
- Buffer* buffer = nullptr;
- if (buf_iter == opencl_pod_buffer_map_.end() ||
- buf_iter->first.first != descriptor_set ||
- buf_iter->first.second != binding) {
- // Ensure no buffer was previously bound for this descriptor set and
- // binding pair.
- for (const auto& buf_info : GetBuffers()) {
- if (buf_info.descriptor_set == descriptor_set &&
- buf_info.binding == binding) {
- return Result("previously bound buffer " +
- buf_info.buffer->GetName() +
- " to PoD args at descriptor set " +
- std::to_string(descriptor_set) + " binding " +
- std::to_string(binding));
+ auto buf_iter = opencl_pod_buffer_map_.lower_bound(
+ std::make_pair(descriptor_set, binding));
+ if (buf_iter == opencl_pod_buffer_map_.end() ||
+ buf_iter->first.first != descriptor_set ||
+ buf_iter->first.second != binding) {
+ // Ensure no buffer was previously bound for this descriptor set and
+ // binding pair.
+ for (const auto& buf_info : GetBuffers()) {
+ if (buf_info.descriptor_set == descriptor_set &&
+ buf_info.binding == binding) {
+ return Result("previously bound buffer " +
+ buf_info.buffer->GetName() +
+ " to PoD args at descriptor set " +
+ std::to_string(descriptor_set) + " binding " +
+ std::to_string(binding));
+ }
}
+
+ // Add a new buffer for this descriptor set and binding.
+ opencl_pod_buffers_.push_back(MakeUnique<Buffer>());
+ buffer = opencl_pod_buffers_.back().get();
+ auto buffer_type =
+ kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD
+ ? BufferType::kStorage
+ : BufferType::kUniform;
+
+ // Use an 8-bit type because all the data in the descriptor map is
+ // byte-based and it simplifies the logic for sizing below.
+ TypeParser parser;
+ auto type = parser.Parse("R8_UINT");
+ auto fmt = MakeUnique<Format>(type.get());
+ buffer->SetFormat(fmt.get());
+ formats_.push_back(std::move(fmt));
+ types_.push_back(std::move(type));
+
+ buffer->SetName(GetName() + "_pod_buffer_" +
+ std::to_string(descriptor_set) + "_" +
+ std::to_string(binding));
+ opencl_pod_buffer_map_.insert(
+ buf_iter,
+ std::make_pair(std::make_pair(descriptor_set, binding), buffer));
+ AddBuffer(buffer, buffer_type, descriptor_set, binding, 0, 0);
+ } else {
+ buffer = buf_iter->second;
}
- // Add a new buffer for this descriptor set and binding.
- opencl_pod_buffers_.push_back(MakeUnique<Buffer>());
- buffer = opencl_pod_buffers_.back().get();
- buffer->SetBufferType(
- kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD
- ? BufferType::kStorage
- : BufferType::kUniform);
-
- // Use an 8-bit type because all the data in the descriptor map is
- // byte-based and it simplifies the logic for sizing below.
- TypeParser parser;
- auto type = parser.Parse("R8_UINT");
- auto fmt = MakeUnique<Format>(type.get());
- buffer->SetFormat(fmt.get());
- formats_.push_back(std::move(fmt));
- types_.push_back(std::move(type));
-
- buffer->SetName(GetName() + "_pod_buffer_" +
- std::to_string(descriptor_set) + "_" +
- std::to_string(binding));
- opencl_pod_buffer_map_.insert(
- buf_iter,
- std::make_pair(std::make_pair(descriptor_set, binding), buffer));
- AddBuffer(buffer, descriptor_set, binding);
- } else {
- buffer = buf_iter->second;
- }
+ // Resize if necessary.
+ if (buffer->ValueCount() < offset + arg_size) {
+ buffer->SetSizeInElements(offset + arg_size);
+ }
- // Resize if necessary.
- if (buffer->ValueCount() < offset + arg_size) {
- buffer->SetSizeInElements(offset + arg_size);
+ // Check the data size.
+ if (arg_size != arg_info.fmt->SizeInBytes()) {
+ std::string message = "SET command uses incorrect data size: kernel " +
+ shader_info.GetEntryPoint();
+ if (uses_name) {
+ message += ", name " + arg_info.name;
+ } else {
+ message += ", number " + std::to_string(arg_info.ordinal);
+ }
+ return Result(message);
+ }
}
- // Check the data size.
- if (arg_size != arg_info.fmt->SizeInBytes()) {
- std::string message = "SET command uses incorrect data size: kernel " +
- shader_info.GetEntryPoint();
- if (uses_name) {
- message += ", name " + arg_info.name;
+ // Convert the argument value into bytes. Currently, only scalar arguments
+ // are supported.
+ const auto arg_byte_size = arg_info.fmt->SizeInBytes();
+ std::vector<Value> data_bytes;
+ for (uint32_t i = 0; i < arg_byte_size; ++i) {
+ Value v;
+ if (arg_info.value.IsFloat()) {
+ if (arg_byte_size == sizeof(double)) {
+ union {
+ uint64_t u;
+ double d;
+ } u;
+ u.d = arg_info.value.AsDouble();
+ v.SetIntValue((u.u >> (i * 8)) & 0xff);
+ } else {
+ union {
+ uint32_t u;
+ float f;
+ } u;
+ u.f = arg_info.value.AsFloat();
+ v.SetIntValue((u.u >> (i * 8)) & 0xff);
+ }
} else {
- message += ", number " + std::to_string(arg_info.ordinal);
+ v.SetIntValue((arg_info.value.AsUint64() >> (i * 8)) & 0xff);
}
- return Result(message);
+ data_bytes.push_back(v);
}
-
- Result r = buffer->SetDataWithOffset({arg_info.value}, offset);
+ Result r = buffer->SetDataWithOffset(data_bytes, offset);
if (!r.IsSuccess())
return r;
}
@@ -595,4 +920,118 @@ Result Pipeline::GenerateOpenCLPodBuffers() {
return {};
}
+Result Pipeline::GenerateOpenCLLiteralSamplers() {
+ for (auto& info : samplers_) {
+ if (info.sampler || info.mask == std::numeric_limits<uint32_t>::max())
+ continue;
+
+ auto literal_sampler = MakeUnique<Sampler>();
+ literal_sampler->SetName("literal." + std::to_string(info.descriptor_set) +
+ "." + std::to_string(info.binding));
+
+ // The values for addressing modes, filtering modes and coordinate
+ // normalization are all defined in the OpenCL header.
+
+ literal_sampler->SetNormalizedCoords(info.mask &
+ kOpenCLNormalizedCoordsBit);
+
+ uint32_t addressing_bits = info.mask & kOpenCLAddressModeBits;
+ AddressMode addressing_mode = AddressMode::kUnknown;
+ if (addressing_bits == kOpenCLAddressModeNone ||
+ addressing_bits == kOpenCLAddressModeClampToEdge) {
+ // CLK_ADDRESS_NONE
+ // CLK_ADDERSS_CLAMP_TO_EDGE
+ addressing_mode = AddressMode::kClampToEdge;
+ } else if (addressing_bits == kOpenCLAddressModeClamp) {
+ // CLK_ADDRESS_CLAMP
+ addressing_mode = AddressMode::kClampToBorder;
+ } else if (addressing_bits == kOpenCLAddressModeRepeat) {
+ // CLK_ADDRESS_REPEAT
+ addressing_mode = AddressMode::kRepeat;
+ } else if (addressing_bits == kOpenCLAddressModeMirroredRepeat) {
+ // CLK_ADDRESS_MIRRORED_REPEAT
+ addressing_mode = AddressMode::kMirroredRepeat;
+ }
+ literal_sampler->SetAddressModeU(addressing_mode);
+ literal_sampler->SetAddressModeV(addressing_mode);
+ // TODO(alan-baker): If this is used with an arrayed image then W should use
+ // kClampToEdge always, but this information is not currently available.
+ literal_sampler->SetAddressModeW(addressing_mode);
+
+ // Next bit is filtering mode.
+ FilterType filtering_mode = FilterType::kUnknown;
+ if (info.mask & kOpenCLFilterModeNearestBit) {
+ filtering_mode = FilterType::kNearest;
+ } else if (info.mask & kOpenCLFilterModeLinearBit) {
+ filtering_mode = FilterType::kLinear;
+ }
+ literal_sampler->SetMagFilter(filtering_mode);
+ literal_sampler->SetMinFilter(filtering_mode);
+
+ // TODO(alan-baker): OpenCL wants the border color to be based on image
+ // channel orders which aren't accessible.
+
+ // clspv never generates multiple MIPMAP levels.
+ literal_sampler->SetMinLOD(0.0f);
+ literal_sampler->SetMaxLOD(0.0f);
+
+ opencl_literal_samplers_.push_back(std::move(literal_sampler));
+ info.sampler = opencl_literal_samplers_.back().get();
+ }
+
+ return {};
+}
+
+Result Pipeline::GenerateOpenCLPushConstants() {
+ if (!IsCompute() || GetShaders().empty() ||
+ GetShaders()[0].GetShader()->GetFormat() != kShaderFormatOpenCLC) {
+ return {};
+ }
+
+ const auto& shader_info = GetShaders()[0];
+ if (shader_info.GetPushConstants().empty())
+ return {};
+
+ Result r = CreatePushConstantBuffer();
+ if (!r.IsSuccess())
+ return r;
+
+ auto* buf = GetPushConstantBuffer().buffer;
+ assert(buf);
+
+ // Determine size and contents of the push constant buffer.
+ for (const auto& pc : shader_info.GetPushConstants()) {
+ assert(pc.size % sizeof(uint32_t) == 0);
+ assert(pc.offset % sizeof(uint32_t) == 0);
+
+ if (buf->GetSizeInBytes() < pc.offset + pc.size)
+ buf->SetSizeInBytes(pc.offset + pc.size);
+
+ std::vector<uint32_t> bytes(pc.size / sizeof(uint32_t));
+ uint32_t base = 0;
+ switch (pc.type) {
+ case Pipeline::ShaderInfo::PushConstant::PushConstantType::kDimensions:
+ // All compute kernel launches are 3D.
+ bytes[base] = 3;
+ break;
+ case Pipeline::ShaderInfo::PushConstant::PushConstantType::kGlobalOffset:
+ // Global offsets are not currently supported.
+ bytes[base] = 0;
+ bytes[base + 1] = 0;
+ bytes[base + 2] = 0;
+ break;
+ case Pipeline::ShaderInfo::PushConstant::PushConstantType::kRegionOffset:
+ // Region offsets are not currently supported.
+ bytes[base] = 0;
+ bytes[base + 1] = 0;
+ bytes[base + 2] = 0;
+ break;
+ }
+ memcpy(buf->ValuePtr()->data() + pc.offset, bytes.data(),
+ bytes.size() * sizeof(uint32_t));
+ }
+
+ return {};
+}
+
} // namespace amber
diff --git a/src/pipeline.h b/src/pipeline.h
index e874613..ec38a3a 100644
--- a/src/pipeline.h
+++ b/src/pipeline.h
@@ -24,6 +24,9 @@
#include "amber/result.h"
#include "src/buffer.h"
+#include "src/command_data.h"
+#include "src/pipeline_data.h"
+#include "src/sampler.h"
#include "src/shader.h"
namespace amber {
@@ -60,6 +63,36 @@ class Pipeline {
return compile_options_;
}
+ enum class RequiredSubgroupSizeSetting : uint32_t {
+ kNotSet = 0,
+ kSetToSpecificSize,
+ kSetToMinimumSize,
+ kSetToMaximumSize
+ };
+
+ void SetRequiredSubgroupSizeSetting(RequiredSubgroupSizeSetting setting,
+ uint32_t size) {
+ required_subgroup_size_setting_ = setting;
+ required_subgroup_size_ = size;
+ }
+ RequiredSubgroupSizeSetting GetRequiredSubgroupSizeSetting() const {
+ return required_subgroup_size_setting_;
+ }
+ uint32_t GetRequiredSubgroupSize() const { return required_subgroup_size_; }
+
+ void SetVaryingSubgroupSize(const bool isSet) {
+ varying_subgroup_size_ = isSet;
+ }
+ bool GetVaryingSubgroupSize() const { return varying_subgroup_size_; }
+
+ void SetRequireFullSubgroups(const bool isSet) {
+ require_full_subgroups_ = isSet;
+ }
+ bool GetRequireFullSubgroups() const { return require_full_subgroups_; }
+
+ void SetEmitDebugInfo(const bool isSet) { emit_debug_info_ = isSet; }
+ bool GetEmitDebugInfo() const { return emit_debug_info_; }
+
void SetShader(Shader* shader) { shader_ = shader; }
const Shader* GetShader() const { return shader_; }
@@ -89,6 +122,10 @@ class Pipeline {
UBO,
POD,
POD_UBO,
+ POD_PUSHCONSTANT,
+ RO_IMAGE,
+ WO_IMAGE,
+ SAMPLER,
} kind;
uint32_t descriptor_set = 0;
@@ -107,6 +144,25 @@ class Pipeline {
return descriptor_map_;
}
+ /// Push constant information for an OpenCL-C shader.
+ struct PushConstant {
+ enum class PushConstantType {
+ kDimensions = 0,
+ kGlobalOffset,
+ kRegionOffset,
+ };
+ PushConstantType type;
+ uint32_t offset = 0;
+ uint32_t size = 0;
+ };
+
+ void AddPushConstant(PushConstant&& pc) {
+ push_constants_.emplace_back(std::move(pc));
+ }
+ const std::vector<PushConstant>& GetPushConstants() const {
+ return push_constants_;
+ }
+
private:
Shader* shader_ = nullptr;
ShaderType shader_type_;
@@ -116,7 +172,13 @@ class Pipeline {
std::map<uint32_t, uint32_t> specialization_;
std::unordered_map<std::string, std::vector<DescriptorMapEntry>>
descriptor_map_;
+ std::vector<PushConstant> push_constants_;
std::vector<std::string> compile_options_;
+ RequiredSubgroupSizeSetting required_subgroup_size_setting_;
+ uint32_t required_subgroup_size_;
+ bool varying_subgroup_size_;
+ bool require_full_subgroups_;
+ bool emit_debug_info_;
};
/// Information on a buffer attached to the pipeline.
@@ -131,12 +193,34 @@ class Pipeline {
uint32_t descriptor_set = 0;
uint32_t binding = 0;
uint32_t location = 0;
+ uint32_t base_mip_level = 0;
+ uint32_t dynamic_offset = 0;
+ std::string arg_name = "";
+ uint32_t arg_no = 0;
+ BufferType type = BufferType::kUnknown;
+ InputRate input_rate = InputRate::kVertex;
+ Format* format;
+ uint32_t offset = 0;
+ uint32_t stride = 0;
+ Sampler* sampler = nullptr;
+ };
+
+ /// Information on a sampler attached to the pipeline.
+ struct SamplerInfo {
+ SamplerInfo() = default;
+ explicit SamplerInfo(Sampler* samp) : sampler(samp) {}
+
+ Sampler* sampler = nullptr;
+ uint32_t descriptor_set = 0;
+ uint32_t binding = 0;
std::string arg_name = "";
uint32_t arg_no = 0;
+ uint32_t mask = 0;
};
static const char* kGeneratedColorBuffer;
static const char* kGeneratedDepthBuffer;
+ static const char* kGeneratedPushConstantBuffer;
explicit Pipeline(PipelineType type);
~Pipeline();
@@ -170,6 +254,15 @@ class Pipeline {
/// Returns information on all bound shaders in this pipeline.
const std::vector<ShaderInfo>& GetShaders() const { return shaders_; }
+ /// Returns the ShaderInfo for |shader| or nullptr.
+ const ShaderInfo* GetShader(Shader* shader) const {
+ for (const auto& info : shaders_) {
+ if (info.GetShader() == shader)
+ return &info;
+ }
+ return nullptr;
+ }
+
/// Sets the |type| of |shader| in the pipeline.
Result SetShaderType(const Shader* shader, ShaderType type);
/// Sets the entry point |name| for |shader| in this pipeline.
@@ -180,30 +273,60 @@ class Pipeline {
/// Sets the compile options for |shader| in this pipeline.
Result SetShaderCompileOptions(const Shader* shader,
const std::vector<std::string>& options);
+ /// Sets required subgroup size.
+ Result SetShaderRequiredSubgroupSize(const Shader* shader,
+ const uint32_t subgroupSize);
+ /// Sets required subgroup size to the device minimum supported subgroup size.
+ Result SetShaderRequiredSubgroupSizeToMinimum(const Shader* shader);
+
+ /// Sets required subgroup size to the device maximum supported subgroup size.
+ Result SetShaderRequiredSubgroupSizeToMaximum(const Shader* shader);
+ /// Sets varying subgroup size property.
+ Result SetShaderVaryingSubgroupSize(const Shader* shader, const bool isSet);
+
+ /// Sets require full subgroups property.
+ Result SetShaderRequireFullSubgroups(const Shader* shader, const bool isSet);
/// Returns a list of all colour attachments in this pipeline.
const std::vector<BufferInfo>& GetColorAttachments() const {
return color_attachments_;
}
/// Adds |buf| as a colour attachment at |location| in the pipeline.
- Result AddColorAttachment(Buffer* buf, uint32_t location);
+ /// Uses |base_mip_level| as the mip level for output.
+ Result AddColorAttachment(Buffer* buf,
+ uint32_t location,
+ uint32_t base_mip_level);
/// Retrieves the location that |buf| is bound to in the pipeline. The
/// location will be written to |loc|. An error result will be return if
/// something goes wrong.
Result GetLocationForColorAttachment(Buffer* buf, uint32_t* loc) const;
- /// Sets |buf| as the depth buffer for this pipeline.
- Result SetDepthBuffer(Buffer* buf);
- /// Returns information on the depth buffer bound to the pipeline. If no
- /// depth buffer is bound the |BufferInfo::buffer| parameter will be nullptr.
- const BufferInfo& GetDepthBuffer() const { return depth_buffer_; }
+ /// Sets |buf| as the depth/stencil buffer for this pipeline.
+ Result SetDepthStencilBuffer(Buffer* buf);
+ /// Returns information on the depth/stencil buffer bound to the pipeline. If
+ /// no depth buffer is bound the |BufferInfo::buffer| parameter will be
+ /// nullptr.
+ const BufferInfo& GetDepthStencilBuffer() const {
+ return depth_stencil_buffer_;
+ }
+
+ /// Returns pipeline data.
+ PipelineData* GetPipelineData() { return &pipeline_data_; }
/// Returns information on all vertex buffers bound to the pipeline.
const std::vector<BufferInfo>& GetVertexBuffers() const {
return vertex_buffers_;
}
- /// Adds |buf| as a vertex buffer at |location| in the pipeline.
- Result AddVertexBuffer(Buffer* buf, uint32_t location);
+ /// Adds |buf| as a vertex buffer at |location| in the pipeline using |rate|
+ /// as the input rate, |format| as vertex data format, |offset| as a starting
+ /// offset for the vertex buffer data, and |stride| for the data stride in
+ /// bytes.
+ Result AddVertexBuffer(Buffer* buf,
+ uint32_t location,
+ InputRate rate,
+ Format* format,
+ uint32_t offset,
+ uint32_t stride);
/// Binds |buf| as the index buffer for this pipeline.
Result SetIndexBuffer(Buffer* buf);
@@ -211,14 +334,39 @@ class Pipeline {
/// buffer bound.
Buffer* GetIndexBuffer() const { return index_buffer_; }
- /// Adds |buf| to the pipeline at the given |descriptor_set| and |binding|.
- void AddBuffer(Buffer* buf, uint32_t descriptor_set, uint32_t binding);
+ /// Adds |buf| of |type| to the pipeline at the given |descriptor_set|,
+ /// |binding|, |base_mip_level|, and |dynamic_offset|.
+ void AddBuffer(Buffer* buf,
+ BufferType type,
+ uint32_t descriptor_set,
+ uint32_t binding,
+ uint32_t base_mip_level,
+ uint32_t dynamic_offset);
/// Adds |buf| to the pipeline at the given |arg_name|.
- void AddBuffer(Buffer* buf, const std::string& arg_name);
+ void AddBuffer(Buffer* buf, BufferType type, const std::string& arg_name);
/// Adds |buf| to the pipeline at the given |arg_no|.
- void AddBuffer(Buffer* buf, uint32_t arg_no);
+ void AddBuffer(Buffer* buf, BufferType type, uint32_t arg_no);
/// Returns information on all buffers in this pipeline.
const std::vector<BufferInfo>& GetBuffers() const { return buffers_; }
+ /// Clears all buffer bindings for given |descriptor_set| and |binding|.
+ void ClearBuffers(uint32_t descriptor_set, uint32_t binding);
+
+ /// Adds |sampler| to the pipeline at the given |descriptor_set| and
+ /// |binding|.
+ void AddSampler(Sampler* sampler, uint32_t descriptor_set, uint32_t binding);
+ /// Adds |sampler| to the pipeline at the given |arg_name|.
+ void AddSampler(Sampler* sampler, const std::string& arg_name);
+ /// Adds |sampler| to the pieline at the given |arg_no|.
+ void AddSampler(Sampler* sampler, uint32_t arg_no);
+ /// Adds an entry for an OpenCL literal sampler.
+ void AddSampler(uint32_t sampler_mask,
+ uint32_t descriptor_set,
+ uint32_t binding);
+ /// Clears all sampler bindings for given |descriptor_set| and |binding|.
+ void ClearSamplers(uint32_t descriptor_set, uint32_t binding);
+
+ /// Returns information on all samplers in this pipeline.
+ const std::vector<SamplerInfo>& GetSamplers() const { return samplers_; }
/// Updates the descriptor set and binding info for the OpenCL-C kernel bound
/// to the pipeline. No effect for other shader formats.
@@ -238,8 +386,8 @@ class Pipeline {
/// Generates a default color attachment in B8G8R8A8_UNORM.
std::unique_ptr<Buffer> GenerateDefaultColorAttachmentBuffer();
- /// Generates a default depth attachment in D32_SFLOAT_S8_UINT format.
- std::unique_ptr<Buffer> GenerateDefaultDepthAttachmentBuffer();
+ /// Generates a default depth/stencil attachment in D32_SFLOAT_S8_UINT format.
+ std::unique_ptr<Buffer> GenerateDefaultDepthStencilAttachmentBuffer();
/// Information on values set for OpenCL-C plain-old-data args.
struct ArgSetInfo {
@@ -259,9 +407,23 @@ class Pipeline {
/// command. This should be called after all other buffers are bound.
Result GenerateOpenCLPodBuffers();
+ /// Generate the samplers necessary for OpenCL literal samplers from the
+ /// descriptor map. This should be called after all other samplers are bound.
+ Result GenerateOpenCLLiteralSamplers();
+
+ /// Generate the push constant buffers necessary for OpenCL kernels.
+ Result GenerateOpenCLPushConstants();
+
private:
void UpdateFramebufferSizes();
+ Result SetShaderRequiredSubgroupSize(
+ const Shader* shader,
+ const ShaderInfo::RequiredSubgroupSizeSetting setting,
+ const uint32_t subgroupSize);
+
+ Result CreatePushConstantBuffer();
+
Result ValidateGraphics() const;
Result ValidateCompute() const;
@@ -272,11 +434,12 @@ class Pipeline {
std::vector<BufferInfo> vertex_buffers_;
std::vector<BufferInfo> buffers_;
std::vector<std::unique_ptr<type::Type>> types_;
+ std::vector<SamplerInfo> samplers_;
std::vector<std::unique_ptr<Format>> formats_;
- BufferInfo depth_buffer_;
+ BufferInfo depth_stencil_buffer_;
BufferInfo push_constant_buffer_;
Buffer* index_buffer_ = nullptr;
-
+ PipelineData pipeline_data_;
uint32_t fb_width_ = 250;
uint32_t fb_height_ = 250;
@@ -284,6 +447,8 @@ class Pipeline {
std::vector<std::unique_ptr<Buffer>> opencl_pod_buffers_;
/// Maps (descriptor set, binding) to the buffer for that binding pair.
std::map<std::pair<uint32_t, uint32_t>, Buffer*> opencl_pod_buffer_map_;
+ std::vector<std::unique_ptr<Sampler>> opencl_literal_samplers_;
+ std::unique_ptr<Buffer> opencl_push_constants_;
};
} // namespace amber
diff --git a/src/pipeline_data.h b/src/pipeline_data.h
index e44dd4e..dc67c03 100644
--- a/src/pipeline_data.h
+++ b/src/pipeline_data.h
@@ -28,6 +28,8 @@ class PipelineData {
~PipelineData();
PipelineData(const PipelineData&);
+ PipelineData& operator=(const PipelineData&) = default;
+
void SetTopology(Topology topo) { topology_ = topo; }
Topology GetTopology() const { return topology_; }
diff --git a/src/pipeline_test.cc b/src/pipeline_test.cc
index dd5064b..e16313c 100644
--- a/src/pipeline_test.cc
+++ b/src/pipeline_test.cc
@@ -31,26 +31,26 @@ class PipelineTest : public testing::Test {
public:
void TearDown() override {
color_buffer_ = nullptr;
- depth_buffer_ = nullptr;
+ depth_stencil_buffer_ = nullptr;
}
void SetupColorAttachment(Pipeline* p, uint32_t location) {
if (!color_buffer_)
color_buffer_ = p->GenerateDefaultColorAttachmentBuffer();
- p->AddColorAttachment(color_buffer_.get(), location);
+ p->AddColorAttachment(color_buffer_.get(), location, 0);
}
- void SetupDepthAttachment(Pipeline* p) {
- if (!depth_buffer_)
- depth_buffer_ = p->GenerateDefaultDepthAttachmentBuffer();
+ void SetupDepthStencilAttachment(Pipeline* p) {
+ if (!depth_stencil_buffer_)
+ depth_stencil_buffer_ = p->GenerateDefaultDepthStencilAttachmentBuffer();
- p->SetDepthBuffer(depth_buffer_.get());
+ p->SetDepthStencilBuffer(depth_stencil_buffer_.get());
}
private:
std::unique_ptr<Buffer> color_buffer_;
- std::unique_ptr<Buffer> depth_buffer_;
+ std::unique_ptr<Buffer> depth_stencil_buffer_;
};
TEST_F(PipelineTest, AddShader) {
@@ -185,7 +185,7 @@ TEST_F(PipelineTest, SetOptimizationForInvalidShader) {
TEST_F(PipelineTest, GraphicsPipelineRequiresColorAttachment) {
Pipeline p(PipelineType::kGraphics);
- SetupDepthAttachment(&p);
+ SetupDepthStencilAttachment(&p);
Result r = p.Validate();
ASSERT_FALSE(r.IsSuccess());
@@ -199,7 +199,7 @@ TEST_F(PipelineTest, GraphicsPipelineRequiresVertexAndFragmentShader) {
Pipeline p(PipelineType::kGraphics);
SetupColorAttachment(&p, 0);
- SetupDepthAttachment(&p);
+ SetupDepthStencilAttachment(&p);
Result r = p.AddShader(&v, kShaderTypeVertex);
EXPECT_TRUE(r.IsSuccess()) << r.Error();
@@ -220,7 +220,7 @@ TEST_F(PipelineTest, GraphicsPipelineMissingVertexShader) {
Pipeline p(PipelineType::kGraphics);
SetupColorAttachment(&p, 0);
- SetupDepthAttachment(&p);
+ SetupDepthStencilAttachment(&p);
Result r = p.AddShader(&g, kShaderTypeGeometry);
EXPECT_TRUE(r.IsSuccess()) << r.Error();
@@ -238,7 +238,7 @@ TEST_F(PipelineTest, ComputePipelineRequiresComputeShader) {
Pipeline p(PipelineType::kCompute);
SetupColorAttachment(&p, 0);
- SetupDepthAttachment(&p);
+ SetupDepthStencilAttachment(&p);
Result r = p.AddShader(&c, kShaderTypeCompute);
EXPECT_TRUE(r.IsSuccess()) << r.Error();
@@ -250,7 +250,7 @@ TEST_F(PipelineTest, ComputePipelineRequiresComputeShader) {
TEST_F(PipelineTest, ComputePipelineWithoutShader) {
Pipeline p(PipelineType::kCompute);
SetupColorAttachment(&p, 0);
- SetupDepthAttachment(&p);
+ SetupDepthStencilAttachment(&p);
Result r = p.Validate();
EXPECT_FALSE(r.IsSuccess()) << r.Error();
@@ -260,9 +260,9 @@ TEST_F(PipelineTest, ComputePipelineWithoutShader) {
TEST_F(PipelineTest, PipelineBufferWithoutFormat) {
Pipeline p(PipelineType::kCompute);
- auto buf = MakeUnique<Buffer>(BufferType::kStorage);
+ auto buf = MakeUnique<Buffer>();
buf->SetName("MyBuffer");
- p.AddBuffer(buf.get(), 0, 0);
+ p.AddBuffer(buf.get(), BufferType::kStorage, 0, 0, 0, 0);
Result r = p.Validate();
EXPECT_FALSE(r.IsSuccess()) << r.Error();
@@ -343,7 +343,7 @@ TEST_F(PipelineTest, Clone) {
p.SetFramebufferHeight(600);
SetupColorAttachment(&p, 0);
- SetupDepthAttachment(&p);
+ SetupDepthStencilAttachment(&p);
Shader f(kShaderTypeFragment);
p.AddShader(&f, kShaderTypeFragment);
@@ -351,21 +351,25 @@ TEST_F(PipelineTest, Clone) {
p.AddShader(&v, kShaderTypeVertex);
p.SetShaderEntryPoint(&v, "my_main");
- auto vtex_buf = MakeUnique<Buffer>(BufferType::kVertex);
+ auto vtex_buf = MakeUnique<Buffer>();
vtex_buf->SetName("vertex_buffer");
- p.AddVertexBuffer(vtex_buf.get(), 1);
+ TypeParser parser;
+ auto int_type = parser.Parse("R32_SINT");
+ auto int_fmt = MakeUnique<Format>(int_type.get());
+ p.AddVertexBuffer(vtex_buf.get(), 1, InputRate::kVertex, int_fmt.get(), 5,
+ 10);
- auto idx_buf = MakeUnique<Buffer>(BufferType::kIndex);
+ auto idx_buf = MakeUnique<Buffer>();
idx_buf->SetName("Index Buffer");
p.SetIndexBuffer(idx_buf.get());
- auto buf1 = MakeUnique<Buffer>(BufferType::kStorage);
+ auto buf1 = MakeUnique<Buffer>();
buf1->SetName("buf1");
- p.AddBuffer(buf1.get(), 1, 1);
+ p.AddBuffer(buf1.get(), BufferType::kStorage, 1, 1, 0, 0);
- auto buf2 = MakeUnique<Buffer>(BufferType::kStorage);
+ auto buf2 = MakeUnique<Buffer>();
buf2->SetName("buf2");
- p.AddBuffer(buf2.get(), 1, 2);
+ p.AddBuffer(buf2.get(), BufferType::kStorage, 1, 2, 0, 16);
auto clone = p.Clone();
EXPECT_EQ("", clone->GetName());
@@ -385,16 +389,22 @@ TEST_F(PipelineTest, Clone) {
ASSERT_EQ(1U, vtex_buffers.size());
EXPECT_EQ(1, vtex_buffers[0].location);
EXPECT_EQ("vertex_buffer", vtex_buffers[0].buffer->GetName());
+ EXPECT_EQ(InputRate::kVertex, vtex_buffers[0].input_rate);
+ EXPECT_EQ(FormatType::kR32_SINT, vtex_buffers[0].format->GetFormatType());
+ EXPECT_EQ(5, vtex_buffers[0].offset);
+ EXPECT_EQ(10, vtex_buffers[0].stride);
auto bufs = clone->GetBuffers();
ASSERT_EQ(2U, bufs.size());
EXPECT_EQ("buf1", bufs[0].buffer->GetName());
EXPECT_EQ(1U, bufs[0].descriptor_set);
EXPECT_EQ(1U, bufs[0].binding);
+ EXPECT_EQ(0U, bufs[0].dynamic_offset);
EXPECT_EQ("buf2", bufs[1].buffer->GetName());
EXPECT_EQ(1U, bufs[1].descriptor_set);
EXPECT_EQ(2U, bufs[1].binding);
+ EXPECT_EQ(16U, bufs[1].dynamic_offset);
}
TEST_F(PipelineTest, OpenCLUpdateBindings) {
@@ -422,13 +432,13 @@ TEST_F(PipelineTest, OpenCLUpdateBindings) {
entry2.arg_ordinal = 1;
p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
- auto a_buf = MakeUnique<Buffer>(BufferType::kStorage);
+ auto a_buf = MakeUnique<Buffer>();
a_buf->SetName("buf1");
- p.AddBuffer(a_buf.get(), "arg_a");
+ p.AddBuffer(a_buf.get(), BufferType::kStorage, "arg_a");
- auto b_buf = MakeUnique<Buffer>(BufferType::kStorage);
+ auto b_buf = MakeUnique<Buffer>();
b_buf->SetName("buf2");
- p.AddBuffer(b_buf.get(), 1);
+ p.AddBuffer(b_buf.get(), BufferType::kStorage, 1);
p.UpdateOpenCLBufferBindings();
@@ -467,18 +477,68 @@ TEST_F(PipelineTest, OpenCLUpdateBindingTypeMismatch) {
entry2.arg_ordinal = 1;
p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
- auto a_buf = MakeUnique<Buffer>(BufferType::kStorage);
+ auto a_buf = MakeUnique<Buffer>();
a_buf->SetName("buf1");
- p.AddBuffer(a_buf.get(), "arg_a");
+ p.AddBuffer(a_buf.get(), BufferType::kStorage, "arg_a");
- auto b_buf = MakeUnique<Buffer>(BufferType::kUniform);
+ auto b_buf = MakeUnique<Buffer>();
b_buf->SetName("buf2");
- p.AddBuffer(b_buf.get(), 1);
+ p.AddBuffer(b_buf.get(), BufferType::kUniform, 1);
auto r = p.UpdateOpenCLBufferBindings();
ASSERT_FALSE(r.IsSuccess());
- EXPECT_EQ("Buffer buf2 must be an uniform binding", r.Error());
+ EXPECT_EQ("Buffer buf2 must be a uniform binding", r.Error());
+}
+
+TEST_F(PipelineTest, OpenCLUpdateBindingImagesAndSamplers) {
+ Pipeline p(PipelineType::kCompute);
+ p.SetName("my_pipeline");
+
+ Shader cs(kShaderTypeCompute);
+ cs.SetFormat(kShaderFormatOpenCLC);
+ p.AddShader(&cs, kShaderTypeCompute);
+ p.SetShaderEntryPoint(&cs, "my_main");
+
+ Pipeline::ShaderInfo::DescriptorMapEntry entry1;
+ entry1.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE;
+ entry1.descriptor_set = 4;
+ entry1.binding = 5;
+ entry1.arg_name = "arg_a";
+ entry1.arg_ordinal = 0;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
+
+ Pipeline::ShaderInfo::DescriptorMapEntry entry2;
+ entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE;
+ entry2.descriptor_set = 3;
+ entry2.binding = 1;
+ entry2.arg_name = "arg_b";
+ entry2.arg_ordinal = 1;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
+
+ Pipeline::ShaderInfo::DescriptorMapEntry entry3;
+ entry2.kind = Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER;
+ entry2.descriptor_set = 3;
+ entry2.binding = 2;
+ entry2.arg_name = "arg_c";
+ entry2.arg_ordinal = 2;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
+
+ auto a_buf = MakeUnique<Buffer>();
+ a_buf->SetName("buf1");
+ p.AddBuffer(a_buf.get(), BufferType::kSampledImage, "arg_a");
+
+ auto b_buf = MakeUnique<Buffer>();
+ b_buf->SetName("buf2");
+ p.AddBuffer(b_buf.get(), BufferType::kStorageImage, 1);
+
+ auto s = MakeUnique<Sampler>();
+ s->SetName("samp");
+ p.AddSampler(s.get(), "arg_c");
+
+ auto r = p.UpdateOpenCLBufferBindings();
+
+ ASSERT_TRUE(r.IsSuccess());
}
TEST_F(PipelineTest, OpenCLGeneratePodBuffers) {
@@ -739,4 +799,157 @@ TEST_F(PipelineTest, OpenCLClone) {
EXPECT_EQ(4U, b2.buffer->ValueCount());
}
+TEST_F(PipelineTest, OpenCLGenerateLiteralSamplers) {
+ Pipeline p(PipelineType::kCompute);
+ p.SetName("my_pipeline");
+
+ p.AddSampler(16, 0, 0);
+ p.AddSampler(41, 0, 1);
+
+ auto r = p.GenerateOpenCLLiteralSamplers();
+ ASSERT_TRUE(r.IsSuccess());
+ for (auto& info : p.GetSamplers()) {
+ if (info.mask == 16) {
+ EXPECT_NE(nullptr, info.sampler);
+ EXPECT_EQ(FilterType::kNearest, info.sampler->GetMagFilter());
+ EXPECT_EQ(FilterType::kNearest, info.sampler->GetMinFilter());
+ EXPECT_EQ(AddressMode::kClampToEdge, info.sampler->GetAddressModeU());
+ EXPECT_EQ(AddressMode::kClampToEdge, info.sampler->GetAddressModeV());
+ EXPECT_EQ(AddressMode::kClampToEdge, info.sampler->GetAddressModeW());
+ EXPECT_EQ(0.0f, info.sampler->GetMinLOD());
+ EXPECT_EQ(0.0f, info.sampler->GetMaxLOD());
+ } else {
+ EXPECT_NE(nullptr, info.sampler);
+ EXPECT_EQ(FilterType::kLinear, info.sampler->GetMagFilter());
+ EXPECT_EQ(FilterType::kLinear, info.sampler->GetMinFilter());
+ EXPECT_EQ(AddressMode::kMirroredRepeat, info.sampler->GetAddressModeU());
+ EXPECT_EQ(AddressMode::kMirroredRepeat, info.sampler->GetAddressModeV());
+ EXPECT_EQ(AddressMode::kMirroredRepeat, info.sampler->GetAddressModeW());
+ EXPECT_EQ(0.0f, info.sampler->GetMinLOD());
+ EXPECT_EQ(0.0f, info.sampler->GetMaxLOD());
+ }
+ }
+}
+
+TEST_F(PipelineTest, OpenCLGeneratePushConstants) {
+ Pipeline p(PipelineType::kCompute);
+ p.SetName("my_pipeline");
+
+ Shader cs(kShaderTypeCompute);
+ cs.SetFormat(kShaderFormatOpenCLC);
+ p.AddShader(&cs, kShaderTypeCompute);
+ p.SetShaderEntryPoint(&cs, "my_main");
+
+ Pipeline::ShaderInfo::PushConstant pc1;
+ pc1.type = Pipeline::ShaderInfo::PushConstant::PushConstantType::kDimensions;
+ pc1.offset = 0;
+ pc1.size = 4;
+ p.GetShaders()[0].AddPushConstant(std::move(pc1));
+
+ Pipeline::ShaderInfo::PushConstant pc2;
+ pc2.type =
+ Pipeline::ShaderInfo::PushConstant::PushConstantType::kGlobalOffset;
+ pc2.offset = 16;
+ pc2.size = 12;
+ p.GetShaders()[0].AddPushConstant(std::move(pc2));
+
+ auto r = p.GenerateOpenCLPushConstants();
+ ASSERT_TRUE(r.IsSuccess());
+
+ const auto& buf = p.GetPushConstantBuffer();
+ EXPECT_EQ(28U, buf.buffer->GetSizeInBytes());
+
+ const uint32_t* bytes = buf.buffer->GetValues<uint32_t>();
+ EXPECT_EQ(3U, bytes[0]);
+ EXPECT_EQ(0U, bytes[4]);
+ EXPECT_EQ(0U, bytes[5]);
+ EXPECT_EQ(0U, bytes[6]);
+}
+
+TEST_F(PipelineTest, OpenCLPodPushConstants) {
+ Pipeline p(PipelineType::kCompute);
+ p.SetName("my_pipeline");
+
+ Shader cs(kShaderTypeCompute);
+ cs.SetFormat(kShaderFormatOpenCLC);
+ p.AddShader(&cs, kShaderTypeCompute);
+ p.SetShaderEntryPoint(&cs, "my_main");
+
+ // Descriptor map.
+ Pipeline::ShaderInfo::DescriptorMapEntry entry1;
+ entry1.kind =
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT;
+ entry1.descriptor_set = static_cast<uint32_t>(-1);
+ entry1.binding = static_cast<uint32_t>(-1);
+ entry1.arg_name = "arg_a";
+ entry1.arg_ordinal = 0;
+ entry1.pod_offset = 0;
+ entry1.pod_arg_size = 4;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry1));
+
+ Pipeline::ShaderInfo::DescriptorMapEntry entry2;
+ entry2.kind =
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT;
+ entry2.descriptor_set = static_cast<uint32_t>(-1);
+ entry2.binding = static_cast<uint32_t>(-1);
+ entry2.arg_name = "arg_b";
+ entry2.arg_ordinal = 1;
+ entry2.pod_offset = 4;
+ entry2.pod_arg_size = 1;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry2));
+
+ Pipeline::ShaderInfo::DescriptorMapEntry entry3;
+ entry3.kind =
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_PUSHCONSTANT;
+ entry3.descriptor_set = static_cast<uint32_t>(-1);
+ entry3.binding = static_cast<uint32_t>(-1);
+ entry3.arg_name = "arg_c";
+ entry3.arg_ordinal = 2;
+ entry3.pod_offset = 8;
+ entry3.pod_arg_size = 4;
+ p.GetShaders()[0].AddDescriptorEntry("my_main", std::move(entry3));
+
+ // Set commands.
+ Value int_value;
+ int_value.SetIntValue(1);
+
+ TypeParser parser;
+ auto int_type = parser.Parse("R32_SINT");
+ auto int_fmt = MakeUnique<Format>(int_type.get());
+ auto char_type = parser.Parse("R8_SINT");
+ auto char_fmt = MakeUnique<Format>(char_type.get());
+
+ Pipeline::ArgSetInfo arg_info1;
+ arg_info1.name = "arg_a";
+ arg_info1.ordinal = 99;
+ arg_info1.fmt = int_fmt.get();
+ arg_info1.value = int_value;
+ p.SetArg(std::move(arg_info1));
+
+ Pipeline::ArgSetInfo arg_info2;
+ arg_info2.name = "arg_b";
+ arg_info2.ordinal = 99;
+ arg_info2.fmt = char_fmt.get();
+ arg_info2.value = int_value;
+ p.SetArg(std::move(arg_info2));
+
+ Pipeline::ArgSetInfo arg_info3;
+ arg_info3.name = "arg_c";
+ arg_info3.ordinal = 99;
+ arg_info3.fmt = int_fmt.get();
+ arg_info3.value = int_value;
+ p.SetArg(std::move(arg_info3));
+
+ auto r = p.GenerateOpenCLPodBuffers();
+ auto* buf = p.GetPushConstantBuffer().buffer;
+ EXPECT_NE(nullptr, buf);
+ EXPECT_EQ(12U, buf->GetSizeInBytes());
+
+ const uint32_t* ints = buf->GetValues<uint32_t>();
+ const uint8_t* bytes = buf->GetValues<uint8_t>();
+ EXPECT_EQ(1U, ints[0]);
+ EXPECT_EQ(1U, bytes[4]);
+ EXPECT_EQ(1U, ints[2]);
+}
+
} // namespace amber
diff --git a/src/platform.h b/src/platform.h
index 7685c23..fc37dd4 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -22,9 +22,14 @@ namespace amber {
#elif defined(__linux__)
#define AMBER_PLATFORM_LINUX 1
#define AMBER_PLATFORM_POSIX 1
+#elif defined(__FreeBSD__)
+#define AMBER_PLATFORM_FREEBSD 1
+#define AMBER_PLATFORM_POSIX 1
#elif defined(__APPLE__)
#define AMBER_PLATFORM_APPLE 1
#define AMBER_PLATFORM_POSIX 1
+#elif defined(__Fuchsia__)
+#define AMBER_PLATFORM_POSIX 1
#else
#error "Unknown platform."
#endif
@@ -37,6 +42,10 @@ namespace amber {
#define AMBER_PLATFORM_LINUX 0
#endif
+#if !defined(AMBER_PLATFORM_FREEBSD)
+#define AMBER_PLATFORM_FREEBSD 0
+#endif
+
#if !defined(AMBER_PLATFORM_APPLE)
#define AMBER_PLATFORM_APPLE 0
#endif
diff --git a/src/result.cc b/src/result.cc
index 39b3a85..160070d 100644
--- a/src/result.cc
+++ b/src/result.cc
@@ -14,16 +14,44 @@
#include "amber/result.h"
-namespace amber {
-
-Result::Result() : succeeded_(true) {}
-
-Result::Result(const std::string& err) : succeeded_(false), error_(err) {}
+#include <sstream>
-Result::Result(const Result&) = default;
-
-Result::~Result() = default;
+namespace amber {
-Result& Result::operator=(const Result&) = default;
+Result::Result(const std::string& err) {
+ errors_.emplace_back(err);
+}
+
+Result& Result::operator+=(const Result& res) {
+ errors_.insert(std::end(errors_), std::begin(res.errors_),
+ std::end(res.errors_));
+ return *this;
+}
+
+Result& Result::operator+=(const std::string& err) {
+ errors_.emplace_back(err);
+ return *this;
+}
+
+std::string Result::Error() const {
+ static const char* kNoErrorMsg = "<no error message given>";
+ switch (errors_.size()) {
+ case 0:
+ return "";
+ case 1:
+ return errors_[0].size() > 0 ? errors_[0] : kNoErrorMsg;
+ default: {
+ std::stringstream ss;
+ ss << errors_.size() << " errors:";
+ for (size_t i = 0; i < errors_.size(); i++) {
+ auto& err = errors_[i];
+ ss << "\n";
+ ss << " (" << (i + 1) << ") ";
+ ss << (err.size() > 0 ? err : kNoErrorMsg);
+ }
+ return ss.str();
+ }
+ }
+}
} // namespace amber
diff --git a/src/result_test.cc b/src/result_test.cc
index 7f850f9..de74837 100644
--- a/src/result_test.cc
+++ b/src/result_test.cc
@@ -31,6 +31,12 @@ TEST_F(ResultTest, ErrorWithString) {
EXPECT_EQ("Test Failed", r.Error());
}
+TEST_F(ResultTest, ErrorWithEmptyString) {
+ Result r("");
+ EXPECT_FALSE(r.IsSuccess());
+ EXPECT_EQ("<no error message given>", r.Error());
+}
+
TEST_F(ResultTest, Copy) {
Result r("Testing");
Result r2(r);
@@ -39,4 +45,71 @@ TEST_F(ResultTest, Copy) {
EXPECT_EQ("Testing", r2.Error());
}
+TEST_F(ResultTest, Append1String) {
+ Result r;
+ r += "Test Failed";
+ EXPECT_EQ("Test Failed", r.Error());
+}
+
+TEST_F(ResultTest, Append3Strings) {
+ Result r;
+ r += "Error one";
+ r += "Error two";
+ r += "Error three";
+ EXPECT_EQ(R"(3 errors:
+ (1) Error one
+ (2) Error two
+ (3) Error three)",
+ r.Error());
+}
+
+TEST_F(ResultTest, Append1SingleErrorResult) {
+ Result r;
+ r += Result("Test Failed");
+ EXPECT_EQ("Test Failed", r.Error());
+}
+
+TEST_F(ResultTest, Append3SingleErrorResults) {
+ Result r;
+ r += Result("Error one");
+ r += Result("Error two");
+ r += Result("Error three");
+ EXPECT_EQ(R"(3 errors:
+ (1) Error one
+ (2) Error two
+ (3) Error three)",
+ r.Error());
+}
+
+TEST_F(ResultTest, Append3MixedResults) {
+ Result r;
+ r += Result("Error one");
+ r += Result(); // success
+ r += Result("Error two");
+ EXPECT_EQ(R"(2 errors:
+ (1) Error one
+ (2) Error two)",
+ r.Error());
+}
+
+TEST_F(ResultTest, AppendMultipleErrorResults) {
+ Result r1;
+ r1 += Result("r1 error one");
+ r1 += Result("r1 error two");
+ r1 += Result("r1 error three");
+ r1 += Result("");
+ Result r2;
+ r2 += Result("r2 error one");
+ r2 += r1;
+ r2 += Result("r2 error two");
+ EXPECT_EQ(R"(6 errors:
+ (1) r2 error one
+ (2) r1 error one
+ (3) r1 error two
+ (4) r1 error three
+ (5) <no error message given>
+ (6) r2 error two)",
+ r2.Error());
+}
+
} // namespace amber
diff --git a/src/sampler.cc b/src/sampler.cc
new file mode 100644
index 0000000..b3fe47b
--- /dev/null
+++ b/src/sampler.cc
@@ -0,0 +1,22 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/sampler.h"
+
+namespace amber {
+
+Sampler::Sampler() = default;
+Sampler::~Sampler() = default;
+
+} // namespace amber
diff --git a/src/sampler.h b/src/sampler.h
new file mode 100644
index 0000000..c44316a
--- /dev/null
+++ b/src/sampler.h
@@ -0,0 +1,105 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_SAMPLER_H_
+#define SRC_SAMPLER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "amber/result.h"
+#include "amber/value.h"
+#include "src/format.h"
+
+namespace amber {
+
+enum class FilterType : int8_t { kUnknown = -1, kNearest = 0, kLinear = 1 };
+
+enum class AddressMode : int8_t {
+ kUnknown = -1,
+ kRepeat = 0,
+ kMirroredRepeat = 1,
+ kClampToEdge = 2,
+ kClampToBorder = 3,
+ kMirrorClampToEdge = 4
+};
+
+enum class BorderColor : int8_t {
+ kUnknown = -1,
+ kFloatTransparentBlack = 0,
+ kIntTransparentBlack = 1,
+ kFloatOpaqueBlack = 2,
+ kIntOpaqueBlack = 3,
+ kFloatOpaqueWhite = 4,
+ kIntOpaqueWhite = 5
+};
+
+class Sampler {
+ public:
+ Sampler();
+ ~Sampler();
+
+ void SetName(const std::string& name) { name_ = name; }
+ std::string GetName() const { return name_; }
+
+ void SetMagFilter(FilterType filter) { mag_filter_ = filter; }
+ FilterType GetMagFilter() const { return mag_filter_; }
+
+ void SetMinFilter(FilterType filter) { min_filter_ = filter; }
+ FilterType GetMinFilter() const { return min_filter_; }
+
+ void SetMipmapMode(FilterType filter) { mipmap_mode_ = filter; }
+ FilterType GetMipmapMode() const { return mipmap_mode_; }
+
+ void SetAddressModeU(AddressMode mode) { address_mode_u_ = mode; }
+ AddressMode GetAddressModeU() const { return address_mode_u_; }
+
+ void SetAddressModeV(AddressMode mode) { address_mode_v_ = mode; }
+ AddressMode GetAddressModeV() const { return address_mode_v_; }
+
+ void SetAddressModeW(AddressMode mode) { address_mode_w_ = mode; }
+ AddressMode GetAddressModeW() const { return address_mode_w_; }
+
+ void SetBorderColor(BorderColor color) { border_color_ = color; }
+ BorderColor GetBorderColor() const { return border_color_; }
+
+ void SetMinLOD(float min_lod) { min_lod_ = min_lod; }
+ float GetMinLOD() const { return min_lod_; }
+
+ void SetMaxLOD(float max_lod) { max_lod_ = max_lod; }
+ float GetMaxLOD() const { return max_lod_; }
+
+ void SetNormalizedCoords(bool norm) { normalized_coords_ = norm; }
+ bool GetNormalizedCoords() const { return normalized_coords_; }
+
+ private:
+ std::string name_;
+ FilterType min_filter_ = FilterType::kNearest;
+ FilterType mag_filter_ = FilterType::kNearest;
+ FilterType mipmap_mode_ = FilterType::kNearest;
+ AddressMode address_mode_u_ = AddressMode::kRepeat;
+ AddressMode address_mode_v_ = AddressMode::kRepeat;
+ AddressMode address_mode_w_ = AddressMode::kRepeat;
+ BorderColor border_color_ = BorderColor::kFloatTransparentBlack;
+ float min_lod_ = 0.0f;
+ float max_lod_ = 1.0f;
+ bool normalized_coords_ = true;
+};
+
+} // namespace amber
+
+#endif // SRC_SAMPLER_H_
diff --git a/src/script.cc b/src/script.cc
index 4fa6f45..7d7483f 100644
--- a/src/script.cc
+++ b/src/script.cc
@@ -14,28 +14,43 @@
#include "src/script.h"
+#include "src/make_unique.h"
#include "src/type_parser.h"
namespace amber {
-Script::Script() = default;
+Script::Script() : virtual_files_(MakeUnique<VirtualFileStore>()) {}
Script::~Script() = default;
std::vector<ShaderInfo> Script::GetShaderInfo() const {
std::vector<ShaderInfo> ret;
for (const auto& shader : shaders_) {
- // TODO(dsinclair): The name returned should be the
- // `pipeline_name + shader_name` instead of just shader name when we have
- // pipelines everywhere
+ bool in_pipeline = false;
+ // A given shader could be in multiple pipelines with different
+ // optimizations so make sure we check and report all pipelines.
+ for (const auto& pipeline : pipelines_) {
+ auto shader_info = pipeline->GetShader(shader.get());
+ if (shader_info) {
+ ret.emplace_back(
+ ShaderInfo{shader->GetFormat(), shader->GetType(),
+ pipeline->GetName() + "-" + shader->GetName(),
+ shader->GetData(), shader_info->GetShaderOptimizations(),
+ shader->GetTargetEnv(), shader_info->GetData()});
- // TODO(dsinclair): The optimization passes should be retrieved from the
- // pipeline and returned here instead of an empty array.
- ret.emplace_back(ShaderInfo{shader->GetFormat(),
- shader->GetType(),
- shader->GetName(),
- shader->GetData(),
- {}});
+ in_pipeline = true;
+ }
+ }
+
+ if (!in_pipeline) {
+ ret.emplace_back(ShaderInfo{shader->GetFormat(),
+ shader->GetType(),
+ shader->GetName(),
+ shader->GetData(),
+ {},
+ shader->GetTargetEnv(),
+ {}});
+ }
}
return ret;
}
@@ -88,7 +103,18 @@ bool Script::IsKnownFeature(const std::string& name) const {
name == "sparseResidencyAliased" ||
name == "variableMultisampleRate" || name == "inheritedQueries" ||
name == "VariablePointerFeatures.variablePointers" ||
- name == "VariablePointerFeatures.variablePointersStorageBuffer";
+ name == "VariablePointerFeatures.variablePointersStorageBuffer" ||
+ name == "Float16Int8Features.shaderFloat16" ||
+ name == "Float16Int8Features.shaderInt8" ||
+ name == "Storage8BitFeatures.storageBuffer8BitAccess" ||
+ name == "Storage8BitFeatures.uniformAndStorageBuffer8BitAccess" ||
+ name == "Storage8BitFeatures.storagePushConstant8" ||
+ name == "Storage16BitFeatures.storageBuffer16BitAccess" ||
+ name == "Storage16BitFeatures.uniformAndStorageBuffer16BitAccess" ||
+ name == "Storage16BitFeatures.storagePushConstant16" ||
+ name == "Storage16BitFeatures.storageInputOutput16" ||
+ name == "SubgroupSizeControl.subgroupSizeControl" ||
+ name == "SubgroupSizeControl.computeFullSubgroups";
}
type::Type* Script::ParseType(const std::string& str) {
diff --git a/src/script.h b/src/script.h
index cb28873..196b62e 100644
--- a/src/script.h
+++ b/src/script.h
@@ -15,6 +15,7 @@
#ifndef SRC_SCRIPT_H_
#define SRC_SCRIPT_H_
+#include <algorithm>
#include <cstdint>
#include <map>
#include <memory>
@@ -29,7 +30,9 @@
#include "src/engine.h"
#include "src/format.h"
#include "src/pipeline.h"
+#include "src/sampler.h"
#include "src/shader.h"
+#include "src/virtual_file_store.h"
namespace amber {
@@ -130,12 +133,41 @@ class Script : public RecipeImpl {
return buffers_;
}
+ /// Adds |sampler| to the list of known sampler. The |sampler| must have a
+ /// unique name over all samplers in the script.
+ Result AddSampler(std::unique_ptr<Sampler> sampler) {
+ if (name_to_sampler_.count(sampler->GetName()) > 0)
+ return Result("duplicate sampler name provided");
+
+ samplers_.push_back(std::move(sampler));
+ name_to_sampler_[samplers_.back()->GetName()] = samplers_.back().get();
+ return {};
+ }
+
+ /// Retrieves the sampler with |name|, |nullptr| if not found.
+ Sampler* GetSampler(const std::string& name) const {
+ auto it = name_to_sampler_.find(name);
+ return it == name_to_sampler_.end() ? nullptr : it->second;
+ }
+
+ /// Retrieves a list of all samplers.
+ const std::vector<std::unique_ptr<Sampler>>& GetSamplers() const {
+ return samplers_;
+ }
+
/// Adds |feature| to the list of features that must be supported by the
/// engine.
void AddRequiredFeature(const std::string& feature) {
engine_info_.required_features.push_back(feature);
}
+ /// Checks if |feature| is in required features
+ bool IsRequiredFeature(const std::string& feature) const {
+ return std::find(engine_info_.required_features.begin(),
+ engine_info_.required_features.end(),
+ feature) != engine_info_.required_features.end();
+ }
+
/// Adds |ext| to the list of device extensions that must be supported.
void AddRequiredDeviceExtension(const std::string& ext) {
engine_info_.required_device_extensions.push_back(ext);
@@ -199,6 +231,22 @@ class Script : public RecipeImpl {
return it == name_to_type_.end() ? nullptr : it->second.get();
}
+ // Returns the virtual file store.
+ VirtualFileStore* GetVirtualFiles() const { return virtual_files_.get(); }
+
+ /// Adds the virtual file with content |content| to the virtual file path
+ /// |path|. If there's already a virtual file with the given path, an error is
+ /// returned.
+ Result AddVirtualFile(const std::string& path, const std::string& content) {
+ return virtual_files_->Add(path, content);
+ }
+
+ /// Look up the virtual file by path. If the file was found, the content is
+ /// assigned to content.
+ Result GetVirtualFile(const std::string& path, std::string* content) const {
+ return virtual_files_->Get(path, content);
+ }
+
type::Type* ParseType(const std::string& str);
private:
@@ -212,14 +260,17 @@ class Script : public RecipeImpl {
std::string spv_env_;
std::map<std::string, Shader*> name_to_shader_;
std::map<std::string, Buffer*> name_to_buffer_;
+ std::map<std::string, Sampler*> name_to_sampler_;
std::map<std::string, Pipeline*> name_to_pipeline_;
std::map<std::string, std::unique_ptr<type::Type>> name_to_type_;
std::vector<std::unique_ptr<Shader>> shaders_;
std::vector<std::unique_ptr<Command>> commands_;
std::vector<std::unique_ptr<Buffer>> buffers_;
+ std::vector<std::unique_ptr<Sampler>> samplers_;
std::vector<std::unique_ptr<Pipeline>> pipelines_;
std::vector<std::unique_ptr<type::Type>> types_;
std::vector<std::unique_ptr<Format>> formats_;
+ std::unique_ptr<VirtualFileStore> virtual_files_;
};
} // namespace amber
diff --git a/src/script_test.cc b/src/script_test.cc
index 0450051..c3d602e 100644
--- a/src/script_test.cc
+++ b/src/script_test.cc
@@ -21,41 +21,46 @@
#include "src/shader.h"
namespace amber {
-namespace {
-
-class ScriptProxy : public Script {
- public:
- ScriptProxy() = default;
- ~ScriptProxy() override = default;
-};
-
-} // namespace
using ScriptTest = testing::Test;
TEST_F(ScriptTest, GetShaderInfo) {
- ScriptProxy sp;
+ Script s;
+
+ auto p = MakeUnique<Pipeline>(PipelineType::kGraphics);
+ p->SetName("my_pipeline");
+ auto pipeline = p.get();
+
+ Result r = s.AddPipeline(std::move(p));
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto shader = MakeUnique<Shader>(kShaderTypeVertex);
+ r = pipeline->AddShader(shader.get(), ShaderType::kShaderTypeVertex);
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+
+ pipeline->SetShaderOptimizations(shader.get(), {"opt1", "opt2"});
+
shader->SetName("Shader1");
shader->SetFormat(kShaderFormatGlsl);
shader->SetData("This is my shader data");
- sp.AddShader(std::move(shader));
+ s.AddShader(std::move(shader));
shader = MakeUnique<Shader>(kShaderTypeFragment);
shader->SetName("Shader2");
shader->SetFormat(kShaderFormatSpirvAsm);
shader->SetData("More shader data");
- sp.AddShader(std::move(shader));
+ s.AddShader(std::move(shader));
- auto info = sp.GetShaderInfo();
+ auto info = s.GetShaderInfo();
ASSERT_EQ(2U, info.size());
- EXPECT_EQ("Shader1", info[0].shader_name);
+ EXPECT_EQ("my_pipeline-Shader1", info[0].shader_name);
EXPECT_EQ(kShaderFormatGlsl, info[0].format);
EXPECT_EQ(kShaderTypeVertex, info[0].type);
EXPECT_EQ("This is my shader data", info[0].shader_source);
- EXPECT_TRUE(info[0].optimizations.empty());
+ ASSERT_EQ(2u, info[0].optimizations.size());
+ EXPECT_EQ("opt1", info[0].optimizations[0]);
+ EXPECT_EQ("opt2", info[0].optimizations[1]);
EXPECT_EQ("Shader2", info[1].shader_name);
EXPECT_EQ(kShaderFormatSpirvAsm, info[1].format);
@@ -65,8 +70,8 @@ TEST_F(ScriptTest, GetShaderInfo) {
}
TEST_F(ScriptTest, GetShaderInfoNoShaders) {
- ScriptProxy sp;
- auto info = sp.GetShaderInfo();
+ Script s;
+ auto info = s.GetShaderInfo();
EXPECT_TRUE(info.empty());
}
@@ -216,7 +221,7 @@ TEST_F(ScriptTest, GetPipelines) {
}
TEST_F(ScriptTest, AddBuffer) {
- auto buffer = MakeUnique<Buffer>(BufferType::kStorage);
+ auto buffer = MakeUnique<Buffer>();
buffer->SetName("my_buffer");
Script s;
@@ -225,14 +230,14 @@ TEST_F(ScriptTest, AddBuffer) {
}
TEST_F(ScriptTest, AddDuplicateBuffer) {
- auto buffer1 = MakeUnique<Buffer>(BufferType::kStorage);
+ auto buffer1 = MakeUnique<Buffer>();
buffer1->SetName("my_buffer");
Script s;
Result r = s.AddBuffer(std::move(buffer1));
ASSERT_TRUE(r.IsSuccess()) << r.Error();
- auto buffer2 = MakeUnique<Buffer>(BufferType::kUniform);
+ auto buffer2 = MakeUnique<Buffer>();
buffer2->SetName("my_buffer");
r = s.AddBuffer(std::move(buffer2));
@@ -241,7 +246,7 @@ TEST_F(ScriptTest, AddDuplicateBuffer) {
}
TEST_F(ScriptTest, GetBuffer) {
- auto buffer = MakeUnique<Buffer>(BufferType::kStorage);
+ auto buffer = MakeUnique<Buffer>();
buffer->SetName("my_buffer");
const auto* ptr = buffer.get();
@@ -265,7 +270,7 @@ TEST_F(ScriptTest, GetBuffersEmpty) {
}
TEST_F(ScriptTest, GetBuffers) {
- auto buffer1 = MakeUnique<Buffer>(BufferType::kStorage);
+ auto buffer1 = MakeUnique<Buffer>();
buffer1->SetName("my_buffer1");
const auto* ptr1 = buffer1.get();
@@ -274,7 +279,7 @@ TEST_F(ScriptTest, GetBuffers) {
Result r = s.AddBuffer(std::move(buffer1));
ASSERT_TRUE(r.IsSuccess()) << r.Error();
- auto buffer2 = MakeUnique<Buffer>(BufferType::kUniform);
+ auto buffer2 = MakeUnique<Buffer>();
buffer2->SetName("my_buffer2");
const auto* ptr2 = buffer2.get();
diff --git a/src/shader.h b/src/shader.h
index 653924b..29d6e5e 100644
--- a/src/shader.h
+++ b/src/shader.h
@@ -33,12 +33,18 @@ class Shader {
void SetName(const std::string& name) { name_ = name; }
const std::string& GetName() const { return name_; }
+ void SetFilePath(const std::string& path) { file_path_ = path; }
+ const std::string& GetFilePath() const { return file_path_; }
+
void SetFormat(ShaderFormat fmt) { shader_format_ = fmt; }
ShaderFormat GetFormat() const { return shader_format_; }
- /// Sets the compiled shader to |data|.
+ void SetTargetEnv(const std::string& env) { target_env_ = env; }
+ const std::string& GetTargetEnv() const { return target_env_; }
+
+ /// Sets the source shader to |data|.
void SetData(const std::string& data) { data_ = data; }
- /// Returns the compiled shader source.
+ /// Returns the source shader source.
const std::string& GetData() const { return data_; }
private:
@@ -46,6 +52,8 @@ class Shader {
ShaderFormat shader_format_;
std::string data_;
std::string name_;
+ std::string file_path_;
+ std::string target_env_;
};
} // namespace amber
diff --git a/src/shader_compiler.cc b/src/shader_compiler.cc
index 0fd4cf3..c0084d8 100644
--- a/src/shader_compiler.cc
+++ b/src/shader_compiler.cc
@@ -39,7 +39,7 @@
#include "src/dxc_helper.h"
#endif // AMBER_ENABLE_DXC
-#ifdef AMBER_ENABLE_CLSPV
+#if AMBER_ENABLE_CLSPV
#include "src/clspv_helper.h"
#endif // AMBER_ENABLE_CLSPV
@@ -48,16 +48,29 @@ namespace amber {
ShaderCompiler::ShaderCompiler() = default;
ShaderCompiler::ShaderCompiler(const std::string& env,
- bool disable_spirv_validation)
- : spv_env_(env), disable_spirv_validation_(disable_spirv_validation) {}
+ bool disable_spirv_validation,
+ VirtualFileStore* virtual_files)
+ : spv_env_(env),
+ disable_spirv_validation_(disable_spirv_validation),
+ virtual_files_(virtual_files) {
+ // Do not warn about virtual_files_ not being used.
+ // This is conditionally used based on preprocessor defines.
+ (void)virtual_files_;
+}
ShaderCompiler::~ShaderCompiler() = default;
std::pair<Result, std::vector<uint32_t>> ShaderCompiler::Compile(
+ Pipeline* pipeline,
Pipeline::ShaderInfo* shader_info,
const ShaderMap& shader_map) const {
const auto shader = shader_info->GetShader();
- auto it = shader_map.find(shader->GetName());
+ std::string key = shader->GetName();
+ const std::string pipeline_name = pipeline->GetName();
+ if (pipeline_name != "") {
+ key = pipeline_name + "-" + key;
+ }
+ auto it = shader_map.find(key);
if (it != shader_map.end()) {
#if AMBER_ENABLE_CLSPV
if (shader->GetFormat() == kShaderFormatOpenCLC) {
@@ -120,7 +133,7 @@ std::pair<Result, std::vector<uint32_t>> ShaderCompiler::Compile(
#if AMBER_ENABLE_DXC
} else if (shader->GetFormat() == kShaderFormatHlsl) {
- Result r = CompileHlsl(shader, &results);
+ Result r = CompileHlsl(shader, shader_info->GetEmitDebugInfo(), &results);
if (!r.IsSuccess())
return {r, {}};
#endif // AMBER_ENABLE_DXC
@@ -135,7 +148,7 @@ std::pair<Result, std::vector<uint32_t>> ShaderCompiler::Compile(
#if AMBER_ENABLE_CLSPV
} else if (shader->GetFormat() == kShaderFormatOpenCLC) {
- Result r = CompileOpenCLC(shader_info, &results);
+ Result r = CompileOpenCLC(shader_info, pipeline, target_env, &results);
if (!r.IsSuccess())
return {r, {}};
#endif // AMBER_ENABLE_CLSPV
@@ -249,6 +262,7 @@ Result ShaderCompiler::CompileGlsl(const Shader*,
#if AMBER_ENABLE_DXC
Result ShaderCompiler::CompileHlsl(const Shader* shader,
+ bool emit_debug_info,
std::vector<uint32_t>* result) const {
std::string target;
if (shader->GetType() == kShaderTypeCompute)
@@ -263,10 +277,12 @@ Result ShaderCompiler::CompileHlsl(const Shader* shader,
return Result("Unknown shader type");
return dxchelper::Compile(shader->GetData(), "main", target, spv_env_,
- result);
+ shader->GetFilePath(), virtual_files_,
+ emit_debug_info, result);
}
#else
Result ShaderCompiler::CompileHlsl(const Shader*,
+ bool,
std::vector<uint32_t>*) const {
return {};
}
@@ -274,13 +290,10 @@ Result ShaderCompiler::CompileHlsl(const Shader*,
#if AMBER_ENABLE_CLSPV
Result ShaderCompiler::CompileOpenCLC(Pipeline::ShaderInfo* shader_info,
+ Pipeline* pipeline,
+ spv_target_env env,
std::vector<uint32_t>* result) const {
- return clspvhelper::Compile(shader_info, result);
-}
-#else
-Result ShaderCompiler::CompileOpenCLC(Pipeline::ShaderInfo*,
- std::vector<uint32_t>*) const {
- return {};
+ return clspvhelper::Compile(shader_info, pipeline, env, result);
}
#endif // AMBER_ENABLE_CLSPV
@@ -291,12 +304,14 @@ const uint32_t kVulkan = 0;
// Values for versions of the Vulkan API, used in the Shaderc API
const uint32_t kVulkan_1_0 = (uint32_t(1) << 22);
const uint32_t kVulkan_1_1 = (uint32_t(1) << 22) | (1 << 12);
+const uint32_t kVulkan_1_2 = (uint32_t(1) << 22) | (2 << 12);
// Values for SPIR-V versions, used in the Shaderc API
const uint32_t kSpv_1_0 = uint32_t(0x10000);
const uint32_t kSpv_1_1 = uint32_t(0x10100);
const uint32_t kSpv_1_2 = uint32_t(0x10200);
const uint32_t kSpv_1_3 = uint32_t(0x10300);
const uint32_t kSpv_1_4 = uint32_t(0x10400);
+const uint32_t kSpv_1_5 = uint32_t(0x10500);
#if AMBER_ENABLE_SHADERC
// Check that we have the right values, from the original definitions
@@ -307,6 +322,8 @@ static_assert(kVulkan_1_0 == shaderc_env_version_vulkan_1_0,
"enum vulkan1.0 value mismatch");
static_assert(kVulkan_1_1 == shaderc_env_version_vulkan_1_1,
"enum vulkan1.1 value mismatch");
+static_assert(kVulkan_1_2 == shaderc_env_version_vulkan_1_2,
+ "enum vulkan1.2 value mismatch");
static_assert(kSpv_1_0 == shaderc_spirv_version_1_0,
"enum spv1.0 value mismatch");
static_assert(kSpv_1_1 == shaderc_spirv_version_1_1,
@@ -317,6 +334,8 @@ static_assert(kSpv_1_3 == shaderc_spirv_version_1_3,
"enum spv1.3 value mismatch");
static_assert(kSpv_1_4 == shaderc_spirv_version_1_4,
"enum spv1.4 value mismatch");
+static_assert(kSpv_1_5 == shaderc_spirv_version_1_5,
+ "enum spv1.5 value mismatch");
#endif
} // namespace
@@ -345,7 +364,12 @@ Result ParseSpvEnv(const std::string& spv_env,
} else if (spv_env == "spv1.3") {
values = {kVulkan, kVulkan_1_1, kSpv_1_3};
} else if (spv_env == "spv1.4") {
- values = {kVulkan, kVulkan_1_1, kSpv_1_4};
+ // Vulkan 1.2 requires support for SPIR-V 1.4,
+ // but Vulkan 1.1 permits it with an extension.
+ // So Vulkan 1.2 is the right answer here.
+ values = {kVulkan, kVulkan_1_2, kSpv_1_4};
+ } else if (spv_env == "spv1.5") {
+ values = {kVulkan, kVulkan_1_2, kSpv_1_5};
} else if (spv_env == "vulkan1.0") {
values = {kVulkan, kVulkan_1_0, kSpv_1_0};
} else if (spv_env == "vulkan1.1") {
@@ -353,6 +377,8 @@ Result ParseSpvEnv(const std::string& spv_env,
values = {kVulkan, kVulkan_1_1, kSpv_1_3};
} else if (spv_env == "vulkan1.1spv1.4") {
values = {kVulkan, kVulkan_1_1, kSpv_1_4};
+ } else if (spv_env == "vulkan1.2") {
+ values = {kVulkan, kVulkan_1_2, kSpv_1_5};
} else {
return Result(std::string("Unrecognized environment ") + spv_env);
}
diff --git a/src/shader_compiler.h b/src/shader_compiler.h
index bf5fc49..5470069 100644
--- a/src/shader_compiler.h
+++ b/src/shader_compiler.h
@@ -21,8 +21,12 @@
#include "amber/amber.h"
#include "amber/result.h"
+#if AMBER_ENABLE_CLSPV
+#include "spirv-tools/libspirv.h"
+#endif
#include "src/pipeline.h"
#include "src/shader.h"
+#include "src/virtual_file_store.h"
namespace amber {
@@ -30,7 +34,9 @@ namespace amber {
class ShaderCompiler {
public:
ShaderCompiler();
- ShaderCompiler(const std::string& env, bool disable_spirv_validation);
+ ShaderCompiler(const std::string& env,
+ bool disable_spirv_validation,
+ VirtualFileStore* virtual_files);
~ShaderCompiler();
/// Returns a result code and a compilation of the given shader.
@@ -41,19 +47,32 @@ class ShaderCompiler {
/// If |shader_info| specifies shader optimizations to run and there is no
/// entry in |shader_map| for that shader, then the SPIRV-Tools optimizer will
/// be invoked to produce the shader binary.
+ ///
+ /// |pipeline| is the pipeline containing |shader_info|. The name is used to
+ /// prefix shaders used in multiple pipelines with different optimization
+ /// flags. The pipeline is used in OPENCL-C compiles to create the literal
+ /// sampler bindings.
std::pair<Result, std::vector<uint32_t>> Compile(
+ Pipeline* pipeline,
Pipeline::ShaderInfo* shader_info,
const ShaderMap& shader_map) const;
private:
Result ParseHex(const std::string& data, std::vector<uint32_t>* result) const;
Result CompileGlsl(const Shader* shader, std::vector<uint32_t>* result) const;
- Result CompileHlsl(const Shader* shader, std::vector<uint32_t>* result) const;
+ Result CompileHlsl(const Shader* shader,
+ bool emit_debug_info,
+ std::vector<uint32_t>* result) const;
+#if AMBER_ENABLE_CLSPV
Result CompileOpenCLC(Pipeline::ShaderInfo* shader,
+ Pipeline* pipeline,
+ spv_target_env env,
std::vector<uint32_t>* result) const;
+#endif
std::string spv_env_;
bool disable_spirv_validation_ = false;
+ VirtualFileStore* virtual_files_ = nullptr;
};
// Parses the SPIR-V environment string, and returns the corresponding
diff --git a/src/shader_compiler_test.cc b/src/shader_compiler_test.cc
index 5816d42..db8f43e 100644
--- a/src/shader_compiler_test.cc
+++ b/src/shader_compiler_test.cc
@@ -19,9 +19,10 @@
#include <vector>
#include "gtest/gtest.h"
+#include "src/sampler.h"
#include "src/shader_data.h"
#if AMBER_ENABLE_SHADERC
-#include "shaderc/env.h"
+#include "shaderc/shaderc.hpp"
#endif
namespace amber {
@@ -107,10 +108,11 @@ void main() {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, ShaderMap());
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, ShaderMap());
ASSERT_TRUE(r.IsSuccess()) << r.Error();
EXPECT_FALSE(binary.empty());
- EXPECT_EQ(0x07230203, binary[0]); // Verify SPIR-V header present.
+ EXPECT_EQ(0x07230203u, binary[0]); // Verify SPIR-V header present.
}
#endif // AMBER_ENABLE_SHADERC
@@ -125,10 +127,11 @@ TEST_F(ShaderCompilerTest, CompilesSpirvAsm) {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, ShaderMap());
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, ShaderMap());
ASSERT_TRUE(r.IsSuccess());
EXPECT_FALSE(binary.empty());
- EXPECT_EQ(0x07230203, binary[0]); // Verify SPIR-V header present.
+ EXPECT_EQ(0x07230203u, binary[0]); // Verify SPIR-V header present.
}
TEST_F(ShaderCompilerTest, InvalidSpirvHex) {
@@ -144,7 +147,8 @@ TEST_F(ShaderCompilerTest, InvalidSpirvHex) {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, ShaderMap());
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, ShaderMap());
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("Invalid shader: error: line 0: Invalid SPIR-V magic number.\n",
r.Error());
@@ -160,7 +164,8 @@ TEST_F(ShaderCompilerTest, InvalidHex) {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, ShaderMap());
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, ShaderMap());
ASSERT_FALSE(r.IsSuccess());
EXPECT_EQ("Invalid shader: error: line 0: Invalid SPIR-V magic number.\n",
r.Error());
@@ -211,11 +216,12 @@ OpFunctionEnd
ShaderCompiler sc;
Result r;
std::vector<uint32_t> unopt_binary;
- std::tie(r, unopt_binary) = sc.Compile(&unoptimized, ShaderMap());
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, unopt_binary) = sc.Compile(&pipeline, &unoptimized, ShaderMap());
ASSERT_TRUE(r.IsSuccess());
std::vector<uint32_t> opt_binary;
- std::tie(r, opt_binary) = sc.Compile(&optimized, ShaderMap());
+ std::tie(r, opt_binary) = sc.Compile(&pipeline, &optimized, ShaderMap());
ASSERT_TRUE(r.IsSuccess());
EXPECT_NE(opt_binary.size(), unopt_binary.size());
}
@@ -231,10 +237,11 @@ TEST_F(ShaderCompilerTest, CompilesSpirvHex) {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, ShaderMap());
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, ShaderMap());
ASSERT_TRUE(r.IsSuccess());
EXPECT_FALSE(binary.empty());
- EXPECT_EQ(0x07230203, binary[0]); // Verify SPIR-V header present.
+ EXPECT_EQ(0x07230203u, binary[0]); // Verify SPIR-V header present.
}
TEST_F(ShaderCompilerTest, FailsOnInvalidShader) {
@@ -249,7 +256,8 @@ TEST_F(ShaderCompilerTest, FailsOnInvalidShader) {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, ShaderMap());
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, ShaderMap());
ASSERT_FALSE(r.IsSuccess());
}
@@ -259,6 +267,7 @@ TEST_F(ShaderCompilerTest, ReturnsCachedShader) {
std::string contents = "Just Random\nText()\nThat doesn't work.";
static const char kShaderName[] = "CachedShader";
+ static const char kShaderNameWithPipeline[] = "pipeline-CachedShader";
Shader shader(kShaderTypeVertex);
shader.SetName(kShaderName);
shader.SetFormat(kShaderFormatGlsl);
@@ -267,13 +276,15 @@ TEST_F(ShaderCompilerTest, ReturnsCachedShader) {
std::vector<uint32_t> src_bytes = {1, 2, 3, 4, 5};
ShaderMap map;
- map[kShaderName] = src_bytes;
+ map[kShaderNameWithPipeline] = src_bytes;
ShaderCompiler sc;
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, map);
+ Pipeline pipeline(PipelineType::kCompute);
+ pipeline.SetName("pipeline");
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, map);
ASSERT_TRUE(r.IsSuccess()) << r.Error();
ASSERT_EQ(binary.size(), src_bytes.size());
@@ -297,10 +308,11 @@ kernel void TestShader(global int* in, global int* out) {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, ShaderMap());
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, ShaderMap());
ASSERT_TRUE(r.IsSuccess());
EXPECT_FALSE(binary.empty());
- EXPECT_EQ(0x07230203, binary[0]); // Verify SPIR-V header present.
+ EXPECT_EQ(0x07230203u, binary[0]); // Verify SPIR-V header present.
}
TEST_F(ShaderCompilerTest, ClspvDisallowCaching) {
@@ -323,7 +335,8 @@ kernel void TestShader(global int* in, global int* out) {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info, map);
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info, map);
ASSERT_FALSE(r.IsSuccess());
EXPECT_TRUE(binary.empty());
}
@@ -343,10 +356,12 @@ kernel void TestShader(global int* in, global int* out, int m, int b) {
Result r;
std::vector<uint32_t> binary;
Pipeline::ShaderInfo shader_info1(&shader, kShaderTypeCompute);
- std::tie(r, binary) = sc.Compile(&shader_info1, ShaderMap());
+ shader_info1.SetCompileOptions({"-cluster-pod-kernel-args=0"});
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info1, ShaderMap());
ASSERT_TRUE(r.IsSuccess());
EXPECT_FALSE(binary.empty());
- EXPECT_EQ(0x07230203, binary[0]); // Verify SPIR-V header present.
+ EXPECT_EQ(0x07230203u, binary[0]); // Verify SPIR-V header present.
auto iter = shader_info1.GetDescriptorMap().find("TestShader");
ASSERT_NE(iter, shader_info1.GetDescriptorMap().end());
uint32_t max_binding = 0;
@@ -357,15 +372,15 @@ kernel void TestShader(global int* in, global int* out, int m, int b) {
entry.kind == Pipeline::ShaderInfo::DescriptorMapEntry::Kind::POD_UBO;
}
EXPECT_EQ(3U, max_binding);
- EXPECT_FALSE(has_pod_ubo);
+ EXPECT_TRUE(has_pod_ubo);
binary.clear();
Pipeline::ShaderInfo shader_info2(&shader, kShaderTypeCompute);
shader_info2.SetCompileOptions({"-cluster-pod-kernel-args", "-pod-ubo"});
- std::tie(r, binary) = sc.Compile(&shader_info2, ShaderMap());
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info2, ShaderMap());
ASSERT_TRUE(r.IsSuccess());
EXPECT_FALSE(binary.empty());
- EXPECT_EQ(0x07230203, binary[0]); // Verify SPIR-V header present.
+ EXPECT_EQ(0x07230203u, binary[0]); // Verify SPIR-V header present.
iter = shader_info2.GetDescriptorMap().find("TestShader");
ASSERT_NE(iter, shader_info2.GetDescriptorMap().end());
max_binding = 0;
@@ -378,6 +393,90 @@ kernel void TestShader(global int* in, global int* out, int m, int b) {
EXPECT_EQ(2U, max_binding);
EXPECT_TRUE(has_pod_ubo);
}
+
+TEST_F(ShaderCompilerTest, ClspvImagesAndSamplers) {
+ std::string data = R"(
+kernel void TestShader(read_only image2d_t ro_image, write_only image2d_t wo_image, sampler_t sampler) {
+ int2 coord = (int2)(0, 0);
+ float4 texel = read_imagef(ro_image, sampler, coord);
+ write_imagef(wo_image, coord, texel);
+}
+)";
+
+ Shader shader(kShaderTypeCompute);
+ shader.SetName("TestShader");
+ shader.SetFormat(kShaderFormatOpenCLC);
+ shader.SetData(data);
+
+ ShaderCompiler sc;
+ Result r;
+ std::vector<uint32_t> binary;
+ Pipeline::ShaderInfo shader_info1(&shader, kShaderTypeCompute);
+ Pipeline pipeline(PipelineType::kCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info1, ShaderMap());
+ ASSERT_TRUE(r.IsSuccess());
+ EXPECT_FALSE(binary.empty());
+ EXPECT_EQ(0x07230203u, binary[0]); // Verify SPIR-V header present.
+ auto iter = shader_info1.GetDescriptorMap().find("TestShader");
+ for (const auto& entry : iter->second) {
+ if (entry.binding == 0) {
+ EXPECT_EQ(entry.kind,
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::RO_IMAGE);
+ } else if (entry.binding == 1) {
+ EXPECT_EQ(entry.kind,
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::WO_IMAGE);
+ } else if (entry.binding == 2) {
+ EXPECT_EQ(entry.kind,
+ Pipeline::ShaderInfo::DescriptorMapEntry::Kind::SAMPLER);
+ } else {
+ ASSERT_TRUE(false);
+ }
+ }
+}
+
+TEST_F(ShaderCompilerTest, ClspvLiteralSamplers) {
+ std::string data = R"(
+const sampler_t s1 = CLK_ADDRESS_NONE | CLK_FILTER_NEAREST | CLK_NORMALIZED_COORDS_FALSE;
+const sampler_t s2 = CLK_ADDRESS_MIRRORED_REPEAT | CLK_FILTER_LINEAR | CLK_NORMALIZED_COORDS_TRUE;
+
+kernel void foo(read_only image2d_t im, global float4* out) {
+ out[0] = read_imagef(im, s1, (int2)(0));
+ out[1] = read_imagef(im, s2, (int2)(0));
+}
+)";
+
+ Pipeline pipeline(PipelineType::kCompute);
+ pipeline.SetName("pipe");
+ Shader shader(kShaderTypeCompute);
+ shader.SetName("foo");
+ shader.SetFormat(kShaderFormatOpenCLC);
+ shader.SetData(data);
+
+ ShaderCompiler sc;
+ Result r;
+ std::vector<uint32_t> binary;
+ Pipeline::ShaderInfo shader_info1(&shader, kShaderTypeCompute);
+ std::tie(r, binary) = sc.Compile(&pipeline, &shader_info1, ShaderMap());
+ ASSERT_TRUE(r.IsSuccess());
+ EXPECT_FALSE(binary.empty());
+ EXPECT_EQ(0x07230203u, binary[0]); // Verify SPIR-V header present.
+ bool found_s1 = false;
+ bool found_s2 = false;
+ EXPECT_EQ(0, pipeline.GetSamplers()[0].descriptor_set);
+ EXPECT_EQ(0, pipeline.GetSamplers()[1].descriptor_set);
+ EXPECT_NE(pipeline.GetSamplers()[0].binding,
+ pipeline.GetSamplers()[1].binding);
+ if (pipeline.GetSamplers()[0].mask == 0x10 ||
+ pipeline.GetSamplers()[1].mask == 0x10) {
+ found_s1 = true;
+ }
+ if (pipeline.GetSamplers()[0].mask == (0x1 | 0x8 | 0x20) ||
+ pipeline.GetSamplers()[1].mask == (0x1 | 0x8 | 0x20)) {
+ found_s2 = true;
+ }
+ EXPECT_EQ(true, found_s1);
+ EXPECT_EQ(true, found_s2);
+}
#endif // AMBER_ENABLE_CLSPV
struct ParseSpvEnvCase {
@@ -410,11 +509,13 @@ TEST_P(ParseSpvEnvTest, Samples) {
const uint32_t vulkan = 0;
const uint32_t vulkan_1_0 = ((uint32_t(1) << 22));
const uint32_t vulkan_1_1 = ((uint32_t(1) << 22) | (1 << 12));
+const uint32_t vulkan_1_2 = ((uint32_t(1) << 22) | (2 << 12));
const uint32_t spv_1_0 = uint32_t(0x10000);
const uint32_t spv_1_1 = uint32_t(0x10100);
const uint32_t spv_1_2 = uint32_t(0x10200);
const uint32_t spv_1_3 = uint32_t(0x10300);
const uint32_t spv_1_4 = uint32_t(0x10400);
+const uint32_t spv_1_5 = uint32_t(0x10500);
INSTANTIATE_TEST_SUITE_P(ParseSpvEnvFailures,
ParseSpvEnvTest,
@@ -424,7 +525,7 @@ INSTANTIATE_TEST_SUITE_P(ParseSpvEnvFailures,
{"spv99.9", false, 0u, 0u, 0u},
{"spv1.0.1", false, 0u, 0u, 0u},
{"spv1.0.1", false, 0u, 0u, 0u},
- {"spv1.5", false, 0u, 0u, 0u},
+ {"spv1.9", false, 0u, 0u, 0u},
{"vulkan99", false, 0u, 0u, 0u},
{"vulkan99.9", false, 0u, 0u, 0u},
}));
@@ -437,11 +538,13 @@ INSTANTIATE_TEST_SUITE_P(ParseSpvEnvSuccesses,
{"spv1.1", true, vulkan, vulkan_1_1, spv_1_1},
{"spv1.2", true, vulkan, vulkan_1_1, spv_1_2},
{"spv1.3", true, vulkan, vulkan_1_1, spv_1_3},
- {"spv1.4", true, vulkan, vulkan_1_1, spv_1_4},
+ {"spv1.4", true, vulkan, vulkan_1_2, spv_1_4},
+ {"spv1.5", true, vulkan, vulkan_1_2, spv_1_5},
{"vulkan1.0", true, vulkan, vulkan_1_0, spv_1_0},
{"vulkan1.1", true, vulkan, vulkan_1_1, spv_1_3},
{"vulkan1.1spv1.4", true, vulkan, vulkan_1_1,
spv_1_4},
+ {"vulkan1.2", true, vulkan, vulkan_1_2, spv_1_5},
}));
} // namespace amber
diff --git a/src/tokenizer.cc b/src/tokenizer.cc
index 54bf88c..4475aab 100644
--- a/src/tokenizer.cc
+++ b/src/tokenizer.cc
@@ -31,7 +31,7 @@ Result Token::ConvertToDouble() {
if (IsDouble())
return {};
- if (IsString() || IsEOL() || IsEOS())
+ if (IsIdentifier() || IsEOL() || IsEOS())
return Result("Invalid conversion to double");
if (IsInteger()) {
@@ -74,11 +74,91 @@ std::unique_ptr<Token> Tokenizer::NextToken() {
return MakeUnique<Token>(TokenType::kEOL);
}
+ if (data_[current_position_] == '"') {
+ current_position_++; // Skip opening quote
+ std::string tok_str;
+ bool escape = false;
+ for (; current_position_ < data_.length(); current_position_++) {
+ auto c = data_[current_position_];
+ switch (c) {
+ case '\\':
+ if (!escape) {
+ escape = true;
+ continue;
+ }
+ break;
+ case '"':
+ if (!escape) {
+ current_position_++; // Skip closing quote
+ auto tok = MakeUnique<Token>(TokenType::kString);
+ tok->SetStringValue(tok_str);
+ return tok;
+ }
+ break;
+ case 'a':
+ if (escape) {
+ tok_str += '\a';
+ escape = false;
+ continue;
+ }
+ break;
+ case 'b':
+ if (escape) {
+ tok_str += '\b';
+ escape = false;
+ continue;
+ }
+ break;
+ case 't':
+ if (escape) {
+ tok_str += '\t';
+ escape = false;
+ continue;
+ }
+ break;
+ case 'n':
+ if (escape) {
+ tok_str += '\n';
+ escape = false;
+ continue;
+ }
+ break;
+ case 'v':
+ if (escape) {
+ tok_str += '\v';
+ escape = false;
+ continue;
+ }
+ break;
+ case 'f':
+ if (escape) {
+ tok_str += '\f';
+ escape = false;
+ continue;
+ }
+ break;
+ case 'r':
+ if (escape) {
+ tok_str += '\r';
+ escape = false;
+ continue;
+ }
+ break;
+ }
+ escape = false;
+ tok_str += c;
+ }
+
+ auto tok = MakeUnique<Token>(TokenType::kString);
+ tok->SetStringValue(tok_str);
+ return tok;
+ }
+
// If the current position is a , ( or ) then handle it specially as we don't
// want to consume any other characters.
if (data_[current_position_] == ',' || data_[current_position_] == '(' ||
data_[current_position_] == ')') {
- auto tok = MakeUnique<Token>(TokenType::kString);
+ auto tok = MakeUnique<Token>(TokenType::kIdentifier);
std::string str(1, data_[current_position_]);
tok->SetStringValue(str);
++current_position_;
@@ -125,7 +205,7 @@ std::unique_ptr<Token> Tokenizer::NextToken() {
}
}
- auto tok = MakeUnique<Token>(TokenType::kString);
+ auto tok = MakeUnique<Token>(TokenType::kIdentifier);
tok->SetStringValue(tok_str);
return tok;
}
@@ -178,6 +258,17 @@ std::unique_ptr<Token> Tokenizer::NextToken() {
return tok;
}
+std::unique_ptr<Token> Tokenizer::PeekNextToken() {
+ // Use NextToken() and restore location pointers.
+ auto orig_position = current_position_;
+ auto orig_line = current_line_;
+ std::unique_ptr<Token> tok = NextToken();
+ current_position_ = orig_position;
+ current_line_ = orig_line;
+
+ return tok;
+}
+
std::string Tokenizer::ExtractToNext(const std::string& str) {
size_t pos = data_.find(str, current_position_);
std::string ret;
diff --git a/src/tokenizer.h b/src/tokenizer.h
index 7349c68..53efec6 100644
--- a/src/tokenizer.h
+++ b/src/tokenizer.h
@@ -26,6 +26,7 @@ namespace amber {
enum class TokenType : uint8_t {
kEOS = 0,
kEOL,
+ kIdentifier,
kString,
kInteger,
kDouble,
@@ -41,18 +42,19 @@ class Token {
bool IsHex() const { return type_ == TokenType::kHex; }
bool IsInteger() const { return type_ == TokenType::kInteger; }
bool IsDouble() const { return type_ == TokenType::kDouble; }
+ bool IsIdentifier() const { return type_ == TokenType::kIdentifier; }
bool IsString() const { return type_ == TokenType::kString; }
bool IsEOS() const { return type_ == TokenType::kEOS; }
bool IsEOL() const { return type_ == TokenType::kEOL; }
bool IsComma() const {
- return type_ == TokenType::kString && string_value_ == ",";
+ return type_ == TokenType::kIdentifier && string_value_ == ",";
}
bool IsOpenBracket() const {
- return type_ == TokenType::kString && string_value_ == "(";
+ return type_ == TokenType::kIdentifier && string_value_ == "(";
}
bool IsCloseBracket() const {
- return type_ == TokenType::kString && string_value_ == ")";
+ return type_ == TokenType::kIdentifier && string_value_ == ")";
}
void SetNegative() { is_negative_ = true; }
@@ -103,6 +105,7 @@ class Tokenizer {
~Tokenizer();
std::unique_ptr<Token> NextToken();
+ std::unique_ptr<Token> PeekNextToken();
std::string ExtractToNext(const std::string& str);
void SetCurrentLine(size_t line) { current_line_ = line; }
diff --git a/src/tokenizer_test.cc b/src/tokenizer_test.cc
index fd7dba4..b78e57a 100644
--- a/src/tokenizer_test.cc
+++ b/src/tokenizer_test.cc
@@ -30,12 +30,12 @@ TEST_F(TokenizerTest, ProcessEmpty) {
EXPECT_TRUE(next->IsEOS());
}
-TEST_F(TokenizerTest, ProcessString) {
- Tokenizer t("TestString");
+TEST_F(TokenizerTest, ProcessIdentifier) {
+ Tokenizer t("TestIdentifier");
auto next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
- EXPECT_EQ("TestString", next->AsString());
+ EXPECT_TRUE(next->IsIdentifier());
+ EXPECT_EQ("TestIdentifier", next->AsString());
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
@@ -133,7 +133,7 @@ TEST_F(TokenizerTest, ProcessStringWithNumberInName) {
Tokenizer t("BufferAccess32");
auto next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("BufferAccess32", next->AsString());
next = t.NextToken();
@@ -145,7 +145,7 @@ TEST_F(TokenizerTest, ProcessMultiStatement) {
Tokenizer t("TestValue 123.456");
auto next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("TestValue", next->AsString());
next = t.NextToken();
@@ -162,7 +162,7 @@ TEST_F(TokenizerTest, ProcessMultiLineStatement) {
Tokenizer t("TestValue 123.456\nAnotherValue\n\nThirdValue 456");
auto next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("TestValue", next->AsString());
EXPECT_EQ(1U, t.GetCurrentLine());
@@ -178,7 +178,7 @@ TEST_F(TokenizerTest, ProcessMultiLineStatement) {
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("AnotherValue", next->AsString());
EXPECT_EQ(2U, t.GetCurrentLine());
@@ -192,7 +192,7 @@ TEST_F(TokenizerTest, ProcessMultiLineStatement) {
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("ThirdValue", next->AsString());
EXPECT_EQ(4U, t.GetCurrentLine());
@@ -221,7 +221,7 @@ ThirdValue 456)");
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("TestValue", next->AsString());
next = t.NextToken();
@@ -235,7 +235,7 @@ ThirdValue 456)");
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("AnotherValue", next->AsString());
next = t.NextToken();
@@ -248,7 +248,7 @@ ThirdValue 456)");
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("ThirdValue", next->AsString());
next = t.NextToken();
@@ -294,10 +294,60 @@ TEST_F(TokenizerTest, StringStartingWithNum) {
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("/ABC", next->AsString());
}
+TEST_F(TokenizerTest, StringQuotedSingleLine) {
+ Tokenizer t("\"Hello world\"");
+ auto next = t.NextToken();
+ ASSERT_TRUE(next != nullptr);
+ EXPECT_TRUE(next->IsString());
+ EXPECT_EQ("Hello world", next->AsString());
+
+ next = t.NextToken();
+ ASSERT_TRUE(next != nullptr);
+ EXPECT_TRUE(next->IsEOS());
+}
+
+TEST_F(TokenizerTest, StringQuotedMultiLine) {
+ Tokenizer t("\"Hello\n\nworld\"");
+ auto next = t.NextToken();
+ ASSERT_TRUE(next != nullptr);
+ EXPECT_TRUE(next->IsString());
+ EXPECT_EQ("Hello\n\nworld", next->AsString());
+
+ next = t.NextToken();
+ ASSERT_TRUE(next != nullptr);
+ EXPECT_TRUE(next->IsEOS());
+}
+
+TEST_F(TokenizerTest, StringQuotedUnterminated) {
+ Tokenizer t("\"Hello\n\nworld");
+ auto next = t.NextToken();
+ ASSERT_TRUE(next != nullptr);
+ EXPECT_TRUE(next->IsString());
+ EXPECT_EQ("Hello\n\nworld", next->AsString());
+
+ next = t.NextToken();
+ ASSERT_TRUE(next != nullptr);
+ EXPECT_TRUE(next->IsEOS());
+}
+
+TEST_F(TokenizerTest, StringQuotedEscapeSequences) {
+ Tokenizer t(R"("_\"\\_a\aa_b\bb_t\tt_n\nn_v\vv_f\ff_r\rr_")");
+ auto next = t.NextToken();
+ ASSERT_TRUE(next != nullptr);
+ EXPECT_TRUE(next->IsString());
+
+ std::string expect = "_\"\\_a\aa_b\bb_t\tt_n\nn_v\vv_f\ff_r\rr_";
+ EXPECT_EQ(expect, next->AsString());
+
+ next = t.NextToken();
+ ASSERT_TRUE(next != nullptr);
+ EXPECT_TRUE(next->IsEOS());
+}
+
TEST_F(TokenizerTest, BracketsAndCommas) {
Tokenizer t("(1.0, 2, abc)");
auto next = t.NextToken();
@@ -324,7 +374,7 @@ TEST_F(TokenizerTest, BracketsAndCommas) {
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("abc", next->AsString());
next = t.NextToken();
@@ -362,7 +412,7 @@ TEST_F(TokenizerTest, DashToken) {
Tokenizer t("-");
auto next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- ASSERT_TRUE(next->IsString());
+ ASSERT_TRUE(next->IsIdentifier());
EXPECT_EQ("-", next->AsString());
}
@@ -421,7 +471,7 @@ TEST_F(TokenizerTest, TokenToDoubleFromString) {
Tokenizer t("INVALID");
auto next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- ASSERT_TRUE(next->IsString());
+ ASSERT_TRUE(next->IsIdentifier());
Result r = next->ConvertToDouble();
ASSERT_FALSE(r.IsSuccess());
@@ -468,13 +518,13 @@ TEST_F(TokenizerTest, Continuations) {
ASSERT_TRUE(next != nullptr);
ASSERT_TRUE(next->IsInteger());
EXPECT_EQ(1, next->AsInt32());
- EXPECT_EQ(1, t.GetCurrentLine());
+ EXPECT_EQ(1u, t.GetCurrentLine());
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
ASSERT_TRUE(next->IsInteger());
EXPECT_EQ(2, next->AsInt32());
- EXPECT_EQ(2, t.GetCurrentLine());
+ EXPECT_EQ(2u, t.GetCurrentLine());
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
@@ -490,7 +540,7 @@ TEST_F(TokenizerTest, ContinuationAtEndOfString) {
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- ASSERT_TRUE(next->IsString());
+ ASSERT_TRUE(next->IsIdentifier());
EXPECT_EQ("\\", next->AsString());
next = t.NextToken();
@@ -507,7 +557,7 @@ TEST_F(TokenizerTest, ContinuationTokenAtOfLine) {
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- ASSERT_TRUE(next->IsString());
+ ASSERT_TRUE(next->IsIdentifier());
EXPECT_EQ("\\2", next->AsString());
next = t.NextToken();
@@ -524,13 +574,13 @@ TEST_F(TokenizerTest, ContinuationTokenInMiddleOfLine) {
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
- ASSERT_TRUE(next->IsString());
+ ASSERT_TRUE(next->IsIdentifier());
EXPECT_EQ("\\", next->AsString());
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
ASSERT_TRUE(next->IsInteger());
- EXPECT_EQ(2U, next->AsInt32());
+ EXPECT_EQ(2u, next->AsInt32());
next = t.NextToken();
ASSERT_TRUE(next != nullptr);
@@ -541,14 +591,14 @@ TEST_F(TokenizerTest, ExtractToNext) {
Tokenizer t("this\nis\na\ntest\nEND");
auto next = t.NextToken();
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("this", next->AsString());
std::string s = t.ExtractToNext("END");
ASSERT_EQ("\nis\na\ntest\n", s);
next = t.NextToken();
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("END", next->AsString());
EXPECT_EQ(5U, t.GetCurrentLine());
@@ -560,7 +610,7 @@ TEST_F(TokenizerTest, ExtractToNextMissingNext) {
Tokenizer t("this\nis\na\ntest\n");
auto next = t.NextToken();
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("this", next->AsString());
std::string s = t.ExtractToNext("END");
@@ -577,7 +627,7 @@ TEST_F(TokenizerTest, ExtractToNextCurrentIsNext) {
ASSERT_EQ("", s);
auto next = t.NextToken();
- EXPECT_TRUE(next->IsString());
+ EXPECT_TRUE(next->IsIdentifier());
EXPECT_EQ("END", next->AsString());
next = t.NextToken();
diff --git a/src/type_parser.cc b/src/type_parser.cc
index a6779f0..7df8be5 100644
--- a/src/type_parser.cc
+++ b/src/type_parser.cc
@@ -40,12 +40,16 @@ std::unique_ptr<type::Type> TypeParser::Parse(const std::string& data) {
for (;;) {
size_t next_pos = data.rfind('_', cur_pos);
if (next_pos == std::string::npos) {
- if (cur_pos != std::string::npos)
- ProcessChunk(data.substr(0, cur_pos + 1));
+ if (cur_pos != std::string::npos) {
+ if (!ProcessChunk(data.substr(0, cur_pos + 1)))
+ return nullptr;
+ }
break;
}
- ProcessChunk(data.substr(next_pos + 1, cur_pos - next_pos));
+ if (!ProcessChunk(data.substr(next_pos + 1, cur_pos - next_pos)))
+ return nullptr;
+
cur_pos = next_pos - 1;
}
@@ -78,8 +82,9 @@ void TypeParser::AddPiece(FormatComponentType type,
pieces_.insert(pieces_.begin(), Pieces{type, mode, bits});
}
-void TypeParser::ProcessChunk(const std::string& data) {
- assert(data.size() > 0);
+bool TypeParser::ProcessChunk(const std::string& data) {
+ if (data.size() == 0)
+ return false;
if (data[0] == 'P') {
if (data == "PACK8")
@@ -89,9 +94,9 @@ void TypeParser::ProcessChunk(const std::string& data) {
else if (data == "PACK32")
pack_size_ = 32;
else
- assert(false);
+ return false;
- return;
+ return true;
}
if (data[0] == 'U') {
@@ -104,9 +109,9 @@ void TypeParser::ProcessChunk(const std::string& data) {
else if (data == "USCALED")
mode_ = FormatMode::kUScaled;
else
- assert(false);
+ return false;
- return;
+ return true;
}
if (data[0] == 'S') {
@@ -123,9 +128,9 @@ void TypeParser::ProcessChunk(const std::string& data) {
else if (data == "S8")
AddPiece(FormatComponentType::kS, mode_, 8);
else
- assert(false);
+ return false;
- return;
+ return true;
}
int32_t cur_pos = static_cast<int32_t>(data.size()) - 1;
@@ -167,6 +172,7 @@ void TypeParser::ProcessChunk(const std::string& data) {
--cur_pos;
}
+ return true;
}
// static
diff --git a/src/type_parser.h b/src/type_parser.h
index d737e86..3c3581d 100644
--- a/src/type_parser.h
+++ b/src/type_parser.h
@@ -41,7 +41,7 @@ class TypeParser {
private:
std::unique_ptr<type::Type> ParseGlslFormat(const std::string& fmt);
- void ProcessChunk(const std::string&);
+ bool ProcessChunk(const std::string&);
void AddPiece(FormatComponentType type, FormatMode mode, uint8_t bits);
void FlushPieces(type::Type* type, FormatMode mode);
diff --git a/src/type_parser_test.cc b/src/type_parser_test.cc
index c26c329..0f923ea 100644
--- a/src/type_parser_test.cc
+++ b/src/type_parser_test.cc
@@ -1208,7 +1208,7 @@ TEST_F(TypeParserTest, Formats) {
if (data.component_count < segs.size()) {
// Only one padding added
- EXPECT_EQ(1, segs.size() - data.component_count);
+ EXPECT_EQ(1u, segs.size() - data.component_count);
EXPECT_TRUE(segs.back().IsPadding());
}
}
diff --git a/src/type_test.cc b/src/type_test.cc
index bc140d4..285ad6f 100644
--- a/src/type_test.cc
+++ b/src/type_test.cc
@@ -37,7 +37,7 @@ TEST_F(TypeTest, IsArray) {
EXPECT_TRUE(i.IsArray());
EXPECT_FALSE(i.IsRuntimeArray());
EXPECT_TRUE(i.IsSizedArray());
- EXPECT_EQ(3, i.ArraySize());
+ EXPECT_EQ(3u, i.ArraySize());
}
TEST_F(TypeTest, IsStruct) {
@@ -62,7 +62,7 @@ TEST_F(TypeTest, Vectors) {
Number i(FormatMode::kSInt, 16);
i.SetRowCount(2);
- EXPECT_EQ(2, i.RowCount());
+ EXPECT_EQ(2u, i.RowCount());
EXPECT_TRUE(i.IsVec());
EXPECT_FALSE(i.IsVec3());
EXPECT_FALSE(i.IsMatrix());
@@ -76,8 +76,8 @@ TEST_F(TypeTest, Matrix) {
i.SetColumnCount(2);
i.SetRowCount(2);
- EXPECT_EQ(2, i.ColumnCount());
- EXPECT_EQ(2, i.RowCount());
+ EXPECT_EQ(2u, i.ColumnCount());
+ EXPECT_EQ(2u, i.RowCount());
EXPECT_FALSE(i.IsVec());
EXPECT_TRUE(i.IsMatrix());
@@ -205,14 +205,14 @@ TEST_F(TypeTest, StructEqual) {
}
TEST_F(TypeTest, NumberDefault32Bits) {
- EXPECT_EQ(4, Number(FormatMode::kUNorm).SizeInBytes());
+ EXPECT_EQ(4u, Number(FormatMode::kUNorm).SizeInBytes());
}
TEST_F(TypeTest, NumberInBytes) {
- EXPECT_EQ(1, Number(FormatMode::kSInt, 8).SizeInBytes());
- EXPECT_EQ(2, Number(FormatMode::kSInt, 16).SizeInBytes());
- EXPECT_EQ(4, Number(FormatMode::kSInt, 32).SizeInBytes());
- EXPECT_EQ(8, Number(FormatMode::kSInt, 64).SizeInBytes());
+ EXPECT_EQ(1u, Number(FormatMode::kSInt, 8).SizeInBytes());
+ EXPECT_EQ(2u, Number(FormatMode::kSInt, 16).SizeInBytes());
+ EXPECT_EQ(4u, Number(FormatMode::kSInt, 32).SizeInBytes());
+ EXPECT_EQ(8u, Number(FormatMode::kSInt, 64).SizeInBytes());
}
TEST_F(TypeTest, IsInt) {
diff --git a/src/verifier.cc b/src/verifier.cc
index a787e9d..18d60c8 100644
--- a/src/verifier.cc
+++ b/src/verifier.cc
@@ -21,6 +21,7 @@
#include <vector>
#include "src/command.h"
+#include "src/float16_helper.h"
namespace amber {
namespace {
@@ -57,78 +58,6 @@ void CopyBitsOfMemoryToBuffer(uint8_t* dst,
std::memcpy(dst, &data, static_cast<size_t>((bits + 7) / kBitsPerByte));
}
-// Convert float |value| whose size is 16 bits to 32 bits float
-// based on IEEE-754.
-float HexFloat16ToFloat(const uint8_t* value) {
- uint32_t sign = (static_cast<uint32_t>(value[1]) & 0x80) << 24U;
- uint32_t exponent = (((static_cast<uint32_t>(value[1]) & 0x7c) >> 2U) + 112U)
- << 23U;
- uint32_t mantissa = ((static_cast<uint32_t>(value[1]) & 0x3) << 8U |
- static_cast<uint32_t>(value[0]))
- << 13U;
-
- uint32_t hex = sign | exponent | mantissa;
- float* hex_float = reinterpret_cast<float*>(&hex);
- return *hex_float;
-}
-
-// Convert float |value| whose size is 11 bits to 32 bits float
-// based on IEEE-754.
-float HexFloat11ToFloat(const uint8_t* value) {
- uint32_t exponent = (((static_cast<uint32_t>(value[1]) << 2U) |
- ((static_cast<uint32_t>(value[0]) & 0xc0) >> 6U)) +
- 112U)
- << 23U;
- uint32_t mantissa = (static_cast<uint32_t>(value[0]) & 0x3f) << 17U;
-
- uint32_t hex = exponent | mantissa;
- float* hex_float = reinterpret_cast<float*>(&hex);
- return *hex_float;
-}
-
-// Convert float |value| whose size is 10 bits to 32 bits float
-// based on IEEE-754.
-float HexFloat10ToFloat(const uint8_t* value) {
- uint32_t exponent = (((static_cast<uint32_t>(value[1]) << 3U) |
- ((static_cast<uint32_t>(value[0]) & 0xe0) >> 5U)) +
- 112U)
- << 23U;
- uint32_t mantissa = (static_cast<uint32_t>(value[0]) & 0x1f) << 18U;
-
- uint32_t hex = exponent | mantissa;
- float* hex_float = reinterpret_cast<float*>(&hex);
- return *hex_float;
-}
-
-// Convert float |value| whose size is |bits| bits to 32 bits float
-// based on IEEE-754.
-// See https://www.khronos.org/opengl/wiki/Small_Float_Formats
-// and https://en.wikipedia.org/wiki/IEEE_754.
-//
-// Sign Exponent Mantissa Exponent-Bias
-// 16 1 5 10 15
-// 11 0 5 6 15
-// 10 0 5 5 15
-// 32 1 8 23 127
-// 64 1 11 52 1023
-//
-// 11 and 10 bits floats are always positive.
-// 14 bits float is used only RGB9_E5 format in OpenGL but it does not exist
-// in Vulkan.
-float HexFloatToFloat(const uint8_t* value, uint8_t bits) {
- switch (bits) {
- case 10:
- return HexFloat10ToFloat(value);
- case 11:
- return HexFloat11ToFloat(value);
- case 16:
- return HexFloat16ToFloat(value);
- }
-
- assert(false && "Invalid bits");
- return 0;
-}
-
// This is based on "18.3. sRGB transfer functions" of
// https://www.khronos.org/registry/DataFormat/specs/1.2/dataformat.1.2.html
double SRGBToLinearValue(double sRGB) {
@@ -146,6 +75,11 @@ bool IsEqualWithTolerance(const double actual,
const double expected,
double tolerance,
const bool is_tolerance_percent = true) {
+ // Special case for NaN.
+ if (std::isunordered(actual, expected)) {
+ return std::isnan(actual) == std::isnan(expected);
+ }
+
double difference = std::fabs(actual - expected);
if (is_tolerance_percent) {
if (difference > ((tolerance / 100.0) * std::fabs(expected))) {
@@ -158,67 +92,84 @@ bool IsEqualWithTolerance(const double actual,
}
template <typename T>
-Result CheckValue(const ProbeSSBOCommand* command,
- const uint8_t* memory,
- const Value& value) {
+Result CheckActualValue(const ProbeSSBOCommand* command,
+ const T actual_value,
+ const Value& value) {
const auto comp = command->GetComparator();
const auto& tolerance = command->GetTolerances();
- const T* ptr = reinterpret_cast<const T*>(memory);
const T val = value.IsInteger() ? static_cast<T>(value.AsUint64())
: static_cast<T>(value.AsDouble());
switch (comp) {
case ProbeSSBOCommand::Comparator::kEqual:
if (value.IsInteger()) {
- if (static_cast<uint64_t>(*ptr) != static_cast<uint64_t>(val)) {
- return Result(std::to_string(*ptr) + " == " + std::to_string(val));
+ if (static_cast<uint64_t>(actual_value) != static_cast<uint64_t>(val)) {
+ return Result(std::to_string(actual_value) +
+ " == " + std::to_string(val));
}
} else {
- if (!IsEqualWithTolerance(static_cast<const double>(*ptr),
+ if (!IsEqualWithTolerance(static_cast<const double>(actual_value),
static_cast<const double>(val), kEpsilon)) {
- return Result(std::to_string(*ptr) + " == " + std::to_string(val));
+ return Result(std::to_string(actual_value) +
+ " == " + std::to_string(val));
}
}
break;
case ProbeSSBOCommand::Comparator::kNotEqual:
if (value.IsInteger()) {
- if (static_cast<uint64_t>(*ptr) == static_cast<uint64_t>(val)) {
- return Result(std::to_string(*ptr) + " != " + std::to_string(val));
+ if (static_cast<uint64_t>(actual_value) == static_cast<uint64_t>(val)) {
+ return Result(std::to_string(actual_value) +
+ " != " + std::to_string(val));
}
} else {
- if (IsEqualWithTolerance(static_cast<const double>(*ptr),
+ if (IsEqualWithTolerance(static_cast<const double>(actual_value),
static_cast<const double>(val), kEpsilon)) {
- return Result(std::to_string(*ptr) + " != " + std::to_string(val));
+ return Result(std::to_string(actual_value) +
+ " != " + std::to_string(val));
}
}
break;
case ProbeSSBOCommand::Comparator::kFuzzyEqual:
if (!IsEqualWithTolerance(
- static_cast<const double>(*ptr), static_cast<const double>(val),
+ static_cast<const double>(actual_value),
+ static_cast<const double>(val),
command->HasTolerances() ? tolerance[0].value : kEpsilon,
command->HasTolerances() ? tolerance[0].is_percent : true)) {
- return Result(std::to_string(*ptr) + " ~= " + std::to_string(val));
+ return Result(std::to_string(actual_value) +
+ " ~= " + std::to_string(val));
}
break;
case ProbeSSBOCommand::Comparator::kLess:
- if (*ptr >= val)
- return Result(std::to_string(*ptr) + " < " + std::to_string(val));
+ if (actual_value >= val)
+ return Result(std::to_string(actual_value) + " < " +
+ std::to_string(val));
break;
case ProbeSSBOCommand::Comparator::kLessOrEqual:
- if (*ptr > val)
- return Result(std::to_string(*ptr) + " <= " + std::to_string(val));
+ if (actual_value > val)
+ return Result(std::to_string(actual_value) +
+ " <= " + std::to_string(val));
break;
case ProbeSSBOCommand::Comparator::kGreater:
- if (*ptr <= val)
- return Result(std::to_string(*ptr) + " > " + std::to_string(val));
+ if (actual_value <= val)
+ return Result(std::to_string(actual_value) + " > " +
+ std::to_string(val));
break;
case ProbeSSBOCommand::Comparator::kGreaterOrEqual:
- if (*ptr < val)
- return Result(std::to_string(*ptr) + " >= " + std::to_string(val));
+ if (actual_value < val)
+ return Result(std::to_string(actual_value) +
+ " >= " + std::to_string(val));
break;
}
return {};
}
+template <typename T>
+Result CheckValue(const ProbeSSBOCommand* command,
+ const uint8_t* memory,
+ const Value& value) {
+ const T* ptr = reinterpret_cast<const T*>(memory);
+ return CheckActualValue<T>(command, *ptr, value);
+}
+
void SetupToleranceForTexels(const ProbeCommand* command,
double* tolerance,
bool* is_tolerance_percent) {
@@ -314,7 +265,7 @@ std::vector<double> GetActualValuesFromTexel(const uint8_t* texel,
actual_values[i] = *ptr;
} else if (type::Type::IsFloat(mode) && num_bits < 32) {
actual_values[i] = static_cast<double>(
- HexFloatToFloat(actual, static_cast<uint8_t>(num_bits)));
+ float16::HexFloatToFloat(actual, static_cast<uint8_t>(num_bits)));
} else {
assert(false && "Incorrect number of bits for number.");
}
@@ -616,28 +567,32 @@ Result Verifier::ProbeSSBO(const ProbeSSBOCommand* command,
Result r;
FormatMode mode = segment.GetFormatMode();
uint32_t num_bits = segment.GetNumBits();
- if (type::Type::IsInt8(mode, num_bits))
+ if (type::Type::IsInt8(mode, num_bits)) {
r = CheckValue<int8_t>(command, ptr, value);
- else if (type::Type::IsUint8(mode, num_bits))
+ } else if (type::Type::IsUint8(mode, num_bits)) {
r = CheckValue<uint8_t>(command, ptr, value);
- else if (type::Type::IsInt16(mode, num_bits))
+ } else if (type::Type::IsInt16(mode, num_bits)) {
r = CheckValue<int16_t>(command, ptr, value);
- else if (type::Type::IsUint16(mode, num_bits))
+ } else if (type::Type::IsUint16(mode, num_bits)) {
r = CheckValue<uint16_t>(command, ptr, value);
- else if (type::Type::IsInt32(mode, num_bits))
+ } else if (type::Type::IsInt32(mode, num_bits)) {
r = CheckValue<int32_t>(command, ptr, value);
- else if (type::Type::IsUint32(mode, num_bits))
+ } else if (type::Type::IsUint32(mode, num_bits)) {
r = CheckValue<uint32_t>(command, ptr, value);
- else if (type::Type::IsInt64(mode, num_bits))
+ } else if (type::Type::IsInt64(mode, num_bits)) {
r = CheckValue<int64_t>(command, ptr, value);
- else if (type::Type::IsUint64(mode, num_bits))
+ } else if (type::Type::IsUint64(mode, num_bits)) {
r = CheckValue<uint64_t>(command, ptr, value);
- else if (type::Type::IsFloat32(mode, num_bits))
+ } else if (type::Type::IsFloat16(mode, num_bits)) {
+ r = CheckActualValue<float>(command, float16::HexFloatToFloat(ptr, 16),
+ value);
+ } else if (type::Type::IsFloat32(mode, num_bits)) {
r = CheckValue<float>(command, ptr, value);
- else if (type::Type::IsFloat64(mode, num_bits))
+ } else if (type::Type::IsFloat64(mode, num_bits)) {
r = CheckValue<double>(command, ptr, value);
- else
+ } else {
return Result("Unknown datum type");
+ }
if (!r.IsSuccess()) {
return Result("Line " + std::to_string(command->GetLine()) +
diff --git a/src/verifier_test.cc b/src/verifier_test.cc
index 42e5aba..d550e29 100644
--- a/src/verifier_test.cc
+++ b/src/verifier_test.cc
@@ -14,6 +14,7 @@
#include "src/verifier.h"
+#include <cmath>
#include <memory>
#include <utility>
#include <vector>
@@ -22,6 +23,7 @@
#include "amber/value.h"
#include "gtest/gtest.h"
#include "src/command.h"
+#include "src/float16_helper.h"
#include "src/make_unique.h"
#include "src/pipeline.h"
#include "src/type_parser.h"
@@ -32,7 +34,7 @@ namespace {
class VerifierTest : public testing::Test {
public:
VerifierTest() = default;
- ~VerifierTest() = default;
+ ~VerifierTest() override = default;
const Format* GetColorFormat() {
if (color_frame_format_)
@@ -1520,4 +1522,98 @@ TEST_F(VerifierTest, ProbeSSBOWithPadding) {
EXPECT_TRUE(r.IsSuccess()) << r.Error();
}
+TEST_F(VerifierTest, ProbeSSBOHexFloat) {
+ Pipeline pipeline(PipelineType::kGraphics);
+ auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
+
+ ProbeSSBOCommand probe_ssbo(color_buf.get());
+
+ TypeParser parser;
+ auto type = parser.Parse("R16_SFLOAT");
+ Format fmt(type.get());
+
+ probe_ssbo.SetFormat(&fmt);
+ probe_ssbo.SetComparator(ProbeSSBOCommand::Comparator::kFuzzyEqual);
+ probe_ssbo.SetTolerances({ProbeCommand::Tolerance{false, 0.1}});
+
+ std::vector<Value> values;
+ values.resize(4);
+ values[0].SetDoubleValue(2.5);
+ values[1].SetDoubleValue(0.73);
+ values[2].SetDoubleValue(10.0);
+ values[3].SetDoubleValue(123.5);
+ probe_ssbo.SetValues(std::move(values));
+
+ const uint16_t ssbo[4] = {
+ float16::FloatToHexFloat16(2.5f), float16::FloatToHexFloat16(0.73f),
+ float16::FloatToHexFloat16(10.0f), float16::FloatToHexFloat16(123.5f)};
+
+ Verifier verifier;
+ Result r = verifier.ProbeSSBO(&probe_ssbo, sizeof(uint16_t) * 4, ssbo);
+ EXPECT_TRUE(r.IsSuccess()) << r.Error();
+}
+
+TEST_F(VerifierTest, ProbeSSBOFloatNaN) {
+ Pipeline pipeline(PipelineType::kGraphics);
+ auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
+
+ ProbeSSBOCommand probe_ssbo(color_buf.get());
+
+ TypeParser parser;
+ auto type = parser.Parse("R32_SFLOAT");
+ Format fmt(type.get());
+
+ probe_ssbo.SetFormat(&fmt);
+ probe_ssbo.SetComparator(ProbeSSBOCommand::Comparator::kEqual);
+
+ // expected=13.7, actual=nan
+ {
+ std::vector<Value> values;
+ values.emplace_back();
+ values.back().SetDoubleValue(13.7);
+ probe_ssbo.SetValues(std::move(values));
+
+ float ssbo = std::nanf("");
+
+ Verifier verifier;
+ Result r =
+ verifier.ProbeSSBO(&probe_ssbo, 1, static_cast<const void*>(&ssbo));
+ EXPECT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("Line 1: Verifier failed: nan == 13.700000, at index 0",
+ r.Error());
+ }
+
+ // expected=nan, actual=13.7
+ {
+ std::vector<Value> values;
+ values.emplace_back();
+ values.back().SetDoubleValue(std::nan(""));
+ probe_ssbo.SetValues(std::move(values));
+
+ float ssbo = 13.7f;
+
+ Verifier verifier;
+ Result r =
+ verifier.ProbeSSBO(&probe_ssbo, 1, static_cast<const void*>(&ssbo));
+ EXPECT_FALSE(r.IsSuccess()) << r.Error();
+ EXPECT_EQ("Line 1: Verifier failed: 13.700000 == nan, at index 0",
+ r.Error());
+ }
+
+ // expected=nan, actual=nan
+ {
+ std::vector<Value> values;
+ values.emplace_back();
+ values.back().SetDoubleValue(std::nan(""));
+ probe_ssbo.SetValues(std::move(values));
+
+ float ssbo = std::nanf("");
+
+ Verifier verifier;
+ Result r =
+ verifier.ProbeSSBO(&probe_ssbo, 1, static_cast<const void*>(&ssbo));
+ EXPECT_TRUE(r.IsSuccess()) << r.Error();
+ }
+}
+
} // namespace amber
diff --git a/src/virtual_file_store.cc b/src/virtual_file_store.cc
new file mode 100644
index 0000000..a14d64c
--- /dev/null
+++ b/src/virtual_file_store.cc
@@ -0,0 +1,48 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/virtual_file_store.h"
+
+namespace amber {
+namespace {
+
+bool HasPrefix(const std::string& str, const std::string& prefix) {
+ return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+std::string TrimPrefix(const std::string& str, const std::string& prefix) {
+ return HasPrefix(str, prefix) ? str.substr(prefix.length()) : str;
+}
+
+std::string ReplaceAll(std::string str,
+ const std::string& substr,
+ const std::string& replacement) {
+ size_t pos = 0;
+ while ((pos = str.find(substr, pos)) != std::string::npos) {
+ str.replace(pos, substr.length(), replacement);
+ pos += replacement.length();
+ }
+ return str;
+}
+
+} // namespace
+
+std::string VirtualFileStore::GetCanonical(const std::string& path) {
+ auto canonical = path;
+ canonical = ReplaceAll(canonical, "\\", "/");
+ canonical = TrimPrefix(canonical, "./");
+ return canonical;
+}
+
+} // namespace amber
diff --git a/src/virtual_file_store.h b/src/virtual_file_store.h
new file mode 100644
index 0000000..f3e3d65
--- /dev/null
+++ b/src/virtual_file_store.h
@@ -0,0 +1,75 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_VIRTUAL_FILE_STORE_H_
+#define SRC_VIRTUAL_FILE_STORE_H_
+
+#include <cassert>
+#include <string>
+#include <unordered_map>
+
+#include "amber/result.h"
+
+namespace amber {
+
+/// Stores a number of virtual files by path.
+class VirtualFileStore {
+ public:
+ /// Return the path sanitized into a canonical form.
+ static std::string GetCanonical(const std::string& path);
+
+ /// Adds the virtual file with content |content| to the virtual file path
+ /// |path|. If there's already a virtual file with the given path, an error is
+ /// returned.
+ Result Add(const std::string& path, const std::string& content) {
+ if (path.length() == 0) {
+ return Result("Virtual file path was empty");
+ }
+
+ auto canonical = GetCanonical(path);
+
+ auto it = files_by_path_.find(canonical);
+ if (it != files_by_path_.end()) {
+ return Result("Virtual file '" + path + "' already declared");
+ }
+ files_by_path_.emplace(canonical, content);
+ return {};
+ }
+
+ /// Look up the virtual file by path. If the file was found, the content is
+ /// assigned to content.
+ Result Get(const std::string& path, std::string* content) const {
+ assert(content);
+
+ if (path.length() == 0) {
+ return Result("Virtual file path was empty");
+ }
+
+ auto canonical = GetCanonical(path);
+
+ auto it = files_by_path_.find(canonical);
+ if (it == files_by_path_.end()) {
+ return Result("Virtual file '" + path + "' not found");
+ }
+ *content = it->second;
+ return {};
+ }
+
+ private:
+ std::unordered_map<std::string, std::string> files_by_path_;
+};
+
+} // namespace amber
+
+#endif // SRC_VIRTUAL_FILE_STORE_H_
diff --git a/src/virtual_file_store_test.cc b/src/virtual_file_store_test.cc
new file mode 100644
index 0000000..89e0320
--- /dev/null
+++ b/src/virtual_file_store_test.cc
@@ -0,0 +1,49 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/virtual_file_store.h"
+
+#include "gtest/gtest.h"
+
+namespace amber {
+
+TEST(VirtualFileStore, Canonical) {
+ ASSERT_EQ("a/b/c.e", VirtualFileStore::GetCanonical("a/b/c.e"));
+ ASSERT_EQ("a/b.c.e", VirtualFileStore::GetCanonical("a/b.c.e"));
+ ASSERT_EQ("a/b/c.e", VirtualFileStore::GetCanonical("a\\b\\c.e"));
+ ASSERT_EQ("a/b/c.e", VirtualFileStore::GetCanonical("./a/b/c.e"));
+}
+
+TEST(VirtualFileStore, AddGet) {
+ VirtualFileStore store;
+ store.Add("a/file.1", "File 1");
+ store.Add("./file.2", "File 2");
+ store.Add("b\\file.3", "File 3");
+
+ std::string content;
+ ASSERT_TRUE(store.Get("a/file.1", &content).IsSuccess());
+ ASSERT_EQ("File 1", content);
+
+ ASSERT_TRUE(store.Get("./file.2", &content).IsSuccess());
+ ASSERT_EQ("File 2", content);
+
+ ASSERT_TRUE(store.Get("b\\file.3", &content).IsSuccess());
+ ASSERT_EQ("File 3", content);
+
+ content = "<not-assigned>";
+ ASSERT_FALSE(store.Get("missing.file", &content).IsSuccess());
+ ASSERT_EQ("<not-assigned>", content);
+}
+
+} // namespace amber
diff --git a/src/vkscript/command_parser.cc b/src/vkscript/command_parser.cc
index dd4f918..0953344 100644
--- a/src/vkscript/command_parser.cc
+++ b/src/vkscript/command_parser.cc
@@ -89,7 +89,7 @@ Result CommandParser::Parse() {
if (token->IsEOL())
continue;
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result(make_error(
"Command not recognized. Received something other then a string: " +
token->ToOriginalString()));
@@ -99,7 +99,7 @@ Result CommandParser::Parse() {
Result r;
if (cmd_name == "draw") {
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result(make_error("Invalid draw command in test: " +
token->ToOriginalString()));
@@ -125,7 +125,7 @@ Result CommandParser::Parse() {
r = ProcessTolerance();
} else if (cmd_name == "relative") {
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "probe")
+ if (!token->IsIdentifier() || token->AsString() != "probe")
return Result(make_error("relative must be used with probe: " +
token->ToOriginalString()));
@@ -137,8 +137,8 @@ Result CommandParser::Parse() {
std::string shader_name = cmd_name;
if (cmd_name == "tessellation") {
token = tokenizer_->NextToken();
- if (!token->IsString() || (token->AsString() != "control" &&
- token->AsString() != "evaluation")) {
+ if (!token->IsIdentifier() || (token->AsString() != "control" &&
+ token->AsString() != "evaluation")) {
return Result(
make_error("Tessellation entrypoint must have "
"<evaluation|control> in name: " +
@@ -148,7 +148,7 @@ Result CommandParser::Parse() {
}
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "entrypoint")
+ if (!token->IsIdentifier() || token->AsString() != "entrypoint")
return Result(make_error("Unknown command: " + shader_name));
r = ProcessEntryPoint(shader_name);
@@ -262,7 +262,7 @@ Result CommandParser::ProcessDrawRect() {
}
auto token = tokenizer_->NextToken();
- while (token->IsString()) {
+ while (token->IsIdentifier()) {
std::string str = token->AsString();
if (str != "ortho" && str != "patch")
return Result("Unknown parameter to draw rect: " + str);
@@ -310,9 +310,10 @@ Result CommandParser::ProcessDrawRect() {
Result CommandParser::ProcessDrawArrays() {
auto cmd = MakeUnique<DrawArraysCommand>(pipeline_, pipeline_data_);
cmd->SetLine(tokenizer_->GetCurrentLine());
+ bool instanced = false;
auto token = tokenizer_->NextToken();
- while (token->IsString()) {
+ while (token->IsIdentifier()) {
std::string str = token->AsString();
if (str != "indexed" && str != "instanced") {
Topology topo = NameToTopology(token->AsString());
@@ -329,7 +330,7 @@ Result CommandParser::ProcessDrawArrays() {
if (str == "indexed") {
cmd->EnableIndexed();
} else {
- cmd->EnableInstanced();
+ instanced = true;
}
token = tokenizer_->NextToken();
}
@@ -349,7 +350,7 @@ Result CommandParser::ProcessDrawArrays() {
cmd->SetVertexCount(token->AsUint32());
token = tokenizer_->NextToken();
- if (cmd->IsInstanced()) {
+ if (instanced) {
if (!token->IsEOL() && !token->IsEOS()) {
if (!token->IsInteger())
return Result("Invalid instance count for draw arrays: " +
@@ -375,7 +376,7 @@ Result CommandParser::ProcessCompute() {
auto token = tokenizer_->NextToken();
// Compute can start a compute line or an entryp oint line ...
- if (token->IsString() && token->AsString() == "entrypoint")
+ if (token->IsIdentifier() && token->AsString() == "entrypoint")
return ProcessEntryPoint("compute");
if (!token->IsInteger())
@@ -409,7 +410,7 @@ Result CommandParser::ProcessClear() {
auto token = tokenizer_->NextToken();
std::string cmd_suffix = "";
- if (token->IsString()) {
+ if (token->IsIdentifier()) {
std::string str = token->AsString();
cmd_suffix = str + " ";
if (str == "depth") {
@@ -543,7 +544,7 @@ Result CommandParser::ProcessSSBO() {
uint32_t val = token->AsUint32();
token = tokenizer_->NextToken();
- if (token->IsString() && token->AsString() != "subdata") {
+ if (token->IsIdentifier() && token->AsString() != "subdata") {
auto& str = token->AsString();
if (str.size() >= 2 && str[0] == ':') {
cmd->SetDescriptorSet(val);
@@ -571,20 +572,21 @@ Result CommandParser::ProcessSSBO() {
auto* buffer = pipeline_->GetBufferForBinding(set, binding);
if (!buffer) {
- auto b = MakeUnique<Buffer>(BufferType::kStorage);
+ auto b = MakeUnique<Buffer>();
b->SetName("AutoBuf-" + std::to_string(script_->GetBuffers().size()));
buffer = b.get();
script_->AddBuffer(std::move(b));
- pipeline_->AddBuffer(buffer, set, binding);
+ pipeline_->ClearBuffers(set, binding);
+ pipeline_->AddBuffer(buffer, BufferType::kStorage, set, binding, 0, 0);
}
cmd->SetBuffer(buffer);
}
- if (token->IsString() && token->AsString() == "subdata") {
+ if (token->IsIdentifier() && token->AsString() == "subdata") {
cmd->SetIsSubdata();
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid type for ssbo command: " +
token->ToOriginalString());
@@ -670,7 +672,7 @@ Result CommandParser::ProcessUniform() {
if (token->IsEOL() || token->IsEOS())
return Result("Missing binding and size values for uniform command: " +
token->ToOriginalString());
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid type value for uniform command: " +
token->ToOriginalString());
@@ -690,7 +692,7 @@ Result CommandParser::ProcessUniform() {
uint32_t val = token->AsUint32();
token = tokenizer_->NextToken();
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result("Invalid type value for uniform ubo command: " +
token->ToOriginalString());
}
@@ -708,7 +710,7 @@ Result CommandParser::ProcessUniform() {
cmd->SetBinding(static_cast<uint32_t>(binding_val));
token = tokenizer_->NextToken();
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result("Invalid type value for uniform ubo command: " +
token->ToOriginalString());
}
@@ -722,11 +724,12 @@ Result CommandParser::ProcessUniform() {
auto* buffer = pipeline_->GetBufferForBinding(set, binding);
if (!buffer) {
- auto b = MakeUnique<Buffer>(BufferType::kUniform);
+ auto b = MakeUnique<Buffer>();
b->SetName("AutoBuf-" + std::to_string(script_->GetBuffers().size()));
buffer = b.get();
script_->AddBuffer(std::move(b));
- pipeline_->AddBuffer(buffer, set, binding);
+ pipeline_->ClearBuffers(set, binding);
+ pipeline_->AddBuffer(buffer, BufferType::kUniform, set, binding, 0, 0);
}
cmd->SetBuffer(buffer);
@@ -738,7 +741,7 @@ Result CommandParser::ProcessUniform() {
// Push constants don't have descriptor set and binding values. So, we do
// not want to try to lookup the buffer or we'll accidentally get whatever
// is bound at 0:0.
- auto b = MakeUnique<Buffer>(BufferType::kUniform);
+ auto b = MakeUnique<Buffer>();
b->SetName("AutoBuf-" + std::to_string(script_->GetBuffers().size()));
cmd->SetBuffer(b.get());
script_->AddBuffer(std::move(b));
@@ -802,7 +805,7 @@ Result CommandParser::ProcessTolerance() {
auto token = tokenizer_->NextToken();
size_t found_tokens = 0;
while (!token->IsEOL() && !token->IsEOS() && found_tokens < 4) {
- if (token->IsString() && token->AsString() == ",") {
+ if (token->IsIdentifier() && token->AsString() == ",") {
token = tokenizer_->NextToken();
continue;
}
@@ -814,7 +817,7 @@ Result CommandParser::ProcessTolerance() {
double value = token->AsDouble();
token = tokenizer_->NextToken();
- if (token->IsString() && token->AsString() != ",") {
+ if (token->IsIdentifier() && token->AsString() != ",") {
if (token->AsString() != "%")
return Result("Invalid value for tolerance command: " +
token->ToOriginalString());
@@ -848,12 +851,12 @@ Result CommandParser::ProcessPatch() {
cmd->SetLine(tokenizer_->GetCurrentLine());
auto token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "parameter")
+ if (!token->IsIdentifier() || token->AsString() != "parameter")
return Result("Missing parameter flag to patch command: " +
token->ToOriginalString());
token = tokenizer_->NextToken();
- if (!token->IsString() || token->AsString() != "vertices")
+ if (!token->IsIdentifier() || token->AsString() != "vertices")
return Result("Missing vertices flag to patch command: " +
token->ToOriginalString());
@@ -880,7 +883,7 @@ Result CommandParser::ProcessEntryPoint(const std::string& name) {
if (token->IsEOL() || token->IsEOS())
return Result("Missing entrypoint name");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Entrypoint name must be a string: " +
token->ToOriginalString());
@@ -899,7 +902,7 @@ Result CommandParser::ProcessEntryPoint(const std::string& name) {
Result CommandParser::ProcessProbe(bool relative) {
auto token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid token in probe command: " +
token->ToOriginalString());
@@ -929,7 +932,7 @@ Result CommandParser::ProcessProbe(bool relative) {
cmd->SetProbeRect();
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid token in probe command: " +
token->ToOriginalString());
} else if (token->AsString() == "all") {
@@ -937,7 +940,7 @@ Result CommandParser::ProcessProbe(bool relative) {
cmd->SetProbeRect();
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid token in probe command: " +
token->ToOriginalString());
}
@@ -1069,7 +1072,7 @@ Result CommandParser::ProcessTopology() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for topology command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid value for topology command: " +
token->ToOriginalString());
@@ -1115,7 +1118,7 @@ Result CommandParser::ProcessPolygonMode() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for polygonMode command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid value for polygonMode command: " +
token->ToOriginalString());
@@ -1144,7 +1147,7 @@ Result CommandParser::ProcessLogicOp() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for logicOp command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid value for logicOp command: " +
token->ToOriginalString());
@@ -1199,7 +1202,7 @@ Result CommandParser::ProcessCullMode() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for cullMode command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid value for cullMode command: " +
token->ToOriginalString());
@@ -1239,7 +1242,7 @@ Result CommandParser::ProcessFrontFace() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for frontFace command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid value for frontFace command: " +
token->ToOriginalString());
@@ -1267,7 +1270,7 @@ Result CommandParser::ProcessBooleanPipelineData(const std::string& name,
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing value for " + name + " command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid value for " + name +
" command: " + token->ToOriginalString());
@@ -1518,7 +1521,7 @@ Result CommandParser::ParseBlendFactor(const std::string& name,
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result(std::string("Missing parameter for ") + name + " command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result(std::string("Invalid parameter for ") + name +
" command: " + token->ToOriginalString());
@@ -1691,7 +1694,7 @@ Result CommandParser::ParseBlendOp(const std::string& name, BlendOp* op) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result(std::string("Missing parameter for ") + name + " command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result(std::string("Invalid parameter for ") + name +
" command: " + token->ToOriginalString());
@@ -1731,7 +1734,7 @@ Result CommandParser::ParseCompareOp(const std::string& name, CompareOp* op) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result(std::string("Missing parameter for ") + name + " command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result(std::string("Invalid parameter for ") + name +
" command: " + token->ToOriginalString());
@@ -1807,7 +1810,7 @@ Result CommandParser::ParseStencilOp(const std::string& name, StencilOp* op) {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result(std::string("Missing parameter for ") + name + " command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result(std::string("Invalid parameter for ") + name +
" command: " + token->ToOriginalString());
@@ -1965,7 +1968,7 @@ Result CommandParser::ProcessColorWriteMask() {
auto token = tokenizer_->NextToken();
if (token->IsEOS() || token->IsEOL())
return Result("Missing parameter for colorWriteMask command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid parameter for colorWriteMask command: " +
token->ToOriginalString());
@@ -2021,7 +2024,7 @@ Result CommandParser::ProcessProbeSSBO() {
auto token = tokenizer_->NextToken();
if (token->IsEOL() || token->IsEOS())
return Result("Missing values for probe ssbo command");
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid type for probe ssbo command: " +
token->ToOriginalString());
@@ -2040,7 +2043,7 @@ Result CommandParser::ProcessProbeSSBO() {
uint32_t set = 0;
uint32_t binding = 0;
token = tokenizer_->NextToken();
- if (token->IsString()) {
+ if (token->IsIdentifier()) {
auto& str = token->AsString();
if (str.size() >= 2 && str[0] == ':') {
set = val;
@@ -2093,7 +2096,7 @@ Result CommandParser::ProcessProbeSSBO() {
cmd->SetOffset(token->AsUint32());
token = tokenizer_->NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result("Invalid comparator for probe ssbo command: " +
token->ToOriginalString());
diff --git a/src/vkscript/command_parser_test.cc b/src/vkscript/command_parser_test.cc
index f88006c..6cf0530 100644
--- a/src/vkscript/command_parser_test.cc
+++ b/src/vkscript/command_parser_test.cc
@@ -191,8 +191,7 @@ TEST_F(CommandParserTest, DrawArrays) {
auto* cmd = cmds[0]->AsDrawArrays();
EXPECT_FALSE(cmd->IsIndexed());
- EXPECT_FALSE(cmd->IsInstanced());
- EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(1U), cmd->GetInstanceCount());
EXPECT_EQ(Topology::kLineList, cmd->GetTopology());
EXPECT_EQ(2U, cmd->GetFirstVertexIndex());
EXPECT_EQ(4U, cmd->GetVertexCount());
@@ -213,8 +212,7 @@ TEST_F(CommandParserTest, DrawArraysIndexed) {
auto* cmd = cmds[0]->AsDrawArrays();
EXPECT_TRUE(cmd->IsIndexed());
- EXPECT_FALSE(cmd->IsInstanced());
- EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(1U), cmd->GetInstanceCount());
EXPECT_EQ(Topology::kTriangleFan, cmd->GetTopology());
EXPECT_EQ(2U, cmd->GetFirstVertexIndex());
EXPECT_EQ(4U, cmd->GetVertexCount());
@@ -247,8 +245,7 @@ TEST_F(CommandParserTest, DrawArraysInstanced) {
auto* cmd = cmds[0]->AsDrawArrays();
EXPECT_FALSE(cmd->IsIndexed());
- EXPECT_TRUE(cmd->IsInstanced());
- EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(1U), cmd->GetInstanceCount());
EXPECT_EQ(Topology::kLineListWithAdjacency, cmd->GetTopology());
EXPECT_EQ(2U, cmd->GetFirstVertexIndex());
EXPECT_EQ(9U, cmd->GetVertexCount());
@@ -283,8 +280,7 @@ TEST_F(CommandParserTest, DrawArraysIndexedAndInstanced) {
auto* cmd = cmds[0]->AsDrawArrays();
EXPECT_TRUE(cmd->IsIndexed());
- EXPECT_TRUE(cmd->IsInstanced());
- EXPECT_EQ(static_cast<uint32_t>(0U), cmd->GetInstanceCount());
+ EXPECT_EQ(static_cast<uint32_t>(1U), cmd->GetInstanceCount());
EXPECT_EQ(Topology::kLineListWithAdjacency, cmd->GetTopology());
EXPECT_EQ(3U, cmd->GetFirstVertexIndex());
EXPECT_EQ(9U, cmd->GetVertexCount());
@@ -305,7 +301,6 @@ TEST_F(CommandParserTest, DrawArraysInstancedWithCount) {
auto* cmd = cmds[0]->AsDrawArrays();
EXPECT_FALSE(cmd->IsIndexed());
- EXPECT_TRUE(cmd->IsInstanced());
EXPECT_EQ(12U, cmd->GetInstanceCount());
EXPECT_EQ(Topology::kLineListWithAdjacency, cmd->GetTopology());
EXPECT_EQ(3U, cmd->GetFirstVertexIndex());
@@ -845,7 +840,7 @@ TEST_P(CommandParserProbeTest, ProbeRgb) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -880,7 +875,7 @@ TEST_P(CommandParserProbeTest, ProbeRgba) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -916,7 +911,7 @@ TEST_P(CommandParserProbeTest, ProbeRect) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -952,7 +947,7 @@ TEST_P(CommandParserProbeTest, ProbeNotRect) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -990,7 +985,7 @@ TEST_F(CommandParserTest, ProbeAllRGB) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1017,7 +1012,7 @@ TEST_F(CommandParserTest, ProbeAllRGBA) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1045,7 +1040,7 @@ TEST_F(CommandParserTest, ProbeCommandRectBrackets) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1077,7 +1072,7 @@ TEST_F(CommandParserTest, ProbeCommandNotRectBrackets) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1109,7 +1104,7 @@ TEST_F(CommandParserTest, ProbeCommandColorBrackets) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1141,7 +1136,7 @@ TEST_F(CommandParserTest, ProbeCommandColorOptionalCommas) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1275,7 +1270,7 @@ TEST_F(CommandParserTest, ProbeErrors) {
for (const auto& probe : probes) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, probe.str);
@@ -1301,7 +1296,7 @@ TEST_F(CommandParserTest, ProbeWithInvalidRGBA) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1315,7 +1310,7 @@ TEST_F(CommandParserTest, ProbeWithRectAndInvalidRGB) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1329,7 +1324,7 @@ TEST_F(CommandParserTest, ProbeWithRectMissingFormat) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1343,7 +1338,7 @@ TEST_F(CommandParserTest, ProbeAllMissingFormat) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -1357,7 +1352,7 @@ TEST_F(CommandParserTest, ProbeAlWithInvalidRGB) {
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
@@ -3902,7 +3897,7 @@ probe all rgba 0.2 0.3 0.4 0.5)";
Pipeline pipeline(PipelineType::kGraphics);
auto color_buf = pipeline.GenerateDefaultColorAttachmentBuffer();
- pipeline.AddColorAttachment(color_buf.get(), 0);
+ pipeline.AddColorAttachment(color_buf.get(), 0, 0);
Script script;
CommandParser cp(&script, &pipeline, 1, data);
diff --git a/src/vkscript/parser.cc b/src/vkscript/parser.cc
index 12e94fb..c84e2d9 100644
--- a/src/vkscript/parser.cc
+++ b/src/vkscript/parser.cc
@@ -35,7 +35,8 @@ const char kDefaultPipelineName[] = "vk_pipeline";
} // namespace
-Parser::Parser() : amber::Parser() {}
+Parser::Parser() : amber::Parser(nullptr) {}
+Parser::Parser(Delegate* delegate) : amber::Parser(delegate) {}
Parser::~Parser() = default;
@@ -96,7 +97,7 @@ Result Parser::GenerateDefaultPipeline(const SectionParser& section_parser) {
// Generate and add a framebuffer
auto color_buf = pipeline->GenerateDefaultColorAttachmentBuffer();
- r = pipeline->AddColorAttachment(color_buf.get(), 0);
+ r = pipeline->AddColorAttachment(color_buf.get(), 0, 0);
if (!r.IsSuccess())
return r;
@@ -155,7 +156,7 @@ Result Parser::ProcessRequireBlock(const SectionParser::Section& section) {
token = tokenizer.NextToken()) {
if (token->IsEOL())
continue;
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result(make_error(
tokenizer,
"Invalid token in requirements block: " + token->ToOriginalString()));
@@ -166,7 +167,7 @@ Result Parser::ProcessRequireBlock(const SectionParser::Section& section) {
script_->AddRequiredFeature(str);
} else if (str == Pipeline::kGeneratedColorBuffer) {
token = tokenizer.NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result(make_error(tokenizer, "Missing framebuffer format"));
TypeParser type_parser;
@@ -186,7 +187,7 @@ Result Parser::ProcessRequireBlock(const SectionParser::Section& section) {
} else if (str == "depthstencil") {
token = tokenizer.NextToken();
- if (!token->IsString())
+ if (!token->IsIdentifier())
return Result(make_error(tokenizer, "Missing depthStencil format"));
TypeParser type_parser;
@@ -198,17 +199,17 @@ Result Parser::ProcessRequireBlock(const SectionParser::Section& section) {
}
auto* pipeline = script_->GetPipeline(kDefaultPipelineName);
- if (pipeline->GetDepthBuffer().buffer != nullptr)
+ if (pipeline->GetDepthStencilBuffer().buffer != nullptr)
return Result("Only one depthstencil command allowed");
auto fmt = MakeUnique<Format>(type.get());
// Generate and add a depth buffer
- auto depth_buf = pipeline->GenerateDefaultDepthAttachmentBuffer();
+ auto depth_buf = pipeline->GenerateDefaultDepthStencilAttachmentBuffer();
depth_buf->SetFormat(fmt.get());
script_->RegisterFormat(std::move(fmt));
script_->RegisterType(std::move(type));
- Result r = pipeline->SetDepthBuffer(depth_buf.get());
+ Result r = pipeline->SetDepthStencilBuffer(depth_buf.get());
if (!r.IsSuccess())
return r;
@@ -298,7 +299,7 @@ Result Parser::ProcessIndicesBlock(const SectionParser::Section& section) {
TypeParser parser;
auto type = parser.Parse("R32_UINT");
auto fmt = MakeUnique<Format>(type.get());
- auto b = MakeUnique<Buffer>(BufferType::kIndex);
+ auto b = MakeUnique<Buffer>();
auto* buf = b.get();
b->SetName("indices");
b->SetFormat(fmt.get());
@@ -347,7 +348,7 @@ Result Parser::ProcessVertexDataBlock(const SectionParser::Section& section) {
uint8_t loc = token->AsUint8();
token = tokenizer.NextToken();
- if (!token->IsString()) {
+ if (!token->IsIdentifier()) {
return Result(
make_error(tokenizer, "Unable to process vertex data header: " +
token->ToOriginalString()));
@@ -433,7 +434,7 @@ Result Parser::ProcessVertexDataBlock(const SectionParser::Section& section) {
auto* pipeline = script_->GetPipeline(kDefaultPipelineName);
for (size_t i = 0; i < headers.size(); ++i) {
- auto buffer = MakeUnique<Buffer>(BufferType::kVertex);
+ auto buffer = MakeUnique<Buffer>();
auto* buf = buffer.get();
buffer->SetName("Vertices" + std::to_string(i));
buffer->SetFormat(headers[i].format);
@@ -443,7 +444,9 @@ Result Parser::ProcessVertexDataBlock(const SectionParser::Section& section) {
script_->AddBuffer(std::move(buffer));
- pipeline->AddVertexBuffer(buf, headers[i].location);
+ pipeline->AddVertexBuffer(buf, headers[i].location, InputRate::kVertex,
+ buf->GetFormat(), 0,
+ buf->GetFormat()->SizeInBytes());
}
return {};
diff --git a/src/vkscript/parser.h b/src/vkscript/parser.h
index d05bdaa..35b541d 100644
--- a/src/vkscript/parser.h
+++ b/src/vkscript/parser.h
@@ -32,6 +32,7 @@ namespace vkscript {
class Parser : public amber::Parser {
public:
Parser();
+ explicit Parser(Delegate* delegate);
~Parser() override;
// amber::Parser
diff --git a/src/vkscript/parser_test.cc b/src/vkscript/parser_test.cc
index 676e75e..0b82a81 100644
--- a/src/vkscript/parser_test.cc
+++ b/src/vkscript/parser_test.cc
@@ -131,11 +131,10 @@ TEST_F(VkScriptParserTest, RequireBlockFramebuffer) {
ASSERT_TRUE(r.IsSuccess());
auto script = parser.GetScript();
- const auto& buffers = script->GetBuffers();
- ASSERT_EQ(1U, buffers.size());
- EXPECT_EQ(BufferType::kColor, buffers[0]->GetBufferType());
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(1U, bufs.size());
EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
- buffers[0]->GetFormat()->GetFormatType());
+ bufs[0]->GetFormat()->GetFormatType());
}
TEST_F(VkScriptParserTest, RequireBlockDepthStencil) {
@@ -147,11 +146,10 @@ TEST_F(VkScriptParserTest, RequireBlockDepthStencil) {
ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto script = parser.GetScript();
- const auto& buffers = script->GetBuffers();
- ASSERT_EQ(2U, buffers.size());
- EXPECT_EQ(BufferType::kDepth, buffers[1]->GetBufferType());
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
EXPECT_EQ(FormatType::kD24_UNORM_S8_UINT,
- buffers[1]->GetFormat()->GetFormatType());
+ bufs[1]->GetFormat()->GetFormatType());
}
TEST_F(VkScriptParserTest, RequireFbSize) {
@@ -165,8 +163,8 @@ TEST_F(VkScriptParserTest, RequireFbSize) {
auto script = parser.GetScript();
const auto& pipelines = script->GetPipelines();
ASSERT_EQ(1U, pipelines.size());
- EXPECT_EQ(300, pipelines[0]->GetFramebufferWidth());
- EXPECT_EQ(400, pipelines[0]->GetFramebufferHeight());
+ EXPECT_EQ(300u, pipelines[0]->GetFramebufferWidth());
+ EXPECT_EQ(400u, pipelines[0]->GetFramebufferHeight());
}
TEST_F(VkScriptParserTest, RequireFbSizeMissingSize) {
@@ -231,15 +229,13 @@ inheritedQueries # line comment
ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto script = parser.GetScript();
- const auto& buffers = script->GetBuffers();
- ASSERT_EQ(2U, buffers.size());
- EXPECT_EQ(BufferType::kColor, buffers[0]->GetBufferType());
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
EXPECT_EQ(FormatType::kR32G32B32A32_SFLOAT,
- buffers[0]->GetFormat()->GetFormatType());
+ bufs[0]->GetFormat()->GetFormatType());
- EXPECT_EQ(BufferType::kDepth, buffers[1]->GetBufferType());
EXPECT_EQ(FormatType::kD24_UNORM_S8_UINT,
- buffers[1]->GetFormat()->GetFormatType());
+ bufs[1]->GetFormat()->GetFormatType());
auto feats = script->GetRequiredFeatures();
EXPECT_EQ("sparseResidency4Samples", feats[0]);
@@ -255,21 +251,18 @@ TEST_F(VkScriptParserTest, IndicesBlock) {
ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto script = parser.GetScript();
- const auto& buffers = script->GetBuffers();
- ASSERT_EQ(2U, buffers.size());
- ASSERT_EQ(BufferType::kIndex, buffers[1]->GetBufferType());
-
- auto buffer_ptr = buffers[1].get();
- auto buffer = buffer_ptr;
- EXPECT_TRUE(buffer->GetFormat()->IsUint32());
- EXPECT_EQ(3U, buffer->ElementCount());
- EXPECT_EQ(3U, buffer->ValueCount());
- EXPECT_EQ(3U * sizeof(uint32_t), buffer->GetSizeInBytes());
-
- const auto* data = buffer->GetValues<uint32_t>();
- EXPECT_EQ(1, data[0]);
- EXPECT_EQ(2, data[1]);
- EXPECT_EQ(3, data[2]);
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
+
+ EXPECT_TRUE(bufs[1]->GetFormat()->IsUint32());
+ EXPECT_EQ(3U, bufs[1]->ElementCount());
+ EXPECT_EQ(3U, bufs[1]->ValueCount());
+ EXPECT_EQ(3U * sizeof(uint32_t), bufs[1]->GetSizeInBytes());
+
+ const auto* data = bufs[1]->GetValues<uint32_t>();
+ EXPECT_EQ(1u, data[0]);
+ EXPECT_EQ(2u, data[1]);
+ EXPECT_EQ(3u, data[2]);
}
TEST_F(VkScriptParserTest, IndicesBlockMultipleLines) {
@@ -286,13 +279,12 @@ TEST_F(VkScriptParserTest, IndicesBlockMultipleLines) {
ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto script = parser.GetScript();
- auto& buffers = script->GetBuffers();
- ASSERT_EQ(2U, buffers.size());
- ASSERT_EQ(buffers[1]->GetBufferType(), BufferType::kIndex);
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
- const auto* data = buffers[1]->GetValues<uint32_t>();
+ const auto* data = bufs[1]->GetValues<uint32_t>();
std::vector<uint16_t> results = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
- ASSERT_EQ(results.size(), buffers[1]->ValueCount());
+ ASSERT_EQ(results.size(), bufs[1]->ValueCount());
for (size_t i = 0; i < results.size(); ++i) {
EXPECT_EQ(results[i], data[i]);
}
@@ -337,8 +329,8 @@ TEST_F(VkScriptParserTest, VertexDataHeaderFormatString) {
ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto script = parser.GetScript();
- const auto& buffers = script->GetBuffers();
- ASSERT_EQ(3U, buffers.size());
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(3U, bufs.size());
ASSERT_EQ(1U, script->GetPipelines().size());
const auto* pipeline = script->GetPipelines()[0].get();
@@ -346,17 +338,14 @@ TEST_F(VkScriptParserTest, VertexDataHeaderFormatString) {
ASSERT_EQ(2U, pipeline->GetVertexBuffers().size());
const auto& pipeline_buffers = pipeline->GetVertexBuffers();
- ASSERT_EQ(BufferType::kVertex, buffers[1]->GetBufferType());
EXPECT_EQ(static_cast<uint8_t>(0U), pipeline_buffers[0].location);
- EXPECT_EQ(FormatType::kR32G32_SFLOAT,
- buffers[1]->GetFormat()->GetFormatType());
- EXPECT_EQ(static_cast<uint32_t>(0), buffers[1]->ElementCount());
+ EXPECT_EQ(FormatType::kR32G32_SFLOAT, bufs[1]->GetFormat()->GetFormatType());
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[1]->ElementCount());
- ASSERT_EQ(BufferType::kVertex, buffers[2]->GetBufferType());
EXPECT_EQ(1U, pipeline_buffers[1].location);
EXPECT_EQ(FormatType::kA8B8G8R8_UNORM_PACK32,
- buffers[2]->GetFormat()->GetFormatType());
- EXPECT_EQ(static_cast<uint32_t>(0), buffers[2]->ElementCount());
+ bufs[2]->GetFormat()->GetFormatType());
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[2]->ElementCount());
}
TEST_F(VkScriptParserTest, VertexDataHeaderGlslString) {
@@ -368,8 +357,8 @@ TEST_F(VkScriptParserTest, VertexDataHeaderGlslString) {
ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto script = parser.GetScript();
- const auto& buffers = script->GetBuffers();
- ASSERT_EQ(3U, buffers.size());
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(3U, bufs.size());
ASSERT_EQ(1U, script->GetPipelines().size());
const auto* pipeline = script->GetPipelines()[0].get();
@@ -377,30 +366,26 @@ TEST_F(VkScriptParserTest, VertexDataHeaderGlslString) {
ASSERT_EQ(2U, pipeline->GetVertexBuffers().size());
const auto& pipeline_buffers = pipeline->GetVertexBuffers();
- ASSERT_EQ(BufferType::kVertex, buffers[1]->GetBufferType());
EXPECT_EQ(static_cast<uint8_t>(0U), pipeline_buffers[0].location);
- EXPECT_EQ(FormatType::kR32G32_SFLOAT,
- buffers[1]->GetFormat()->GetFormatType());
+ EXPECT_EQ(FormatType::kR32G32_SFLOAT, bufs[1]->GetFormat()->GetFormatType());
- auto& segs1 = buffers[1]->GetFormat()->GetSegments();
+ auto& segs1 = bufs[1]->GetFormat()->GetSegments();
ASSERT_EQ(2U, segs1.size());
EXPECT_EQ(FormatMode::kSFloat, segs1[0].GetFormatMode());
EXPECT_EQ(FormatMode::kSFloat, segs1[1].GetFormatMode());
- EXPECT_EQ(static_cast<uint32_t>(0), buffers[1]->ElementCount());
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[1]->ElementCount());
- ASSERT_EQ(BufferType::kVertex, buffers[2]->GetBufferType());
EXPECT_EQ(1U, pipeline_buffers[1].location);
- EXPECT_EQ(FormatType::kR32G32B32_SINT,
- buffers[2]->GetFormat()->GetFormatType());
+ EXPECT_EQ(FormatType::kR32G32B32_SINT, bufs[2]->GetFormat()->GetFormatType());
- auto& segs2 = buffers[2]->GetFormat()->GetSegments();
- ASSERT_EQ(4, segs2.size());
+ auto& segs2 = bufs[2]->GetFormat()->GetSegments();
+ ASSERT_EQ(4u, segs2.size());
EXPECT_EQ(FormatMode::kSInt, segs2[0].GetFormatMode());
EXPECT_EQ(FormatMode::kSInt, segs2[1].GetFormatMode());
EXPECT_EQ(FormatMode::kSInt, segs2[2].GetFormatMode());
EXPECT_TRUE(segs2[3].IsPadding());
- EXPECT_EQ(static_cast<uint32_t>(0), buffers[2]->ElementCount());
+ EXPECT_EQ(static_cast<uint32_t>(0), bufs[2]->ElementCount());
}
TEST_F(VkScriptParserTest, TestBlock) {
@@ -450,21 +435,17 @@ TEST_F(VkScriptParserTest, VertexDataRows) {
ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto script = parser.GetScript();
- const auto& buffers = script->GetBuffers();
- ASSERT_EQ(3U, buffers.size());
-
- ASSERT_EQ(BufferType::kVertex, buffers[1]->GetBufferType());
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(3U, bufs.size());
std::vector<float> seg_0 = {-1.f, -1.f, 0.25f, 0, 0.25f, -1.f, 0.25f, 0};
- const auto* values_0 = buffers[1]->GetValues<float>();
+ const auto* values_0 = bufs[1]->GetValues<float>();
for (size_t i = 0; i < seg_0.size(); ++i) {
EXPECT_FLOAT_EQ(seg_0[i], values_0[i]);
}
- ASSERT_EQ(BufferType::kVertex, buffers[2]->GetBufferType());
-
std::vector<uint8_t> seg_1 = {255, 128, 1, 0, 255, 128, 255, 0};
- const auto* values_1 = buffers[2]->GetValues<uint8_t>();
+ const auto* values_1 = bufs[2]->GetValues<uint8_t>();
for (size_t i = 0; i < seg_1.size(); ++i) {
EXPECT_EQ(seg_1[i], values_1[i]);
}
@@ -509,13 +490,12 @@ TEST_F(VkScriptParserTest, VertexDataRowsWithHex) {
ASSERT_TRUE(r.IsSuccess()) << r.Error();
auto script = parser.GetScript();
- const auto& buffers = script->GetBuffers();
- ASSERT_EQ(2U, buffers.size());
- ASSERT_EQ(BufferType::kVertex, buffers[1]->GetBufferType());
+ const auto& bufs = script->GetBuffers();
+ ASSERT_EQ(2U, bufs.size());
std::vector<uint32_t> seg_0 = {0xff0000ff, 0xffff0000};
- const auto* values_0 = buffers[1]->GetValues<uint32_t>();
- ASSERT_EQ(seg_0.size(), buffers[1]->ValueCount());
+ const auto* values_0 = bufs[1]->GetValues<uint32_t>();
+ ASSERT_EQ(seg_0.size(), bufs[1]->ValueCount());
for (size_t i = 0; i < seg_0.size(); ++i) {
EXPECT_EQ(seg_0[i], values_0[i]);
diff --git a/src/vulkan/CMakeLists.txt b/src/vulkan/CMakeLists.txt
index 6c6d299..b633c71 100644
--- a/src/vulkan/CMakeLists.txt
+++ b/src/vulkan/CMakeLists.txt
@@ -14,17 +14,23 @@
set(VULKAN_ENGINE_SOURCES
buffer_descriptor.cc
+ buffer_backed_descriptor.cc
command_buffer.cc
command_pool.cc
compute_pipeline.cc
device.cc
+ descriptor.cc
engine_vulkan.cc
+ engine_vulkan_debugger.cc
frame_buffer.cc
graphics_pipeline.cc
+ image_descriptor.cc
index_buffer.cc
pipeline.cc
push_constant.cc
resource.cc
+ sampler.cc
+ sampler_descriptor.cc
transfer_buffer.cc
transfer_image.cc
vertex_buffer.cc
@@ -34,6 +40,9 @@ set(VULKAN_ENGINE_SOURCES
add_library(libamberenginevulkan ${VULKAN_ENGINE_SOURCES})
amber_default_compile_options(libamberenginevulkan)
target_include_directories(libamberenginevulkan PRIVATE "${CMAKE_BINARY_DIR}")
+# Add the Vulkan include directory to the list of include paths.
+target_include_directories(libamberenginevulkan PRIVATE "${VulkanHeaders_INCLUDE_DIR}")
+
set_target_properties(libamberenginevulkan PROPERTIES
OUTPUT_NAME "amberenginevulkan"
)
@@ -48,6 +57,10 @@ if(${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
target_compile_options(libamberenginevulkan PRIVATE -Wno-zero-as-null-pointer-constant)
endif()
+if (${AMBER_ENABLE_VK_DEBUGGING})
+ target_link_libraries(libamberenginevulkan cppdap)
+endif()
+
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/src/vk-wrappers.inc.fake
COMMAND
diff --git a/src/vulkan/buffer_backed_descriptor.cc b/src/vulkan/buffer_backed_descriptor.cc
new file mode 100644
index 0000000..2f7aa98
--- /dev/null
+++ b/src/vulkan/buffer_backed_descriptor.cc
@@ -0,0 +1,139 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/vulkan/buffer_backed_descriptor.h"
+
+#include <cstring>
+
+#include "src/vulkan/command_buffer.h"
+#include "src/vulkan/device.h"
+
+namespace amber {
+namespace vulkan {
+
+BufferBackedDescriptor::BufferBackedDescriptor(Buffer* buffer,
+ DescriptorType type,
+ Device* device,
+ uint32_t desc_set,
+ uint32_t binding)
+ : Descriptor(type, device, desc_set, binding) {
+ AddAmberBuffer(buffer);
+}
+
+BufferBackedDescriptor::~BufferBackedDescriptor() = default;
+
+Result BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded(
+ CommandBuffer* command) {
+ auto resources = GetResources();
+ auto buffers = GetAmberBuffers();
+ if (resources.size() != buffers.size())
+ return Result(
+ "Vulkan: BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded() "
+ "resource and buffer vector sizes are not matching");
+
+ for (size_t i = 0; i < resources.size(); i++) {
+ if (!buffers[i]->ValuePtr()->empty()) {
+ resources[i]->UpdateMemoryWithRawData(*buffers[i]->ValuePtr());
+ // If the resource is read-only, keep the buffer data; Amber won't copy
+ // read-only resources back into the host buffers, so it makes sense to
+ // leave the buffer intact.
+ if (!IsReadOnly())
+ buffers[i]->ValuePtr()->clear();
+ }
+
+ resources[i]->CopyToDevice(command);
+ }
+
+ return {};
+}
+
+Result BufferBackedDescriptor::RecordCopyDataToHost(CommandBuffer* command) {
+ if (!IsReadOnly()) {
+ if (GetResources().empty()) {
+ return Result(
+ "Vulkan: BufferBackedDescriptor::RecordCopyDataToHost() no transfer "
+ "resources");
+ }
+
+ for (const auto& r : GetResources())
+ r->CopyToHost(command);
+ }
+
+ return {};
+}
+
+Result BufferBackedDescriptor::MoveResourceToBufferOutput() {
+ // No need to copy results of read only resources.
+ if (IsReadOnly())
+ return {};
+
+ auto resources = GetResources();
+ auto buffers = GetAmberBuffers();
+ if (resources.size() != buffers.size())
+ return Result(
+ "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() resource "
+ "and buffer vector sizes are not matching");
+
+ if (resources.empty()) {
+ return Result(
+ "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() no "
+ "transfer resource");
+ }
+
+ for (size_t i = 0; i < resources.size(); i++) {
+ void* resource_memory_ptr = resources[i]->HostAccessibleMemoryPtr();
+ if (!resource_memory_ptr) {
+ return Result(
+ "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() "
+ "no host accessible memory pointer");
+ }
+
+ if (!buffers[i]->ValuePtr()->empty()) {
+ return Result(
+ "Vulkan: BufferBackedDescriptor::MoveResourceToBufferOutput() "
+ "output buffer is not empty");
+ }
+
+ auto size_in_bytes = resources[i]->GetSizeInBytes();
+ buffers[i]->SetElementCount(size_in_bytes /
+ buffers[i]->GetFormat()->SizeInBytes());
+ buffers[i]->ValuePtr()->resize(size_in_bytes);
+ std::memcpy(buffers[i]->ValuePtr()->data(), resource_memory_ptr,
+ size_in_bytes);
+ }
+
+ return {};
+}
+
+bool BufferBackedDescriptor::IsReadOnly() const {
+ switch (type_) {
+ case DescriptorType::kUniformBuffer:
+ case DescriptorType::kUniformBufferDynamic:
+ case DescriptorType::kUniformTexelBuffer:
+ case DescriptorType::kSampledImage:
+ case DescriptorType::kCombinedImageSampler:
+ return true;
+ case DescriptorType::kStorageBuffer:
+ case DescriptorType::kStorageBufferDynamic:
+ case DescriptorType::kStorageTexelBuffer:
+ case DescriptorType::kStorageImage:
+ return false;
+ default:
+ assert(false && "Unexpected descriptor type");
+ return false;
+ }
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/buffer_backed_descriptor.h b/src/vulkan/buffer_backed_descriptor.h
new file mode 100644
index 0000000..c626dd8
--- /dev/null
+++ b/src/vulkan/buffer_backed_descriptor.h
@@ -0,0 +1,63 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_VULKAN_BUFFER_BACKED_DESCRIPTOR_H_
+#define SRC_VULKAN_BUFFER_BACKED_DESCRIPTOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "amber/result.h"
+#include "amber/value.h"
+#include "amber/vulkan_header.h"
+#include "src/buffer.h"
+#include "src/engine.h"
+#include "src/vulkan/descriptor.h"
+#include "src/vulkan/resource.h"
+
+namespace amber {
+namespace vulkan {
+
+class BufferBackedDescriptor : public Descriptor {
+ public:
+ BufferBackedDescriptor(Buffer* buffer,
+ DescriptorType type,
+ Device* device,
+ uint32_t desc_set,
+ uint32_t binding);
+ ~BufferBackedDescriptor() override;
+
+ Result CreateResourceIfNeeded() override { return {}; }
+ Result RecordCopyDataToResourceIfNeeded(CommandBuffer* command) override;
+ Result RecordCopyDataToHost(CommandBuffer* command) override;
+ Result MoveResourceToBufferOutput() override;
+ uint32_t GetDescriptorCount() override {
+ return static_cast<uint32_t>(amber_buffers_.size());
+ }
+ const std::vector<Buffer*>& GetAmberBuffers() const { return amber_buffers_; }
+ void AddAmberBuffer(Buffer* buffer) { amber_buffers_.push_back(buffer); }
+ BufferBackedDescriptor* AsBufferBackedDescriptor() override { return this; }
+ bool IsReadOnly() const;
+
+ protected:
+ virtual std::vector<Resource*> GetResources() = 0;
+
+ private:
+ std::vector<Buffer*> amber_buffers_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_BUFFER_BACKED_DESCRIPTOR_H_
diff --git a/src/vulkan/buffer_descriptor.cc b/src/vulkan/buffer_descriptor.cc
index 4272cd4..510565d 100644
--- a/src/vulkan/buffer_descriptor.cc
+++ b/src/vulkan/buffer_descriptor.cc
@@ -14,13 +14,8 @@
#include "src/vulkan/buffer_descriptor.h"
-#include <algorithm>
-#include <cassert>
-#include <cstring>
-#include <utility>
#include <vector>
-#include "src/engine.h"
#include "src/make_unique.h"
#include "src/vulkan/command_buffer.h"
#include "src/vulkan/device.h"
@@ -33,96 +28,57 @@ BufferDescriptor::BufferDescriptor(Buffer* buffer,
Device* device,
uint32_t desc_set,
uint32_t binding)
- : device_(device),
- amber_buffer_(buffer),
- type_(type),
- descriptor_set_(desc_set),
- binding_(binding) {}
+ : BufferBackedDescriptor(buffer, type, device, desc_set, binding) {}
BufferDescriptor::~BufferDescriptor() = default;
Result BufferDescriptor::CreateResourceIfNeeded() {
- if (transfer_buffer_) {
+ if (!transfer_buffers_.empty()) {
return Result(
"Vulkan: BufferDescriptor::CreateResourceIfNeeded() must be called "
- "only when |transfer_buffer| is empty");
+ "only when |transfer_buffers| is empty");
}
- if (amber_buffer_ && amber_buffer_->ValuePtr()->empty())
- return {};
-
- uint32_t size_in_bytes =
- amber_buffer_ ? static_cast<uint32_t>(amber_buffer_->ValuePtr()->size())
- : 0;
- transfer_buffer_ = MakeUnique<TransferBuffer>(device_, size_in_bytes);
-
- Result r = transfer_buffer_->Initialize(
- (IsStorageBuffer() ? VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
- : VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) |
- VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
- if (!r.IsSuccess())
- return r;
-
- is_descriptor_set_update_needed_ = true;
- return {};
-}
-
-void BufferDescriptor::RecordCopyDataToResourceIfNeeded(
- CommandBuffer* command) {
- if (!transfer_buffer_)
- return;
-
- if (amber_buffer_ && !amber_buffer_->ValuePtr()->empty()) {
- transfer_buffer_->UpdateMemoryWithRawData(*amber_buffer_->ValuePtr());
- amber_buffer_->ValuePtr()->clear();
- }
-
- transfer_buffer_->CopyToDevice(command);
-}
+ transfer_buffers_.reserve(GetAmberBuffers().size());
+
+ for (const auto& amber_buffer : GetAmberBuffers()) {
+ if (amber_buffer->ValuePtr()->empty())
+ continue;
+
+ uint32_t size_in_bytes =
+ amber_buffer ? static_cast<uint32_t>(amber_buffer->ValuePtr()->size())
+ : 0;
+ transfer_buffers_.emplace_back(MakeUnique<TransferBuffer>(
+ device_, size_in_bytes, amber_buffer->GetFormat()));
+ VkBufferUsageFlags flags =
+ VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+ if (IsUniformBuffer() || IsUniformBufferDynamic()) {
+ flags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ } else if (IsStorageBuffer() || IsStorageBufferDynamic()) {
+ flags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+ } else if (IsUniformTexelBuffer()) {
+ flags |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
+ } else if (IsStorageTexelBuffer()) {
+ flags |= VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT;
+ } else {
+ return Result("Unexpected buffer type when deciding usage flags");
+ }
-Result BufferDescriptor::RecordCopyDataToHost(CommandBuffer* command) {
- if (!transfer_buffer_) {
- return Result(
- "Vulkan: BufferDescriptor::RecordCopyDataToHost() no transfer buffer");
+ Result r = transfer_buffers_.back()->Initialize(flags);
+ if (!r.IsSuccess())
+ return r;
}
- transfer_buffer_->CopyToHost(command);
+ is_descriptor_set_update_needed_ = true;
return {};
}
Result BufferDescriptor::MoveResourceToBufferOutput() {
- if (!transfer_buffer_) {
- return Result(
- "Vulkan: BufferDescriptor::MoveResourceToBufferOutput() no transfer"
- " buffer");
- }
+ Result r = BufferBackedDescriptor::MoveResourceToBufferOutput();
- // Only need to copy the buffer back if we have an attached amber buffer to
- // write too.
- if (amber_buffer_) {
- void* resource_memory_ptr = transfer_buffer_->HostAccessibleMemoryPtr();
- if (!resource_memory_ptr) {
- return Result(
- "Vulkan: BufferDescriptor::MoveResourceToBufferOutput() "
- "no host accessible memory pointer");
- }
+ transfer_buffers_.clear();
- if (!amber_buffer_->ValuePtr()->empty()) {
- return Result(
- "Vulkan: BufferDescriptor::MoveResourceToBufferOutput() "
- "output buffer is not empty");
- }
-
- auto size_in_bytes = transfer_buffer_->GetSizeInBytes();
- amber_buffer_->SetElementCount(size_in_bytes /
- amber_buffer_->GetFormat()->SizeInBytes());
- amber_buffer_->ValuePtr()->resize(size_in_bytes);
- std::memcpy(amber_buffer_->ValuePtr()->data(), resource_memory_ptr,
- size_in_bytes);
- }
-
- transfer_buffer_ = nullptr;
- return {};
+ return r;
}
void BufferDescriptor::UpdateDescriptorSetIfNeeded(
@@ -130,45 +86,42 @@ void BufferDescriptor::UpdateDescriptorSetIfNeeded(
if (!is_descriptor_set_update_needed_)
return;
- VkDescriptorBufferInfo buffer_info = VkDescriptorBufferInfo();
- buffer_info.buffer = transfer_buffer_->GetVkBuffer();
- buffer_info.offset = 0;
- buffer_info.range = VK_WHOLE_SIZE;
+ std::vector<VkDescriptorBufferInfo> buffer_infos;
+ std::vector<VkBufferView> buffer_views;
+
+ for (const auto& buffer : transfer_buffers_) {
+ VkDescriptorBufferInfo buffer_info;
+ buffer_info.buffer = buffer->GetVkBuffer();
+ buffer_info.offset = 0;
+ buffer_info.range = VK_WHOLE_SIZE;
+ buffer_infos.push_back(buffer_info);
+
+ if (IsUniformTexelBuffer() || IsStorageTexelBuffer()) {
+ buffer_views.push_back(*buffer->GetVkBufferView());
+ }
+ }
VkWriteDescriptorSet write = VkWriteDescriptorSet();
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = descriptor_set;
write.dstBinding = binding_;
write.dstArrayElement = 0;
- write.descriptorCount = 1;
- write.descriptorType = IsStorageBuffer() ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
- : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
- write.pBufferInfo = &buffer_info;
+ write.descriptorCount = static_cast<uint32_t>(buffer_infos.size());
+ write.descriptorType = GetVkDescriptorType();
+ write.pBufferInfo = buffer_infos.data();
+ write.pTexelBufferView = buffer_views.data();
device_->GetPtrs()->vkUpdateDescriptorSets(device_->GetVkDevice(), 1, &write,
0, nullptr);
is_descriptor_set_update_needed_ = false;
}
-Result BufferDescriptor::SetSizeInElements(uint32_t element_count) {
- if (!amber_buffer_)
- return Result("missing amber_buffer for SetSizeInElements call");
-
- amber_buffer_->SetSizeInElements(element_count);
- return {};
-}
-
-Result BufferDescriptor::AddToBuffer(const std::vector<Value>& values,
- uint32_t offset) {
- if (!amber_buffer_)
- return Result("missing amber_buffer for AddToBuffer call");
-
- return amber_buffer_->SetDataWithOffset(values, offset);
-}
-
-VkDescriptorType BufferDescriptor::GetVkDescriptorType() const {
- return IsStorageBuffer() ? VK_DESCRIPTOR_TYPE_STORAGE_BUFFER
- : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+std::vector<Resource*> BufferDescriptor::GetResources() {
+ std::vector<Resource*> ret;
+ for (auto& b : transfer_buffers_) {
+ ret.push_back(b.get());
+ }
+ return ret;
}
} // namespace vulkan
diff --git a/src/vulkan/buffer_descriptor.h b/src/vulkan/buffer_descriptor.h
index 3c6a690..d481ae5 100644
--- a/src/vulkan/buffer_descriptor.h
+++ b/src/vulkan/buffer_descriptor.h
@@ -23,6 +23,7 @@
#include "amber/vulkan_header.h"
#include "src/buffer.h"
#include "src/engine.h"
+#include "src/vulkan/buffer_backed_descriptor.h"
#include "src/vulkan/transfer_buffer.h"
namespace amber {
@@ -31,53 +32,32 @@ namespace vulkan {
class CommandBuffer;
class Device;
-enum class DescriptorType : uint8_t {
- kStorageBuffer = 0,
- kUniformBuffer,
-};
-
/// Stores descriptor set and binding information for storage and uniform
/// buffers.
-class BufferDescriptor {
+class BufferDescriptor : public BufferBackedDescriptor {
public:
BufferDescriptor(Buffer* buffer,
DescriptorType type,
Device* device,
uint32_t desc_set,
uint32_t binding);
- ~BufferDescriptor();
-
- uint32_t GetDescriptorSet() const { return descriptor_set_; }
- uint32_t GetBinding() const { return binding_; }
-
- VkDescriptorType GetVkDescriptorType() const;
+ ~BufferDescriptor() override;
- bool IsStorageBuffer() const {
- return type_ == DescriptorType::kStorageBuffer;
+ void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set) override;
+ Result CreateResourceIfNeeded() override;
+ Result MoveResourceToBufferOutput() override;
+ std::vector<uint32_t> GetDynamicOffsets() override {
+ return dynamic_offsets_;
}
- bool IsUniformBuffer() const {
- return type_ == DescriptorType::kUniformBuffer;
- }
-
- Result CreateResourceIfNeeded();
- void RecordCopyDataToResourceIfNeeded(CommandBuffer* command);
- Result RecordCopyDataToHost(CommandBuffer* command);
- Result MoveResourceToBufferOutput();
- void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set);
+ void AddDynamicOffset(uint32_t offset) { dynamic_offsets_.push_back(offset); }
+ BufferDescriptor* AsBufferDescriptor() override { return this; }
- Result SetSizeInElements(uint32_t element_count);
- Result AddToBuffer(const std::vector<Value>& values, uint32_t offset);
+ protected:
+ std::vector<Resource*> GetResources() override;
private:
- Device* device_ = nullptr;
- Buffer* amber_buffer_ = nullptr;
- std::unique_ptr<TransferBuffer> transfer_buffer_;
-
- DescriptorType type_ = DescriptorType::kStorageBuffer;
-
- bool is_descriptor_set_update_needed_ = false;
- uint32_t descriptor_set_ = 0;
- uint32_t binding_ = 0;
+ std::vector<std::unique_ptr<TransferBuffer>> transfer_buffers_;
+ std::vector<uint32_t> dynamic_offsets_;
};
} // namespace vulkan
diff --git a/src/vulkan/command_buffer.cc b/src/vulkan/command_buffer.cc
index bcf0ac9..2843667 100644
--- a/src/vulkan/command_buffer.cc
+++ b/src/vulkan/command_buffer.cc
@@ -26,6 +26,8 @@ CommandBuffer::CommandBuffer(Device* device, CommandPool* pool)
: device_(device), pool_(pool) {}
CommandBuffer::~CommandBuffer() {
+ Reset();
+
if (fence_ != VK_NULL_HANDLE)
device_->GetPtrs()->vkDestroyFence(device_->GetVkDevice(), fence_, nullptr);
@@ -65,6 +67,7 @@ Result CommandBuffer::BeginRecording() {
VK_SUCCESS) {
return Result("Vulkan::Calling vkBeginCommandBuffer Fail");
}
+ guarded_ = true;
return {};
}
@@ -87,6 +90,8 @@ Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms) {
return Result("Vulkan::Calling vkQueueSubmit Fail");
}
+ guarded_ = false;
+
VkResult r = device_->GetPtrs()->vkWaitForFences(
device_->GetVkDevice(), 1, &fence_, VK_TRUE,
static_cast<uint64_t>(timeout_ms) * 1000ULL * 1000ULL /* nanosecond */);
@@ -101,22 +106,28 @@ Result CommandBuffer::SubmitAndReset(uint32_t timeout_ms) {
return {};
}
+void CommandBuffer::Reset() {
+ if (guarded_) {
+ device_->GetPtrs()->vkEndCommandBuffer(command_);
+ device_->GetPtrs()->vkResetCommandBuffer(command_, 0);
+ guarded_ = false;
+ }
+}
+
CommandBufferGuard::CommandBufferGuard(CommandBuffer* buffer)
: buffer_(buffer) {
assert(!buffer_->guarded_);
-
- buffer_->guarded_ = true;
result_ = buffer_->BeginRecording();
}
-CommandBufferGuard::~CommandBufferGuard() = default;
+CommandBufferGuard::~CommandBufferGuard() {
+ if (buffer_->guarded_)
+ buffer_->Reset();
+}
Result CommandBufferGuard::Submit(uint32_t timeout_ms) {
assert(buffer_->guarded_);
-
- result_ = buffer_->SubmitAndReset(timeout_ms);
- buffer_->guarded_ = false;
- return result_;
+ return buffer_->SubmitAndReset(timeout_ms);
}
} // namespace vulkan
diff --git a/src/vulkan/command_buffer.h b/src/vulkan/command_buffer.h
index 6bd0d68..377d052 100644
--- a/src/vulkan/command_buffer.h
+++ b/src/vulkan/command_buffer.h
@@ -49,6 +49,7 @@ class CommandBuffer {
Result BeginRecording();
Result SubmitAndReset(uint32_t timeout_ms);
+ void Reset();
bool guarded_ = false;
diff --git a/src/vulkan/descriptor.cc b/src/vulkan/descriptor.cc
new file mode 100644
index 0000000..169c71c
--- /dev/null
+++ b/src/vulkan/descriptor.cc
@@ -0,0 +1,64 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/vulkan/descriptor.h"
+
+#include <cassert>
+#include <cstring>
+
+#include "src/vulkan/command_buffer.h"
+#include "src/vulkan/device.h"
+
+namespace amber {
+namespace vulkan {
+
+Descriptor::Descriptor(DescriptorType type,
+ Device* device,
+ uint32_t desc_set,
+ uint32_t binding)
+ : device_(device),
+ type_(type),
+ descriptor_set_(desc_set),
+ binding_(binding) {}
+
+Descriptor::~Descriptor() = default;
+
+VkDescriptorType Descriptor::GetVkDescriptorType() const {
+ switch (type_) {
+ case DescriptorType::kStorageBuffer:
+ return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
+ case DescriptorType::kStorageBufferDynamic:
+ return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
+ case DescriptorType::kUniformBuffer:
+ return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
+ case DescriptorType::kUniformBufferDynamic:
+ return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
+ case DescriptorType::kSampler:
+ return VK_DESCRIPTOR_TYPE_SAMPLER;
+ case DescriptorType::kStorageImage:
+ return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
+ case DescriptorType::kCombinedImageSampler:
+ return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ case DescriptorType::kUniformTexelBuffer:
+ return VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+ case DescriptorType::kStorageTexelBuffer:
+ return VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER;
+ default:
+ assert(type_ == DescriptorType::kSampledImage);
+ return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
+ }
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/descriptor.h b/src/vulkan/descriptor.h
new file mode 100644
index 0000000..ba702ff
--- /dev/null
+++ b/src/vulkan/descriptor.h
@@ -0,0 +1,105 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_VULKAN_DESCRIPTOR_H_
+#define SRC_VULKAN_DESCRIPTOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "amber/result.h"
+#include "amber/value.h"
+#include "amber/vulkan_header.h"
+#include "src/buffer.h"
+#include "src/engine.h"
+#include "src/vulkan/resource.h"
+
+namespace amber {
+namespace vulkan {
+
+class CommandBuffer;
+class Device;
+class BufferDescriptor;
+class BufferBackedDescriptor;
+class SamplerDescriptor;
+
+enum class DescriptorType : uint8_t {
+ kStorageBuffer = 0,
+ kStorageBufferDynamic,
+ kUniformBuffer,
+ kUniformBufferDynamic,
+ kStorageImage,
+ kSampledImage,
+ kCombinedImageSampler,
+ kUniformTexelBuffer,
+ kStorageTexelBuffer,
+ kSampler
+};
+
+class Descriptor {
+ public:
+ Descriptor(DescriptorType type,
+ Device* device,
+ uint32_t desc_set,
+ uint32_t binding);
+ virtual ~Descriptor();
+
+ virtual void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set) = 0;
+ virtual Result CreateResourceIfNeeded() = 0;
+ virtual Result RecordCopyDataToResourceIfNeeded(CommandBuffer*) { return {}; }
+ virtual Result RecordCopyDataToHost(CommandBuffer*) { return {}; }
+ virtual Result MoveResourceToBufferOutput() { return {}; }
+ virtual Result SetSizeInElements(uint32_t) { return {}; }
+ virtual Result AddToBuffer(const std::vector<Value>&, uint32_t) { return {}; }
+ virtual uint32_t GetDescriptorCount() { return 1; }
+ virtual std::vector<uint32_t> GetDynamicOffsets() { return {}; }
+ virtual BufferDescriptor* AsBufferDescriptor() { return nullptr; }
+ virtual BufferBackedDescriptor* AsBufferBackedDescriptor() { return nullptr; }
+ virtual SamplerDescriptor* AsSamplerDescriptor() { return nullptr; }
+ uint32_t GetDescriptorSet() const { return descriptor_set_; }
+ uint32_t GetBinding() const { return binding_; }
+ VkDescriptorType GetVkDescriptorType() const;
+ DescriptorType GetDescriptorType() const { return type_; }
+
+ bool IsStorageBuffer() const {
+ return type_ == DescriptorType::kStorageBuffer;
+ }
+ bool IsStorageBufferDynamic() const {
+ return type_ == DescriptorType::kStorageBufferDynamic;
+ }
+ bool IsUniformBuffer() const {
+ return type_ == DescriptorType::kUniformBuffer;
+ }
+ bool IsUniformBufferDynamic() const {
+ return type_ == DescriptorType::kUniformBufferDynamic;
+ }
+ bool IsUniformTexelBuffer() const {
+ return type_ == DescriptorType::kUniformTexelBuffer;
+ }
+ bool IsStorageTexelBuffer() const {
+ return type_ == DescriptorType::kStorageTexelBuffer;
+ }
+
+ protected:
+ Device* device_ = nullptr;
+ DescriptorType type_ = DescriptorType::kStorageBuffer;
+ uint32_t descriptor_set_ = 0;
+ uint32_t binding_ = 0;
+ bool is_descriptor_set_update_needed_ = false;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_DESCRIPTOR_H_
diff --git a/src/vulkan/device.cc b/src/vulkan/device.cc
index b985ac7..22cd77a 100644
--- a/src/vulkan/device.cc
+++ b/src/vulkan/device.cc
@@ -14,6 +14,7 @@
#include "src/vulkan/device.h"
+#include <algorithm>
#include <cstring>
#include <iomanip> // Vulkan wrappers: std::setw(), std::left/right
#include <iostream>
@@ -32,6 +33,25 @@ namespace {
const char kVariablePointers[] = "VariablePointerFeatures.variablePointers";
const char kVariablePointersStorageBuffer[] =
"VariablePointerFeatures.variablePointersStorageBuffer";
+const char kFloat16Int8_Float16[] = "Float16Int8Features.shaderFloat16";
+const char kFloat16Int8_Int8[] = "Float16Int8Features.shaderInt8";
+const char k8BitStorage_Storage[] =
+ "Storage8BitFeatures.storageBuffer8BitAccess";
+const char k8BitStorage_UniformAndStorage[] =
+ "Storage8BitFeatures.uniformAndStorageBuffer8BitAccess";
+const char k8BitStorage_PushConstant[] =
+ "Storage8BitFeatures.storagePushConstant8";
+const char k16BitStorage_Storage[] =
+ "Storage16BitFeatures.storageBuffer16BitAccess";
+const char k16BitStorage_UniformAndStorage[] =
+ "Storage16BitFeatures.uniformAndStorageBuffer16BitAccess";
+const char k16BitStorage_PushConstant[] =
+ "Storage16BitFeatures.storagePushConstant16";
+const char k16BitStorage_InputOutput[] =
+ "Storage16BitFeatures.storageInputOutput16";
+
+const char kSubgroupSizeControl[] = "SubgroupSizeControl.subgroupSizeControl";
+const char kComputeFullSubgroups[] = "SubgroupSizeControl.computeFullSubgroups";
struct BaseOutStructure {
VkStructureType sType;
@@ -365,16 +385,33 @@ Result Device::LoadVulkanPointers(PFN_vkGetInstanceProcAddr getInstanceProcAddr,
if (delegate && delegate->LogGraphicsCalls())
delegate->Log("Loading Vulkan Pointers");
-#include "vk-wrappers.inc"
+#include "vk-wrappers-1-0.inc"
+
+ ptrs_.vkGetPhysicalDeviceProperties(physical_device_,
+ &physical_device_properties_);
+
+ if (SupportsApiVersion(1, 1, 0)) {
+#include "vk-wrappers-1-1.inc"
+ }
return {};
}
+bool Device::SupportsApiVersion(uint32_t major,
+ uint32_t minor,
+ uint32_t patch) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wold-style-cast"
+ return physical_device_properties_.apiVersion >=
+ VK_MAKE_VERSION(major, minor, patch);
+#pragma clang diagnostic pop
+}
+
Result Device::Initialize(
PFN_vkGetInstanceProcAddr getInstanceProcAddr,
Delegate* delegate,
const std::vector<std::string>& required_features,
- const std::vector<std::string>& required_extensions,
+ const std::vector<std::string>& required_device_extensions,
const VkPhysicalDeviceFeatures& available_features,
const VkPhysicalDeviceFeatures2KHR& available_features2,
const std::vector<std::string>& available_extensions) {
@@ -382,47 +419,136 @@ Result Device::Initialize(
if (!r.IsSuccess())
return r;
- bool use_physical_device_features_2 = false;
- // Determine if VkPhysicalDeviceProperties2KHR should be used
- for (auto& ext : required_extensions) {
- if (ext == "VK_KHR_get_physical_device_properties2") {
- use_physical_device_features_2 = true;
- }
+ // Check for the core features. We don't know if available_features or
+ // available_features2 is provided, so check both.
+ if (!AreAllRequiredFeaturesSupported(available_features, required_features) &&
+ !AreAllRequiredFeaturesSupported(available_features2.features,
+ required_features)) {
+ return Result(
+ "Vulkan: Device::Initialize given physical device does not support "
+ "required features");
}
- VkPhysicalDeviceFeatures available_vulkan_features =
- VkPhysicalDeviceFeatures();
- if (use_physical_device_features_2) {
- available_vulkan_features = available_features2.features;
-
- VkPhysicalDeviceVariablePointerFeaturesKHR* var_ptrs = nullptr;
- void* ptr = available_features2.pNext;
- while (ptr != nullptr) {
- BaseOutStructure* s = static_cast<BaseOutStructure*>(ptr);
- if (s->sType ==
- VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR) {
+ // Search for additional features in case they are found in pNext field of
+ // available_features2.
+ VkPhysicalDeviceVariablePointerFeaturesKHR* var_ptrs = nullptr;
+ VkPhysicalDeviceFloat16Int8FeaturesKHR* float16_ptrs = nullptr;
+ VkPhysicalDevice8BitStorageFeaturesKHR* storage8_ptrs = nullptr;
+ VkPhysicalDevice16BitStorageFeaturesKHR* storage16_ptrs = nullptr;
+ VkPhysicalDeviceVulkan11Features* vulkan11_ptrs = nullptr;
+ VkPhysicalDeviceVulkan12Features* vulkan12_ptrs = nullptr;
+ VkPhysicalDeviceSubgroupSizeControlFeaturesEXT*
+ subgroup_size_control_features = nullptr;
+ void* ptr = available_features2.pNext;
+ while (ptr != nullptr) {
+ BaseOutStructure* s = static_cast<BaseOutStructure*>(ptr);
+ switch (s->sType) {
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR:
var_ptrs =
static_cast<VkPhysicalDeviceVariablePointerFeaturesKHR*>(ptr);
break;
- }
- ptr = s->pNext;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR:
+ float16_ptrs =
+ static_cast<VkPhysicalDeviceFloat16Int8FeaturesKHR*>(ptr);
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR:
+ storage8_ptrs =
+ static_cast<VkPhysicalDevice8BitStorageFeaturesKHR*>(ptr);
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR:
+ storage16_ptrs =
+ static_cast<VkPhysicalDevice16BitStorageFeaturesKHR*>(ptr);
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES_EXT:
+ subgroup_size_control_features =
+ static_cast<VkPhysicalDeviceSubgroupSizeControlFeaturesEXT*>(ptr);
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES:
+ vulkan11_ptrs = static_cast<VkPhysicalDeviceVulkan11Features*>(ptr);
+ break;
+ case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES:
+ vulkan12_ptrs = static_cast<VkPhysicalDeviceVulkan12Features*>(ptr);
+ break;
+ default:
+ break;
}
+ ptr = s->pNext;
+ }
- std::vector<std::string> required_features1;
- for (const auto& feature : required_features) {
- // No dot means this is a features1 feature.
- if (feature.find_first_of('.') == std::string::npos) {
- required_features1.push_back(feature);
- continue;
- }
+ // Compare the available additional (non-core) features against the
+ // requirements.
+ //
+ // Vulkan 1.2 added support for defining non-core physical device features
+ // using VkPhysicalDeviceVulkan11Features and VkPhysicalDeviceVulkan12Features
+ // structures. If |vulkan11_ptrs| and/or |vulkan12_ptrs| are null, we must
+ // check for features using the old approach (by checking across various
+ // feature structs); otherwise, we can check features via the new structs.
+ for (const auto& feature : required_features) {
+ // First check the feature structures are provided for the required
+ // features.
+ if ((feature == kVariablePointers ||
+ feature == kVariablePointersStorageBuffer) &&
+ var_ptrs == nullptr && vulkan11_ptrs == nullptr) {
+ return amber::Result(
+ "Variable pointers requested but feature not returned");
+ }
+ if ((feature == k16BitStorage_Storage ||
+ feature == k16BitStorage_UniformAndStorage ||
+ feature == k16BitStorage_PushConstant ||
+ feature == k16BitStorage_InputOutput) &&
+ storage16_ptrs == nullptr && vulkan11_ptrs == nullptr) {
+ return amber::Result(
+ "Shader 16-bit storage requested but feature not returned");
+ }
+ if ((feature == kFloat16Int8_Float16 || feature == kFloat16Int8_Int8) &&
+ float16_ptrs == nullptr && vulkan12_ptrs == nullptr) {
+ return amber::Result(
+ "Shader float16/int8 requested but feature not returned");
+ }
+ if ((feature == k8BitStorage_UniformAndStorage ||
+ feature == k8BitStorage_Storage ||
+ feature == k8BitStorage_PushConstant) &&
+ storage8_ptrs == nullptr && vulkan12_ptrs == nullptr) {
+ return amber::Result(
+ "Shader 8-bit storage requested but feature not returned");
+ }
+ if ((feature == kSubgroupSizeControl || feature == kComputeFullSubgroups) &&
+ subgroup_size_control_features == nullptr) {
+ return amber::Result("Missing subgroup size control features");
+ }
- if ((feature == kVariablePointers ||
- feature == kVariablePointersStorageBuffer) &&
- var_ptrs == nullptr) {
+ // Next check the fields of the feature structures.
+
+ // If Vulkan 1.1 structure exists the features are set there.
+ if (vulkan11_ptrs) {
+ if (feature == kVariablePointers &&
+ vulkan11_ptrs->variablePointers != VK_TRUE) {
+ return amber::Result("Missing variable pointers feature");
+ }
+ if (feature == kVariablePointersStorageBuffer &&
+ vulkan11_ptrs->variablePointersStorageBuffer != VK_TRUE) {
return amber::Result(
- "Variable pointers requested but feature not returned");
+ "Missing variable pointers storage buffer feature");
}
-
+ if (feature == k16BitStorage_Storage &&
+ vulkan11_ptrs->storageBuffer16BitAccess != VK_TRUE) {
+ return amber::Result("Missing 16-bit storage access");
+ }
+ if (feature == k16BitStorage_UniformAndStorage &&
+ vulkan11_ptrs->uniformAndStorageBuffer16BitAccess != VK_TRUE) {
+ return amber::Result("Missing 16-bit uniform and storage access");
+ }
+ if (feature == k16BitStorage_PushConstant &&
+ vulkan11_ptrs->storagePushConstant16 != VK_TRUE) {
+ return amber::Result("Missing 16-bit push constant access");
+ }
+ if (feature == k16BitStorage_InputOutput &&
+ vulkan11_ptrs->storageInputOutput16 != VK_TRUE) {
+ return amber::Result("Missing 16-bit input/output access");
+ }
+ } else {
+ // Vulkan 1.1 structure was not found. Use separate structures per each
+ // feature.
if (feature == kVariablePointers &&
var_ptrs->variablePointers != VK_TRUE) {
return amber::Result("Missing variable pointers feature");
@@ -432,36 +558,115 @@ Result Device::Initialize(
return amber::Result(
"Missing variable pointers storage buffer feature");
}
+ if (feature == k16BitStorage_Storage &&
+ storage16_ptrs->storageBuffer16BitAccess != VK_TRUE) {
+ return amber::Result("Missing 16-bit storage access");
+ }
+ if (feature == k16BitStorage_UniformAndStorage &&
+ storage16_ptrs->uniformAndStorageBuffer16BitAccess != VK_TRUE) {
+ return amber::Result("Missing 16-bit uniform and storage access");
+ }
+ if (feature == k16BitStorage_PushConstant &&
+ storage16_ptrs->storagePushConstant16 != VK_TRUE) {
+ return amber::Result("Missing 16-bit push constant access");
+ }
+ if (feature == k16BitStorage_InputOutput &&
+ storage16_ptrs->storageInputOutput16 != VK_TRUE) {
+ return amber::Result("Missing 16-bit input/output access");
+ }
}
- } else {
- available_vulkan_features = available_features;
- }
+ // If Vulkan 1.2 structure exists the features are set there.
+ if (vulkan12_ptrs) {
+ if (feature == kFloat16Int8_Float16 &&
+ vulkan12_ptrs->shaderFloat16 != VK_TRUE) {
+ return amber::Result("Missing float16 feature");
+ }
+ if (feature == kFloat16Int8_Int8 &&
+ vulkan12_ptrs->shaderInt8 != VK_TRUE) {
+ return amber::Result("Missing int8 feature");
+ }
+ if (feature == k8BitStorage_Storage &&
+ vulkan12_ptrs->storageBuffer8BitAccess != VK_TRUE) {
+ return amber::Result("Missing 8-bit storage access");
+ }
+ if (feature == k8BitStorage_UniformAndStorage &&
+ vulkan12_ptrs->uniformAndStorageBuffer8BitAccess != VK_TRUE) {
+ return amber::Result("Missing 8-bit uniform and storage access");
+ }
+ if (feature == k8BitStorage_PushConstant &&
+ vulkan12_ptrs->storagePushConstant8 != VK_TRUE) {
+ return amber::Result("Missing 8-bit push constant access");
+ }
+ } else {
+ // Vulkan 1.2 structure was not found. Use separate structures per each
+ // feature.
+ if (feature == kFloat16Int8_Float16 &&
+ float16_ptrs->shaderFloat16 != VK_TRUE) {
+ return amber::Result("Missing float16 feature");
+ }
+ if (feature == kFloat16Int8_Int8 && float16_ptrs->shaderInt8 != VK_TRUE) {
+ return amber::Result("Missing int8 feature");
+ }
+ if (feature == k8BitStorage_Storage &&
+ storage8_ptrs->storageBuffer8BitAccess != VK_TRUE) {
+ return amber::Result("Missing 8-bit storage access");
+ }
+ if (feature == k8BitStorage_UniformAndStorage &&
+ storage8_ptrs->uniformAndStorageBuffer8BitAccess != VK_TRUE) {
+ return amber::Result("Missing 8-bit uniform and storage access");
+ }
+ if (feature == k8BitStorage_PushConstant &&
+ storage8_ptrs->storagePushConstant8 != VK_TRUE) {
+ return amber::Result("Missing 8-bit push constant access");
+ }
+ }
- if (!AreAllRequiredFeaturesSupported(available_vulkan_features,
- required_features)) {
- return Result(
- "Vulkan: Device::Initialize given physical device does not support "
- "required features");
+ if (feature == kSubgroupSizeControl &&
+ subgroup_size_control_features->subgroupSizeControl != VK_TRUE) {
+ return amber::Result("Missing subgroup size control feature");
+ }
+ if (feature == kComputeFullSubgroups &&
+ subgroup_size_control_features->computeFullSubgroups != VK_TRUE) {
+ return amber::Result("Missing compute full subgroups feature");
+ }
}
- if (!AreAllExtensionsSupported(available_extensions, required_extensions)) {
+ if (!AreAllExtensionsSupported(available_extensions,
+ required_device_extensions)) {
return Result(
"Vulkan: Device::Initialize given physical device does not support "
"required extensions");
}
- ptrs_.vkGetPhysicalDeviceProperties(physical_device_,
- &physical_device_properties_);
-
ptrs_.vkGetPhysicalDeviceMemoryProperties(physical_device_,
&physical_memory_properties_);
+ subgroup_size_control_properties_ = {};
+ const bool needs_subgroup_size_control =
+ std::find(required_features.begin(), required_features.end(),
+ kSubgroupSizeControl) != required_features.end();
+
+ if (needs_subgroup_size_control) {
+ VkPhysicalDeviceProperties2 properties2 = {};
+ properties2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+ properties2.pNext = &subgroup_size_control_properties_;
+ subgroup_size_control_properties_.sType =
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_PROPERTIES_EXT;
+
+ if (!SupportsApiVersion(1, 1, 0)) {
+ return Result(
+ "Vulkan: Device::Initialize subgroup size control feature also "
+ "requires an API version of 1.1 or higher");
+ }
+ ptrs_.vkGetPhysicalDeviceProperties2(physical_device_, &properties2);
+ }
+
return {};
}
bool Device::IsFormatSupportedByPhysicalDevice(const Format& format,
- Buffer* buffer) {
+ BufferType type) {
VkFormat vk_format = GetVkFormat(format);
VkFormatProperties properties = VkFormatProperties();
GetPtrs()->vkGetPhysicalDeviceFormatProperties(physical_device_, vk_format,
@@ -469,16 +674,18 @@ bool Device::IsFormatSupportedByPhysicalDevice(const Format& format,
VkFormatFeatureFlagBits flag = VK_FORMAT_FEATURE_VERTEX_BUFFER_BIT;
bool is_buffer_type_image = false;
- switch (buffer->GetBufferType()) {
+ switch (type) {
case BufferType::kColor:
+ case BufferType::kStorageImage:
flag = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
is_buffer_type_image = true;
break;
- case BufferType::kDepth:
+ case BufferType::kDepthStencil:
flag = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
is_buffer_type_image = true;
break;
- case BufferType::kSampled:
+ case BufferType::kSampledImage:
+ case BufferType::kCombinedImageSampler:
flag = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
is_buffer_type_image = true;
break;
@@ -918,5 +1125,54 @@ VkFormat Device::GetVkFormat(const Format& format) const {
return ret;
}
+bool Device::IsRequiredSubgroupSizeSupported(
+ const ShaderType type,
+ const uint32_t required_subgroup_size) const {
+ VkShaderStageFlagBits stage = {};
+ switch (type) {
+ case kShaderTypeGeometry:
+ stage = VK_SHADER_STAGE_GEOMETRY_BIT;
+ break;
+ case kShaderTypeFragment:
+ stage = VK_SHADER_STAGE_FRAGMENT_BIT;
+ break;
+ case kShaderTypeVertex:
+ stage = VK_SHADER_STAGE_VERTEX_BIT;
+ break;
+ case kShaderTypeTessellationControl:
+ stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+ break;
+ case kShaderTypeTessellationEvaluation:
+ stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
+ break;
+ case kShaderTypeCompute:
+ stage = VK_SHADER_STAGE_COMPUTE_BIT;
+ break;
+ default:
+ return false;
+ }
+ if ((stage & subgroup_size_control_properties_.requiredSubgroupSizeStages) ==
+ 0) {
+ return false;
+ }
+ if (required_subgroup_size == 0 ||
+ required_subgroup_size <
+ subgroup_size_control_properties_.minSubgroupSize ||
+ required_subgroup_size >
+ subgroup_size_control_properties_.maxSubgroupSize) {
+ return false;
+ }
+
+ return true;
+}
+
+uint32_t Device::GetMinSubgroupSize() const {
+ return subgroup_size_control_properties_.minSubgroupSize;
+}
+
+uint32_t Device::GetMaxSubgroupSize() const {
+ return subgroup_size_control_properties_.maxSubgroupSize;
+}
+
} // namespace vulkan
} // namespace amber
diff --git a/src/vulkan/device.h b/src/vulkan/device.h
index 700e6e8..8cd3ba1 100644
--- a/src/vulkan/device.h
+++ b/src/vulkan/device.h
@@ -30,7 +30,8 @@ namespace amber {
namespace vulkan {
struct VulkanPtrs {
-#include "vk-wrappers.h" // NOLINT(build/include)
+#include "vk-wrappers-1-0.h" // NOLINT(build/include)
+#include "vk-wrappers-1-1.h" // NOLINT(build/include)
};
/// Wrapper around a Vulkan Device object.
@@ -41,19 +42,19 @@ class Device {
uint32_t queue_family_index,
VkDevice device,
VkQueue queue);
- ~Device();
+ virtual ~Device();
Result Initialize(PFN_vkGetInstanceProcAddr getInstanceProcAddr,
Delegate* delegate,
const std::vector<std::string>& required_features,
- const std::vector<std::string>& required_extensions,
+ const std::vector<std::string>& required_device_extensions,
const VkPhysicalDeviceFeatures& available_features,
const VkPhysicalDeviceFeatures2KHR& available_features2,
const std::vector<std::string>& available_extensions);
/// Returns true if |format| and the |buffer|s buffer type combination is
/// supported by the physical device.
- bool IsFormatSupportedByPhysicalDevice(const Format& format, Buffer* buffer);
+ bool IsFormatSupportedByPhysicalDevice(const Format& format, BufferType type);
VkDevice GetVkDevice() const { return device_; }
VkQueue GetVkQueue() const { return queue_; }
@@ -67,23 +68,37 @@ class Device {
bool IsDescriptorSetInBounds(uint32_t descriptor_set) const;
/// Returns true if the memory at |memory_type_index| has |flags| set.
- bool HasMemoryFlags(uint32_t memory_type_index,
- const VkMemoryPropertyFlags flags) const;
+ virtual bool HasMemoryFlags(uint32_t memory_type_index,
+ const VkMemoryPropertyFlags flags) const;
/// Returns true if the memory at |memory_type_index| is host accessible.
bool IsMemoryHostAccessible(uint32_t memory_type_index) const;
- /// Returns true if the memory at |memory_type_index| is host corherent.
+ /// Returns true if the memory at |memory_type_index| is host coherent.
bool IsMemoryHostCoherent(uint32_t memory_type_index) const;
/// Returns the pointers to the Vulkan API methods.
- const VulkanPtrs* GetPtrs() const { return &ptrs_; }
+ virtual const VulkanPtrs* GetPtrs() const { return &ptrs_; }
+
+ /// Returns true if the required subgroup size is supported for given stage
+ bool IsRequiredSubgroupSizeSupported(
+ const ShaderType type,
+ const uint32_t required_subgroup_size) const;
+ /// Returns the minimum required subgroup size or 0 if subgroup size control
+ /// is not supported.
+ uint32_t GetMinSubgroupSize() const;
+ /// Returns the maximum required subgroup size or 0 if subgroup size control
+ /// is not supported.
+ uint32_t GetMaxSubgroupSize() const;
private:
Result LoadVulkanPointers(PFN_vkGetInstanceProcAddr, Delegate* delegate);
+ bool SupportsApiVersion(uint32_t major, uint32_t minor, uint32_t patch);
VkInstance instance_ = VK_NULL_HANDLE;
VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
VkPhysicalDeviceProperties physical_device_properties_;
VkPhysicalDeviceMemoryProperties physical_memory_properties_;
+ VkPhysicalDeviceSubgroupSizeControlPropertiesEXT
+ subgroup_size_control_properties_;
VkDevice device_ = VK_NULL_HANDLE;
VkQueue queue_ = VK_NULL_HANDLE;
uint32_t queue_family_index_ = 0;
diff --git a/src/vulkan/engine_vulkan.cc b/src/vulkan/engine_vulkan.cc
index f9db6b7..536993f 100644
--- a/src/vulkan/engine_vulkan.cc
+++ b/src/vulkan/engine_vulkan.cc
@@ -28,6 +28,9 @@ namespace amber {
namespace vulkan {
namespace {
+const uint32_t kTrianglesPerCell = 2;
+const uint32_t kVerticesPerTriangle = 3;
+
Result ToVkShaderStage(ShaderType type, VkShaderStageFlagBits* ret) {
switch (type) {
case kShaderTypeGeometry:
@@ -76,17 +79,11 @@ bool AreAllExtensionsSupported(
EngineVulkan::EngineVulkan() : Engine() {}
EngineVulkan::~EngineVulkan() {
- for (auto it = pipeline_map_.begin(); it != pipeline_map_.end(); ++it) {
- auto& info = it->second;
-
- for (auto mod_it = info.shader_info.begin();
- mod_it != info.shader_info.end(); ++mod_it) {
- auto vk_device = device_->GetVkDevice();
- if (vk_device != VK_NULL_HANDLE &&
- mod_it->second.shader != VK_NULL_HANDLE) {
- device_->GetPtrs()->vkDestroyShaderModule(
- vk_device, mod_it->second.shader, nullptr);
- }
+ auto vk_device = device_->GetVkDevice();
+ if (vk_device != VK_NULL_HANDLE) {
+ for (auto shader : shaders_) {
+ device_->GetPtrs()->vkDestroyShaderModule(vk_device, shader.second,
+ nullptr);
}
}
}
@@ -143,25 +140,23 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
auto& info = pipeline_map_[pipeline];
for (const auto& shader_info : pipeline->GetShaders()) {
- Result r =
- SetShader(pipeline, shader_info.GetShaderType(), shader_info.GetData());
+ Result r = SetShader(pipeline, shader_info);
if (!r.IsSuccess())
return r;
}
for (const auto& colour_info : pipeline->GetColorAttachments()) {
auto fmt = colour_info.buffer->GetFormat();
- if (!device_->IsFormatSupportedByPhysicalDevice(*fmt, colour_info.buffer))
+ if (!device_->IsFormatSupportedByPhysicalDevice(*fmt, colour_info.type))
return Result("Vulkan color attachment format is not supported");
}
- Format* depth_fmt = nullptr;
- if (pipeline->GetDepthBuffer().buffer) {
- const auto& depth_info = pipeline->GetDepthBuffer();
+ if (pipeline->GetDepthStencilBuffer().buffer) {
+ const auto& depth_stencil_info = pipeline->GetDepthStencilBuffer();
- depth_fmt = depth_info.buffer->GetFormat();
- if (!device_->IsFormatSupportedByPhysicalDevice(*depth_fmt,
- depth_info.buffer)) {
+ auto fmt = depth_stencil_info.buffer->GetFormat();
+ if (!device_->IsFormatSupportedByPhysicalDevice(*fmt,
+ depth_stencil_info.type)) {
return Result("Vulkan depth attachment format is not supported");
}
}
@@ -181,8 +176,9 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
return r;
} else {
vk_pipeline = MakeUnique<GraphicsPipeline>(
- device_.get(), pipeline->GetColorAttachments(), depth_fmt,
- engine_data.fence_timeout_ms, stage_create_info);
+ device_.get(), pipeline->GetColorAttachments(),
+ pipeline->GetDepthStencilBuffer(), engine_data.fence_timeout_ms,
+ stage_create_info);
r = vk_pipeline->AsGraphics()->Initialize(pipeline->GetFramebufferWidth(),
pipeline->GetFramebufferHeight(),
@@ -207,13 +203,15 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
for (const auto& vtex_info : pipeline->GetVertexBuffers()) {
auto fmt = vtex_info.buffer->GetFormat();
- if (!device_->IsFormatSupportedByPhysicalDevice(*fmt, vtex_info.buffer))
+ if (!device_->IsFormatSupportedByPhysicalDevice(*fmt, vtex_info.type))
return Result("Vulkan vertex buffer format is not supported");
if (!info.vertex_buffer)
info.vertex_buffer = MakeUnique<VertexBuffer>(device_.get());
info.vertex_buffer->SetData(static_cast<uint8_t>(vtex_info.location),
- vtex_info.buffer);
+ vtex_info.buffer, vtex_info.input_rate,
+ vtex_info.format, vtex_info.offset,
+ vtex_info.stride);
}
if (pipeline->GetIndexBuffer()) {
@@ -230,20 +228,53 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
for (const auto& buf_info : pipeline->GetBuffers()) {
auto type = BufferCommand::BufferType::kSSBO;
- if (buf_info.buffer->GetBufferType() == BufferType::kUniform) {
+ if (buf_info.type == BufferType::kStorageImage) {
+ type = BufferCommand::BufferType::kStorageImage;
+ } else if (buf_info.type == BufferType::kSampledImage) {
+ type = BufferCommand::BufferType::kSampledImage;
+ } else if (buf_info.type == BufferType::kCombinedImageSampler) {
+ type = BufferCommand::BufferType::kCombinedImageSampler;
+ } else if (buf_info.type == BufferType::kUniformTexelBuffer) {
+ type = BufferCommand::BufferType::kUniformTexelBuffer;
+ } else if (buf_info.type == BufferType::kStorageTexelBuffer) {
+ type = BufferCommand::BufferType::kStorageTexelBuffer;
+ } else if (buf_info.type == BufferType::kUniform) {
type = BufferCommand::BufferType::kUniform;
- } else if (buf_info.buffer->GetBufferType() != BufferType::kStorage) {
+ } else if (buf_info.type == BufferType::kUniformDynamic) {
+ type = BufferCommand::BufferType::kUniformDynamic;
+ } else if (buf_info.type == BufferType::kStorageDynamic) {
+ type = BufferCommand::BufferType::kSSBODynamic;
+ } else if (buf_info.type != BufferType::kStorage) {
return Result("Vulkan: CreatePipeline - unknown buffer type: " +
- std::to_string(static_cast<uint32_t>(
- buf_info.buffer->GetBufferType())));
+ std::to_string(static_cast<uint32_t>(buf_info.type)));
}
auto cmd = MakeUnique<BufferCommand>(type, pipeline);
cmd->SetDescriptorSet(buf_info.descriptor_set);
cmd->SetBinding(buf_info.binding);
+ cmd->SetBaseMipLevel(buf_info.base_mip_level);
+ cmd->SetDynamicOffset(buf_info.dynamic_offset);
cmd->SetBuffer(buf_info.buffer);
+ cmd->SetSampler(buf_info.sampler);
+
+ if (cmd->GetValues().empty()) {
+ cmd->GetBuffer()->SetSizeInElements(cmd->GetBuffer()->ElementCount());
+ } else {
+ cmd->GetBuffer()->SetDataWithOffset(cmd->GetValues(), cmd->GetOffset());
+ }
+
+ r = info.vk_pipeline->AddBufferDescriptor(cmd.get());
+ if (!r.IsSuccess())
+ return r;
+ }
- r = info.vk_pipeline->AddDescriptor(cmd.get());
+ for (const auto& sampler_info : pipeline->GetSamplers()) {
+ auto cmd = MakeUnique<SamplerCommand>(pipeline);
+ cmd->SetDescriptorSet(sampler_info.descriptor_set);
+ cmd->SetBinding(sampler_info.binding);
+ cmd->SetSampler(sampler_info.sampler);
+
+ r = info.vk_pipeline->AddSamplerDescriptor(cmd.get());
if (!r.IsSuccess())
return r;
}
@@ -252,32 +283,76 @@ Result EngineVulkan::CreatePipeline(amber::Pipeline* pipeline) {
}
Result EngineVulkan::SetShader(amber::Pipeline* pipeline,
- ShaderType type,
- const std::vector<uint32_t>& data) {
+ const amber::Pipeline::ShaderInfo& shader) {
+ const auto type = shader.GetShaderType();
+ const auto& data = shader.GetData();
+ const auto shader_name = shader.GetShader()->GetName();
auto& info = pipeline_map_[pipeline];
auto it = info.shader_info.find(type);
if (it != info.shader_info.end())
return Result("Vulkan::Setting Duplicated Shader Types Fail");
- VkShaderModuleCreateInfo create_info = VkShaderModuleCreateInfo();
- create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
- create_info.codeSize = data.size() * sizeof(uint32_t);
- create_info.pCode = data.data();
+ VkShaderModule shader_module;
+ if (shaders_.find(shader_name) != shaders_.end()) {
+ shader_module = shaders_[shader_name];
+ } else {
+ VkShaderModuleCreateInfo create_info = VkShaderModuleCreateInfo();
+ create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
+ create_info.codeSize = data.size() * sizeof(uint32_t);
+ create_info.pCode = data.data();
+
+ if (device_->GetPtrs()->vkCreateShaderModule(
+ device_->GetVkDevice(), &create_info, nullptr, &shader_module) !=
+ VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateShaderModule Fail");
+ }
- VkShaderModule shader;
- if (device_->GetPtrs()->vkCreateShaderModule(device_->GetVkDevice(),
- &create_info, nullptr,
- &shader) != VK_SUCCESS) {
- return Result("Vulkan::Calling vkCreateShaderModule Fail");
+ shaders_[shader_name] = shader_module;
}
- info.shader_info[type].shader = shader;
+ info.shader_info[type].shader = shader_module;
for (auto& shader_info : pipeline->GetShaders()) {
if (shader_info.GetShaderType() != type)
continue;
+ const auto required_subgroup_size_setting =
+ shader_info.GetRequiredSubgroupSizeSetting();
+ uint32_t required_subgroup_size_uint = 0;
+ switch (required_subgroup_size_setting) {
+ case amber::Pipeline::ShaderInfo::RequiredSubgroupSizeSetting::
+ kSetToMinimumSize:
+ required_subgroup_size_uint = device_->GetMinSubgroupSize();
+ break;
+ case amber::Pipeline::ShaderInfo::RequiredSubgroupSizeSetting::
+ kSetToMaximumSize:
+ required_subgroup_size_uint = device_->GetMaxSubgroupSize();
+ break;
+ default:
+ required_subgroup_size_uint = shader_info.GetRequiredSubgroupSize();
+ break;
+ }
+ if (required_subgroup_size_uint > 0) {
+ if (!device_->IsRequiredSubgroupSizeSupported(
+ type, required_subgroup_size_uint)) {
+ return Result(
+ "Vulkan::Setting Required subgroup size is not supported by the "
+ "device.");
+ }
+ }
+ info.shader_info[type].required_subgroup_size = required_subgroup_size_uint;
+
+ info.shader_info[type].create_flags = 0;
+ if (shader_info.GetVaryingSubgroupSize()) {
+ info.shader_info[type].create_flags |=
+ VK_PIPELINE_SHADER_STAGE_CREATE_ALLOW_VARYING_SUBGROUP_SIZE_BIT_EXT;
+ }
+ if (shader_info.GetRequireFullSubgroups()) {
+ info.shader_info[type].create_flags |=
+ VK_PIPELINE_SHADER_STAGE_CREATE_REQUIRE_FULL_SUBGROUPS_BIT_EXT;
+ }
+
const auto& shader_spec_info = shader_info.GetSpecialization();
if (shader_spec_info.empty())
continue;
@@ -322,6 +397,7 @@ Result EngineVulkan::GetVkShaderStageInfo(
stage_info[stage_count] = VkPipelineShaderStageCreateInfo();
stage_info[stage_count].sType =
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+ stage_info[stage_count].flags = it.second.create_flags;
stage_info[stage_count].stage = stage;
stage_info[stage_count].module = it.second.shader;
stage_info[stage_count].pName = nullptr;
@@ -330,6 +406,17 @@ Result EngineVulkan::GetVkShaderStageInfo(
stage_info[stage_count].pSpecializationInfo =
it.second.specialization_info.get();
}
+
+ if (stage == VK_SHADER_STAGE_COMPUTE_BIT &&
+ it.second.required_subgroup_size > 0) {
+ VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT* pSubgroupSize =
+ new VkPipelineShaderStageRequiredSubgroupSizeCreateInfoEXT();
+ pSubgroupSize->sType =
+ VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO_EXT; // NOLINT(whitespace/line_length)
+ pSubgroupSize->pNext = nullptr;
+ pSubgroupSize->requiredSubgroupSize = it.second.required_subgroup_size;
+ stage_info[stage_count].pNext = pSubgroupSize;
+ }
++stage_count;
}
*out = stage_info;
@@ -417,7 +504,8 @@ Result EngineVulkan::DoDrawRect(const DrawRectCommand* command) {
buf->SetData(std::move(values));
auto vertex_buffer = MakeUnique<VertexBuffer>(device_.get());
- vertex_buffer->SetData(0, buf.get());
+ vertex_buffer->SetData(0, buf.get(), InputRate::kVertex, buf->GetFormat(), 0,
+ buf->GetFormat()->SizeInBytes());
DrawArraysCommand draw(command->GetPipeline(), *command->GetPipelineData());
draw.SetTopology(command->IsPatch() ? Topology::kPatchList
@@ -433,6 +521,93 @@ Result EngineVulkan::DoDrawRect(const DrawRectCommand* command) {
return {};
}
+Result EngineVulkan::DoDrawGrid(const DrawGridCommand* command) {
+ auto& info = pipeline_map_[command->GetPipeline()];
+ if (!info.vk_pipeline->IsGraphics())
+ return Result("Vulkan::DrawGrid for Non-Graphics Pipeline");
+
+ auto* graphics = info.vk_pipeline->AsGraphics();
+
+ float x = command->GetX();
+ float y = command->GetY();
+ float width = command->GetWidth();
+ float height = command->GetHeight();
+ const uint32_t columns = command->GetColumns();
+ const uint32_t rows = command->GetRows();
+ const uint32_t vertices =
+ columns * rows * kVerticesPerTriangle * kTrianglesPerCell;
+
+ // Ortho calculation
+ const float frame_width = static_cast<float>(graphics->GetWidth());
+ const float frame_height = static_cast<float>(graphics->GetHeight());
+ x = ((x / frame_width) * 2.0f) - 1.0f;
+ y = ((y / frame_height) * 2.0f) - 1.0f;
+ width = (width / frame_width) * 2.0f;
+ height = (height / frame_height) * 2.0f;
+
+ std::vector<Value> values(vertices * 2);
+
+ const float cell_width = width / static_cast<float>(columns);
+ const float cell_height = height / static_cast<float>(rows);
+
+ for (uint32_t i = 0, c = 0; i < rows; i++) {
+ for (uint32_t j = 0; j < columns; j++, c += 12) {
+ // Calculate corners
+ float x0 = x + cell_width * static_cast<float>(j);
+ float y0 = y + cell_height * static_cast<float>(i);
+ float x1 = x + cell_width * static_cast<float>(j + 1);
+ float y1 = y + cell_height * static_cast<float>(i + 1);
+
+ // Bottom right
+ values[c + 0].SetDoubleValue(static_cast<double>(x1));
+ values[c + 1].SetDoubleValue(static_cast<double>(y1));
+ // Bottom left
+ values[c + 2].SetDoubleValue(static_cast<double>(x0));
+ values[c + 3].SetDoubleValue(static_cast<double>(y1));
+ // Top left
+ values[c + 4].SetDoubleValue(static_cast<double>(x0));
+ values[c + 5].SetDoubleValue(static_cast<double>(y0));
+ // Bottom right
+ values[c + 6].SetDoubleValue(static_cast<double>(x1));
+ values[c + 7].SetDoubleValue(static_cast<double>(y1));
+ // Top left
+ values[c + 8].SetDoubleValue(static_cast<double>(x0));
+ values[c + 9].SetDoubleValue(static_cast<double>(y0));
+ // Top right
+ values[c + 10].SetDoubleValue(static_cast<double>(x1));
+ values[c + 11].SetDoubleValue(static_cast<double>(y0));
+ }
+ }
+
+ // |format| is not Format for frame buffer but for vertex buffer.
+ // Since draw rect command contains its vertex information and it
+ // does not include a format of vertex buffer, we can choose any
+ // one that is suitable. We use VK_FORMAT_R32G32_SFLOAT for it.
+ TypeParser parser;
+ auto type = parser.Parse("R32G32_SFLOAT");
+ Format fmt(type.get());
+
+ auto buf = MakeUnique<Buffer>();
+ buf->SetFormat(&fmt);
+ buf->SetData(std::move(values));
+
+ auto vertex_buffer = MakeUnique<VertexBuffer>(device_.get());
+ vertex_buffer->SetData(0, buf.get(), InputRate::kVertex, buf->GetFormat(), 0,
+ buf->GetFormat()->SizeInBytes());
+
+ DrawArraysCommand draw(command->GetPipeline(), *command->GetPipelineData());
+ draw.SetTopology(Topology::kTriangleList);
+ draw.SetFirstVertexIndex(0);
+ draw.SetVertexCount(vertices);
+ draw.SetInstanceCount(1);
+
+ Result r = graphics->Draw(&draw, vertex_buffer.get());
+ if (!r.IsSuccess())
+ return r;
+
+ return {};
+}
+
Result EngineVulkan::DoDrawArrays(const DrawArraysCommand* command) {
auto& info = pipeline_map_[command->GetPipeline()];
if (!info.vk_pipeline)
@@ -482,8 +657,17 @@ Result EngineVulkan::DoBuffer(const BufferCommand* cmd) {
"Vulkan::DoBuffer exceed maxBoundDescriptorSets limit of physical "
"device");
}
- auto& info = pipeline_map_[cmd->GetPipeline()];
- return info.vk_pipeline->AddDescriptor(cmd);
+ if (cmd->GetValues().empty()) {
+ cmd->GetBuffer()->SetSizeInElements(cmd->GetBuffer()->ElementCount());
+ } else {
+ cmd->GetBuffer()->SetDataWithOffset(cmd->GetValues(), cmd->GetOffset());
+ }
+ if (cmd->IsPushConstant()) {
+ auto& info = pipeline_map_[cmd->GetPipeline()];
+ return info.vk_pipeline->AddPushConstantBuffer(cmd->GetBuffer(),
+ cmd->GetOffset());
+ }
+ return {};
}
} // namespace vulkan
diff --git a/src/vulkan/engine_vulkan.h b/src/vulkan/engine_vulkan.h
index 7dab176..ef438cc 100644
--- a/src/vulkan/engine_vulkan.h
+++ b/src/vulkan/engine_vulkan.h
@@ -19,6 +19,7 @@
#include <memory>
#include <string>
#include <unordered_map>
+#include <utility>
#include <vector>
#include "amber/vulkan_header.h"
@@ -53,6 +54,7 @@ class EngineVulkan : public Engine {
Result DoClearDepth(const ClearDepthCommand* cmd) override;
Result DoClear(const ClearCommand* cmd) override;
Result DoDrawRect(const DrawRectCommand* cmd) override;
+ Result DoDrawGrid(const DrawGridCommand* cmd) override;
Result DoDrawArrays(const DrawArraysCommand* cmd) override;
Result DoCompute(const ComputeCommand* cmd) override;
Result DoEntryPoint(const EntryPointCommand* cmd) override;
@@ -60,6 +62,8 @@ class EngineVulkan : public Engine {
const PatchParameterVerticesCommand* cmd) override;
Result DoBuffer(const BufferCommand* cmd) override;
+ std::pair<Debugger*, Result> GetDebugger(VirtualFileStore*) override;
+
private:
struct PipelineInfo {
std::unique_ptr<Pipeline> vk_pipeline;
@@ -70,6 +74,8 @@ class EngineVulkan : public Engine {
specialization_entries;
std::unique_ptr<std::vector<uint32_t>> specialization_data;
std::unique_ptr<VkSpecializationInfo> specialization_info;
+ uint32_t required_subgroup_size;
+ VkPipelineShaderStageCreateFlags create_flags;
};
std::unordered_map<ShaderType, ShaderInfo, CastHash<ShaderType>>
shader_info;
@@ -78,20 +84,17 @@ class EngineVulkan : public Engine {
Result GetVkShaderStageInfo(
amber::Pipeline* pipeline,
std::vector<VkPipelineShaderStageCreateInfo>* out);
- bool IsFormatSupportedByPhysicalDevice(BufferType type,
- VkPhysicalDevice physical_device,
- VkFormat format);
- bool IsDescriptorSetInBounds(VkPhysicalDevice physical_device,
- uint32_t descriptor_set);
Result SetShader(amber::Pipeline* pipeline,
- ShaderType type,
- const std::vector<uint32_t>& data);
+ const amber::Pipeline::ShaderInfo& shader);
std::unique_ptr<Device> device_;
std::unique_ptr<CommandPool> pool_;
std::map<amber::Pipeline*, PipelineInfo> pipeline_map_;
+
+ std::unique_ptr<Debugger> debugger_;
+ std::map<std::string, VkShaderModule> shaders_;
};
} // namespace vulkan
diff --git a/src/vulkan/engine_vulkan_debugger.cc b/src/vulkan/engine_vulkan_debugger.cc
new file mode 100644
index 0000000..d0f1432
--- /dev/null
+++ b/src/vulkan/engine_vulkan_debugger.cc
@@ -0,0 +1,1180 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/vulkan/engine_vulkan.h"
+
+#if AMBER_ENABLE_VK_DEBUGGING
+
+#include <stdlib.h>
+
+#include <chrono> // NOLINT(build/c++11)
+#include <condition_variable> // NOLINT(build/c++11)
+#include <fstream>
+#include <mutex> // NOLINT(build/c++11)
+#include <sstream>
+#include <thread> // NOLINT(build/c++11)
+#include <unordered_map>
+
+#include "dap/network.h"
+#include "dap/protocol.h"
+#include "dap/session.h"
+#include "src/virtual_file_store.h"
+
+// Set to 1 to enable verbose debugger logging
+#define ENABLE_DEBUGGER_LOG 0
+
+#if ENABLE_DEBUGGER_LOG
+#define DEBUGGER_LOG(...) \
+ do { \
+ printf(__VA_ARGS__); \
+ printf("\n"); \
+ } while (false)
+#else
+#define DEBUGGER_LOG(...)
+#endif
+
+namespace amber {
+namespace vulkan {
+
+namespace {
+
+// kThreadTimeout is the maximum time to wait for a thread to complete.
+static constexpr const auto kThreadTimeout = std::chrono::minutes(1);
+
+// kStepEventTimeout is the maximum time to wait for a thread to complete a step
+// request.
+static constexpr const auto kStepEventTimeout = std::chrono::seconds(1);
+
+// Event provides a basic wait-and-signal synchronization primitive.
+class Event {
+ public:
+ // Wait blocks until the event is fired, or the timeout is reached.
+ // If the Event was signalled, then Wait returns true, otherwise false.
+ template <typename Rep, typename Period>
+ bool Wait(const std::chrono::duration<Rep, Period>& duration) {
+ std::unique_lock<std::mutex> lock(mutex_);
+ return cv_.wait_for(lock, duration, [&] { return signalled_; });
+ }
+
+ // Signal signals the Event, unblocking any calls to Wait.
+ void Signal() {
+ std::unique_lock<std::mutex> lock(mutex_);
+ signalled_ = true;
+ cv_.notify_all();
+ }
+
+ private:
+ std::condition_variable cv_;
+ std::mutex mutex_;
+ bool signalled_ = false;
+};
+
+// Split slices str into all substrings separated by sep and returns a vector of
+// the substrings between those separators.
+std::vector<std::string> Split(const std::string& str, const std::string& sep) {
+ std::vector<std::string> out;
+ std::size_t cur = 0;
+ std::size_t prev = 0;
+ while ((cur = str.find(sep, prev)) != std::string::npos) {
+ out.push_back(str.substr(prev, cur - prev));
+ prev = cur + 1;
+ }
+ out.push_back(str.substr(prev));
+ return out;
+}
+
+// GlobalInvocationId holds a three-element unsigned integer index, used to
+// identifiy a single compute invocation.
+struct GlobalInvocationId {
+ size_t hash() const { return x << 20 | y << 10 | z; }
+ bool operator==(const GlobalInvocationId& other) const {
+ return x == other.x && y == other.y && z == other.z;
+ }
+
+ uint32_t x;
+ uint32_t y;
+ uint32_t z;
+};
+
+// WindowSpacePosition holds a two-element unsigned integer index, used to
+// identifiy a single fragment invocation.
+struct WindowSpacePosition {
+ size_t hash() const { return x << 10 | y; }
+ bool operator==(const WindowSpacePosition& other) const {
+ return x == other.x && y == other.y;
+ }
+
+ uint32_t x;
+ uint32_t y;
+};
+
+// Forward declaration.
+struct Variable;
+
+// Variables is a list of Variable (), with helper methods.
+class Variables : public std::vector<Variable> {
+ public:
+ inline const Variable* Find(const std::string& name) const;
+ inline std::string AllNames() const;
+};
+
+// Variable holds a debugger returned named value (local, global, etc).
+// Variables can hold child variables (for structs, arrays, etc).
+struct Variable {
+ std::string name;
+ std::string value;
+ Variables children;
+
+ // Get parses the Variable value for the requested type, assigning the result
+ // to |out|. Returns true on success, otherwise false.
+ bool Get(uint32_t* out) const {
+ *out = static_cast<uint32_t>(std::atoi(value.c_str()));
+ return true; // TODO(bclayton): Verify the value parsed correctly.
+ }
+
+ bool Get(int64_t* out) const {
+ *out = static_cast<int64_t>(std::atoi(value.c_str()));
+ return true; // TODO(bclayton): Verify the value parsed correctly.
+ }
+
+ bool Get(double* out) const {
+ *out = std::atof(value.c_str());
+ return true; // TODO(bclayton): Verify the value parsed correctly.
+ }
+
+ bool Get(std::string* out) const {
+ *out = value;
+ return true;
+ }
+
+ bool Get(GlobalInvocationId* out) const {
+ auto x = children.Find("x");
+ auto y = children.Find("y");
+ auto z = children.Find("z");
+ return (x != nullptr && y != nullptr && z != nullptr && x->Get(&out->x) &&
+ y->Get(&out->y) && z->Get(&out->z));
+ }
+
+ bool Get(WindowSpacePosition* out) const {
+ auto x = children.Find("x");
+ auto y = children.Find("y");
+ return (x != nullptr && y != nullptr && x->Get(&out->x) && y->Get(&out->y));
+ }
+};
+
+const Variable* Variables::Find(const std::string& name) const {
+ for (auto& child : *this) {
+ if (child.name == name) {
+ return &child;
+ }
+ }
+ return nullptr;
+}
+
+std::string Variables::AllNames() const {
+ std::string out;
+ for (auto& var : *this) {
+ if (out.size() > 0) {
+ out += ", ";
+ }
+ out += "'" + var.name + "'";
+ }
+ return out;
+}
+
+// Client wraps a dap::Session and a error handler, and provides a more
+// convenient interface for talking to the debugger. Client also provides basic
+// immutable data caching to help performance.
+class Client {
+ static constexpr const char* kLocals = "locals";
+ static constexpr const char* kLane = "Lane";
+
+ public:
+ using ErrorHandler = std::function<void(const std::string&)>;
+ using SourceLines = std::vector<std::string>;
+
+ Client(const std::shared_ptr<dap::Session>& session,
+ const ErrorHandler& onerror)
+ : session_(session), onerror_(onerror) {}
+
+ // TopStackFrame retrieves the frame at the top of the thread's call stack.
+ // Returns true on success, false on error.
+ bool TopStackFrame(dap::integer thread_id, dap::StackFrame* frame) {
+ std::vector<dap::StackFrame> stack;
+ if (!Callstack(thread_id, &stack)) {
+ return false;
+ }
+ *frame = stack.front();
+ return true;
+ }
+
+ // Callstack retrieves the thread's full call stack.
+ // Returns true on success, false on error.
+ bool Callstack(dap::integer thread_id, std::vector<dap::StackFrame>* stack) {
+ dap::StackTraceRequest request;
+ request.threadId = thread_id;
+ auto response = session_->send(request).get();
+ if (response.error) {
+ onerror_(response.error.message);
+ return false;
+ }
+ if (response.response.stackFrames.size() == 0) {
+ onerror_("Stack frame is empty");
+ return false;
+ }
+ *stack = response.response.stackFrames;
+ return true;
+ }
+
+ // FrameLocation retrieves the current frame source location, and optional
+ // source line text.
+ // Returns true on success, false on error.
+ bool FrameLocation(VirtualFileStore* virtual_files,
+ const dap::StackFrame& frame,
+ debug::Location* location,
+ std::string* line = nullptr) {
+ location->line = static_cast<uint32_t>(frame.line);
+
+ if (!frame.source.has_value()) {
+ onerror_("Stack frame with name '" + frame.name + "' has no source");
+ return false;
+ } else if (frame.source->path.has_value()) {
+ location->file = frame.source.value().path.value();
+ } else if (frame.source->name.has_value()) {
+ location->file = frame.source.value().name.value();
+ } else {
+ onerror_("Frame source had no path or name");
+ return false;
+ }
+
+ if (location->line < 1) {
+ onerror_("Line location is " + std::to_string(location->line));
+ return false;
+ }
+
+ if (line != nullptr) {
+ SourceLines lines;
+ if (!SourceContent(virtual_files, frame.source.value(), &lines)) {
+ return false;
+ }
+ if (location->line > lines.size()) {
+ onerror_("Line " + std::to_string(location->line) +
+ " is greater than the number of lines in the source file (" +
+ std::to_string(lines.size()) + ")");
+ return false;
+ }
+ if (location->line < 1) {
+ onerror_("Line number reported was " + std::to_string(location->line));
+ return false;
+ }
+ *line = lines[location->line - 1];
+ }
+
+ return true;
+ }
+
+ // SourceContext retrieves the the SourceLines for the given source.
+ // Returns true on success, false on error.
+ bool SourceContent(VirtualFileStore* virtual_files,
+ const dap::Source& source,
+ SourceLines* out) {
+ auto path = source.path.value("");
+
+ if (source.sourceReference.has_value()) {
+ auto ref = source.sourceReference.value();
+ auto it = sourceCache_.by_ref.find(ref);
+ if (it != sourceCache_.by_ref.end()) {
+ *out = it->second;
+ return true;
+ }
+
+ dap::SourceRequest request;
+ dap::SourceResponse response;
+ request.sourceReference = ref;
+ if (!Send(request, &response)) {
+ return false;
+ }
+ auto lines = Split(response.content, "\n");
+ sourceCache_.by_ref.emplace(ref, lines);
+ *out = lines;
+ return true;
+ }
+
+ if (path != "") {
+ auto it = sourceCache_.by_path.find(path);
+ if (it != sourceCache_.by_path.end()) {
+ *out = it->second;
+ return true;
+ }
+
+ // First search the virtual file store.
+ std::string content;
+ if (virtual_files->Get(path, &content).IsSuccess()) {
+ auto lines = Split(content, "\n");
+ sourceCache_.by_path.emplace(path, lines);
+ *out = lines;
+ return true;
+ }
+
+ // TODO(bclayton) - We shouldn't be doing direct file IO here. We should
+ // bubble the IO request to the amber 'embedder'.
+ // See: https://github.com/google/amber/issues/777
+ if (auto file = std::ifstream(path)) {
+ SourceLines lines;
+ std::string line;
+ while (std::getline(file, line)) {
+ lines.emplace_back(line);
+ }
+
+ sourceCache_.by_path.emplace(path, lines);
+ *out = lines;
+ return true;
+ }
+ }
+
+ onerror_("Could not get source content");
+ return false;
+ }
+
+ // Send sends the request to the debugger, waits for the request to complete,
+ // and then assigns the response to |res|.
+ // Returns true on success, false on error.
+ template <typename REQUEST, typename RESPONSE>
+ bool Send(const REQUEST& request, RESPONSE* res) {
+ auto r = session_->send(request).get();
+ if (r.error) {
+ onerror_(r.error.message);
+ return false;
+ }
+ *res = r.response;
+ return true;
+ }
+
+ // Send sends the request to the debugger, and waits for the request to
+ // complete.
+ // Returns true on success, false on error.
+ template <typename REQUEST>
+ bool Send(const REQUEST& request) {
+ using RESPONSE = typename REQUEST::Response;
+ RESPONSE response;
+ return Send(request, &response);
+ }
+
+ // GetVariables fetches the fully traversed set of Variables from the debugger
+ // for the given reference identifier.
+ // Returns true on success, false on error.
+ bool GetVariables(dap::integer variablesRef, Variables* out) {
+ dap::VariablesRequest request;
+ dap::VariablesResponse response;
+ request.variablesReference = variablesRef;
+ if (!Send(request, &response)) {
+ return false;
+ }
+ for (auto var : response.variables) {
+ Variable v;
+ v.name = var.name;
+ v.value = var.value;
+ if (var.variablesReference > 0) {
+ if (!GetVariables(var.variablesReference, &v.children)) {
+ return false;
+ }
+ }
+ out->emplace_back(v);
+ }
+ return true;
+ }
+
+ // GetLocals fetches the fully traversed set of local Variables from the
+ // debugger for the given stack frame.
+ // Returns true on success, false on error.
+ bool GetLocals(const dap::StackFrame& frame, Variables* out) {
+ dap::ScopesRequest scopeReq;
+ dap::ScopesResponse scopeRes;
+ scopeReq.frameId = frame.id;
+ if (!Send(scopeReq, &scopeRes)) {
+ return false;
+ }
+
+ for (auto scope : scopeRes.scopes) {
+ if (scope.presentationHint.value("") == kLocals) {
+ return GetVariables(scope.variablesReference, out);
+ }
+ }
+
+ onerror_("Locals scope not found");
+ return false;
+ }
+
+ // GetLane returns a pointer to the Variables representing the thread's SIMD
+ // lane with the given index, or nullptr if the lane was not found.
+ const Variables* GetLane(const Variables& lanes, int lane) {
+ auto out = lanes.Find(std::string(kLane) + " " + std::to_string(lane));
+ if (out == nullptr) {
+ return nullptr;
+ }
+ return &out->children;
+ }
+
+ private:
+ struct SourceCache {
+ std::unordered_map<int64_t, SourceLines> by_ref;
+ std::unordered_map<std::string, SourceLines> by_path;
+ };
+
+ std::shared_ptr<dap::Session> session_;
+ ErrorHandler onerror_;
+ SourceCache sourceCache_;
+};
+
+// InvocationKey is a tagged-union structure that identifies a single shader
+// invocation.
+struct InvocationKey {
+ // Hash is a custom hasher that can enable InvocationKeys to be used as keys
+ // in std containers.
+ struct Hash {
+ size_t operator()(const InvocationKey& key) const;
+ };
+
+ enum class Type { kGlobalInvocationId, kVertexIndex, kWindowSpacePosition };
+ union Data {
+ GlobalInvocationId global_invocation_id;
+ uint32_t vertex_id;
+ WindowSpacePosition window_space_position;
+ };
+
+ explicit InvocationKey(const GlobalInvocationId&);
+ explicit InvocationKey(const WindowSpacePosition&);
+ InvocationKey(Type, const Data&);
+
+ bool operator==(const InvocationKey& other) const;
+
+ // String returns a human-readable description of the key.
+ std::string String() const;
+
+ Type type;
+ Data data;
+};
+
+size_t InvocationKey::Hash::operator()(const InvocationKey& key) const {
+ size_t hash = 31 * static_cast<size_t>(key.type);
+ switch (key.type) {
+ case Type::kGlobalInvocationId:
+ hash += key.data.global_invocation_id.hash();
+ break;
+ case Type::kVertexIndex:
+ hash += key.data.vertex_id;
+ break;
+ case Type::kWindowSpacePosition:
+ hash += key.data.window_space_position.hash();
+ break;
+ }
+ return hash;
+}
+
+InvocationKey::InvocationKey(const GlobalInvocationId& id)
+ : type(Type::kGlobalInvocationId) {
+ data.global_invocation_id = id;
+}
+
+InvocationKey::InvocationKey(const WindowSpacePosition& pos)
+ : type(Type::kWindowSpacePosition) {
+ data.window_space_position = pos;
+}
+
+InvocationKey::InvocationKey(Type type_, const Data& data_)
+ : type(type_), data(data_) {}
+
+std::string InvocationKey::String() const {
+ std::stringstream ss;
+ switch (type) {
+ case Type::kGlobalInvocationId:
+ ss << "GlobalInvocation(" << data.global_invocation_id.x << ", "
+ << data.global_invocation_id.y << ", " << data.global_invocation_id.z
+ << ")";
+ break;
+ case Type::kVertexIndex:
+ ss << "VertexIndex(" << data.vertex_id << ")";
+ break;
+ case Type::kWindowSpacePosition:
+ ss << "WindowSpacePosition(" << data.window_space_position.x << ", "
+ << data.window_space_position.y << ")";
+ break;
+ }
+ return ss.str();
+}
+
+bool InvocationKey::operator==(const InvocationKey& other) const {
+ if (type != other.type) {
+ return false;
+ }
+ switch (type) {
+ case Type::kGlobalInvocationId:
+ return data.global_invocation_id == other.data.global_invocation_id;
+ case Type::kVertexIndex:
+ return data.vertex_id == other.data.vertex_id;
+ case Type::kWindowSpacePosition:
+ return data.window_space_position == other.data.window_space_position;
+ }
+ return false;
+}
+
+// Thread controls and verifies a single debugger thread of execution.
+class Thread : public debug::Thread {
+ public:
+ Thread(VirtualFileStore* virtual_files,
+ std::shared_ptr<dap::Session> session,
+ dap::integer threadId,
+ int lane,
+ std::shared_ptr<const debug::ThreadScript> script)
+ : virtual_files_(virtual_files),
+ thread_id_(threadId),
+ lane_(lane),
+ client_(session, [this](const std::string& err) { OnError(err); }) {
+ // The thread script runs concurrently with other debugger thread scripts.
+ // Run on a separate amber thread.
+ thread_ = std::thread([this, script] {
+ script->Run(this); // Begin running the thread script.
+ done_.Signal(); // Signal when done.
+ });
+ }
+
+ ~Thread() override { Flush(); }
+
+ // Flush waits for the debugger thread script to complete, and returns any
+ // errors encountered.
+ Result Flush() {
+ if (done_.Wait(kThreadTimeout)) {
+ if (thread_.joinable()) {
+ thread_.join();
+ }
+ } else {
+ error_ += "Timed out performing actions";
+ }
+ return error_;
+ }
+
+ // OnStep is called when the debugger sends a 'stopped' event with reason
+ // 'step'.
+ void OnStep() { on_stepped_.Signal(); }
+
+ // WaitForSteppedEvent halts execution of the thread until OnStep() is
+ // called, or kStepEventTimeout is reached.
+ void WaitForStepEvent(const std::string& request) {
+ if (!on_stepped_.Wait(kStepEventTimeout)) {
+ OnError("Timeout waiting for " + request + " request to complete");
+ }
+ }
+
+ // debug::Thread compliance
+ void StepOver() override {
+ DEBUGGER_LOG("StepOver()");
+ dap::NextRequest request;
+ request.threadId = thread_id_;
+ client_.Send(request);
+ WaitForStepEvent("step-over");
+ }
+
+ void StepIn() override {
+ DEBUGGER_LOG("StepIn()");
+ dap::StepInRequest request;
+ request.threadId = thread_id_;
+ client_.Send(request);
+ WaitForStepEvent("step-in");
+ }
+
+ void StepOut() override {
+ DEBUGGER_LOG("StepOut()");
+ dap::StepOutRequest request;
+ request.threadId = thread_id_;
+ client_.Send(request);
+ WaitForStepEvent("step-out");
+ }
+
+ void Continue() override {
+ DEBUGGER_LOG("Continue()");
+ dap::ContinueRequest request;
+ request.threadId = thread_id_;
+ client_.Send(request);
+ }
+
+ void ExpectLocation(const debug::Location& location,
+ const std::string& line) override {
+ DEBUGGER_LOG("ExpectLocation('%s', %d)", location.file.c_str(),
+ location.line);
+
+ dap::StackFrame frame;
+ if (!client_.TopStackFrame(thread_id_, &frame)) {
+ return;
+ }
+
+ debug::Location got_location;
+ std::string got_source_line;
+ if (!client_.FrameLocation(virtual_files_, frame, &got_location,
+ &got_source_line)) {
+ return;
+ }
+
+ if (got_location.file != location.file) {
+ OnError("Expected file to be '" + location.file + "' but file was '" +
+ got_location.file + "'");
+ } else if (got_location.line != location.line) {
+ std::stringstream ss;
+ ss << "Expected line " << std::to_string(location.line);
+ if (line != "") {
+ ss << " `" << line << "`";
+ }
+ ss << " but line was " << std::to_string(got_location.line) << " `"
+ << got_source_line << "`";
+ OnError(ss.str());
+ } else if (line != "" && got_source_line != line) {
+ OnError("Expected source for line " + std::to_string(location.line) +
+ " to be: `" + line + "` but line was: `" + got_source_line + "`");
+ }
+ }
+
+ void ExpectCallstack(
+ const std::vector<debug::StackFrame>& callstack) override {
+ DEBUGGER_LOG("ExpectCallstack()");
+
+ std::vector<dap::StackFrame> got_stack;
+ if (!client_.Callstack(thread_id_, &got_stack)) {
+ return;
+ }
+
+ std::stringstream ss;
+
+ size_t count = std::min(callstack.size(), got_stack.size());
+ for (size_t i = 0; i < count; i++) {
+ auto const& got_frame = got_stack[i];
+ auto const& want_frame = callstack[i];
+ bool ok = got_frame.name == want_frame.name;
+ if (ok && want_frame.location.file != "") {
+ ok = got_frame.source.has_value() &&
+ got_frame.source->name.value("") == want_frame.location.file;
+ }
+ if (ok && want_frame.location.line != 0) {
+ ok = got_frame.line == static_cast<int>(want_frame.location.line);
+ }
+ if (!ok) {
+ ss << "Unexpected stackframe at frame " << i
+ << "\nGot: " << FrameString(got_frame)
+ << "\nExpected: " << FrameString(want_frame) << "\n";
+ }
+ }
+
+ if (got_stack.size() > callstack.size()) {
+ ss << "Callstack has an additional "
+ << (got_stack.size() - callstack.size()) << " unexpected frames\n";
+ } else if (callstack.size() > got_stack.size()) {
+ ss << "Callstack is missing " << (callstack.size() - got_stack.size())
+ << " frames\n";
+ }
+
+ if (ss.str().size() > 0) {
+ ss << "Full callstack:\n";
+ for (auto& frame : got_stack) {
+ ss << " " << FrameString(frame) << "\n";
+ }
+ OnError(ss.str());
+ }
+ }
+
+ void ExpectLocal(const std::string& name, int64_t value) override {
+ DEBUGGER_LOG("ExpectLocal('%s', %d)", name.c_str(), (int)value);
+ ExpectLocalT(name, value, [](int64_t a, int64_t b) { return a == b; });
+ }
+
+ void ExpectLocal(const std::string& name, double value) override {
+ DEBUGGER_LOG("ExpectLocal('%s', %f)", name.c_str(), value);
+ ExpectLocalT(name, value,
+ [](double a, double b) { return std::abs(a - b) < 0.00001; });
+ }
+
+ void ExpectLocal(const std::string& name, const std::string& value) override {
+ DEBUGGER_LOG("ExpectLocal('%s', '%s')", name.c_str(), value.c_str());
+ ExpectLocalT(name, value, [](const std::string& a, const std::string& b) {
+ return a == b;
+ });
+ }
+
+ // ExpectLocalT verifies that the local variable with the given name has the
+ // expected value. |name| may contain `.` delimiters to index structure or
+ // array types. ExpectLocalT uses the function equal to compare the expected
+ // and actual values, returning true if considered equal, otherwise false.
+ // EQUAL_FUNC is a function-like type with the signature:
+ // bool(T a, T b)
+ template <typename T, typename EQUAL_FUNC>
+ void ExpectLocalT(const std::string& name,
+ const T& expect,
+ EQUAL_FUNC&& equal) {
+ dap::StackFrame frame;
+ if (!client_.TopStackFrame(thread_id_, &frame)) {
+ return;
+ }
+
+ Variables locals;
+ if (!client_.GetLocals(frame, &locals)) {
+ return;
+ }
+
+ if (auto lane = client_.GetLane(locals, lane_)) {
+ auto owner = lane;
+ const Variable* var = nullptr;
+ std::string path;
+ for (auto part : Split(name, ".")) {
+ var = owner->Find(part);
+ if (!var) {
+ if (owner == lane) {
+ OnError("Local '" + name + "' not found.\nLocals: " +
+ lane->AllNames() + ".\nLanes: " + locals.AllNames() + ".");
+ } else {
+ OnError("Local '" + path + "' does not contain '" + part +
+ "'\nChildren: " + owner->AllNames());
+ }
+ return;
+ }
+ owner = &var->children;
+ path += (path.size() > 0) ? "." + part : part;
+ }
+
+ T got = {};
+ if (!var->Get(&got)) {
+ OnError("Local '" + name + "' was not of expected type");
+ return;
+ }
+
+ if (!equal(got, expect)) {
+ std::stringstream ss;
+ ss << "Local '" << name << "' did not have expected value. Value is '"
+ << got << "', expected '" << expect << "'";
+ OnError(ss.str());
+ return;
+ }
+ }
+ }
+
+ private:
+ void OnError(const std::string& err) {
+ DEBUGGER_LOG("ERROR: %s", err.c_str());
+ error_ += err;
+ }
+
+ std::string FrameString(const dap::StackFrame& frame) {
+ std::stringstream ss;
+ ss << frame.name;
+ if (frame.source.has_value() && frame.source->name.has_value()) {
+ ss << " " << frame.source->name.value() << ":" << frame.line;
+ }
+ return ss.str();
+ }
+
+ std::string FrameString(const debug::StackFrame& frame) {
+ std::stringstream ss;
+ ss << frame.name;
+ if (frame.location.file != "") {
+ ss << " " << frame.location.file;
+ if (frame.location.line != 0) {
+ ss << ":" << frame.location.line;
+ }
+ }
+ return ss.str();
+ }
+
+ VirtualFileStore* const virtual_files_;
+ const dap::integer thread_id_;
+ const int lane_;
+ Client client_;
+ std::thread thread_;
+ Event done_;
+ Event on_stepped_;
+ Result error_;
+};
+
+// VkDebugger is a private implementation of the Engine::Debugger interface.
+class VkDebugger : public Engine::Debugger {
+ static constexpr const char* kComputeShaderFunctionName = "ComputeShader";
+ static constexpr const char* kVertexShaderFunctionName = "VertexShader";
+ static constexpr const char* kFragmentShaderFunctionName = "FragmentShader";
+ static constexpr const char* kGlobalInvocationId = "globalInvocationId";
+ static constexpr const char* kWindowSpacePosition = "windowSpacePosition";
+ static constexpr const char* kVertexIndex = "vertexIndex";
+
+ public:
+ explicit VkDebugger(VirtualFileStore* virtual_files)
+ : virtual_files_(virtual_files) {}
+
+ /// Connect establishes the connection to the shader debugger. Must be
+ /// called before any of the |debug::Events| methods.
+ Result Connect() {
+ auto port_str = getenv("VK_DEBUGGER_PORT");
+ if (!port_str) {
+ return Result("The environment variable VK_DEBUGGER_PORT was not set\n");
+ }
+ int port = atoi(port_str);
+ if (port == 0) {
+ return Result("Could not parse port number from VK_DEBUGGER_PORT ('" +
+ std::string(port_str) + "')");
+ }
+
+ constexpr int kMaxAttempts = 10;
+ // The socket might take a while to open - retry connecting.
+ for (int attempt = 0; attempt < kMaxAttempts; attempt++) {
+ auto connection = dap::net::connect("localhost", port);
+ if (!connection) {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ continue;
+ }
+
+ // Socket opened. Create the debugger session and bind.
+ session_ = dap::Session::create();
+ session_->bind(connection);
+
+ // Register the thread stopped event.
+ // This is fired when breakpoints are hit (amongst other reasons).
+ // See:
+ // https://microsoft.github.io/debug-adapter-protocol/specification#Events_Stopped
+ session_->registerHandler([&](const dap::StoppedEvent& event) {
+ DEBUGGER_LOG("THREAD STOPPED. Reason: %s", event.reason.c_str());
+ if (event.reason == "function breakpoint" ||
+ event.reason == "breakpoint") {
+ OnBreakpointHit(event.threadId.value(0));
+ } else if (event.reason == "step") {
+ OnStep(event.threadId.value(0));
+ }
+ });
+
+ // Start the debugger initialization sequence.
+ // See: https://microsoft.github.io/debug-adapter-protocol/overview for
+ // details.
+
+ dap::InitializeRequest init_req = {};
+ auto init_res = session_->send(init_req).get();
+ if (init_res.error) {
+ DEBUGGER_LOG("InitializeRequest failed: %s",
+ init_res.error.message.c_str());
+ return Result(init_res.error.message);
+ }
+
+ // Set breakpoints on the various shader types, we do this even if we
+ // don't actually care about these threads. Once the breakpoint is hit,
+ // the pendingThreads_ map is probed, if nothing matches the thread is
+ // resumed.
+ // TODO(bclayton): Once we have conditional breakpoint support, we can
+ // reduce the number of breakpoints / scope of breakpoints.
+ dap::SetFunctionBreakpointsRequest fbp_req = {};
+ dap::FunctionBreakpoint fbp = {};
+ fbp.name = kComputeShaderFunctionName;
+ fbp_req.breakpoints.emplace_back(fbp);
+ fbp.name = kVertexShaderFunctionName;
+ fbp_req.breakpoints.emplace_back(fbp);
+ fbp.name = kFragmentShaderFunctionName;
+ fbp_req.breakpoints.emplace_back(fbp);
+ auto fbp_res = session_->send(fbp_req).get();
+ if (fbp_res.error) {
+ DEBUGGER_LOG("SetFunctionBreakpointsRequest failed: %s",
+ fbp_res.error.message.c_str());
+ return Result(fbp_res.error.message);
+ }
+
+ // ConfigurationDone signals the initialization has completed.
+ dap::ConfigurationDoneRequest cfg_req = {};
+ auto cfg_res = session_->send(cfg_req).get();
+ if (cfg_res.error) {
+ DEBUGGER_LOG("ConfigurationDoneRequest failed: %s",
+ cfg_res.error.message.c_str());
+ return Result(cfg_res.error.message);
+ }
+
+ return Result();
+ }
+ return Result("Unable to connect to debugger on port " +
+ std::to_string(port));
+ }
+
+ // Flush checks that all breakpoints were hit, waits for all threads to
+ // complete, and returns the globbed together results for all threads.
+ Result Flush() override {
+ Result result;
+ {
+ std::unique_lock<std::mutex> lock(error_mutex_);
+ result += error_;
+ }
+ {
+ std::unique_lock<std::mutex> lock(threads_mutex_);
+ for (auto& pending : pendingThreads_) {
+ result += "Thread did not run: " + pending.first.String();
+ }
+ for (auto& it : runningThreads_) {
+ result += it.second->Flush();
+ }
+ runningThreads_.clear();
+ completedThreads_.clear();
+ }
+ return result;
+ }
+
+ // debug::Events compliance
+ void BreakOnComputeGlobalInvocation(
+ uint32_t x,
+ uint32_t y,
+ uint32_t z,
+ const std::shared_ptr<const debug::ThreadScript>& script) override {
+ std::unique_lock<std::mutex> lock(threads_mutex_);
+ pendingThreads_.emplace(GlobalInvocationId{x, y, z}, script);
+ }
+
+ void BreakOnVertexIndex(
+ uint32_t index,
+ const std::shared_ptr<const debug::ThreadScript>& script) override {
+ InvocationKey::Data data;
+ data.vertex_id = index;
+ auto key = InvocationKey{InvocationKey::Type::kVertexIndex, data};
+ std::unique_lock<std::mutex> lock(threads_mutex_);
+ pendingThreads_.emplace(key, script);
+ }
+
+ void BreakOnFragmentWindowSpacePosition(
+ uint32_t x,
+ uint32_t y,
+ const std::shared_ptr<const debug::ThreadScript>& script) override {
+ std::unique_lock<std::mutex> lock(threads_mutex_);
+ pendingThreads_.emplace(WindowSpacePosition{x, y}, script);
+ }
+
+ private:
+ void StartThread(
+ dap::integer thread_id,
+ int lane,
+ const std::shared_ptr<const amber::debug::ThreadScript>& script) {
+ auto thread =
+ MakeUnique<Thread>(virtual_files_, session_, thread_id, lane, script);
+ auto it = runningThreads_.find(thread_id);
+ if (it != runningThreads_.end()) {
+ // The debugger has now started working on something else for the given
+ // thread id.
+ // Flush the Thread to ensure that all tasks have actually finished, and
+ // move the Thread to the completedThreads_ list.
+ auto completed_thread = std::move(it->second);
+ error_ += completed_thread->Flush();
+ completedThreads_.emplace_back(std::move(completed_thread));
+ }
+ runningThreads_[thread_id] = std::move(thread);
+ }
+
+ // OnBreakpointHit is called when a debugger breakpoint is hit (breakpoints
+ // are set at shader entry points). pendingThreads_ is checked to see if this
+ // thread needs testing, and if so, creates a new ::Thread.
+ // If there's no pendingThread_ entry for the given thread, it is resumed to
+ // allow the shader to continue executing.
+ void OnBreakpointHit(dap::integer thread_id) {
+ DEBUGGER_LOG("Breakpoint hit: thread %d", (int)thread_id);
+ Client client(session_, [this](const std::string& err) { OnError(err); });
+
+ std::unique_lock<std::mutex> lock(threads_mutex_);
+ for (auto it = pendingThreads_.begin(); it != pendingThreads_.end(); it++) {
+ auto& key = it->first;
+ auto& script = it->second;
+ switch (key.type) {
+ case InvocationKey::Type::kGlobalInvocationId: {
+ auto invocation_id = key.data.global_invocation_id;
+ int lane;
+ if (FindGlobalInvocationId(thread_id, invocation_id, &lane)) {
+ DEBUGGER_LOG("Breakpoint hit: GetGlobalInvocationId: [%d, %d, %d]",
+ invocation_id.x, invocation_id.y, invocation_id.z);
+ StartThread(thread_id, lane, script);
+ pendingThreads_.erase(it);
+ return;
+ }
+ break;
+ }
+ case InvocationKey::Type::kVertexIndex: {
+ auto vertex_id = key.data.vertex_id;
+ int lane;
+ if (FindVertexIndex(thread_id, vertex_id, &lane)) {
+ DEBUGGER_LOG("Breakpoint hit: VertexId: %d", vertex_id);
+ StartThread(thread_id, lane, script);
+ pendingThreads_.erase(it);
+ return;
+ }
+ break;
+ }
+ case InvocationKey::Type::kWindowSpacePosition: {
+ auto position = key.data.window_space_position;
+ int lane;
+ if (FindWindowSpacePosition(thread_id, position, &lane)) {
+ DEBUGGER_LOG("Breakpoint hit: VertexId: [%d, %d]", position.x,
+ position.y);
+ StartThread(thread_id, lane, script);
+ pendingThreads_.erase(it);
+ return;
+ }
+ break;
+ }
+ }
+ }
+
+ // No pending tests for this thread. Let it carry on...
+ dap::ContinueRequest request;
+ request.threadId = thread_id;
+ client.Send(request);
+ }
+
+ // OnStep is called when a debugger finishes a step request.
+ void OnStep(dap::integer thread_id) {
+ DEBUGGER_LOG("Step event: thread %d", (int)thread_id);
+ Client client(session_, [this](const std::string& err) { OnError(err); });
+
+ std::unique_lock<std::mutex> lock(threads_mutex_);
+ auto it = runningThreads_.find(thread_id);
+ if (it == runningThreads_.end()) {
+ OnError("Step event reported for non-running thread " +
+ std::to_string(thread_id.operator int64_t()));
+ }
+
+ it->second->OnStep();
+ }
+
+ // FindLocal looks for the shader's local variable with the given name and
+ // value in the stack frames' locals, returning true if found, and assigns the
+ // index of the SIMD lane it was found in to |lane|.
+ template <typename T>
+ bool FindLocal(dap::integer thread_id,
+ const char* name,
+ const T& value,
+ int* lane) {
+ Client client(session_, [this](const std::string& err) { OnError(err); });
+
+ dap::StackFrame frame;
+ if (!client.TopStackFrame(thread_id, &frame)) {
+ return false;
+ }
+
+ dap::ScopesRequest scopeReq;
+ dap::ScopesResponse scopeRes;
+ scopeReq.frameId = frame.id;
+ if (!client.Send(scopeReq, &scopeRes)) {
+ return false;
+ }
+
+ Variables locals;
+ if (!client.GetLocals(frame, &locals)) {
+ return false;
+ }
+
+ for (int i = 0;; i++) {
+ auto lane_var = client.GetLane(locals, i);
+ if (!lane_var) {
+ break;
+ }
+ if (auto var = lane_var->Find(name)) {
+ T got;
+ if (var->Get(&got)) {
+ if (got == value) {
+ *lane = i;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ // FindGlobalInvocationId looks for the compute shader's global invocation id
+ // in the stack frames' locals, returning true if found, and assigns the index
+ // of the SIMD lane it was found in to |lane|.
+ // TODO(bclayton): This value should probably be in the globals, not locals!
+ bool FindGlobalInvocationId(dap::integer thread_id,
+ const GlobalInvocationId& id,
+ int* lane) {
+ return FindLocal(thread_id, kGlobalInvocationId, id, lane);
+ }
+
+ // FindVertexIndex looks for the requested vertex shader's vertex index in the
+ // stack frames' locals, returning true if found, and assigns the index of the
+ // SIMD lane it was found in to |lane|.
+ // TODO(bclayton): This value should probably be in the globals, not locals!
+ bool FindVertexIndex(dap::integer thread_id, uint32_t index, int* lane) {
+ return FindLocal(thread_id, kVertexIndex, index, lane);
+ }
+
+ // FindWindowSpacePosition looks for the fragment shader's window space
+ // position in the stack frames' locals, returning true if found, and assigns
+ // the index of the SIMD lane it was found in to |lane|.
+ // TODO(bclayton): This value should probably be in the globals, not locals!
+ bool FindWindowSpacePosition(dap::integer thread_id,
+ const WindowSpacePosition& pos,
+ int* lane) {
+ return FindLocal(thread_id, kWindowSpacePosition, pos, lane);
+ }
+
+ void OnError(const std::string& error) {
+ DEBUGGER_LOG("ERROR: %s", error.c_str());
+ error_ += error;
+ }
+
+ using PendingThreadsMap =
+ std::unordered_map<InvocationKey,
+ std::shared_ptr<const debug::ThreadScript>,
+ InvocationKey::Hash>;
+ using ThreadVector = std::vector<std::unique_ptr<Thread>>;
+ using ThreadMap = std::unordered_map<int64_t, std::unique_ptr<Thread>>;
+ VirtualFileStore* const virtual_files_;
+ std::shared_ptr<dap::Session> session_;
+ std::mutex threads_mutex_;
+
+ // The threads that are waiting to be started.
+ PendingThreadsMap pendingThreads_; // guarded by threads_mutex_
+
+ // All threads that have been started.
+ ThreadMap runningThreads_; // guarded by threads_mutex_
+
+ // Threads that have finished.
+ ThreadVector completedThreads_; // guarded by threads_mutex_
+ std::mutex error_mutex_;
+ Result error_; // guarded by error_mutex_
+};
+} // namespace
+
+std::pair<Engine::Debugger*, Result> EngineVulkan::GetDebugger(
+ VirtualFileStore* virtual_files) {
+ if (!debugger_) {
+ auto debugger = new VkDebugger(virtual_files);
+ debugger_.reset(debugger);
+ auto res = debugger->Connect();
+ if (!res.IsSuccess()) {
+ return {nullptr, res};
+ }
+ }
+ return {debugger_.get(), Result()};
+}
+
+} // namespace vulkan
+} // namespace amber
+
+#else // AMBER_ENABLE_VK_DEBUGGING
+
+namespace amber {
+namespace vulkan {
+
+std::pair<Engine::Debugger*, Result> EngineVulkan::GetDebugger(
+ VirtualFileStore*) {
+ return {nullptr,
+ Result("Amber was not built with AMBER_ENABLE_VK_DEBUGGING enabled")};
+}
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // AMBER_ENABLE_VK_DEBUGGING
diff --git a/src/vulkan/find_vulkan.cmake b/src/vulkan/find_vulkan.cmake
index 69d5fc9..53e96fb 100644
--- a/src/vulkan/find_vulkan.cmake
+++ b/src/vulkan/find_vulkan.cmake
@@ -32,7 +32,6 @@ if (NOT ${Vulkan_FOUND})
CACHE PATH "vk_registry_dir" FORCE)
set(VulkanRegistry_DIRS ${VulkanRegistry_DIR}
CACHE PATH "vk_registry_dir" FORCE)
- include_directories(BEFORE "${VulkanHeaders_INCLUDE_DIR}")
set(VULKAN_LIB vulkan)
message(STATUS "Amber: using local vulkan")
endif()
@@ -47,7 +46,6 @@ if (NOT ${Vulkan_FOUND})
message(STATUS "Amber: Using Vulkan header dir ${X}")
list(APPEND CMAKE_REQUIRED_INCLUDES "${X}")
- # Add the directory to the list of include paths, before any others.
include_directories(BEFORE "${X}")
CHECK_INCLUDE_FILE(vulkan/vulkan.h HAVE_VULKAN_HEADER)
@@ -64,6 +62,8 @@ if (NOT ${Vulkan_FOUND})
# for the library.
# TODO(dneto): Actually check for the libraries.
set(Vulkan_FOUND TRUE)
+ set(VulkanHeaders_INCLUDE_DIR "${X}")
+ set(VulkanHeaders_INCLUDE_DIRS "${VulkanHeaders_INCLUDE_DIR}")
endif()
endif()
unset(X)
@@ -77,12 +77,13 @@ if (NOT ${Vulkan_FOUND})
message(STATUS "Amber: Using Vulkan header dir ${X}")
list(APPEND CMAKE_REQUIRED_INCLUDES "${X}")
- # Add the directory to the list of include paths, before any others.
include_directories(BEFORE "${X}")
if (EXISTS "${X}/vkDefs.h")
set(VULKAN_CTS_HEADER TRUE)
set(Vulkan_FOUND TRUE)
+ set(VulkanHeaders_INCLUDE_DIR "${X}")
+ set(VulkanHeaders_INCLUDE_DIRS "${VulkanHeaders_INCLUDE_DIR}")
endif()
endif()
unset(X)
@@ -106,8 +107,10 @@ if (NOT ${Vulkan_FOUND})
message(STATUS "Amber: Using Vulkan from Vulkan SDK at $ENV{VULKAN_SDK}")
# Use the imported library target set up by find_package.
set(VULKAN_LIB Vulkan::Vulkan)
- # Add the Vulkan include directory to the list of include paths.
- include_directories("${Vulkan_INCLUDE_DIRS}")
+ set(VulkanHeaders_INCLUDE_DIR "${Vulkan_INCLUDE_DIR}"
+ CACHE PATH "vk headers dir" FORCE)
+ set(VulkanHeaders_INCLUDE_DIRS "${Vulkan_INCLUDE_DIRS}"
+ CACHE PATH "vk headers dir" FORCE)
endif()
endif()
endif()
diff --git a/src/vulkan/frame_buffer.cc b/src/vulkan/frame_buffer.cc
index 00871f4..47eb82d 100644
--- a/src/vulkan/frame_buffer.cc
+++ b/src/vulkan/frame_buffer.cc
@@ -29,10 +29,12 @@ namespace vulkan {
FrameBuffer::FrameBuffer(
Device* device,
const std::vector<const amber::Pipeline::BufferInfo*>& color_attachments,
+ amber::Pipeline::BufferInfo depth_stencil_attachment,
uint32_t width,
uint32_t height)
: device_(device),
color_attachments_(color_attachments),
+ depth_stencil_attachment_(depth_stencil_attachment),
width_(width),
height_(height) {}
@@ -43,8 +45,7 @@ FrameBuffer::~FrameBuffer() {
}
}
-Result FrameBuffer::Initialize(VkRenderPass render_pass,
- const Format* depth_format) {
+Result FrameBuffer::Initialize(VkRenderPass render_pass) {
std::vector<VkImageView> attachments;
if (!color_attachments_.empty()) {
@@ -63,7 +64,9 @@ Result FrameBuffer::Initialize(VkRenderPass render_pass,
for (auto* info : color_attachments_) {
color_images_.push_back(MakeUnique<TransferImage>(
device_, *info->buffer->GetFormat(), VK_IMAGE_ASPECT_COLOR_BIT,
- width_, height_, depth_));
+ VK_IMAGE_TYPE_2D, width_ << info->base_mip_level,
+ height_ << info->base_mip_level, depth_, info->buffer->GetMipLevels(),
+ info->base_mip_level, 1u, 1u));
Result r = color_images_.back()->Initialize(
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
@@ -75,22 +78,26 @@ Result FrameBuffer::Initialize(VkRenderPass render_pass,
}
}
- if (depth_format && depth_format->IsFormatKnown()) {
- depth_image_ = MakeUnique<TransferImage>(
- device_, *depth_format,
- static_cast<VkImageAspectFlags>(depth_format->HasStencilComponent()
- ? VK_IMAGE_ASPECT_DEPTH_BIT |
- VK_IMAGE_ASPECT_STENCIL_BIT
- : VK_IMAGE_ASPECT_DEPTH_BIT),
- width_, height_, depth_);
+ if (depth_stencil_attachment_.buffer &&
+ depth_stencil_attachment_.buffer->GetFormat()->IsFormatKnown()) {
+ VkImageAspectFlags aspect = 0;
+ if (depth_stencil_attachment_.buffer->GetFormat()->HasDepthComponent())
+ aspect |= VK_IMAGE_ASPECT_DEPTH_BIT;
+ if (depth_stencil_attachment_.buffer->GetFormat()->HasStencilComponent())
+ aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
+ assert(aspect != 0);
- Result r = depth_image_->Initialize(
+ depth_stencil_image_ = MakeUnique<TransferImage>(
+ device_, *depth_stencil_attachment_.buffer->GetFormat(), aspect,
+ VK_IMAGE_TYPE_2D, width_, height_, depth_, 1u, 0u, 1u, 1u);
+
+ Result r = depth_stencil_image_->Initialize(
VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT |
VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
if (!r.IsSuccess())
return r;
- attachments.push_back(depth_image_->GetVkImageView());
+ attachments.push_back(depth_stencil_image_->GetVkImageView());
}
VkFramebufferCreateInfo frame_buffer_info = VkFramebufferCreateInfo();
@@ -119,8 +126,8 @@ void FrameBuffer::ChangeFrameLayout(CommandBuffer* command,
for (auto& img : color_images_)
img->ImageBarrier(command, color_layout, color_stage);
- if (depth_image_)
- depth_image_->ImageBarrier(command, depth_layout, depth_stage);
+ if (depth_stencil_image_)
+ depth_stencil_image_->ImageBarrier(command, depth_layout, depth_stage);
}
void FrameBuffer::ChangeFrameToDrawLayout(CommandBuffer* command) {
@@ -151,9 +158,12 @@ void FrameBuffer::ChangeFrameToWriteLayout(CommandBuffer* command) {
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT);
}
-void FrameBuffer::TransferColorImagesToHost(CommandBuffer* command) {
+void FrameBuffer::TransferImagesToHost(CommandBuffer* command) {
for (auto& img : color_images_)
img->CopyToHost(command);
+
+ if (depth_stencil_image_)
+ depth_stencil_image_->CopyToHost(command);
}
void FrameBuffer::CopyImagesToBuffers() {
@@ -165,11 +175,21 @@ void FrameBuffer::CopyImagesToBuffers() {
std::memcpy(values->data(), img->HostAccessibleMemoryPtr(),
info->buffer->GetSizeInBytes());
}
+
+ if (depth_stencil_image_) {
+ auto* values = depth_stencil_attachment_.buffer->ValuePtr();
+ values->resize(depth_stencil_attachment_.buffer->GetSizeInBytes());
+ std::memcpy(values->data(), depth_stencil_image_->HostAccessibleMemoryPtr(),
+ depth_stencil_attachment_.buffer->GetSizeInBytes());
+ }
}
-void FrameBuffer::TransferColorImagesToDevice(CommandBuffer* command) {
+void FrameBuffer::TransferImagesToDevice(CommandBuffer* command) {
for (auto& img : color_images_)
img->CopyToDevice(command);
+
+ if (depth_stencil_image_)
+ depth_stencil_image_->CopyToDevice(command);
}
void FrameBuffer::CopyBuffersToImages() {
@@ -184,6 +204,16 @@ void FrameBuffer::CopyBuffersToImages() {
std::memcpy(img->HostAccessibleMemoryPtr(), values->data(),
info->buffer->GetSizeInBytes());
}
+
+ if (depth_stencil_image_) {
+ auto* values = depth_stencil_attachment_.buffer->ValuePtr();
+ // Nothing to do if our local buffer is empty
+ if (!values->empty()) {
+ std::memcpy(depth_stencil_image_->HostAccessibleMemoryPtr(),
+ values->data(),
+ depth_stencil_attachment_.buffer->GetSizeInBytes());
+ }
+ }
}
} // namespace vulkan
diff --git a/src/vulkan/frame_buffer.h b/src/vulkan/frame_buffer.h
index 202726b..064b6d3 100644
--- a/src/vulkan/frame_buffer.h
+++ b/src/vulkan/frame_buffer.h
@@ -33,11 +33,12 @@ class FrameBuffer {
FrameBuffer(
Device* device,
const std::vector<const amber::Pipeline::BufferInfo*>& color_attachments,
+ amber::Pipeline::BufferInfo depth_stencil_attachment,
uint32_t width,
uint32_t height);
~FrameBuffer();
- Result Initialize(VkRenderPass render_pass, const Format* depth_format);
+ Result Initialize(VkRenderPass render_pass);
void ChangeFrameToDrawLayout(CommandBuffer* command);
void ChangeFrameToProbeLayout(CommandBuffer* command);
@@ -51,8 +52,8 @@ class FrameBuffer {
// Only record the command for copying the image that backs this
// framebuffer to the host accessible buffer. The actual submission
// of the command must be done later.
- void TransferColorImagesToHost(CommandBuffer* command);
- void TransferColorImagesToDevice(CommandBuffer* command);
+ void TransferImagesToHost(CommandBuffer* command);
+ void TransferImagesToDevice(CommandBuffer* command);
void CopyImagesToBuffers();
void CopyBuffersToImages();
@@ -69,9 +70,10 @@ class FrameBuffer {
Device* device_ = nullptr;
std::vector<const amber::Pipeline::BufferInfo*> color_attachments_;
+ amber::Pipeline::BufferInfo depth_stencil_attachment_;
VkFramebuffer frame_ = VK_NULL_HANDLE;
std::vector<std::unique_ptr<TransferImage>> color_images_;
- std::unique_ptr<TransferImage> depth_image_;
+ std::unique_ptr<TransferImage> depth_stencil_image_;
uint32_t width_ = 0;
uint32_t height_ = 0;
uint32_t depth_ = 1;
diff --git a/src/vulkan/graphics_pipeline.cc b/src/vulkan/graphics_pipeline.cc
index a63819a..b83bea1 100644
--- a/src/vulkan/graphics_pipeline.cc
+++ b/src/vulkan/graphics_pipeline.cc
@@ -90,6 +90,8 @@ VkStencilOp ToVkStencilOp(StencilOp op) {
return VK_STENCIL_OP_INCREMENT_AND_WRAP;
case StencilOp::kDecrementAndWrap:
return VK_STENCIL_OP_DECREMENT_AND_WRAP;
+ case StencilOp::kUnknown:
+ break;
}
assert(false && "Vulkan::Unknown StencilOp");
return VK_STENCIL_OP_KEEP;
@@ -113,6 +115,8 @@ VkCompareOp ToVkCompareOp(CompareOp op) {
return VK_COMPARE_OP_GREATER_OR_EQUAL;
case CompareOp::kAlways:
return VK_COMPARE_OP_ALWAYS;
+ case CompareOp::kUnknown:
+ break;
}
assert(false && "Vulkan::Unknown CompareOp");
return VK_COMPARE_OP_NEVER;
@@ -381,14 +385,14 @@ class RenderPassGuard {
GraphicsPipeline::GraphicsPipeline(
Device* device,
const std::vector<amber::Pipeline::BufferInfo>& color_buffers,
- Format* depth_stencil_format,
+ amber::Pipeline::BufferInfo depth_stencil_buffer,
uint32_t fence_timeout_ms,
const std::vector<VkPipelineShaderStageCreateInfo>& shader_stage_info)
: Pipeline(PipelineType::kGraphics,
device,
fence_timeout_ms,
shader_stage_info),
- depth_stencil_format_(depth_stencil_format) {
+ depth_stencil_buffer_(depth_stencil_buffer) {
for (const auto& info : color_buffers)
color_buffers_.push_back(&info);
}
@@ -426,10 +430,11 @@ Result GraphicsPipeline::CreateRenderPass() {
subpass_desc.colorAttachmentCount = static_cast<uint32_t>(color_refer.size());
subpass_desc.pColorAttachments = color_refer.data();
- if (depth_stencil_format_ && depth_stencil_format_->IsFormatKnown()) {
+ if (depth_stencil_buffer_.buffer &&
+ depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
attachment_desc.push_back(kDefaultAttachmentDesc);
attachment_desc.back().format =
- device_->GetVkFormat(*depth_stencil_format_);
+ device_->GetVkFormat(*depth_stencil_buffer_.buffer->GetFormat());
attachment_desc.back().initialLayout =
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachment_desc.back().finalLayout =
@@ -541,31 +546,25 @@ Result GraphicsPipeline::CreateVkGraphicsPipeline(
"null");
}
+ std::vector<VkVertexInputBindingDescription> vertex_bindings;
+ std::vector<VkVertexInputAttributeDescription> vertex_attribs;
+ if (vertex_buffer != nullptr) {
+ vertex_bindings = vertex_buffer->GetVkVertexInputBinding();
+ vertex_attribs = vertex_buffer->GetVkVertexInputAttr();
+ }
+
VkPipelineVertexInputStateCreateInfo vertex_input_info =
VkPipelineVertexInputStateCreateInfo();
vertex_input_info.sType =
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
- vertex_input_info.vertexBindingDescriptionCount = 1;
-
- VkVertexInputBindingDescription vertex_binding_desc =
- VkVertexInputBindingDescription();
- if (vertex_buffer != nullptr) {
- vertex_binding_desc = vertex_buffer->GetVkVertexInputBinding();
- const auto& vertex_attr_desc = vertex_buffer->GetVkVertexInputAttr();
-
- vertex_input_info.pVertexBindingDescriptions = &vertex_binding_desc;
- vertex_input_info.vertexAttributeDescriptionCount =
- static_cast<uint32_t>(vertex_attr_desc.size());
- vertex_input_info.pVertexAttributeDescriptions = vertex_attr_desc.data();
- } else {
- vertex_binding_desc.binding = 0;
- vertex_binding_desc.stride = 0;
- vertex_binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
-
- vertex_input_info.pVertexBindingDescriptions = &vertex_binding_desc;
- vertex_input_info.vertexAttributeDescriptionCount = 0;
- vertex_input_info.pVertexAttributeDescriptions = nullptr;
- }
+ vertex_input_info.vertexBindingDescriptionCount =
+ static_cast<uint32_t>(vertex_bindings.size());
+ vertex_input_info.pVertexBindingDescriptions =
+ vertex_bindings.empty() ? nullptr : vertex_bindings.data();
+ vertex_input_info.vertexAttributeDescriptionCount =
+ static_cast<uint32_t>(vertex_attribs.size());
+ vertex_input_info.pVertexAttributeDescriptions =
+ vertex_attribs.empty() ? nullptr : vertex_attribs.data();
VkPipelineInputAssemblyStateCreateInfo input_assembly_info =
VkPipelineInputAssemblyStateCreateInfo();
@@ -650,7 +649,8 @@ Result GraphicsPipeline::CreateVkGraphicsPipeline(
}
VkPipelineDepthStencilStateCreateInfo depthstencil_info;
- if (depth_stencil_format_ && depth_stencil_format_->IsFormatKnown()) {
+ if (depth_stencil_buffer_.buffer &&
+ depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
depthstencil_info = GetVkPipelineDepthStencilInfo(pipeline_data);
pipeline_info.pDepthStencilState = &depthstencil_info;
}
@@ -694,8 +694,9 @@ Result GraphicsPipeline::Initialize(uint32_t width,
if (!r.IsSuccess())
return r;
- frame_ = MakeUnique<FrameBuffer>(device_, color_buffers_, width, height);
- r = frame_->Initialize(render_pass_, depth_stencil_format_);
+ frame_ = MakeUnique<FrameBuffer>(device_, color_buffers_,
+ depth_stencil_buffer_, width, height);
+ r = frame_->Initialize(render_pass_);
if (!r.IsSuccess())
return r;
@@ -741,7 +742,8 @@ Result GraphicsPipeline::SetClearColor(float r, float g, float b, float a) {
}
Result GraphicsPipeline::SetClearStencil(uint32_t stencil) {
- if (!depth_stencil_format_ || !depth_stencil_format_->IsFormatKnown()) {
+ if (!depth_stencil_buffer_.buffer ||
+ !depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
return Result(
"Vulkan::ClearStencilCommand No DepthStencil Buffer for FrameBuffer "
"Exists");
@@ -752,7 +754,8 @@ Result GraphicsPipeline::SetClearStencil(uint32_t stencil) {
}
Result GraphicsPipeline::SetClearDepth(float depth) {
- if (!depth_stencil_format_ || !depth_stencil_format_->IsFormatKnown()) {
+ if (!depth_stencil_buffer_.buffer ||
+ !depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
return Result(
"Vulkan::ClearStencilCommand No DepthStencil Buffer for FrameBuffer "
"Exists");
@@ -767,32 +770,13 @@ Result GraphicsPipeline::Clear() {
colour_clear.color = {
{clear_color_r_, clear_color_g_, clear_color_b_, clear_color_a_}};
- Result r = ClearBuffer(colour_clear, VK_IMAGE_ASPECT_COLOR_BIT);
- if (!r.IsSuccess())
- return r;
-
- if (!depth_stencil_format_ || !depth_stencil_format_->IsFormatKnown())
- return {};
-
- VkClearValue depth_clear;
- depth_clear.depthStencil = {clear_depth_, clear_stencil_};
-
- return ClearBuffer(
- depth_clear,
- depth_stencil_format_ && depth_stencil_format_->HasStencilComponent()
- ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT
- : VK_IMAGE_ASPECT_DEPTH_BIT);
-}
-
-Result GraphicsPipeline::ClearBuffer(const VkClearValue& clear_value,
- VkImageAspectFlags aspect) {
CommandBufferGuard cmd_buf_guard(GetCommandBuffer());
if (!cmd_buf_guard.IsRecording())
return cmd_buf_guard.GetResult();
frame_->ChangeFrameToWriteLayout(GetCommandBuffer());
frame_->CopyBuffersToImages();
- frame_->TransferColorImagesToDevice(GetCommandBuffer());
+ frame_->TransferImagesToDevice(GetCommandBuffer());
{
RenderPassGuard render_pass_guard(this);
@@ -800,9 +784,27 @@ Result GraphicsPipeline::ClearBuffer(const VkClearValue& clear_value,
std::vector<VkClearAttachment> clears;
for (size_t i = 0; i < color_buffers_.size(); ++i) {
VkClearAttachment clear_attachment = VkClearAttachment();
- clear_attachment.aspectMask = aspect;
+ clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clear_attachment.colorAttachment = static_cast<uint32_t>(i);
- clear_attachment.clearValue = clear_value;
+ clear_attachment.clearValue = colour_clear;
+
+ clears.push_back(clear_attachment);
+ }
+
+ if (depth_stencil_buffer_.buffer &&
+ depth_stencil_buffer_.buffer->GetFormat()->IsFormatKnown()) {
+ VkClearValue depth_stencil_clear;
+ depth_stencil_clear.depthStencil = {clear_depth_, clear_stencil_};
+
+ VkImageAspectFlags aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
+ if (depth_stencil_buffer_.buffer->GetFormat()->HasStencilComponent())
+ aspect |= VK_IMAGE_ASPECT_STENCIL_BIT;
+
+ VkClearAttachment clear_attachment = VkClearAttachment();
+ clear_attachment.aspectMask = aspect;
+ clear_attachment.colorAttachment =
+ static_cast<uint32_t>(color_buffers_.size());
+ clear_attachment.clearValue = depth_stencil_clear;
clears.push_back(clear_attachment);
}
@@ -817,7 +819,7 @@ Result GraphicsPipeline::ClearBuffer(const VkClearValue& clear_value,
clears.data(), 1, &clear_rect);
}
- frame_->TransferColorImagesToHost(command_.get());
+ frame_->TransferImagesToHost(command_.get());
Result r = cmd_buf_guard.Submit(GetFenceTimeout());
if (!r.IsSuccess())
@@ -861,7 +863,7 @@ Result GraphicsPipeline::Draw(const DrawArraysCommand* command,
frame_->ChangeFrameToWriteLayout(GetCommandBuffer());
frame_->CopyBuffersToImages();
- frame_->TransferColorImagesToDevice(GetCommandBuffer());
+ frame_->TransferImagesToDevice(GetCommandBuffer());
{
RenderPassGuard render_pass_guard(this);
@@ -879,10 +881,6 @@ Result GraphicsPipeline::Draw(const DrawArraysCommand* command,
if (vertex_buffer != nullptr)
vertex_buffer->BindToCommandBuffer(command_.get());
- uint32_t instance_count = command->GetInstanceCount();
- if (instance_count == 0 && command->GetVertexCount() != 0)
- instance_count = 1;
-
if (command->IsIndexed()) {
if (!index_buffer_)
return Result("Vulkan: Draw indexed is used without given indices");
@@ -896,20 +894,21 @@ Result GraphicsPipeline::Draw(const DrawArraysCommand* command,
// becomes the vertex offset and firstIndex will always be zero."
device_->GetPtrs()->vkCmdDrawIndexed(
command_->GetVkCommandBuffer(),
- command->GetVertexCount(), /* indexCount */
- instance_count, /* instanceCount */
- 0, /* firstIndex */
+ command->GetVertexCount(), /* indexCount */
+ command->GetInstanceCount(), /* instanceCount */
+ 0, /* firstIndex */
static_cast<int32_t>(
command->GetFirstVertexIndex()), /* vertexOffset */
- 0 /* firstInstance */);
+ command->GetFirstInstance()); /* firstInstance */
} else {
- device_->GetPtrs()->vkCmdDraw(command_->GetVkCommandBuffer(),
- command->GetVertexCount(), instance_count,
- command->GetFirstVertexIndex(), 0);
+ device_->GetPtrs()->vkCmdDraw(
+ command_->GetVkCommandBuffer(), command->GetVertexCount(),
+ command->GetInstanceCount(), command->GetFirstVertexIndex(),
+ command->GetFirstInstance());
}
}
- frame_->TransferColorImagesToHost(command_.get());
+ frame_->TransferImagesToHost(command_.get());
r = cmd_buf_guard.Submit(GetFenceTimeout());
if (!r.IsSuccess())
diff --git a/src/vulkan/graphics_pipeline.h b/src/vulkan/graphics_pipeline.h
index e08bdc3..7076231 100644
--- a/src/vulkan/graphics_pipeline.h
+++ b/src/vulkan/graphics_pipeline.h
@@ -42,7 +42,7 @@ class GraphicsPipeline : public Pipeline {
GraphicsPipeline(
Device* device,
const std::vector<amber::Pipeline::BufferInfo>& color_buffers,
- Format* depth_stencil_format,
+ amber::Pipeline::BufferInfo depth_stencil_buffer,
uint32_t fence_timeout_ms,
const std::vector<VkPipelineShaderStageCreateInfo>&);
~GraphicsPipeline() override;
@@ -52,8 +52,6 @@ class GraphicsPipeline : public Pipeline {
Result SetIndexBuffer(Buffer* buffer);
Result Clear();
- Result ClearBuffer(const VkClearValue& clear_value,
- VkImageAspectFlags aspect);
Result SetClearColor(float r, float g, float b, float a);
Result SetClearStencil(uint32_t stencil);
@@ -90,7 +88,7 @@ class GraphicsPipeline : public Pipeline {
// color buffers are owned by the amber::Pipeline.
std::vector<const amber::Pipeline::BufferInfo*> color_buffers_;
- Format* depth_stencil_format_;
+ amber::Pipeline::BufferInfo depth_stencil_buffer_;
std::unique_ptr<IndexBuffer> index_buffer_;
uint32_t frame_width_ = 0;
diff --git a/src/vulkan/image_descriptor.cc b/src/vulkan/image_descriptor.cc
new file mode 100644
index 0000000..550ce0b
--- /dev/null
+++ b/src/vulkan/image_descriptor.cc
@@ -0,0 +1,189 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/vulkan/image_descriptor.h"
+
+#include "src/vulkan/device.h"
+#include "src/vulkan/resource.h"
+
+namespace amber {
+namespace vulkan {
+
+ImageDescriptor::ImageDescriptor(Buffer* buffer,
+ DescriptorType type,
+ Device* device,
+ uint32_t base_mip_level,
+ uint32_t desc_set,
+ uint32_t binding)
+ : BufferBackedDescriptor(buffer, type, device, desc_set, binding),
+ base_mip_level_(base_mip_level),
+ vulkan_sampler_(device) {}
+
+ImageDescriptor::~ImageDescriptor() = default;
+
+Result ImageDescriptor::RecordCopyDataToResourceIfNeeded(
+ CommandBuffer* command) {
+ for (auto& image : transfer_images_) {
+ image->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT);
+ }
+
+ Result r = BufferBackedDescriptor::RecordCopyDataToResourceIfNeeded(command);
+ if (!r.IsSuccess())
+ return r;
+
+ // Just do this as early as possible.
+ for (auto& image : transfer_images_) {
+ image->ImageBarrier(command, VK_IMAGE_LAYOUT_GENERAL,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
+ }
+
+ return {};
+}
+
+Result ImageDescriptor::CreateResourceIfNeeded() {
+ if (!transfer_images_.empty()) {
+ return Result(
+ "Vulkan: ImageDescriptor::CreateResourceIfNeeded() must be called "
+ "only when |transfer_images| is empty");
+ }
+
+ transfer_images_.reserve(GetAmberBuffers().size());
+
+ for (const auto& amber_buffer : GetAmberBuffers()) {
+ if (amber_buffer->ValuePtr()->empty())
+ continue;
+
+ // Default to 2D image.
+ VkImageType image_type = VK_IMAGE_TYPE_2D;
+ switch (amber_buffer->GetImageDimension()) {
+ case ImageDimension::k1D:
+ image_type = VK_IMAGE_TYPE_1D;
+ break;
+ case ImageDimension::k2D:
+ image_type = VK_IMAGE_TYPE_2D;
+ break;
+ case ImageDimension::k3D:
+ image_type = VK_IMAGE_TYPE_3D;
+ break;
+ default:
+ break;
+ }
+
+ Format* fmt = amber_buffer->GetFormat();
+ VkImageAspectFlags aspect = 0;
+ if (fmt->HasDepthComponent() && fmt->HasStencilComponent()) {
+ aspect = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
+ } else if (fmt->HasDepthComponent()) {
+ aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
+ } else if (fmt->HasStencilComponent()) {
+ aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
+ } else {
+ aspect = VK_IMAGE_ASPECT_COLOR_BIT;
+ }
+
+ transfer_images_.emplace_back(MakeUnique<TransferImage>(
+ device_, *fmt, aspect, image_type, amber_buffer->GetWidth(),
+ amber_buffer->GetHeight(), amber_buffer->GetDepth(),
+ amber_buffer->GetMipLevels(), base_mip_level_, VK_REMAINING_MIP_LEVELS,
+ amber_buffer->GetSamples()));
+ VkImageUsageFlags usage =
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+
+ if (type_ == DescriptorType::kStorageImage) {
+ usage |= VK_IMAGE_USAGE_STORAGE_BIT;
+ } else {
+ assert(type_ == DescriptorType::kSampledImage ||
+ type_ == DescriptorType::kCombinedImageSampler);
+ usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
+ }
+
+ Result r = transfer_images_.back()->Initialize(usage);
+
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ if (amber_sampler_) {
+ Result r = vulkan_sampler_.CreateSampler(amber_sampler_);
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ is_descriptor_set_update_needed_ = true;
+ return {};
+}
+
+Result ImageDescriptor::RecordCopyDataToHost(CommandBuffer* command) {
+ if (!IsReadOnly()) {
+ for (auto& image : transfer_images_) {
+ image->ImageBarrier(command, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ VK_PIPELINE_STAGE_TRANSFER_BIT);
+ }
+
+ BufferBackedDescriptor::RecordCopyDataToHost(command);
+ }
+
+ return {};
+}
+
+Result ImageDescriptor::MoveResourceToBufferOutput() {
+ Result r = BufferBackedDescriptor::MoveResourceToBufferOutput();
+
+ transfer_images_.clear();
+
+ return r;
+}
+
+void ImageDescriptor::UpdateDescriptorSetIfNeeded(
+ VkDescriptorSet descriptor_set) {
+ if (!is_descriptor_set_update_needed_)
+ return;
+
+ // Always use general layout.
+ VkImageLayout layout = VK_IMAGE_LAYOUT_GENERAL;
+
+ std::vector<VkDescriptorImageInfo> image_infos;
+
+ for (const auto& image : transfer_images_) {
+ VkDescriptorImageInfo image_info = {vulkan_sampler_.GetVkSampler(),
+ image->GetVkImageView(), layout};
+ image_infos.push_back(image_info);
+ }
+
+ VkWriteDescriptorSet write = VkWriteDescriptorSet();
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.dstSet = descriptor_set;
+ write.dstBinding = binding_;
+ write.dstArrayElement = 0;
+ write.descriptorCount = static_cast<uint32_t>(image_infos.size());
+ write.descriptorType = GetVkDescriptorType();
+ write.pImageInfo = image_infos.data();
+
+ device_->GetPtrs()->vkUpdateDescriptorSets(device_->GetVkDevice(), 1, &write,
+ 0, nullptr);
+
+ is_descriptor_set_update_needed_ = false;
+}
+
+std::vector<Resource*> ImageDescriptor::GetResources() {
+ std::vector<Resource*> ret;
+ for (auto& i : transfer_images_) {
+ ret.push_back(i.get());
+ }
+ return ret;
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/image_descriptor.h b/src/vulkan/image_descriptor.h
new file mode 100644
index 0000000..5f8dd0e
--- /dev/null
+++ b/src/vulkan/image_descriptor.h
@@ -0,0 +1,58 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_VULKAN_IMAGE_DESCRIPTOR_H_
+#define SRC_VULKAN_IMAGE_DESCRIPTOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "src/vulkan/buffer_backed_descriptor.h"
+#include "src/vulkan/sampler.h"
+#include "src/vulkan/transfer_image.h"
+
+namespace amber {
+namespace vulkan {
+
+class ImageDescriptor : public BufferBackedDescriptor {
+ public:
+ ImageDescriptor(Buffer* buffer,
+ DescriptorType type,
+ Device* device,
+ uint32_t base_mip_level,
+ uint32_t desc_set,
+ uint32_t binding);
+ ~ImageDescriptor() override;
+
+ void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set) override;
+ Result RecordCopyDataToResourceIfNeeded(CommandBuffer* command) override;
+ Result CreateResourceIfNeeded() override;
+ Result RecordCopyDataToHost(CommandBuffer* command) override;
+ Result MoveResourceToBufferOutput() override;
+ void SetAmberSampler(amber::Sampler* sampler) { amber_sampler_ = sampler; }
+
+ protected:
+ std::vector<Resource*> GetResources() override;
+
+ private:
+ uint32_t base_mip_level_ = 0;
+ std::vector<std::unique_ptr<TransferImage>> transfer_images_;
+ amber::Sampler* amber_sampler_ = nullptr;
+ amber::vulkan::Sampler vulkan_sampler_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_IMAGE_DESCRIPTOR_H_
diff --git a/src/vulkan/index_buffer.cc b/src/vulkan/index_buffer.cc
index a86194e..73a9083 100644
--- a/src/vulkan/index_buffer.cc
+++ b/src/vulkan/index_buffer.cc
@@ -37,7 +37,7 @@ Result IndexBuffer::SendIndexData(CommandBuffer* command, Buffer* buffer) {
return Result("IndexBuffer::SendIndexData |buffer| is empty");
transfer_buffer_ =
- MakeUnique<TransferBuffer>(device_, buffer->GetSizeInBytes());
+ MakeUnique<TransferBuffer>(device_, buffer->GetSizeInBytes(), nullptr);
Result r = transfer_buffer_->Initialize(VK_BUFFER_USAGE_INDEX_BUFFER_BIT |
VK_BUFFER_USAGE_TRANSFER_DST_BIT);
if (!r.IsSuccess())
diff --git a/src/vulkan/pipeline.cc b/src/vulkan/pipeline.cc
index 28b4e6b..e0b875c 100644
--- a/src/vulkan/pipeline.cc
+++ b/src/vulkan/pipeline.cc
@@ -25,6 +25,8 @@
#include "src/vulkan/compute_pipeline.h"
#include "src/vulkan/device.h"
#include "src/vulkan/graphics_pipeline.h"
+#include "src/vulkan/image_descriptor.h"
+#include "src/vulkan/sampler_descriptor.h"
namespace amber {
namespace vulkan {
@@ -89,11 +91,11 @@ Result Pipeline::CreateDescriptorSetLayouts() {
// If there are no descriptors for this descriptor set we only
// need to create its layout and there will be no bindings.
std::vector<VkDescriptorSetLayoutBinding> bindings;
- for (auto& desc : info.buffer_descriptors) {
+ for (auto& desc : info.descriptors) {
bindings.emplace_back();
bindings.back().binding = desc->GetBinding();
bindings.back().descriptorType = desc->GetVkDescriptorType();
- bindings.back().descriptorCount = 1;
+ bindings.back().descriptorCount = desc->GetDescriptorCount();
bindings.back().stageFlags = VK_SHADER_STAGE_ALL;
}
desc_info.bindingCount = static_cast<uint32_t>(bindings.size());
@@ -115,20 +117,20 @@ Result Pipeline::CreateDescriptorPools() {
continue;
std::vector<VkDescriptorPoolSize> pool_sizes;
- for (auto& desc : info.buffer_descriptors) {
+ for (auto& desc : info.descriptors) {
VkDescriptorType type = desc->GetVkDescriptorType();
auto it = find_if(pool_sizes.begin(), pool_sizes.end(),
[&type](const VkDescriptorPoolSize& size) {
return size.type == type;
});
if (it != pool_sizes.end()) {
- it->descriptorCount += 1;
+ it->descriptorCount += desc->GetDescriptorCount();
continue;
}
pool_sizes.emplace_back();
pool_sizes.back().type = type;
- pool_sizes.back().descriptorCount = 1;
+ pool_sizes.back().descriptorCount = desc->GetDescriptorCount();
}
VkDescriptorPoolCreateInfo pool_info = VkDescriptorPoolCreateInfo();
@@ -223,7 +225,7 @@ Result Pipeline::CreateVkDescriptorRelatedObjectsIfNeeded() {
void Pipeline::UpdateDescriptorSetsIfNeeded() {
for (auto& info : descriptor_set_info_) {
- for (auto& desc : info.buffer_descriptors)
+ for (auto& desc : info.descriptors)
desc->UpdateDescriptorSetIfNeeded(info.vk_desc_set);
}
}
@@ -239,15 +241,11 @@ Result Pipeline::AddPushConstantBuffer(const Buffer* buf, uint32_t offset) {
return push_constant_->AddBuffer(buf, offset);
}
-Result Pipeline::AddDescriptor(const BufferCommand* cmd) {
- if (cmd == nullptr)
- return Result("Pipeline::AddDescriptor BufferCommand is nullptr");
- if (cmd->IsPushConstant())
- return AddPushConstantBuffer(cmd->GetBuffer(), cmd->GetOffset());
- if (!cmd->IsSSBO() && !cmd->IsUniform())
- return Result("Pipeline::AddDescriptor not supported buffer type");
+Result Pipeline::GetDescriptorSlot(uint32_t desc_set,
+ uint32_t binding,
+ Descriptor** desc) {
+ *desc = nullptr;
- const uint32_t desc_set = cmd->GetDescriptorSet();
if (desc_set >= descriptor_set_info_.size()) {
for (size_t i = descriptor_set_info_.size();
i <= static_cast<size_t>(desc_set); ++i) {
@@ -266,45 +264,131 @@ Result Pipeline::AddDescriptor(const BufferCommand* cmd) {
}
descriptor_set_info_[desc_set].empty = false;
- auto& descriptors = descriptor_set_info_[desc_set].buffer_descriptors;
- BufferDescriptor* desc = nullptr;
+ auto& descriptors = descriptor_set_info_[desc_set].descriptors;
for (auto& descriptor : descriptors) {
- if (descriptor->GetBinding() == cmd->GetBinding())
- desc = descriptor.get();
+ if (descriptor->GetBinding() == binding)
+ *desc = descriptor.get();
}
- if (desc == nullptr) {
- auto desc_type = cmd->IsSSBO() ? DescriptorType::kStorageBuffer
- : DescriptorType::kUniformBuffer;
- auto buffer_desc = MakeUnique<BufferDescriptor>(
- cmd->GetBuffer(), desc_type, device_, cmd->GetDescriptorSet(),
- cmd->GetBinding());
- descriptors.push_back(std::move(buffer_desc));
+ return {};
+}
+Result Pipeline::AddBufferDescriptor(const BufferCommand* cmd) {
+ if (cmd == nullptr)
+ return Result("Pipeline::AddBufferDescriptor BufferCommand is nullptr");
+ if (!cmd->IsSSBO() && !cmd->IsUniform() && !cmd->IsStorageImage() &&
+ !cmd->IsSampledImage() && !cmd->IsCombinedImageSampler() &&
+ !cmd->IsUniformTexelBuffer() && !cmd->IsStorageTexelBuffer() &&
+ !cmd->IsUniformDynamic() && !cmd->IsSSBODynamic()) {
+ return Result("Pipeline::AddBufferDescriptor not supported buffer type");
+ }
+
+ Descriptor* desc;
+ Result r =
+ GetDescriptorSlot(cmd->GetDescriptorSet(), cmd->GetBinding(), &desc);
+ if (!r.IsSuccess())
+ return r;
+
+ auto& descriptors = descriptor_set_info_[cmd->GetDescriptorSet()].descriptors;
+
+ bool is_image = false;
+ DescriptorType desc_type = DescriptorType::kUniformBuffer;
+
+ if (cmd->IsStorageImage()) {
+ desc_type = DescriptorType::kStorageImage;
+ is_image = true;
+ } else if (cmd->IsSampledImage()) {
+ desc_type = DescriptorType::kSampledImage;
+ is_image = true;
+ } else if (cmd->IsCombinedImageSampler()) {
+ desc_type = DescriptorType::kCombinedImageSampler;
+ is_image = true;
+ } else if (cmd->IsUniformTexelBuffer()) {
+ desc_type = DescriptorType::kUniformTexelBuffer;
+ } else if (cmd->IsStorageTexelBuffer()) {
+ desc_type = DescriptorType::kStorageTexelBuffer;
+ } else if (cmd->IsSSBO()) {
+ desc_type = DescriptorType::kStorageBuffer;
+ } else if (cmd->IsUniformDynamic()) {
+ desc_type = DescriptorType::kUniformBufferDynamic;
+ } else if (cmd->IsSSBODynamic()) {
+ desc_type = DescriptorType::kStorageBufferDynamic;
+ }
+
+ if (desc == nullptr) {
+ if (is_image) {
+ auto image_desc = MakeUnique<ImageDescriptor>(
+ cmd->GetBuffer(), desc_type, device_, cmd->GetBaseMipLevel(),
+ cmd->GetDescriptorSet(), cmd->GetBinding());
+ if (cmd->IsCombinedImageSampler())
+ image_desc->SetAmberSampler(cmd->GetSampler());
+ descriptors.push_back(std::move(image_desc));
+ } else {
+ auto buffer_desc = MakeUnique<BufferDescriptor>(
+ cmd->GetBuffer(), desc_type, device_, cmd->GetDescriptorSet(),
+ cmd->GetBinding());
+ descriptors.push_back(std::move(buffer_desc));
+ }
desc = descriptors.back().get();
+ } else {
+ if (desc->GetDescriptorType() != desc_type) {
+ return Result(
+ "Descriptors bound to the same binding needs to have matching "
+ "descriptor types");
+ }
+ // Check that the buffer is not added already.
+ const auto& buffers = desc->AsBufferBackedDescriptor()->GetAmberBuffers();
+ if (std::find(buffers.begin(), buffers.end(), cmd->GetBuffer()) !=
+ buffers.end()) {
+ return Result("Buffer has been added already");
+ }
+ desc->AsBufferBackedDescriptor()->AddAmberBuffer(cmd->GetBuffer());
}
+ if (cmd->IsUniformDynamic() || cmd->IsSSBODynamic())
+ desc->AsBufferDescriptor()->AddDynamicOffset(cmd->GetDynamicOffset());
+
if (cmd->IsSSBO() && !desc->IsStorageBuffer()) {
return Result(
- "Vulkan::AddDescriptor BufferCommand for SSBO uses wrong descriptor "
+ "Vulkan::AddBufferDescriptor BufferCommand for SSBO uses wrong "
+ "descriptor "
"set and binding");
}
if (cmd->IsUniform() && !desc->IsUniformBuffer()) {
return Result(
- "Vulkan::AddDescriptor BufferCommand for UBO uses wrong descriptor set "
+ "Vulkan::AddBufferDescriptor BufferCommand for UBO uses wrong "
+ "descriptor set "
"and binding");
}
- auto* buf_desc = static_cast<BufferDescriptor*>(desc);
- if (cmd->GetValues().empty()) {
- Result r = buf_desc->SetSizeInElements(cmd->GetBuffer()->ElementCount());
- if (!r.IsSuccess())
- return r;
+ return {};
+}
+
+Result Pipeline::AddSamplerDescriptor(const SamplerCommand* cmd) {
+ if (cmd == nullptr)
+ return Result("Pipeline::AddSamplerDescriptor SamplerCommand is nullptr");
+
+ Descriptor* desc;
+ Result r =
+ GetDescriptorSlot(cmd->GetDescriptorSet(), cmd->GetBinding(), &desc);
+ if (!r.IsSuccess())
+ return r;
+
+ auto& descriptors = descriptor_set_info_[cmd->GetDescriptorSet()].descriptors;
+
+ if (desc == nullptr) {
+ auto sampler_desc = MakeUnique<SamplerDescriptor>(
+ cmd->GetSampler(), DescriptorType::kSampler, device_,
+ cmd->GetDescriptorSet(), cmd->GetBinding());
+ descriptors.push_back(std::move(sampler_desc));
} else {
- Result r = buf_desc->AddToBuffer(cmd->GetValues(), cmd->GetOffset());
- if (!r.IsSuccess())
- return r;
+ if (desc->GetDescriptorType() != DescriptorType::kSampler) {
+ return Result(
+ "Descriptors bound to the same binding needs to have matching "
+ "descriptor types");
+ }
+ desc->AsSamplerDescriptor()->AddAmberSampler(cmd->GetSampler());
}
return {};
@@ -317,7 +401,7 @@ Result Pipeline::SendDescriptorDataToDeviceIfNeeded() {
return guard.GetResult();
for (auto& info : descriptor_set_info_) {
- for (auto& desc : info.buffer_descriptors) {
+ for (auto& desc : info.descriptors) {
Result r = desc->CreateResourceIfNeeded();
if (!r.IsSuccess())
return r;
@@ -340,8 +424,11 @@ Result Pipeline::SendDescriptorDataToDeviceIfNeeded() {
return guard.GetResult();
for (auto& info : descriptor_set_info_) {
- for (auto& desc : info.buffer_descriptors)
- desc->RecordCopyDataToResourceIfNeeded(command_.get());
+ for (auto& desc : info.descriptors) {
+ Result r = desc->RecordCopyDataToResourceIfNeeded(command_.get());
+ if (!r.IsSuccess())
+ return r;
+ }
}
return guard.Submit(GetFenceTimeout());
}
@@ -351,12 +438,35 @@ void Pipeline::BindVkDescriptorSets(const VkPipelineLayout& pipeline_layout) {
if (descriptor_set_info_[i].empty)
continue;
+ // Sort descriptors by binding number to get correct order of dynamic
+ // offsets.
+ typedef std::pair<uint32_t, std::vector<uint32_t>> binding_offsets_pair;
+ std::vector<binding_offsets_pair> binding_offsets;
+ for (const auto& desc : descriptor_set_info_[i].descriptors) {
+ binding_offsets.push_back(
+ {desc->GetBinding(), desc->GetDynamicOffsets()});
+ }
+
+ std::sort(std::begin(binding_offsets), std::end(binding_offsets),
+ [](const binding_offsets_pair& a, const binding_offsets_pair& b) {
+ return a.first < b.first;
+ });
+
+ // Add the sorted dynamic offsets.
+ std::vector<uint32_t> dynamic_offsets;
+ for (const auto& binding_offset : binding_offsets) {
+ for (auto offset : binding_offset.second) {
+ dynamic_offsets.push_back(offset);
+ }
+ }
+
device_->GetPtrs()->vkCmdBindDescriptorSets(
command_->GetVkCommandBuffer(),
IsGraphics() ? VK_PIPELINE_BIND_POINT_GRAPHICS
: VK_PIPELINE_BIND_POINT_COMPUTE,
pipeline_layout, static_cast<uint32_t>(i), 1,
- &descriptor_set_info_[i].vk_desc_set, 0, nullptr);
+ &descriptor_set_info_[i].vk_desc_set,
+ static_cast<uint32_t>(dynamic_offsets.size()), dynamic_offsets.data());
}
}
@@ -367,7 +477,7 @@ Result Pipeline::ReadbackDescriptorsToHostDataQueue() {
return guard.GetResult();
for (auto& desc_set : descriptor_set_info_) {
- for (auto& desc : desc_set.buffer_descriptors)
+ for (auto& desc : desc_set.descriptors)
desc->RecordCopyDataToHost(command_.get());
}
@@ -377,7 +487,7 @@ Result Pipeline::ReadbackDescriptorsToHostDataQueue() {
}
for (auto& desc_set : descriptor_set_info_) {
- for (auto& desc : desc_set.buffer_descriptors) {
+ for (auto& desc : desc_set.descriptors) {
Result r = desc->MoveResourceToBufferOutput();
if (!r.IsSuccess())
return r;
diff --git a/src/vulkan/pipeline.h b/src/vulkan/pipeline.h
index 78aa252..98b0c2a 100644
--- a/src/vulkan/pipeline.h
+++ b/src/vulkan/pipeline.h
@@ -24,7 +24,7 @@
#include "amber/vulkan_header.h"
#include "src/cast_hash.h"
#include "src/engine.h"
-#include "src/vulkan/buffer_descriptor.h"
+#include "src/vulkan/buffer_backed_descriptor.h"
#include "src/vulkan/command_buffer.h"
#include "src/vulkan/push_constant.h"
@@ -49,7 +49,8 @@ class Pipeline {
GraphicsPipeline* AsGraphics();
ComputePipeline* AsCompute();
- Result AddDescriptor(const BufferCommand*);
+ Result AddBufferDescriptor(const BufferCommand*);
+ Result AddSamplerDescriptor(const SamplerCommand*);
/// Add |buffer| data to the push constants at |offset|.
Result AddPushConstantBuffer(const Buffer* buf, uint32_t offset);
@@ -76,6 +77,9 @@ class Pipeline {
/// Initializes the pipeline.
Result Initialize(CommandPool* pool);
+ Result GetDescriptorSlot(uint32_t desc_set,
+ uint32_t binding,
+ Descriptor** desc);
void UpdateDescriptorSetsIfNeeded();
Result SendDescriptorDataToDeviceIfNeeded();
@@ -103,7 +107,7 @@ class Pipeline {
VkDescriptorSetLayout layout = VK_NULL_HANDLE;
VkDescriptorPool pool = VK_NULL_HANDLE;
VkDescriptorSet vk_desc_set = VK_NULL_HANDLE;
- std::vector<std::unique_ptr<BufferDescriptor>> buffer_descriptors;
+ std::vector<std::unique_ptr<Descriptor>> descriptors;
};
/// Creates Vulkan descriptor related objects.
@@ -116,7 +120,7 @@ class Pipeline {
std::vector<DescriptorSetInfo> descriptor_set_info_;
std::vector<VkPipelineShaderStageCreateInfo> shader_stage_info_;
- uint32_t fence_timeout_ms_ = 100;
+ uint32_t fence_timeout_ms_ = 1000;
bool descriptor_related_objects_already_created_ = false;
std::unordered_map<VkShaderStageFlagBits,
std::string,
diff --git a/src/vulkan/pipeline_test.cc b/src/vulkan/pipeline_test.cc
new file mode 100644
index 0000000..53eca44
--- /dev/null
+++ b/src/vulkan/pipeline_test.cc
@@ -0,0 +1,53 @@
+// Copyright 2020 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/vulkan/pipeline.h"
+
+#include "gtest/gtest.h"
+#include "src/pipeline.h"
+#include "src/vulkan/compute_pipeline.h"
+
+namespace amber {
+namespace vulkan {
+
+using VulkanPipelineTest = testing::Test;
+
+TEST_F(VulkanPipelineTest, AddBufferDescriptorAddPushConstant) {
+ amber::Pipeline amber_pipeline(PipelineType::kCompute);
+ std::vector<VkPipelineShaderStageCreateInfo> create_infos;
+ ComputePipeline pipeline(nullptr, 0, create_infos);
+
+ auto cmd = MakeUnique<BufferCommand>(BufferCommand::BufferType::kPushConstant,
+ &amber_pipeline);
+ // Push constant buffers should not be passed to AddBufferDescriptor().
+ Result r = pipeline.AddBufferDescriptor(cmd.get());
+ ASSERT_FALSE(r.IsSuccess());
+}
+
+TEST_F(VulkanPipelineTest, AddBufferDescriptorAddBufferTwice) {
+ amber::Pipeline amber_pipeline(PipelineType::kCompute);
+ std::vector<VkPipelineShaderStageCreateInfo> create_infos;
+ ComputePipeline pipeline(nullptr, 0, create_infos);
+
+ auto cmd = MakeUnique<BufferCommand>(BufferCommand::BufferType::kUniform,
+ &amber_pipeline);
+ Result r = pipeline.AddBufferDescriptor(cmd.get());
+ ASSERT_TRUE(r.IsSuccess()) << r.Error();
+ // Adding same buffer again should fail.
+ r = pipeline.AddBufferDescriptor(cmd.get());
+ ASSERT_FALSE(r.IsSuccess());
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/resource.cc b/src/vulkan/resource.cc
index 05768e5..a52df3d 100644
--- a/src/vulkan/resource.cc
+++ b/src/vulkan/resource.cc
@@ -14,9 +14,9 @@
#include "src/vulkan/resource.h"
+#include <cstring>
#include <limits>
-#include "src/make_unique.h"
#include "src/vulkan/command_buffer.h"
#include "src/vulkan/device.h"
@@ -98,7 +98,6 @@ uint32_t Resource::ChooseMemory(uint32_t memory_type_bits,
return first_non_zero;
}
-
Result Resource::AllocateAndBindMemoryToVkBuffer(VkBuffer buffer,
VkDeviceMemory* memory,
VkMemoryPropertyFlags flags,
@@ -167,8 +166,14 @@ void Resource::UnMapMemory(VkDeviceMemory memory) {
device_->GetPtrs()->vkUnmapMemory(device_->GetVkDevice(), memory);
}
+void Resource::UpdateMemoryWithRawData(const std::vector<uint8_t>& raw_data) {
+ size_t effective_size =
+ raw_data.size() > GetSizeInBytes() ? GetSizeInBytes() : raw_data.size();
+ std::memcpy(HostAccessibleMemoryPtr(), raw_data.data(), effective_size);
+}
+
void Resource::MemoryBarrier(CommandBuffer* command_buffer) {
- // TODO(jaebaek): Current memory barrier is natively implemented.
+ // TODO(jaebaek): Current memory barrier is naively implemented.
// Update it with the following access flags:
// (r = read, w = write)
//
diff --git a/src/vulkan/resource.h b/src/vulkan/resource.h
index cb94ecd..1ed447a 100644
--- a/src/vulkan/resource.h
+++ b/src/vulkan/resource.h
@@ -44,6 +44,7 @@ class Resource {
void* HostAccessibleMemoryPtr() const { return memory_ptr_; }
uint32_t GetSizeInBytes() const { return size_in_bytes_; }
+ void UpdateMemoryWithRawData(const std::vector<uint8_t>& raw_data);
protected:
Resource(Device* device, uint32_t size);
diff --git a/src/vulkan/sampler.cc b/src/vulkan/sampler.cc
new file mode 100644
index 0000000..979c098
--- /dev/null
+++ b/src/vulkan/sampler.cc
@@ -0,0 +1,96 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/vulkan/sampler.h"
+
+namespace amber {
+namespace vulkan {
+namespace {
+
+VkSamplerAddressMode GetVkAddressMode(AddressMode mode) {
+ switch (mode) {
+ case AddressMode::kRepeat:
+ return VK_SAMPLER_ADDRESS_MODE_REPEAT;
+ case AddressMode::kMirroredRepeat:
+ return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
+ case AddressMode::kClampToEdge:
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ case AddressMode::kClampToBorder:
+ return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
+ default:
+ assert(mode == AddressMode::kMirrorClampToEdge);
+ return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
+ }
+}
+
+VkBorderColor GetVkBorderColor(BorderColor color) {
+ switch (color) {
+ case BorderColor::kFloatTransparentBlack:
+ return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
+ case BorderColor::kIntTransparentBlack:
+ return VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
+ case BorderColor::kFloatOpaqueBlack:
+ return VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK;
+ case BorderColor::kIntOpaqueBlack:
+ return VK_BORDER_COLOR_INT_OPAQUE_BLACK;
+ case BorderColor::kFloatOpaqueWhite:
+ return VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
+ default:
+ assert(color == BorderColor::kIntOpaqueWhite);
+ return VK_BORDER_COLOR_INT_OPAQUE_WHITE;
+ }
+}
+
+} // namespace
+
+Sampler::Sampler(Device* device) : device_(device) {}
+
+Result Sampler::CreateSampler(amber::Sampler* sampler) {
+ VkSamplerCreateInfo sampler_info = VkSamplerCreateInfo();
+ sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ sampler_info.magFilter = sampler->GetMagFilter() == FilterType::kLinear
+ ? VK_FILTER_LINEAR
+ : VK_FILTER_NEAREST;
+ sampler_info.minFilter = sampler->GetMinFilter() == FilterType::kLinear
+ ? VK_FILTER_LINEAR
+ : VK_FILTER_NEAREST;
+ sampler_info.mipmapMode = sampler->GetMipmapMode() == FilterType::kLinear
+ ? VK_SAMPLER_MIPMAP_MODE_LINEAR
+ : VK_SAMPLER_MIPMAP_MODE_NEAREST;
+ sampler_info.addressModeU = GetVkAddressMode(sampler->GetAddressModeU());
+ sampler_info.addressModeV = GetVkAddressMode(sampler->GetAddressModeV());
+ sampler_info.addressModeW = GetVkAddressMode(sampler->GetAddressModeW());
+ sampler_info.borderColor = GetVkBorderColor(sampler->GetBorderColor());
+ sampler_info.minLod = sampler->GetMinLOD();
+ sampler_info.maxLod = sampler->GetMaxLOD();
+ sampler_info.unnormalizedCoordinates =
+ (sampler->GetNormalizedCoords() ? VK_FALSE : VK_TRUE);
+
+ if (device_->GetPtrs()->vkCreateSampler(device_->GetVkDevice(), &sampler_info,
+ nullptr, &sampler_) != VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateSampler Fail");
+ }
+
+ return {};
+}
+
+Sampler::~Sampler() {
+ if (sampler_ != VK_NULL_HANDLE) {
+ device_->GetPtrs()->vkDestroySampler(device_->GetVkDevice(), sampler_,
+ nullptr);
+ }
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/sampler.h b/src/vulkan/sampler.h
new file mode 100644
index 0000000..82e4fd2
--- /dev/null
+++ b/src/vulkan/sampler.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_VULKAN_SAMPLER_H_
+#define SRC_VULKAN_SAMPLER_H_
+
+#include "src/sampler.h"
+#include "src/vulkan/device.h"
+
+namespace amber {
+namespace vulkan {
+
+class Sampler {
+ public:
+ explicit Sampler(Device* device);
+ ~Sampler();
+
+ Result CreateSampler(amber::Sampler* sampler);
+ VkSampler GetVkSampler() { return sampler_; }
+
+ private:
+ VkSampler sampler_ = VK_NULL_HANDLE;
+ Device* device_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_SAMPLER_H_
diff --git a/src/vulkan/sampler_descriptor.cc b/src/vulkan/sampler_descriptor.cc
new file mode 100644
index 0000000..59f6c77
--- /dev/null
+++ b/src/vulkan/sampler_descriptor.cc
@@ -0,0 +1,70 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 "src/vulkan/sampler_descriptor.h"
+
+#include "src/vulkan/device.h"
+#include "src/vulkan/resource.h"
+
+namespace amber {
+namespace vulkan {
+
+SamplerDescriptor::SamplerDescriptor(amber::Sampler* sampler,
+ DescriptorType type,
+ Device* device,
+ uint32_t desc_set,
+ uint32_t binding)
+ : Descriptor(type, device, desc_set, binding) {
+ AddAmberSampler(sampler);
+}
+
+SamplerDescriptor::~SamplerDescriptor() = default;
+
+Result SamplerDescriptor::CreateResourceIfNeeded() {
+ vulkan_samplers_.reserve(amber_samplers_.size());
+ for (const auto& sampler : amber_samplers_) {
+ vulkan_samplers_.emplace_back(MakeUnique<Sampler>(device_));
+ Result r = vulkan_samplers_.back()->CreateSampler(sampler);
+ if (!r.IsSuccess())
+ return r;
+ }
+
+ return {};
+}
+
+void SamplerDescriptor::UpdateDescriptorSetIfNeeded(
+ VkDescriptorSet descriptor_set) {
+ std::vector<VkDescriptorImageInfo> image_infos;
+
+ for (auto& sampler : vulkan_samplers_) {
+ VkDescriptorImageInfo image_info = {sampler->GetVkSampler(), VK_NULL_HANDLE,
+ VK_IMAGE_LAYOUT_GENERAL};
+ image_infos.push_back(image_info);
+ }
+
+ VkWriteDescriptorSet write = VkWriteDescriptorSet();
+ write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ write.dstSet = descriptor_set;
+ write.dstBinding = binding_;
+ write.dstArrayElement = 0;
+ write.descriptorCount = static_cast<uint32_t>(image_infos.size());
+ write.descriptorType = GetVkDescriptorType();
+ write.pImageInfo = image_infos.data();
+
+ device_->GetPtrs()->vkUpdateDescriptorSets(device_->GetVkDevice(), 1, &write,
+ 0, nullptr);
+}
+
+} // namespace vulkan
+} // namespace amber
diff --git a/src/vulkan/sampler_descriptor.h b/src/vulkan/sampler_descriptor.h
new file mode 100644
index 0000000..64d08fa
--- /dev/null
+++ b/src/vulkan/sampler_descriptor.h
@@ -0,0 +1,55 @@
+// Copyright 2019 The Amber Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT 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 SRC_VULKAN_SAMPLER_DESCRIPTOR_H_
+#define SRC_VULKAN_SAMPLER_DESCRIPTOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "src/vulkan/descriptor.h"
+#include "src/vulkan/sampler.h"
+#include "src/vulkan/transfer_image.h"
+
+namespace amber {
+namespace vulkan {
+
+class SamplerDescriptor : public Descriptor {
+ public:
+ SamplerDescriptor(amber::Sampler* sampler,
+ DescriptorType type,
+ Device* device,
+ uint32_t desc_set,
+ uint32_t binding);
+ ~SamplerDescriptor() override;
+
+ void UpdateDescriptorSetIfNeeded(VkDescriptorSet descriptor_set) override;
+ Result CreateResourceIfNeeded() override;
+ void AddAmberSampler(amber::Sampler* sampler) {
+ amber_samplers_.push_back(sampler);
+ }
+ uint32_t GetDescriptorCount() override {
+ return static_cast<uint32_t>(amber_samplers_.size());
+ }
+ SamplerDescriptor* AsSamplerDescriptor() override { return this; }
+
+ private:
+ std::vector<amber::Sampler*> amber_samplers_;
+ std::vector<std::unique_ptr<amber::vulkan::Sampler>> vulkan_samplers_;
+};
+
+} // namespace vulkan
+} // namespace amber
+
+#endif // SRC_VULKAN_SAMPLER_DESCRIPTOR_H_
diff --git a/src/vulkan/transfer_buffer.cc b/src/vulkan/transfer_buffer.cc
index 92f388f..a13b22e 100644
--- a/src/vulkan/transfer_buffer.cc
+++ b/src/vulkan/transfer_buffer.cc
@@ -14,26 +14,34 @@
#include "src/vulkan/transfer_buffer.h"
-#include <cstring>
-
#include "src/vulkan/command_buffer.h"
#include "src/vulkan/device.h"
namespace amber {
namespace vulkan {
-TransferBuffer::TransferBuffer(Device* device, uint32_t size_in_bytes)
- : Resource(device, size_in_bytes) {}
+TransferBuffer::TransferBuffer(Device* device,
+ uint32_t size_in_bytes,
+ Format* format)
+ : Resource(device, size_in_bytes) {
+ if (format)
+ format_ = device->GetVkFormat(*format);
+}
TransferBuffer::~TransferBuffer() {
- if (memory_ != VK_NULL_HANDLE) {
- UnMapMemory(memory_);
- device_->GetPtrs()->vkFreeMemory(device_->GetVkDevice(), memory_, nullptr);
- }
+ if (device_) {
+ device_->GetPtrs()->vkDestroyBufferView(device_->GetVkDevice(), view_,
+ nullptr);
+
+ if (memory_ != VK_NULL_HANDLE) {
+ UnMapMemory(memory_);
+ device_->GetPtrs()->vkFreeMemory(device_->GetVkDevice(), memory_,
+ nullptr);
+ }
- if (buffer_ != VK_NULL_HANDLE)
device_->GetPtrs()->vkDestroyBuffer(device_->GetVkDevice(), buffer_,
nullptr);
+ }
}
Result TransferBuffer::Initialize(const VkBufferUsageFlags usage) {
@@ -49,6 +57,23 @@ Result TransferBuffer::Initialize(const VkBufferUsageFlags usage) {
if (!r.IsSuccess())
return r;
+ // Create buffer view
+ if (usage & (VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT |
+ VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) {
+ VkBufferViewCreateInfo buffer_view_info = VkBufferViewCreateInfo();
+ buffer_view_info.sType = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
+ buffer_view_info.buffer = buffer_;
+ buffer_view_info.format = format_;
+ buffer_view_info.offset = 0;
+ buffer_view_info.range = VK_WHOLE_SIZE;
+
+ if (device_->GetPtrs()->vkCreateBufferView(device_->GetVkDevice(),
+ &buffer_view_info, nullptr,
+ &view_) != VK_SUCCESS) {
+ return Result("Vulkan::Calling vkCreateBufferView Fail");
+ }
+ }
+
if (!device_->IsMemoryHostAccessible(memory_type_index) ||
!device_->IsMemoryHostCoherent(memory_type_index)) {
return Result(
@@ -71,12 +96,5 @@ void TransferBuffer::CopyToHost(CommandBuffer* command_buffer) {
MemoryBarrier(command_buffer);
}
-void TransferBuffer::UpdateMemoryWithRawData(
- const std::vector<uint8_t>& raw_data) {
- size_t effective_size =
- raw_data.size() > GetSizeInBytes() ? GetSizeInBytes() : raw_data.size();
- std::memcpy(HostAccessibleMemoryPtr(), raw_data.data(), effective_size);
-}
-
} // namespace vulkan
} // namespace amber
diff --git a/src/vulkan/transfer_buffer.h b/src/vulkan/transfer_buffer.h
index 764c364..2c0a51d 100644
--- a/src/vulkan/transfer_buffer.h
+++ b/src/vulkan/transfer_buffer.h
@@ -19,6 +19,7 @@
#include "amber/result.h"
#include "amber/vulkan_header.h"
+#include "src/format.h"
#include "src/vulkan/resource.h"
namespace amber {
@@ -30,10 +31,11 @@ class Device;
/// Wrapper around a Vulkan VkBuffer object.
class TransferBuffer : public Resource {
public:
- TransferBuffer(Device* device, uint32_t size_in_bytes);
+ TransferBuffer(Device* device, uint32_t size_in_bytes, Format* format);
~TransferBuffer() override;
Result Initialize(const VkBufferUsageFlags usage);
+ const VkBufferView* GetVkBufferView() const { return &view_; }
VkBuffer GetVkBuffer() const { return buffer_; }
@@ -44,11 +46,11 @@ class TransferBuffer : public Resource {
/// device to the host.
void CopyToHost(CommandBuffer* command_buffer) override;
- void UpdateMemoryWithRawData(const std::vector<uint8_t>& raw_data);
-
private:
VkBuffer buffer_ = VK_NULL_HANDLE;
VkDeviceMemory memory_ = VK_NULL_HANDLE;
+ VkBufferView view_ = VK_NULL_HANDLE;
+ VkFormat format_ = VK_FORMAT_UNDEFINED;
};
} // namespace vulkan
diff --git a/src/vulkan/transfer_image.cc b/src/vulkan/transfer_image.cc
index dbbb21b..545549d 100644
--- a/src/vulkan/transfer_image.cc
+++ b/src/vulkan/transfer_image.cc
@@ -14,7 +14,9 @@
#include "src/vulkan/transfer_image.h"
+#include <cstring>
#include <limits>
+#include <vector>
#include "src/vulkan/command_buffer.h"
#include "src/vulkan/device.h"
@@ -41,19 +43,59 @@ const VkImageCreateInfo kDefaultImageInfo = {
VK_IMAGE_LAYOUT_UNDEFINED, /* initialLayout */
};
+VkSampleCountFlagBits GetVkSampleCount(uint32_t samples) {
+ switch (samples) {
+ case 1u:
+ return VK_SAMPLE_COUNT_1_BIT;
+ case 2u:
+ return VK_SAMPLE_COUNT_2_BIT;
+ case 4u:
+ return VK_SAMPLE_COUNT_4_BIT;
+ case 8u:
+ return VK_SAMPLE_COUNT_8_BIT;
+ case 16u:
+ return VK_SAMPLE_COUNT_16_BIT;
+ case 32u:
+ return VK_SAMPLE_COUNT_32_BIT;
+ case 64u:
+ return VK_SAMPLE_COUNT_64_BIT;
+ }
+
+ return VK_SAMPLE_COUNT_FLAG_BITS_MAX_ENUM;
+}
+
} // namespace
TransferImage::TransferImage(Device* device,
const Format& format,
VkImageAspectFlags aspect,
+ VkImageType image_type,
uint32_t x,
uint32_t y,
- uint32_t z)
- : Resource(device, x * y * z * format.SizeInBytes()),
+ uint32_t z,
+ uint32_t mip_levels,
+ uint32_t base_mip_level,
+ uint32_t used_mip_levels,
+ uint32_t samples)
+ : Resource(
+ device,
+ x * y * z *
+ (format.SizeInBytes() +
+ // D24_UNORM_S8_UINT requires 32bit component for depth when
+ // performing buffer copies. Reserve extra room to handle that.
+ (format.GetFormatType() == FormatType::kD24_UNORM_S8_UINT ? 1
+ : 0))),
image_info_(kDefaultImageInfo),
- aspect_(aspect) {
+ aspect_(aspect),
+ mip_levels_(mip_levels),
+ base_mip_level_(base_mip_level),
+ used_mip_levels_(used_mip_levels),
+ samples_(samples) {
image_info_.format = device_->GetVkFormat(format);
+ image_info_.imageType = image_type;
image_info_.extent = {x, y, z};
+ image_info_.mipLevels = mip_levels;
+ image_info_.samples = GetVkSampleCount(samples);
}
TransferImage::~TransferImage() {
@@ -82,7 +124,7 @@ TransferImage::~TransferImage() {
Result TransferImage::Initialize(VkImageUsageFlags usage) {
if (image_ != VK_NULL_HANDLE)
- return Result("Vulkan::TransferImage was already initalized");
+ return Result("Vulkan::TransferImage was already initialized");
image_info_.usage = usage;
@@ -98,7 +140,15 @@ Result TransferImage::Initialize(VkImageUsageFlags usage) {
if (!r.IsSuccess())
return r;
- r = CreateVkImageView();
+ if (aspect_ & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT) &&
+ !(usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) {
+ // Combined depth/stencil image used as a descriptor. Only one aspect can be
+ // used for the image view.
+ r = CreateVkImageView(VK_IMAGE_ASPECT_DEPTH_BIT);
+ } else {
+ r = CreateVkImageView(aspect_);
+ }
+
if (!r.IsSuccess())
return r;
@@ -124,12 +174,29 @@ Result TransferImage::Initialize(VkImageUsageFlags usage) {
return MapMemory(host_accessible_memory_);
}
-Result TransferImage::CreateVkImageView() {
+VkImageViewType TransferImage::GetImageViewType() const {
+ // TODO(alan-baker): handle other view types.
+ // 1D-array, 2D-array, Cube, Cube-array.
+ switch (image_info_.imageType) {
+ case VK_IMAGE_TYPE_1D:
+ return VK_IMAGE_VIEW_TYPE_1D;
+ case VK_IMAGE_TYPE_2D:
+ return VK_IMAGE_VIEW_TYPE_2D;
+ case VK_IMAGE_TYPE_3D:
+ return VK_IMAGE_VIEW_TYPE_3D;
+ default:
+ break;
+ }
+
+ // Default to 2D image view.
+ return VK_IMAGE_VIEW_TYPE_2D;
+}
+
+Result TransferImage::CreateVkImageView(VkImageAspectFlags aspect) {
VkImageViewCreateInfo image_view_info = VkImageViewCreateInfo();
image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
image_view_info.image = image_;
- // TODO(jaebaek): Set .viewType correctly
- image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ image_view_info.viewType = GetImageViewType();
image_view_info.format = image_info_.format;
image_view_info.components = {
VK_COMPONENT_SWIZZLE_R,
@@ -138,11 +205,11 @@ Result TransferImage::CreateVkImageView() {
VK_COMPONENT_SWIZZLE_A,
};
image_view_info.subresourceRange = {
- aspect_, /* aspectMask */
- 0, /* baseMipLevel */
- 1, /* levelCount */
- 0, /* baseArrayLayer */
- 1, /* layerCount */
+ aspect, /* aspectMask */
+ base_mip_level_, /* baseMipLevel */
+ used_mip_levels_, /* levelCount */
+ 0, /* baseArrayLayer */
+ 1, /* layerCount */
};
if (device_->GetPtrs()->vkCreateImageView(device_->GetVkDevice(),
@@ -154,42 +221,86 @@ Result TransferImage::CreateVkImageView() {
return {};
}
-VkBufferImageCopy TransferImage::CreateBufferImageCopy() {
+VkBufferImageCopy TransferImage::CreateBufferImageCopy(
+ VkImageAspectFlags aspect,
+ uint32_t mip_level) {
VkBufferImageCopy copy_region = VkBufferImageCopy();
- copy_region.bufferOffset = 0;
+ if (aspect == VK_IMAGE_ASPECT_STENCIL_BIT) {
+ // Store stencil data at the end of the buffer after depth data.
+ copy_region.bufferOffset =
+ GetSizeInBytes() - image_info_.extent.width * image_info_.extent.height;
+ } else {
+ copy_region.bufferOffset = 0;
+ }
// Row length of 0 results in tight packing of rows, so the row stride
// is the number of texels times the texel stride.
copy_region.bufferRowLength = 0;
copy_region.bufferImageHeight = 0;
copy_region.imageSubresource = {
- aspect_, /* aspectMask */
- 0, /* mipLevel */
- 0, /* baseArrayLayer */
- 1, /* layerCount */
+ aspect, /* aspectMask */
+ mip_level, /* mipLevel */
+ 0, /* baseArrayLayer */
+ 1, /* layerCount */
};
copy_region.imageOffset = {0, 0, 0};
- copy_region.imageExtent = {image_info_.extent.width,
- image_info_.extent.height, 1};
+ copy_region.imageExtent = {image_info_.extent.width >> mip_level,
+ image_info_.extent.height >> mip_level,
+ image_info_.extent.depth};
return copy_region;
}
void TransferImage::CopyToHost(CommandBuffer* command_buffer) {
- auto copy_region = CreateBufferImageCopy();
+ const VkImageAspectFlagBits aspects[] = {VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_ASPECT_DEPTH_BIT,
+ VK_IMAGE_ASPECT_STENCIL_BIT};
+ // Copy operations don't support multisample images.
+ if (samples_ > 1)
+ return;
+
+ std::vector<VkBufferImageCopy> copy_regions;
+ uint32_t last_mip_level = used_mip_levels_ == VK_REMAINING_MIP_LEVELS
+ ? mip_levels_
+ : base_mip_level_ + used_mip_levels_;
+ for (uint32_t i = base_mip_level_; i < last_mip_level; i++) {
+ for (auto aspect : aspects) {
+ if (aspect_ & aspect) {
+ copy_regions.push_back(CreateBufferImageCopy(aspect, i));
+ }
+ }
+ }
device_->GetPtrs()->vkCmdCopyImageToBuffer(
command_buffer->GetVkCommandBuffer(), image_,
- VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, host_accessible_buffer_, 1,
- &copy_region);
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, host_accessible_buffer_,
+ static_cast<uint32_t>(copy_regions.size()), copy_regions.data());
MemoryBarrier(command_buffer);
}
void TransferImage::CopyToDevice(CommandBuffer* command_buffer) {
- auto copy_region = CreateBufferImageCopy();
+ // Copy operations don't support multisample images.
+ if (samples_ > 1)
+ return;
+
+ const VkImageAspectFlagBits aspects[] = {VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_ASPECT_DEPTH_BIT,
+ VK_IMAGE_ASPECT_STENCIL_BIT};
+ std::vector<VkBufferImageCopy> copy_regions;
+ uint32_t last_mip_level = used_mip_levels_ == VK_REMAINING_MIP_LEVELS
+ ? mip_levels_
+ : base_mip_level_ + used_mip_levels_;
+ for (uint32_t i = base_mip_level_; i < last_mip_level; i++) {
+ for (auto aspect : aspects) {
+ if (aspect_ & aspect) {
+ copy_regions.push_back(CreateBufferImageCopy(aspect, i));
+ }
+ }
+ }
device_->GetPtrs()->vkCmdCopyBufferToImage(
command_buffer->GetVkCommandBuffer(), host_accessible_buffer_, image_,
- VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &copy_region);
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ static_cast<uint32_t>(copy_regions.size()), copy_regions.data());
MemoryBarrier(command_buffer);
}
@@ -208,11 +319,11 @@ void TransferImage::ImageBarrier(CommandBuffer* command_buffer,
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image_;
barrier.subresourceRange = {
- aspect_, /* aspectMask */
- 0, /* baseMipLevel */
- 1, /* levelCount */
- 0, /* baseArrayLayer */
- 1, /* layerCount */
+ aspect_, /* aspectMask */
+ 0, /* baseMipLevel */
+ VK_REMAINING_MIP_LEVELS, /* levelCount */
+ 0, /* baseArrayLayer */
+ 1, /* layerCount */
};
switch (layout_) {
@@ -238,10 +349,12 @@ void TransferImage::ImageBarrier(CommandBuffer* command_buffer,
switch (to_layout) {
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
- barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
+ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
- barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
+ barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
+ VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
barrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;
diff --git a/src/vulkan/transfer_image.h b/src/vulkan/transfer_image.h
index bac7610..741ee3c 100644
--- a/src/vulkan/transfer_image.h
+++ b/src/vulkan/transfer_image.h
@@ -32,9 +32,14 @@ class TransferImage : public Resource {
TransferImage(Device* device,
const Format& format,
VkImageAspectFlags aspect,
+ VkImageType image_type,
uint32_t x,
uint32_t y,
- uint32_t z);
+ uint32_t z,
+ uint32_t mip_levels,
+ uint32_t base_mip_level,
+ uint32_t used_mip_levels,
+ uint32_t samples);
~TransferImage() override;
Result Initialize(VkImageUsageFlags usage);
@@ -52,13 +57,16 @@ class TransferImage : public Resource {
void CopyToHost(CommandBuffer* command_buffer) override;
private:
- Result CreateVkImageView();
+ Result CreateVkImageView(VkImageAspectFlags aspect);
Result AllocateAndBindMemoryToVkImage(VkImage image,
VkDeviceMemory* memory,
VkMemoryPropertyFlags flags,
bool force_flags,
uint32_t* memory_type_index);
- VkBufferImageCopy CreateBufferImageCopy();
+ VkBufferImageCopy CreateBufferImageCopy(VkImageAspectFlags aspect,
+ uint32_t mip_level);
+
+ VkImageViewType GetImageViewType() const;
/// An extra `VkBuffer` is used to facilitate the transfer of data from the
/// host into the `VkImage` on the device.
@@ -74,6 +82,11 @@ class TransferImage : public Resource {
VkImageLayout layout_ = VK_IMAGE_LAYOUT_UNDEFINED;
VkPipelineStageFlags stage_ = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
+
+ uint32_t mip_levels_;
+ uint32_t base_mip_level_;
+ uint32_t used_mip_levels_;
+ uint32_t samples_;
};
} // namespace vulkan
diff --git a/src/vulkan/vertex_buffer.cc b/src/vulkan/vertex_buffer.cc
index fe58d66..7503681 100644
--- a/src/vulkan/vertex_buffer.cc
+++ b/src/vulkan/vertex_buffer.cc
@@ -23,70 +23,80 @@
namespace amber {
namespace vulkan {
+namespace {
+
+VkVertexInputRate GetVkInputRate(InputRate rate) {
+ return rate == InputRate::kVertex ? VK_VERTEX_INPUT_RATE_VERTEX
+ : VK_VERTEX_INPUT_RATE_INSTANCE;
+}
+
+} // namespace
VertexBuffer::VertexBuffer(Device* device) : device_(device) {}
VertexBuffer::~VertexBuffer() = default;
-void VertexBuffer::SetData(uint8_t location, Buffer* buffer) {
- auto format = buffer->GetFormat();
-
+void VertexBuffer::SetData(uint8_t location,
+ Buffer* buffer,
+ InputRate rate,
+ Format* format,
+ uint32_t offset,
+ uint32_t stride) {
+ const uint32_t binding = static_cast<uint32_t>(vertex_attr_desc_.size());
vertex_attr_desc_.emplace_back();
- // TODO(jaebaek): Support multiple binding
- vertex_attr_desc_.back().binding = 0;
+ vertex_attr_desc_.back().binding = binding;
vertex_attr_desc_.back().location = location;
- vertex_attr_desc_.back().offset = stride_in_bytes_;
+ vertex_attr_desc_.back().offset = offset;
vertex_attr_desc_.back().format = device_->GetVkFormat(*format);
- stride_in_bytes_ += format->SizeInBytes();
- data_.push_back(buffer);
-}
-
-Result VertexBuffer::FillVertexBufferWithData(CommandBuffer* command) {
- // Send vertex data from host to device.
- uint8_t* ptr_in_stride_begin =
- static_cast<uint8_t*>(transfer_buffer_->HostAccessibleMemoryPtr());
- for (uint32_t i = 0; i < GetVertexCount(); ++i) {
- uint8_t* ptr = ptr_in_stride_begin;
- for (uint32_t j = 0; j < data_.size(); ++j) {
- size_t bytes = data_[j]->GetFormat()->SizeInBytes();
- std::memcpy(ptr, data_[j]->GetValues<uint8_t>() + (i * bytes), bytes);
- ptr += bytes;
- }
- ptr_in_stride_begin += Get4BytesAlignedStride();
- }
+ vertex_binding_desc_.emplace_back();
+ vertex_binding_desc_.back().binding = binding;
+ vertex_binding_desc_.back().stride = stride;
+ vertex_binding_desc_.back().inputRate = GetVkInputRate(rate);
- transfer_buffer_->CopyToDevice(command);
- return {};
+ data_.push_back(buffer);
}
void VertexBuffer::BindToCommandBuffer(CommandBuffer* command) {
- const VkDeviceSize offset = 0;
- const VkBuffer buffer = transfer_buffer_->GetVkBuffer();
- // TODO(jaebaek): Support multiple binding
- device_->GetPtrs()->vkCmdBindVertexBuffers(command->GetVkCommandBuffer(), 0,
- 1, &buffer, &offset);
+ std::vector<VkBuffer> buffers;
+ std::vector<VkDeviceSize> offsets;
+
+ for (const auto& buf : data_) {
+ buffers.push_back(buffer_to_vk_buffer_[buf]);
+ offsets.push_back(0);
+ }
+ device_->GetPtrs()->vkCmdBindVertexBuffers(
+ command->GetVkCommandBuffer(), 0, static_cast<uint32_t>(buffers.size()),
+ buffers.data(), offsets.data());
}
Result VertexBuffer::SendVertexData(CommandBuffer* command) {
if (!is_vertex_data_pending_)
return Result("Vulkan::Vertices data was already sent");
- const uint32_t n_vertices = GetVertexCount();
- if (n_vertices == 0)
- return Result("Vulkan::Data for VertexBuffer is empty");
+ buffer_to_vk_buffer_.clear();
- uint32_t bytes = Get4BytesAlignedStride() * n_vertices;
+ for (const auto& buf : data_) {
+ if (buffer_to_vk_buffer_.count(buf) != 0) {
+ continue;
+ }
+
+ // Create a new transfer buffer to hold vertex data.
+ uint32_t bytes = buf->GetSizeInBytes();
+ transfer_buffers_.push_back(
+ MakeUnique<TransferBuffer>(device_, bytes, nullptr));
+ Result r = transfer_buffers_.back()->Initialize(
+ VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
+
+ std::memcpy(transfer_buffers_.back()->HostAccessibleMemoryPtr(),
+ buf->GetValues<void>(), bytes);
+ transfer_buffers_.back()->CopyToDevice(command);
- if (!transfer_buffer_) {
- transfer_buffer_ = MakeUnique<TransferBuffer>(device_, bytes);
- Result r = transfer_buffer_->Initialize(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT |
- VK_BUFFER_USAGE_TRANSFER_DST_BIT);
if (!r.IsSuccess())
return r;
- }
- FillVertexBufferWithData(command);
+ buffer_to_vk_buffer_[buf] = transfer_buffers_.back()->GetVkBuffer();
+ }
is_vertex_data_pending_ = false;
return {};
diff --git a/src/vulkan/vertex_buffer.h b/src/vulkan/vertex_buffer.h
index e7b216e..871a9cc 100644
--- a/src/vulkan/vertex_buffer.h
+++ b/src/vulkan/vertex_buffer.h
@@ -15,6 +15,7 @@
#ifndef SRC_VULKAN_VERTEX_BUFFER_H_
#define SRC_VULKAN_VERTEX_BUFFER_H_
+#include <map>
#include <memory>
#include <vector>
@@ -40,48 +41,35 @@ class VertexBuffer {
Result SendVertexData(CommandBuffer* command);
bool VertexDataSent() const { return !is_vertex_data_pending_; }
- void SetData(uint8_t location, Buffer* buffer);
+ void SetData(uint8_t location,
+ Buffer* buffer,
+ InputRate rate,
+ Format* format,
+ uint32_t offset,
+ uint32_t stride);
const std::vector<VkVertexInputAttributeDescription>& GetVkVertexInputAttr()
const {
return vertex_attr_desc_;
}
-
- VkVertexInputBindingDescription GetVkVertexInputBinding() const {
- VkVertexInputBindingDescription vertex_binding_desc =
- VkVertexInputBindingDescription();
- vertex_binding_desc.binding = 0;
- vertex_binding_desc.stride = Get4BytesAlignedStride();
- vertex_binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
- return vertex_binding_desc;
- }
-
- uint32_t GetVertexCount() const {
- if (data_.empty())
- return 0;
- return data_[0]->ElementCount();
+ const std::vector<VkVertexInputBindingDescription>& GetVkVertexInputBinding()
+ const {
+ return vertex_binding_desc_;
}
void BindToCommandBuffer(CommandBuffer* command);
- void SetBufferForTest(std::unique_ptr<TransferBuffer> buffer);
-
private:
- Result FillVertexBufferWithData(CommandBuffer* command);
-
- uint32_t Get4BytesAlignedStride() const {
- return ((stride_in_bytes_ + 3) / 4) * 4;
- }
-
Device* device_ = nullptr;
bool is_vertex_data_pending_ = true;
- std::unique_ptr<TransferBuffer> transfer_buffer_;
- uint32_t stride_in_bytes_ = 0;
+ std::vector<std::unique_ptr<TransferBuffer>> transfer_buffers_;
std::vector<Buffer*> data_;
+ std::vector<VkVertexInputBindingDescription> vertex_binding_desc_;
std::vector<VkVertexInputAttributeDescription> vertex_attr_desc_;
+ std::map<Buffer*, VkBuffer> buffer_to_vk_buffer_;
};
} // namespace vulkan
diff --git a/src/vulkan/vertex_buffer_test.cc b/src/vulkan/vertex_buffer_test.cc
index c023d5a..eb8a7bd 100644
--- a/src/vulkan/vertex_buffer_test.cc
+++ b/src/vulkan/vertex_buffer_test.cc
@@ -21,75 +21,152 @@
#include "src/format.h"
#include "src/make_unique.h"
#include "src/type_parser.h"
-#include "src/vulkan/transfer_buffer.h"
+#include "src/vulkan/command_buffer.h"
+#include "src/vulkan/command_pool.h"
+#include "src/vulkan/device.h"
namespace amber {
namespace vulkan {
namespace {
-class BufferForTest : public TransferBuffer {
+class DummyDevice : public Device {
public:
- BufferForTest(Device* device, uint32_t size_in_bytes)
- : TransferBuffer(device, size_in_bytes) {
- memory_.resize(4096);
- SetMemoryPtr(memory_.data());
+ DummyDevice()
+ : Device(VkInstance(),
+ VkPhysicalDevice(),
+ 0u,
+ VkDevice(this),
+ VkQueue()) {
+ memory_.resize(64);
+ dummyPtrs_.vkCreateBuffer = vkCreateBuffer;
+ dummyPtrs_.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements;
+ dummyPtrs_.vkAllocateMemory = vkAllocateMemory;
+ dummyPtrs_.vkBindBufferMemory = vkBindBufferMemory;
+ dummyPtrs_.vkMapMemory = vkMapMemory;
+ dummyPtrs_.vkCmdPipelineBarrier = vkCmdPipelineBarrier;
+ dummyPtrs_.vkAllocateCommandBuffers = vkAllocateCommandBuffers;
+ dummyPtrs_.vkCreateFence = vkCreateFence;
+ dummyPtrs_.vkDestroyBufferView = vkDestroyBufferView;
+ dummyPtrs_.vkFreeMemory = vkFreeMemory;
+ dummyPtrs_.vkDestroyBuffer = vkDestroyBuffer;
}
- ~BufferForTest() override = default;
+ ~DummyDevice() override {}
- void CopyToDevice(CommandBuffer*) override {}
+ const VulkanPtrs* GetPtrs() const override { return &dummyPtrs_; }
+
+ bool HasMemoryFlags(uint32_t, const VkMemoryPropertyFlags) const override {
+ return true;
+ }
+
+ void* GetMemoryPtr() { return memory_.data(); }
private:
+ VulkanPtrs dummyPtrs_;
std::vector<uint8_t> memory_;
+
+ static VkResult vkCreateBuffer(VkDevice,
+ const VkBufferCreateInfo*,
+ const VkAllocationCallbacks*,
+ VkBuffer* pBuffer) {
+ *pBuffer = VkBuffer(1);
+ return VK_SUCCESS;
+ }
+ static void vkGetBufferMemoryRequirements(
+ VkDevice,
+ VkBuffer,
+ VkMemoryRequirements* pMemoryRequirements) {
+ pMemoryRequirements->alignment = 0;
+ pMemoryRequirements->size = 0;
+ pMemoryRequirements->memoryTypeBits = 0xffffffff;
+ }
+ static VkResult vkAllocateMemory(VkDevice,
+ const VkMemoryAllocateInfo*,
+ const VkAllocationCallbacks*,
+ VkDeviceMemory*) {
+ return VK_SUCCESS;
+ }
+ static VkResult vkBindBufferMemory(VkDevice,
+ VkBuffer,
+ VkDeviceMemory,
+ VkDeviceSize) {
+ return VK_SUCCESS;
+ }
+ static VkResult vkMapMemory(VkDevice device,
+ VkDeviceMemory,
+ VkDeviceSize,
+ VkDeviceSize,
+ VkMemoryMapFlags,
+ void** ppData) {
+ DummyDevice* devicePtr = reinterpret_cast<DummyDevice*>(device);
+ *ppData = devicePtr->GetMemoryPtr();
+ return VK_SUCCESS;
+ }
+ static void vkCmdPipelineBarrier(VkCommandBuffer,
+ VkPipelineStageFlags,
+ VkPipelineStageFlags,
+ VkDependencyFlags,
+ uint32_t,
+ const VkMemoryBarrier*,
+ uint32_t,
+ const VkBufferMemoryBarrier*,
+ uint32_t,
+ const VkImageMemoryBarrier*) {}
+ static VkResult vkAllocateCommandBuffers(VkDevice,
+ const VkCommandBufferAllocateInfo*,
+ VkCommandBuffer*) {
+ return VK_SUCCESS;
+ }
+ static VkResult vkCreateFence(VkDevice,
+ const VkFenceCreateInfo*,
+ const VkAllocationCallbacks*,
+ VkFence*) {
+ return VK_SUCCESS;
+ }
+ static void vkDestroyBufferView(VkDevice,
+ VkBufferView,
+ const VkAllocationCallbacks*) {}
+ static void vkFreeMemory(VkDevice,
+ VkDeviceMemory,
+ const VkAllocationCallbacks*) {}
+ static void vkDestroyBuffer(VkDevice,
+ VkBuffer,
+ const VkAllocationCallbacks*) {}
};
class VertexBufferTest : public testing::Test {
public:
- VertexBufferTest() {
- vertex_buffer_ = MakeUnique<VertexBuffer>(nullptr);
-
- std::unique_ptr<TransferBuffer> buffer =
- MakeUnique<BufferForTest>(nullptr, 0U);
- buffer_memory_ = buffer->HostAccessibleMemoryPtr();
- vertex_buffer_->SetBufferForTest(std::move(buffer));
+ VertexBufferTest()
+ : device_(MakeUnique<DummyDevice>()),
+ commandPool_(MakeUnique<CommandPool>(device_.get())),
+ commandBuffer_(
+ MakeUnique<CommandBuffer>(device_.get(), commandPool_.get())),
+ vertex_buffer_(MakeUnique<VertexBuffer>(device_.get())) {
+ commandBuffer_->Initialize();
}
- ~VertexBufferTest() = default;
-
- Result SetIntData(uint8_t location,
- Format* format,
- std::vector<Value> values) {
- auto buffer = MakeUnique<Buffer>();
- buffer->SetFormat(format);
- buffer->SetData(std::move(values));
-
- vertex_buffer_->SetData(location, buffer.get());
- return vertex_buffer_->SendVertexData(nullptr);
- }
+ ~VertexBufferTest() override { vertex_buffer_.reset(); }
- Result SetDoubleData(uint8_t location,
- Format* format,
- std::vector<Value> values) {
+ Result SetData(uint8_t location, Format* format, std::vector<Value> values) {
auto buffer = MakeUnique<Buffer>();
buffer->SetFormat(format);
buffer->SetData(std::move(values));
- vertex_buffer_->SetData(location, buffer.get());
- return vertex_buffer_->SendVertexData(nullptr);
+ vertex_buffer_->SetData(location, buffer.get(), InputRate::kVertex, format,
+ 0, format->SizeInBytes());
+ return vertex_buffer_->SendVertexData(commandBuffer_.get());
}
- const void* GetVkBufferPtr() const { return buffer_memory_; }
+ const void* GetVkBufferPtr() { return device_->GetMemoryPtr(); }
private:
+ std::unique_ptr<DummyDevice> device_;
+ std::unique_ptr<CommandPool> commandPool_;
+ std::unique_ptr<CommandBuffer> commandBuffer_;
std::unique_ptr<VertexBuffer> vertex_buffer_;
- const void* buffer_memory_ = nullptr;
};
} // namespace
-void VertexBuffer::SetBufferForTest(std::unique_ptr<TransferBuffer> buffer) {
- transfer_buffer_ = std::move(buffer);
-}
-
TEST_F(VertexBufferTest, R8G8B8A8_UINT) {
std::vector<Value> values(4);
values[0].SetIntValue(55);
@@ -100,7 +177,7 @@ TEST_F(VertexBufferTest, R8G8B8A8_UINT) {
TypeParser parser;
auto type = parser.Parse("R8G8B8A8_UINT");
Format fmt(type.get());
- Result r = SetIntData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const uint8_t* ptr = static_cast<const uint8_t*>(GetVkBufferPtr());
EXPECT_EQ(55, ptr[0]);
@@ -119,7 +196,7 @@ TEST_F(VertexBufferTest, R16G16B16A16_UINT) {
TypeParser parser;
auto type = parser.Parse("R16G16B16A16_UINT");
Format fmt(type.get());
- Result r = SetIntData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const uint16_t* ptr = static_cast<const uint16_t*>(GetVkBufferPtr());
EXPECT_EQ(55, ptr[0]);
@@ -138,7 +215,7 @@ TEST_F(VertexBufferTest, R32G32B32A32_UINT) {
TypeParser parser;
auto type = parser.Parse("R32G32B32A32_UINT");
Format fmt(type.get());
- Result r = SetIntData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const uint32_t* ptr = static_cast<const uint32_t*>(GetVkBufferPtr());
EXPECT_EQ(55, ptr[0]);
@@ -157,7 +234,7 @@ TEST_F(VertexBufferTest, R64G64B64A64_UINT) {
TypeParser parser;
auto type = parser.Parse("R64G64B64A64_UINT");
Format fmt(type.get());
- Result r = SetIntData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const uint64_t* ptr = static_cast<const uint64_t*>(GetVkBufferPtr());
EXPECT_EQ(55, ptr[0]);
@@ -176,7 +253,7 @@ TEST_F(VertexBufferTest, R8G8B8A8_SNORM) {
TypeParser parser;
auto type = parser.Parse("R8G8B8A8_SNORM");
Format fmt(type.get());
- Result r = SetIntData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const int8_t* ptr = static_cast<const int8_t*>(GetVkBufferPtr());
EXPECT_EQ(-55, ptr[0]);
@@ -195,7 +272,7 @@ TEST_F(VertexBufferTest, R16G16B16A16_SNORM) {
TypeParser parser;
auto type = parser.Parse("R16G16B16A16_SNORM");
Format fmt(type.get());
- Result r = SetIntData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const int16_t* ptr = static_cast<const int16_t*>(GetVkBufferPtr());
EXPECT_EQ(-55, ptr[0]);
@@ -214,7 +291,7 @@ TEST_F(VertexBufferTest, R32G32B32A32_SINT) {
TypeParser parser;
auto type = parser.Parse("R32G32B32A32_SINT");
Format fmt(type.get());
- Result r = SetIntData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const int32_t* ptr = static_cast<const int32_t*>(GetVkBufferPtr());
EXPECT_EQ(-55, ptr[0]);
@@ -233,7 +310,7 @@ TEST_F(VertexBufferTest, R64G64B64A64_SINT) {
TypeParser parser;
auto type = parser.Parse("R64G64B64A64_SINT");
Format fmt(type.get());
- Result r = SetIntData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const int64_t* ptr = static_cast<const int64_t*>(GetVkBufferPtr());
EXPECT_EQ(-55, ptr[0]);
@@ -251,7 +328,7 @@ TEST_F(VertexBufferTest, R32G32B32_SFLOAT) {
TypeParser parser;
auto type = parser.Parse("R32G32B32_SFLOAT");
Format fmt(type.get());
- Result r = SetDoubleData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const float* ptr = static_cast<const float*>(GetVkBufferPtr());
EXPECT_FLOAT_EQ(-6.0f, ptr[0]);
@@ -268,7 +345,7 @@ TEST_F(VertexBufferTest, R64G64B64_SFLOAT) {
TypeParser parser;
auto type = parser.Parse("R64G64B64_SFLOAT");
Format fmt(type.get());
- Result r = SetDoubleData(0, &fmt, values);
+ Result r = SetData(0, &fmt, values);
const double* ptr = static_cast<const double*>(GetVkBufferPtr());
EXPECT_DOUBLE_EQ(-6.0, ptr[0]);
diff --git a/src/vulkan/vk-funcs.inc b/src/vulkan/vk-funcs-1-0.inc
index 36d2864..22c37e8 100644
--- a/src/vulkan/vk-funcs.inc
+++ b/src/vulkan/vk-funcs-1-0.inc
@@ -32,6 +32,7 @@ AMBER_VK_FUNC(vkCreateImage)
AMBER_VK_FUNC(vkCreateImageView)
AMBER_VK_FUNC(vkCreatePipelineLayout)
AMBER_VK_FUNC(vkCreateRenderPass)
+AMBER_VK_FUNC(vkCreateSampler)
AMBER_VK_FUNC(vkCreateShaderModule)
AMBER_VK_FUNC(vkDestroyBuffer)
AMBER_VK_FUNC(vkDestroyBufferView)
@@ -45,6 +46,7 @@ AMBER_VK_FUNC(vkDestroyImageView)
AMBER_VK_FUNC(vkDestroyPipeline)
AMBER_VK_FUNC(vkDestroyPipelineLayout)
AMBER_VK_FUNC(vkDestroyRenderPass)
+AMBER_VK_FUNC(vkDestroySampler)
AMBER_VK_FUNC(vkDestroyShaderModule)
AMBER_VK_FUNC(vkEndCommandBuffer)
AMBER_VK_FUNC(vkFreeCommandBuffers)
diff --git a/src/vulkan/vk-funcs-1-1.inc b/src/vulkan/vk-funcs-1-1.inc
new file mode 100644
index 0000000..7fca3c5
--- /dev/null
+++ b/src/vulkan/vk-funcs-1-1.inc
@@ -0,0 +1 @@
+AMBER_VK_FUNC(vkGetPhysicalDeviceProperties2)
diff --git a/src/vulkan/vk-wrappers.h b/src/vulkan/vk-wrappers.h
deleted file mode 100644
index 31172c9..0000000
--- a/src/vulkan/vk-wrappers.h
+++ /dev/null
@@ -1,63 +0,0 @@
-PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
-PFN_vkAllocateDescriptorSets vkAllocateDescriptorSets;
-PFN_vkAllocateMemory vkAllocateMemory;
-PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
-PFN_vkBindBufferMemory vkBindBufferMemory;
-PFN_vkBindImageMemory vkBindImageMemory;
-PFN_vkCmdBeginRenderPass vkCmdBeginRenderPass;
-PFN_vkCmdBindDescriptorSets vkCmdBindDescriptorSets;
-PFN_vkCmdBindIndexBuffer vkCmdBindIndexBuffer;
-PFN_vkCmdBindPipeline vkCmdBindPipeline;
-PFN_vkCmdBindVertexBuffers vkCmdBindVertexBuffers;
-PFN_vkCmdClearAttachments vkCmdClearAttachments;
-PFN_vkCmdCopyBuffer vkCmdCopyBuffer;
-PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
-PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer;
-PFN_vkCmdDispatch vkCmdDispatch;
-PFN_vkCmdDraw vkCmdDraw;
-PFN_vkCmdDrawIndexed vkCmdDrawIndexed;
-PFN_vkCmdEndRenderPass vkCmdEndRenderPass;
-PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
-PFN_vkCmdPushConstants vkCmdPushConstants;
-PFN_vkCreateBuffer vkCreateBuffer;
-PFN_vkCreateBufferView vkCreateBufferView;
-PFN_vkCreateCommandPool vkCreateCommandPool;
-PFN_vkCreateComputePipelines vkCreateComputePipelines;
-PFN_vkCreateDescriptorPool vkCreateDescriptorPool;
-PFN_vkCreateDescriptorSetLayout vkCreateDescriptorSetLayout;
-PFN_vkCreateFence vkCreateFence;
-PFN_vkCreateFramebuffer vkCreateFramebuffer;
-PFN_vkCreateGraphicsPipelines vkCreateGraphicsPipelines;
-PFN_vkCreateImage vkCreateImage;
-PFN_vkCreateImageView vkCreateImageView;
-PFN_vkCreatePipelineLayout vkCreatePipelineLayout;
-PFN_vkCreateRenderPass vkCreateRenderPass;
-PFN_vkCreateShaderModule vkCreateShaderModule;
-PFN_vkDestroyBuffer vkDestroyBuffer;
-PFN_vkDestroyBufferView vkDestroyBufferView;
-PFN_vkDestroyCommandPool vkDestroyCommandPool;
-PFN_vkDestroyDescriptorPool vkDestroyDescriptorPool;
-PFN_vkDestroyDescriptorSetLayout vkDestroyDescriptorSetLayout;
-PFN_vkDestroyFence vkDestroyFence;
-PFN_vkDestroyFramebuffer vkDestroyFramebuffer;
-PFN_vkDestroyImage vkDestroyImage;
-PFN_vkDestroyImageView vkDestroyImageView;
-PFN_vkDestroyPipeline vkDestroyPipeline;
-PFN_vkDestroyPipelineLayout vkDestroyPipelineLayout;
-PFN_vkDestroyRenderPass vkDestroyRenderPass;
-PFN_vkDestroyShaderModule vkDestroyShaderModule;
-PFN_vkEndCommandBuffer vkEndCommandBuffer;
-PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
-PFN_vkFreeMemory vkFreeMemory;
-PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
-PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
-PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
-PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
-PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties;
-PFN_vkMapMemory vkMapMemory;
-PFN_vkQueueSubmit vkQueueSubmit;
-PFN_vkResetCommandBuffer vkResetCommandBuffer;
-PFN_vkResetFences vkResetFences;
-PFN_vkUnmapMemory vkUnmapMemory;
-PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
-PFN_vkWaitForFences vkWaitForFences;
diff --git a/src/vulkan/vk-wrappers.inc b/src/vulkan/vk-wrappers.inc
deleted file mode 100644
index 1258476..0000000
--- a/src/vulkan/vk-wrappers.inc
+++ /dev/null
@@ -1,252 +0,0 @@
-
-if (!(ptrs_.vkAllocateCommandBuffers = reinterpret_cast<PFN_vkAllocateCommandBuffers>(getInstanceProcAddr(instance_, "vkAllocateCommandBuffers")))) {
- return Result("Vulkan: Unable to load vkAllocateCommandBuffers pointer");
-}
-
-if (!(ptrs_.vkAllocateDescriptorSets = reinterpret_cast<PFN_vkAllocateDescriptorSets>(getInstanceProcAddr(instance_, "vkAllocateDescriptorSets")))) {
- return Result("Vulkan: Unable to load vkAllocateDescriptorSets pointer");
-}
-
-if (!(ptrs_.vkAllocateMemory = reinterpret_cast<PFN_vkAllocateMemory>(getInstanceProcAddr(instance_, "vkAllocateMemory")))) {
- return Result("Vulkan: Unable to load vkAllocateMemory pointer");
-}
-
-if (!(ptrs_.vkBeginCommandBuffer = reinterpret_cast<PFN_vkBeginCommandBuffer>(getInstanceProcAddr(instance_, "vkBeginCommandBuffer")))) {
- return Result("Vulkan: Unable to load vkBeginCommandBuffer pointer");
-}
-
-if (!(ptrs_.vkBindBufferMemory = reinterpret_cast<PFN_vkBindBufferMemory>(getInstanceProcAddr(instance_, "vkBindBufferMemory")))) {
- return Result("Vulkan: Unable to load vkBindBufferMemory pointer");
-}
-
-if (!(ptrs_.vkBindImageMemory = reinterpret_cast<PFN_vkBindImageMemory>(getInstanceProcAddr(instance_, "vkBindImageMemory")))) {
- return Result("Vulkan: Unable to load vkBindImageMemory pointer");
-}
-
-if (!(ptrs_.vkCmdBeginRenderPass = reinterpret_cast<PFN_vkCmdBeginRenderPass>(getInstanceProcAddr(instance_, "vkCmdBeginRenderPass")))) {
- return Result("Vulkan: Unable to load vkCmdBeginRenderPass pointer");
-}
-
-if (!(ptrs_.vkCmdBindDescriptorSets = reinterpret_cast<PFN_vkCmdBindDescriptorSets>(getInstanceProcAddr(instance_, "vkCmdBindDescriptorSets")))) {
- return Result("Vulkan: Unable to load vkCmdBindDescriptorSets pointer");
-}
-
-if (!(ptrs_.vkCmdBindIndexBuffer = reinterpret_cast<PFN_vkCmdBindIndexBuffer>(getInstanceProcAddr(instance_, "vkCmdBindIndexBuffer")))) {
- return Result("Vulkan: Unable to load vkCmdBindIndexBuffer pointer");
-}
-
-if (!(ptrs_.vkCmdBindPipeline = reinterpret_cast<PFN_vkCmdBindPipeline>(getInstanceProcAddr(instance_, "vkCmdBindPipeline")))) {
- return Result("Vulkan: Unable to load vkCmdBindPipeline pointer");
-}
-
-if (!(ptrs_.vkCmdBindVertexBuffers = reinterpret_cast<PFN_vkCmdBindVertexBuffers>(getInstanceProcAddr(instance_, "vkCmdBindVertexBuffers")))) {
- return Result("Vulkan: Unable to load vkCmdBindVertexBuffers pointer");
-}
-
-if (!(ptrs_.vkCmdClearAttachments = reinterpret_cast<PFN_vkCmdClearAttachments>(getInstanceProcAddr(instance_, "vkCmdClearAttachments")))) {
- return Result("Vulkan: Unable to load vkCmdClearAttachments pointer");
-}
-
-if (!(ptrs_.vkCmdCopyBuffer = reinterpret_cast<PFN_vkCmdCopyBuffer>(getInstanceProcAddr(instance_, "vkCmdCopyBuffer")))) {
- return Result("Vulkan: Unable to load vkCmdCopyBuffer pointer");
-}
-
-if (!(ptrs_.vkCmdCopyBufferToImage = reinterpret_cast<PFN_vkCmdCopyBufferToImage>(getInstanceProcAddr(instance_, "vkCmdCopyBufferToImage")))) {
- return Result("Vulkan: Unable to load vkCmdCopyBufferToImage pointer");
-}
-
-if (!(ptrs_.vkCmdCopyImageToBuffer = reinterpret_cast<PFN_vkCmdCopyImageToBuffer>(getInstanceProcAddr(instance_, "vkCmdCopyImageToBuffer")))) {
- return Result("Vulkan: Unable to load vkCmdCopyImageToBuffer pointer");
-}
-
-if (!(ptrs_.vkCmdDispatch = reinterpret_cast<PFN_vkCmdDispatch>(getInstanceProcAddr(instance_, "vkCmdDispatch")))) {
- return Result("Vulkan: Unable to load vkCmdDispatch pointer");
-}
-
-if (!(ptrs_.vkCmdDraw = reinterpret_cast<PFN_vkCmdDraw>(getInstanceProcAddr(instance_, "vkCmdDraw")))) {
- return Result("Vulkan: Unable to load vkCmdDraw pointer");
-}
-
-if (!(ptrs_.vkCmdDrawIndexed = reinterpret_cast<PFN_vkCmdDrawIndexed>(getInstanceProcAddr(instance_, "vkCmdDrawIndexed")))) {
- return Result("Vulkan: Unable to load vkCmdDrawIndexed pointer");
-}
-
-if (!(ptrs_.vkCmdEndRenderPass = reinterpret_cast<PFN_vkCmdEndRenderPass>(getInstanceProcAddr(instance_, "vkCmdEndRenderPass")))) {
- return Result("Vulkan: Unable to load vkCmdEndRenderPass pointer");
-}
-
-if (!(ptrs_.vkCmdPipelineBarrier = reinterpret_cast<PFN_vkCmdPipelineBarrier>(getInstanceProcAddr(instance_, "vkCmdPipelineBarrier")))) {
- return Result("Vulkan: Unable to load vkCmdPipelineBarrier pointer");
-}
-
-if (!(ptrs_.vkCmdPushConstants = reinterpret_cast<PFN_vkCmdPushConstants>(getInstanceProcAddr(instance_, "vkCmdPushConstants")))) {
- return Result("Vulkan: Unable to load vkCmdPushConstants pointer");
-}
-
-if (!(ptrs_.vkCreateBuffer = reinterpret_cast<PFN_vkCreateBuffer>(getInstanceProcAddr(instance_, "vkCreateBuffer")))) {
- return Result("Vulkan: Unable to load vkCreateBuffer pointer");
-}
-
-if (!(ptrs_.vkCreateBufferView = reinterpret_cast<PFN_vkCreateBufferView>(getInstanceProcAddr(instance_, "vkCreateBufferView")))) {
- return Result("Vulkan: Unable to load vkCreateBufferView pointer");
-}
-
-if (!(ptrs_.vkCreateCommandPool = reinterpret_cast<PFN_vkCreateCommandPool>(getInstanceProcAddr(instance_, "vkCreateCommandPool")))) {
- return Result("Vulkan: Unable to load vkCreateCommandPool pointer");
-}
-
-if (!(ptrs_.vkCreateComputePipelines = reinterpret_cast<PFN_vkCreateComputePipelines>(getInstanceProcAddr(instance_, "vkCreateComputePipelines")))) {
- return Result("Vulkan: Unable to load vkCreateComputePipelines pointer");
-}
-
-if (!(ptrs_.vkCreateDescriptorPool = reinterpret_cast<PFN_vkCreateDescriptorPool>(getInstanceProcAddr(instance_, "vkCreateDescriptorPool")))) {
- return Result("Vulkan: Unable to load vkCreateDescriptorPool pointer");
-}
-
-if (!(ptrs_.vkCreateDescriptorSetLayout = reinterpret_cast<PFN_vkCreateDescriptorSetLayout>(getInstanceProcAddr(instance_, "vkCreateDescriptorSetLayout")))) {
- return Result("Vulkan: Unable to load vkCreateDescriptorSetLayout pointer");
-}
-
-if (!(ptrs_.vkCreateFence = reinterpret_cast<PFN_vkCreateFence>(getInstanceProcAddr(instance_, "vkCreateFence")))) {
- return Result("Vulkan: Unable to load vkCreateFence pointer");
-}
-
-if (!(ptrs_.vkCreateFramebuffer = reinterpret_cast<PFN_vkCreateFramebuffer>(getInstanceProcAddr(instance_, "vkCreateFramebuffer")))) {
- return Result("Vulkan: Unable to load vkCreateFramebuffer pointer");
-}
-
-if (!(ptrs_.vkCreateGraphicsPipelines = reinterpret_cast<PFN_vkCreateGraphicsPipelines>(getInstanceProcAddr(instance_, "vkCreateGraphicsPipelines")))) {
- return Result("Vulkan: Unable to load vkCreateGraphicsPipelines pointer");
-}
-
-if (!(ptrs_.vkCreateImage = reinterpret_cast<PFN_vkCreateImage>(getInstanceProcAddr(instance_, "vkCreateImage")))) {
- return Result("Vulkan: Unable to load vkCreateImage pointer");
-}
-
-if (!(ptrs_.vkCreateImageView = reinterpret_cast<PFN_vkCreateImageView>(getInstanceProcAddr(instance_, "vkCreateImageView")))) {
- return Result("Vulkan: Unable to load vkCreateImageView pointer");
-}
-
-if (!(ptrs_.vkCreatePipelineLayout = reinterpret_cast<PFN_vkCreatePipelineLayout>(getInstanceProcAddr(instance_, "vkCreatePipelineLayout")))) {
- return Result("Vulkan: Unable to load vkCreatePipelineLayout pointer");
-}
-
-if (!(ptrs_.vkCreateRenderPass = reinterpret_cast<PFN_vkCreateRenderPass>(getInstanceProcAddr(instance_, "vkCreateRenderPass")))) {
- return Result("Vulkan: Unable to load vkCreateRenderPass pointer");
-}
-
-if (!(ptrs_.vkCreateShaderModule = reinterpret_cast<PFN_vkCreateShaderModule>(getInstanceProcAddr(instance_, "vkCreateShaderModule")))) {
- return Result("Vulkan: Unable to load vkCreateShaderModule pointer");
-}
-
-if (!(ptrs_.vkDestroyBuffer = reinterpret_cast<PFN_vkDestroyBuffer>(getInstanceProcAddr(instance_, "vkDestroyBuffer")))) {
- return Result("Vulkan: Unable to load vkDestroyBuffer pointer");
-}
-
-if (!(ptrs_.vkDestroyBufferView = reinterpret_cast<PFN_vkDestroyBufferView>(getInstanceProcAddr(instance_, "vkDestroyBufferView")))) {
- return Result("Vulkan: Unable to load vkDestroyBufferView pointer");
-}
-
-if (!(ptrs_.vkDestroyCommandPool = reinterpret_cast<PFN_vkDestroyCommandPool>(getInstanceProcAddr(instance_, "vkDestroyCommandPool")))) {
- return Result("Vulkan: Unable to load vkDestroyCommandPool pointer");
-}
-
-if (!(ptrs_.vkDestroyDescriptorPool = reinterpret_cast<PFN_vkDestroyDescriptorPool>(getInstanceProcAddr(instance_, "vkDestroyDescriptorPool")))) {
- return Result("Vulkan: Unable to load vkDestroyDescriptorPool pointer");
-}
-
-if (!(ptrs_.vkDestroyDescriptorSetLayout = reinterpret_cast<PFN_vkDestroyDescriptorSetLayout>(getInstanceProcAddr(instance_, "vkDestroyDescriptorSetLayout")))) {
- return Result("Vulkan: Unable to load vkDestroyDescriptorSetLayout pointer");
-}
-
-if (!(ptrs_.vkDestroyFence = reinterpret_cast<PFN_vkDestroyFence>(getInstanceProcAddr(instance_, "vkDestroyFence")))) {
- return Result("Vulkan: Unable to load vkDestroyFence pointer");
-}
-
-if (!(ptrs_.vkDestroyFramebuffer = reinterpret_cast<PFN_vkDestroyFramebuffer>(getInstanceProcAddr(instance_, "vkDestroyFramebuffer")))) {
- return Result("Vulkan: Unable to load vkDestroyFramebuffer pointer");
-}
-
-if (!(ptrs_.vkDestroyImage = reinterpret_cast<PFN_vkDestroyImage>(getInstanceProcAddr(instance_, "vkDestroyImage")))) {
- return Result("Vulkan: Unable to load vkDestroyImage pointer");
-}
-
-if (!(ptrs_.vkDestroyImageView = reinterpret_cast<PFN_vkDestroyImageView>(getInstanceProcAddr(instance_, "vkDestroyImageView")))) {
- return Result("Vulkan: Unable to load vkDestroyImageView pointer");
-}
-
-if (!(ptrs_.vkDestroyPipeline = reinterpret_cast<PFN_vkDestroyPipeline>(getInstanceProcAddr(instance_, "vkDestroyPipeline")))) {
- return Result("Vulkan: Unable to load vkDestroyPipeline pointer");
-}
-
-if (!(ptrs_.vkDestroyPipelineLayout = reinterpret_cast<PFN_vkDestroyPipelineLayout>(getInstanceProcAddr(instance_, "vkDestroyPipelineLayout")))) {
- return Result("Vulkan: Unable to load vkDestroyPipelineLayout pointer");
-}
-
-if (!(ptrs_.vkDestroyRenderPass = reinterpret_cast<PFN_vkDestroyRenderPass>(getInstanceProcAddr(instance_, "vkDestroyRenderPass")))) {
- return Result("Vulkan: Unable to load vkDestroyRenderPass pointer");
-}
-
-if (!(ptrs_.vkDestroyShaderModule = reinterpret_cast<PFN_vkDestroyShaderModule>(getInstanceProcAddr(instance_, "vkDestroyShaderModule")))) {
- return Result("Vulkan: Unable to load vkDestroyShaderModule pointer");
-}
-
-if (!(ptrs_.vkEndCommandBuffer = reinterpret_cast<PFN_vkEndCommandBuffer>(getInstanceProcAddr(instance_, "vkEndCommandBuffer")))) {
- return Result("Vulkan: Unable to load vkEndCommandBuffer pointer");
-}
-
-if (!(ptrs_.vkFreeCommandBuffers = reinterpret_cast<PFN_vkFreeCommandBuffers>(getInstanceProcAddr(instance_, "vkFreeCommandBuffers")))) {
- return Result("Vulkan: Unable to load vkFreeCommandBuffers pointer");
-}
-
-if (!(ptrs_.vkFreeMemory = reinterpret_cast<PFN_vkFreeMemory>(getInstanceProcAddr(instance_, "vkFreeMemory")))) {
- return Result("Vulkan: Unable to load vkFreeMemory pointer");
-}
-
-if (!(ptrs_.vkGetBufferMemoryRequirements = reinterpret_cast<PFN_vkGetBufferMemoryRequirements>(getInstanceProcAddr(instance_, "vkGetBufferMemoryRequirements")))) {
- return Result("Vulkan: Unable to load vkGetBufferMemoryRequirements pointer");
-}
-
-if (!(ptrs_.vkGetImageMemoryRequirements = reinterpret_cast<PFN_vkGetImageMemoryRequirements>(getInstanceProcAddr(instance_, "vkGetImageMemoryRequirements")))) {
- return Result("Vulkan: Unable to load vkGetImageMemoryRequirements pointer");
-}
-
-if (!(ptrs_.vkGetPhysicalDeviceFormatProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceFormatProperties>(getInstanceProcAddr(instance_, "vkGetPhysicalDeviceFormatProperties")))) {
- return Result("Vulkan: Unable to load vkGetPhysicalDeviceFormatProperties pointer");
-}
-
-if (!(ptrs_.vkGetPhysicalDeviceMemoryProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceMemoryProperties>(getInstanceProcAddr(instance_, "vkGetPhysicalDeviceMemoryProperties")))) {
- return Result("Vulkan: Unable to load vkGetPhysicalDeviceMemoryProperties pointer");
-}
-
-if (!(ptrs_.vkGetPhysicalDeviceProperties = reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(getInstanceProcAddr(instance_, "vkGetPhysicalDeviceProperties")))) {
- return Result("Vulkan: Unable to load vkGetPhysicalDeviceProperties pointer");
-}
-
-if (!(ptrs_.vkMapMemory = reinterpret_cast<PFN_vkMapMemory>(getInstanceProcAddr(instance_, "vkMapMemory")))) {
- return Result("Vulkan: Unable to load vkMapMemory pointer");
-}
-
-if (!(ptrs_.vkQueueSubmit = reinterpret_cast<PFN_vkQueueSubmit>(getInstanceProcAddr(instance_, "vkQueueSubmit")))) {
- return Result("Vulkan: Unable to load vkQueueSubmit pointer");
-}
-
-if (!(ptrs_.vkResetCommandBuffer = reinterpret_cast<PFN_vkResetCommandBuffer>(getInstanceProcAddr(instance_, "vkResetCommandBuffer")))) {
- return Result("Vulkan: Unable to load vkResetCommandBuffer pointer");
-}
-
-if (!(ptrs_.vkResetFences = reinterpret_cast<PFN_vkResetFences>(getInstanceProcAddr(instance_, "vkResetFences")))) {
- return Result("Vulkan: Unable to load vkResetFences pointer");
-}
-
-if (!(ptrs_.vkUnmapMemory = reinterpret_cast<PFN_vkUnmapMemory>(getInstanceProcAddr(instance_, "vkUnmapMemory")))) {
- return Result("Vulkan: Unable to load vkUnmapMemory pointer");
-}
-
-if (!(ptrs_.vkUpdateDescriptorSets = reinterpret_cast<PFN_vkUpdateDescriptorSets>(getInstanceProcAddr(instance_, "vkUpdateDescriptorSets")))) {
- return Result("Vulkan: Unable to load vkUpdateDescriptorSets pointer");
-}
-
-if (!(ptrs_.vkWaitForFences = reinterpret_cast<PFN_vkWaitForFences>(getInstanceProcAddr(instance_, "vkWaitForFences")))) {
- return Result("Vulkan: Unable to load vkWaitForFences pointer");
-}
diff --git a/tests/cases/address_modes_float.amber b/tests/cases/address_modes_float.amber
new file mode 100755
index 0000000..31035c9
--- /dev/null
+++ b/tests/cases/address_modes_float.amber
@@ -0,0 +1,138 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) texture2D tex;
+uniform layout(set=0, binding=1) sampler tex_sampler;
+void main() {
+ color_out = texture(sampler2D(tex, tex_sampler), texcoords_in);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+# Define samplers for all floating point border colors.
+SAMPLER sampler_float_opaque_white \
+ ADDRESS_MODE_U clamp_to_border \
+ ADDRESS_MODE_V clamp_to_border \
+ BORDER_COLOR float_opaque_white
+
+SAMPLER sampler_float_opaque_black \
+ ADDRESS_MODE_U clamp_to_border \
+ ADDRESS_MODE_V clamp_to_border \
+ BORDER_COLOR float_opaque_black
+
+SAMPLER sampler_float_transparent_black \
+ ADDRESS_MODE_U clamp_to_border \
+ ADDRESS_MODE_V clamp_to_border \
+ BORDER_COLOR float_transparent_black
+
+BUFFER position DATA_TYPE vec2<float> DATA
+-1.0 -1.0
+ 0.0 -1.0
+ 0.0 0.0
+-1.0 0.0
+
+ 0.0 -1.0
+ 1.0 -1.0
+ 1.0 0.0
+ 0.0 0.0
+
+-1.0 0.0
+ 0.0 0.0
+ 0.0 1.0
+-1.0 1.0
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+-1.0 -1.0
+ 2.0 -1.0
+ 2.0 2.0
+-1.0 2.0
+
+-1.0 -1.0
+ 2.0 -1.0
+ 2.0 2.0
+-1.0 2.0
+
+-1.0 -1.0
+ 2.0 -1.0
+ 2.0 2.0
+-1.0 2.0
+END
+
+PIPELINE graphics pipeline_texgen
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline_float_opaque_white
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler_float_opaque_white DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+DERIVE_PIPELINE pipeline_float_opaque_black FROM pipeline_float_opaque_white
+ BIND SAMPLER sampler_float_opaque_black DESCRIPTOR_SET 0 BINDING 1
+END
+
+DERIVE_PIPELINE pipeline_float_transparent_black FROM pipeline_float_opaque_white
+ BIND SAMPLER sampler_float_transparent_black DESCRIPTOR_SET 0 BINDING 1
+END
+
+# Generate texture: a rectangle at the lower right corner.
+CLEAR_COLOR pipeline_texgen 0 0 255 255
+CLEAR pipeline_texgen
+RUN pipeline_texgen DRAW_RECT POS 128 128 SIZE 128 128
+
+# Draw the texture with coordinates going beyond 0 and 1 to trigger border color.
+RUN pipeline_float_opaque_white DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+RUN pipeline_float_opaque_black DRAW_ARRAY AS TRIANGLE_FAN START_IDX 4 COUNT 4
+RUN pipeline_float_transparent_black DRAW_ARRAY AS TRIANGLE_FAN START_IDX 8 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 255 255 255 255
+EXPECT framebuffer IDX 129 1 SIZE 1 1 EQ_RGBA 0 0 0 255
+EXPECT framebuffer IDX 1 129 SIZE 1 1 EQ_RGBA 0 0 0 0
diff --git a/tests/cases/address_modes_int.amber b/tests/cases/address_modes_int.amber
new file mode 100755
index 0000000..36852c5
--- /dev/null
+++ b/tests/cases/address_modes_int.amber
@@ -0,0 +1,138 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out uvec4 color_out;
+void main() {
+ color_out = uvec4(255, 0.0, 0.0, 255);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) utexture2D tex;
+uniform layout(set=0, binding=1) sampler tex_sampler;
+void main() {
+ color_out = texture(usampler2D(tex, tex_sampler), texcoords_in);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UINT
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+# Define samplers for all integer border colors.
+SAMPLER sampler_int_opaque_white \
+ ADDRESS_MODE_U clamp_to_border \
+ ADDRESS_MODE_V clamp_to_border \
+ BORDER_COLOR int_opaque_white
+
+SAMPLER sampler_int_opaque_black \
+ ADDRESS_MODE_U clamp_to_border \
+ ADDRESS_MODE_V clamp_to_border \
+ BORDER_COLOR int_opaque_black
+
+SAMPLER sampler_int_transparent_black \
+ ADDRESS_MODE_U clamp_to_border \
+ ADDRESS_MODE_V clamp_to_border \
+ BORDER_COLOR int_transparent_black
+
+BUFFER position DATA_TYPE vec2<float> DATA
+-1.0 -1.0
+ 0.0 -1.0
+ 0.0 0.0
+-1.0 0.0
+
+ 0.0 -1.0
+ 1.0 -1.0
+ 1.0 0.0
+ 0.0 0.0
+
+-1.0 0.0
+ 0.0 0.0
+ 0.0 1.0
+-1.0 1.0
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+-1.0 -1.0
+ 2.0 -1.0
+ 2.0 2.0
+-1.0 2.0
+
+-1.0 -1.0
+ 2.0 -1.0
+ 2.0 2.0
+-1.0 2.0
+
+-1.0 -1.0
+ 2.0 -1.0
+ 2.0 2.0
+-1.0 2.0
+END
+
+PIPELINE graphics pipeline_texgen
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline_int_opaque_white
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler_int_opaque_white DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+DERIVE_PIPELINE pipeline_int_opaque_black FROM pipeline_int_opaque_white
+ BIND SAMPLER sampler_int_opaque_black DESCRIPTOR_SET 0 BINDING 1
+END
+
+DERIVE_PIPELINE pipeline_int_transparent_black FROM pipeline_int_opaque_white
+ BIND SAMPLER sampler_int_transparent_black DESCRIPTOR_SET 0 BINDING 1
+END
+
+# Generate texture: a rectangle at the lower right corner.
+CLEAR_COLOR pipeline_texgen 0 0 255 255
+CLEAR pipeline_texgen
+RUN pipeline_texgen DRAW_RECT POS 128 128 SIZE 128 128
+
+# Draw the texture with coordinates going beyond 0 and 1 to trigger border color.
+RUN pipeline_int_opaque_white DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+RUN pipeline_int_opaque_black DRAW_ARRAY AS TRIANGLE_FAN START_IDX 4 COUNT 4
+RUN pipeline_int_transparent_black DRAW_ARRAY AS TRIANGLE_FAN START_IDX 8 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 255 255 255 255
+EXPECT framebuffer IDX 129 1 SIZE 1 1 EQ_RGBA 0 0 0 255
+EXPECT framebuffer IDX 1 129 SIZE 1 1 EQ_RGBA 0 0 0 0
diff --git a/tests/cases/buffer_load_binary.amber b/tests/cases/buffer_load_binary.amber
new file mode 100644
index 0000000..2eae472
--- /dev/null
+++ b/tests/cases/buffer_load_binary.amber
@@ -0,0 +1,55 @@
+#!amber
+#
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute compute_shader GLSL
+#version 430
+
+layout(set = 0, binding = 0) buffer block0 {
+ float in_data[12];
+};
+
+layout(set = 0, binding = 1) buffer block1 {
+ float out_data[12];
+};
+
+void main() {
+ for (int i = 0; i < 12; i++)
+ out_data[i] = in_data[i] * 2.0;
+}
+END
+
+BUFFER buf0 DATA_TYPE vec4<float> SIZE 3 FILE BINARY vec4data.bin
+BUFFER buf1 DATA_TYPE vec4<float> SIZE 3 FILL 0.0
+
+BUFFER ref0 DATA_TYPE vec4<float> DATA
+1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0
+END
+
+BUFFER ref1 DATA_TYPE vec4<float> DATA
+2.0 4.0 6.0 8.0 10.0 12.0 14.0 16.0 18.0 20.0 22.0 24.0
+END
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+
+ BIND BUFFER buf0 AS storage DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER buf1 AS storage DESCRIPTOR_SET 0 BINDING 1
+END
+
+RUN pipeline 1 1 1
+
+EXPECT buf0 EQ_BUFFER ref0
+EXPECT buf1 EQ_BUFFER ref1
diff --git a/tests/cases/buffer_load_text.amber b/tests/cases/buffer_load_text.amber
new file mode 100644
index 0000000..569224d
--- /dev/null
+++ b/tests/cases/buffer_load_text.amber
@@ -0,0 +1,55 @@
+#!amber
+#
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute compute_shader GLSL
+#version 430
+
+layout(set = 0, binding = 0) buffer block0 {
+ float in_data[12];
+};
+
+layout(set = 0, binding = 1) buffer block1 {
+ float out_data[12];
+};
+
+void main() {
+ for (int i = 0; i < 12; i++)
+ out_data[i] = in_data[i] * 2.0;
+}
+END
+
+BUFFER buf0 DATA_TYPE vec4<float> SIZE 3 FILE TEXT vec4data.txt
+BUFFER buf1 DATA_TYPE vec4<float> SIZE 3 FILL 0.0
+
+BUFFER ref0 DATA_TYPE vec4<float> DATA
+1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0
+END
+
+BUFFER ref1 DATA_TYPE vec4<float> DATA
+2.0 4.0 6.0 8.0 10.0 12.0 14.0 16.0 18.0 20.0 22.0 24.0
+END
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+
+ BIND BUFFER buf0 AS storage DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER buf1 AS storage DESCRIPTOR_SET 0 BINDING 1
+END
+
+RUN pipeline 1 1 1
+
+EXPECT buf0 EQ_BUFFER ref0
+EXPECT buf1 EQ_BUFFER ref1
diff --git a/tests/cases/compute_descriptor_array_ssbo.amber b/tests/cases/compute_descriptor_array_ssbo.amber
new file mode 100644
index 0000000..bd4c773
--- /dev/null
+++ b/tests/cases/compute_descriptor_array_ssbo.amber
@@ -0,0 +1,49 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute compute_shader GLSL
+#version 450
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+layout(binding = 0) buffer block0
+{
+ int data;
+} ssbo_array[2];
+
+void main()
+{
+ ssbo_array[0].data = 1;
+ ssbo_array[1].data = 2;
+}
+END
+
+BUFFER buf0 DATA_TYPE int32 DATA
+0
+END
+
+BUFFER buf1 DATA_TYPE int32 DATA
+0
+END
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+
+ BIND BUFFER_ARRAY buf0 buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+RUN pipeline 1 1 1
+
+EXPECT buf0 IDX 0 EQ 1
+EXPECT buf1 IDX 0 EQ 2
diff --git a/tests/cases/compute_descriptor_array_storagetexelbuffer.amber b/tests/cases/compute_descriptor_array_storagetexelbuffer.amber
new file mode 100644
index 0000000..d4a1994
--- /dev/null
+++ b/tests/cases/compute_descriptor_array_storagetexelbuffer.amber
@@ -0,0 +1,63 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute compute_shader GLSL
+#version 430
+layout(local_size_x=4,local_size_y=1) in;
+uniform layout(set=0, binding=0) samplerBuffer texelsIn;
+uniform layout(set=0, binding=1, rgba32f) imageBuffer texelsOut[2];
+
+void main ()
+{
+ vec4 color = texelFetch(texelsIn, int(gl_GlobalInvocationID));
+ imageStore(texelsOut[0], int(gl_GlobalInvocationID), color);
+ imageStore(texelsOut[1], int(gl_GlobalInvocationID), color * 2.0);
+}
+END
+
+BUFFER texel_buffer_in DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+ 0 255 0 255
+ 0 0 255 255
+ 0 255 255 255
+END
+
+BUFFER texel_buffer_out0 DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0
+BUFFER texel_buffer_out1 DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0
+
+BUFFER ref0 DATA_TYPE R32G32B32A32_SFLOAT DATA
+1.0 0.0 0.0 1.0
+0.0 1.0 0.0 1.0
+0.0 0.0 1.0 1.0
+0.0 1.0 1.0 1.0
+END
+
+BUFFER ref1 DATA_TYPE R32G32B32A32_SFLOAT DATA
+2.0 0.0 0.0 2.0
+0.0 2.0 0.0 2.0
+0.0 0.0 2.0 2.0
+0.0 2.0 2.0 2.0
+END
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texel_buffer_in AS uniform_texel_buffer DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER_ARRAY texel_buffer_out0 texel_buffer_out1 AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING 1
+END
+
+RUN pipeline 1 1 1
+
+EXPECT texel_buffer_out0 EQ_BUFFER ref0
+EXPECT texel_buffer_out1 EQ_BUFFER ref1
diff --git a/tests/cases/compute_dynamic_buffers.amber b/tests/cases/compute_dynamic_buffers.amber
new file mode 100644
index 0000000..9412e30
--- /dev/null
+++ b/tests/cases/compute_dynamic_buffers.amber
@@ -0,0 +1,54 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute compute_shader GLSL
+#version 430
+
+layout(set = 0, binding = 0) buffer block0
+{
+ vec4 data0;
+};
+
+layout(set = 0, binding = 1) buffer block1
+{
+ vec4 data1;
+};
+
+void main()
+{
+ data0 = vec4(1, 2, 3, 4);
+ data1 = vec4(5, 6, 7, 8);
+}
+END
+
+# The Vulkan spec lists the maximum value of minStorageBufferOffsetAlignment
+# (i.e. the maximum possible alignment requirement) as 256 bytes.
+# Allocate enough space to hold one vec4 (each 16 bytes)
+# after the alignment (256 / 16 + 1).
+BUFFER buf0 DATA_TYPE vec4<float> SIZE 17 FILL 0.0
+BUFFER buf1 DATA_TYPE vec4<float> SIZE 17 FILL 0.0
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+
+ BIND BUFFER buf0 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 0 OFFSET 0
+ # Using the maximum possible required offset alignment of 256 bytes to support all implementations.
+ BIND BUFFER buf1 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 1 OFFSET 256
+END
+
+RUN pipeline 1 1 1
+
+EXPECT buf0 IDX 0 EQ 1.0 2.0 3.0 4.0
+EXPECT buf1 IDX 256 EQ 5.0 6.0 7.0 8.0
diff --git a/tests/cases/compute_dynamic_buffers_descriptor_array.amber b/tests/cases/compute_dynamic_buffers_descriptor_array.amber
new file mode 100644
index 0000000..fe07b6c
--- /dev/null
+++ b/tests/cases/compute_dynamic_buffers_descriptor_array.amber
@@ -0,0 +1,48 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute compute_shader GLSL
+#version 430
+
+layout(set = 0, binding = 0) buffer block0
+{
+ vec4 data;
+} ssbo_array[2];
+
+void main()
+{
+ ssbo_array[0].data = vec4(1, 2, 3, 4);
+ ssbo_array[1].data = vec4(5, 6, 7, 8);
+}
+END
+
+# The Vulkan spec lists the maximum value of minStorageBufferOffsetAlignment
+# (i.e. the maximum possible alignment requirement) as 256 bytes.
+# Allocate enough space to hold one vec4 (each 16 bytes)
+# after the alignment (256 / 16 + 1).
+BUFFER buf0 DATA_TYPE vec4<float> SIZE 17 FILL 0.0
+BUFFER buf1 DATA_TYPE vec4<float> SIZE 17 FILL 0.0
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+
+ # Using the maximum possible required offset alignment of 256 bytes to support all implementations.
+ BIND BUFFER_ARRAY buf0 buf1 AS storage_dynamic DESCRIPTOR_SET 0 BINDING 0 OFFSET 0 256
+END
+
+RUN pipeline 1 1 1
+
+EXPECT buf0 IDX 0 EQ 1.0 2.0 3.0 4.0
+EXPECT buf1 IDX 256 EQ 5.0 6.0 7.0 8.0
diff --git a/tests/cases/compute_mat2x4_row_major_col_major.amber b/tests/cases/compute_mat2x4_row_major_col_major.amber
new file mode 100644
index 0000000..210132c
--- /dev/null
+++ b/tests/cases/compute_mat2x4_row_major_col_major.amber
@@ -0,0 +1,89 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Demonstrate reads and writes on row major and column major matrices.
+
+SHADER compute copy_vertex GLSL
+#version 450
+
+layout(set=0, binding=0) buffer A {
+ layout(column_major) mat2x4 mcols;
+ layout(row_major) mat2x4 mrows;
+} inbound;
+
+layout(set=0, binding=1) buffer B {
+ vec4 first;
+ vec4 second;
+} outbound;
+
+void main() {
+ outbound.first = inbound.mcols[1];
+ outbound.second = inbound.mrows[1]; // Read is a gather
+ inbound.mcols[0] = inbound.mrows[0];
+ inbound.mrows[1] = inbound.mcols[1]; // Write is a scatter
+}
+END
+
+BUFFER buf0 DATA_TYPE float DATA
+
+ # Column major mcols.
+
+ # first column
+ 00.0 01.0 02.0 03.0
+ # second column
+ 10.0 11.0 12.0 13.0
+
+ # Row major
+ # first row
+ 100.0 110.0
+ # second row
+ 101.0 111.0
+ # third row
+ 102.0 112.0
+ # fourth row
+ 103.0 113.0
+END
+
+BUFFER buf1 DATA_TYPE float DATA
+ # Initialize with garbage data.
+ -1.0 -1.0 -1.0 -1.0
+ -2.0 -2.0 -2.0 -2.0
+END
+
+PIPELINE compute pipeline
+ ATTACH copy_vertex
+ BIND BUFFER buf0 AS storage DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER buf1 AS storage DESCRIPTOR_SET 0 BINDING 1
+END
+
+RUN pipeline 1 1 1
+
+# Check the column vectors we copied out.
+# From inbound.mcols[1]
+EXPECT buf1 IDX 0 EQ 10.0 11.0 12. 13.0
+# From inbound.mrows[1]
+# Did a gather from mrows[1] to collect these values before writing them out.
+EXPECT buf1 IDX 16 EQ 110.0 111.0 112. 113.0
+
+# Check the contents of inbound.mcols
+EXPECT buf0 IDX 0 EQ 100.0 101.0 102.0 103.0 # Did a gather from mrows[0]
+EXPECT buf0 IDX 16 EQ 10.0 11.0 12.0 13.0 # Second column is unchanged
+# Check the conents of inbound.mrows
+# Writing to second column of row major is a scatter operation
+EXPECT buf0 IDX 32 EQ 100.0 10.0
+EXPECT buf0 IDX 40 EQ 101.0 11.0
+EXPECT buf0 IDX 48 EQ 102.0 12.0
+EXPECT buf0 IDX 56 EQ 103.0 13.0
+
diff --git a/tests/cases/debugger_hlsl_basic_compute.amber b/tests/cases/debugger_hlsl_basic_compute.amber
new file mode 100644
index 0000000..84db5df
--- /dev/null
+++ b/tests/cases/debugger_hlsl_basic_compute.amber
@@ -0,0 +1,72 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET ENGINE_DATA fence_timeout_ms 1000000
+
+VIRTUAL_FILE "compute.hlsl"
+
+[[vk::binding(0)]]
+StructuredBuffer<int> data;
+
+[numthreads(1,1,1)]
+void main() {
+ int a = data[0];
+ int b = data[1];
+ int c = data[2];
+ int d = data[3];
+ data[0] = a + b + c + d;
+}
+END
+
+SHADER compute shader HLSL VIRTUAL_FILE "compute.hlsl"
+
+BUFFER data DATA_TYPE int32 DATA
+ 10 20 30 40
+END
+
+PIPELINE compute pipeline
+ ATTACH shader
+ SHADER_OPTIMIZATION shader
+ --legalize-hlsl
+ END
+ BIND BUFFER data AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+DEBUG pipeline 1 1 1
+ THREAD GLOBAL_INVOCATION_ID 0 0 0
+ EXPECT LOCATION "compute.hlsl" 7 " int a = data[0];"
+ STEP_IN
+ EXPECT LOCATION "compute.hlsl" 8 " int b = data[1];"
+ EXPECT LOCAL "a" EQ 10
+ STEP_IN
+ EXPECT LOCATION "compute.hlsl" 9 " int c = data[2];"
+ EXPECT LOCAL "a" EQ 10
+ EXPECT LOCAL "b" EQ 20
+ STEP_IN
+ EXPECT LOCATION "compute.hlsl" 10 " int d = data[3];"
+ EXPECT LOCAL "a" EQ 10
+ EXPECT LOCAL "b" EQ 20
+ EXPECT LOCAL "c" EQ 30
+ STEP_IN
+ EXPECT LOCATION "compute.hlsl" 11 " data[0] = a + b + c + d;"
+ EXPECT LOCAL "a" EQ 10
+ EXPECT LOCAL "b" EQ 20
+ EXPECT LOCAL "c" EQ 30
+ EXPECT LOCAL "d" EQ 40
+ CONTINUE
+ END
+END
+
+EXPECT data IDX 0 EQ 100
diff --git a/tests/cases/debugger_hlsl_basic_fragment.amber b/tests/cases/debugger_hlsl_basic_fragment.amber
new file mode 100644
index 0000000..331a592
--- /dev/null
+++ b/tests/cases/debugger_hlsl_basic_fragment.amber
@@ -0,0 +1,104 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET ENGINE_DATA fence_timeout_ms 1000000
+
+VIRTUAL_FILE "vs.hlsl"
+struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION) {
+ VS_OUTPUT vout;
+ vout.pos = pos;
+ vout.color = float4(25.1 / 255.0, 50.1 / 255.0, 100.1 / 255.0, 1.0);
+ return vout;
+}
+END
+
+VIRTUAL_FILE "fs.hlsl"
+float4 main(float4 color : COLOR) : SV_TARGET {
+ int r = color.r * 255;
+ int g = color.g * 255;
+ int b = color.b * 255;
+ int a = color.a * 255;
+ return color;
+}
+END
+
+SHADER vertex vtex_shader HLSL VIRTUAL_FILE "vs.hlsl"
+SHADER fragment frag_shader HLSL VIRTUAL_FILE "fs.hlsl"
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+# Full frame
+-128 -128
+ 127 127
+-128 127
+-128 -128
+ 127 127
+ 127 -128
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ FRAMEBUFFER_SIZE 32 32
+
+ ATTACH vtex_shader
+ SHADER_OPTIMIZATION vtex_shader
+ --inline-entry-points-exhaustive
+ --eliminate-dead-functions
+ END
+
+ ATTACH frag_shader
+ SHADER_OPTIMIZATION frag_shader
+ --inline-entry-points-exhaustive
+ --eliminate-dead-functions
+ END
+
+ VERTEX_DATA position_buf LOCATION 0
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+CLEAR pipeline
+
+DEBUG pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+ THREAD FRAGMENT_WINDOW_SPACE_POSITION 10 10
+ EXPECT LOCATION "fs.hlsl" 2 " int r = color.r * 255;"
+ STEP_IN
+ EXPECT LOCAL "r" EQ 25
+ EXPECT LOCATION "fs.hlsl" 3 " int g = color.g * 255;"
+ STEP_IN
+ EXPECT LOCAL "r" EQ 25
+ EXPECT LOCAL "g" EQ 50
+ EXPECT LOCATION "fs.hlsl" 4 " int b = color.b * 255;"
+ STEP_IN
+ EXPECT LOCAL "r" EQ 25
+ EXPECT LOCAL "g" EQ 50
+ EXPECT LOCAL "b" EQ 100
+ EXPECT LOCATION "fs.hlsl" 5 " int a = color.a * 255;"
+ STEP_IN
+ EXPECT LOCAL "r" EQ 25
+ EXPECT LOCAL "g" EQ 50
+ EXPECT LOCAL "b" EQ 100
+ EXPECT LOCAL "a" EQ 255
+ EXPECT LOCATION "fs.hlsl" 6 " return color;"
+ CONTINUE
+ END
+END
+
+EXPECT framebuffer IDX 0 0 SIZE 32 32 EQ_RGB 25 50 100
diff --git a/tests/cases/debugger_hlsl_basic_fragment_with_legalization.amber b/tests/cases/debugger_hlsl_basic_fragment_with_legalization.amber
new file mode 100644
index 0000000..ccbbfa5
--- /dev/null
+++ b/tests/cases/debugger_hlsl_basic_fragment_with_legalization.amber
@@ -0,0 +1,102 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET ENGINE_DATA fence_timeout_ms 1000000
+
+VIRTUAL_FILE "vs.hlsl"
+struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION) {
+ VS_OUTPUT vout;
+ vout.pos = pos;
+ vout.color = float4(25.1 / 255.0, 50.1 / 255.0, 100.1 / 255.0, 1.0);
+ return vout;
+}
+END
+
+VIRTUAL_FILE "fs.hlsl"
+float4 main(float4 color : COLOR) : SV_TARGET {
+ int r = color.r * 255;
+ int g = color.g * 255;
+ int b = color.b * 255;
+ int a = color.a * 255;
+ return color;
+}
+END
+
+SHADER vertex vtex_shader HLSL VIRTUAL_FILE "vs.hlsl"
+SHADER fragment frag_shader HLSL VIRTUAL_FILE "fs.hlsl"
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+# Full frame
+-128 -128
+ 127 127
+-128 127
+-128 -128
+ 127 127
+ 127 -128
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ FRAMEBUFFER_SIZE 32 32
+
+ ATTACH vtex_shader
+ SHADER_OPTIMIZATION vtex_shader
+ --legalize-hlsl
+ END
+
+ ATTACH frag_shader
+ SHADER_OPTIMIZATION frag_shader
+ --legalize-hlsl
+ END
+
+ VERTEX_DATA position_buf LOCATION 0
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+CLEAR pipeline
+
+DEBUG pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+ THREAD FRAGMENT_WINDOW_SPACE_POSITION 10 10
+ EXPECT LOCATION "fs.hlsl" 2 " int r = color.r * 255;"
+ STEP_IN
+ EXPECT LOCAL "r" EQ 25
+ EXPECT LOCATION "fs.hlsl" 3 " int g = color.g * 255;"
+ STEP_IN
+ EXPECT LOCAL "r" EQ 25
+ EXPECT LOCAL "g" EQ 50
+ EXPECT LOCATION "fs.hlsl" 4 " int b = color.b * 255;"
+ STEP_IN
+ EXPECT LOCAL "r" EQ 25
+ EXPECT LOCAL "g" EQ 50
+ EXPECT LOCAL "b" EQ 100
+ EXPECT LOCATION "fs.hlsl" 5 " int a = color.a * 255;"
+ STEP_IN
+ EXPECT LOCAL "r" EQ 25
+ EXPECT LOCAL "g" EQ 50
+ EXPECT LOCAL "b" EQ 100
+ EXPECT LOCAL "a" EQ 255
+ EXPECT LOCATION "fs.hlsl" 7 "}"
+ CONTINUE
+ END
+END
+
+EXPECT framebuffer IDX 0 0 SIZE 32 32 EQ_RGB 25 50 100
diff --git a/tests/cases/debugger_hlsl_basic_vertex.amber b/tests/cases/debugger_hlsl_basic_vertex.amber
new file mode 100644
index 0000000..86d0381
--- /dev/null
+++ b/tests/cases/debugger_hlsl_basic_vertex.amber
@@ -0,0 +1,130 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET ENGINE_DATA fence_timeout_ms 1000000
+
+VIRTUAL_FILE "vs.hlsl"
+struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+ float4 color : COLOR) {
+ VS_OUTPUT vout;
+ vout.pos = pos;
+ vout.color = color;
+ return vout;
+}
+END
+
+VIRTUAL_FILE "fs.hlsl"
+float4 main(float4 color : COLOR) : SV_TARGET {
+ return color;
+}
+END
+
+SHADER vertex vtex_shader HLSL VIRTUAL_FILE "vs.hlsl"
+SHADER fragment frag_shader HLSL VIRTUAL_FILE "fs.hlsl"
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+# Full frame
+-128 -128
+ 127 127
+-128 127
+-128 -128
+ 127 127
+ 127 -128
+END
+
+BUFFER vert_color DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ FRAMEBUFFER_SIZE 32 32
+
+ ATTACH vtex_shader
+ SHADER_OPTIMIZATION vtex_shader
+ --inline-entry-points-exhaustive
+ --eliminate-dead-functions
+ END
+
+ ATTACH frag_shader
+ SHADER_OPTIMIZATION frag_shader
+ --inline-entry-points-exhaustive
+ --eliminate-dead-functions
+ END
+
+ VERTEX_DATA position_buf LOCATION 0
+ VERTEX_DATA vert_color LOCATION 1
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+CLEAR pipeline
+
+DEBUG pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+ THREAD VERTEX_INDEX 0
+ EXPECT LOCATION "vs.hlsl" 9 " vout.pos = pos;"
+ EXPECT LOCAL "pos.x" EQ -1.000000
+ EXPECT LOCAL "pos.y" EQ -1.000000
+ EXPECT LOCAL "pos.z" EQ 0.000000
+ EXPECT LOCAL "color.x" EQ 1.000000
+ EXPECT LOCAL "color.y" EQ 0.000000
+ EXPECT LOCAL "color.z" EQ 0.000000
+ STEP_IN
+ EXPECT LOCAL "vout.pos.x" EQ -1.000000
+ EXPECT LOCAL "vout.pos.y" EQ -1.000000
+ EXPECT LOCAL "vout.pos.z" EQ 0.000000
+ EXPECT LOCATION "vs.hlsl" 10 " vout.color = color;"
+ STEP_IN
+ EXPECT LOCAL "vout.color.x" EQ 1.000000
+ EXPECT LOCAL "vout.color.y" EQ 0.000000
+ EXPECT LOCAL "vout.color.z" EQ 0.000000
+ EXPECT LOCATION "vs.hlsl" 11 " return vout;"
+ CONTINUE
+ END
+END
+
+EXPECT framebuffer IDX 0 0 SIZE 32 32 EQ_RGB 255 0 0
diff --git a/tests/cases/debugger_hlsl_basic_vertex_with_legalization.amber b/tests/cases/debugger_hlsl_basic_vertex_with_legalization.amber
new file mode 100644
index 0000000..628646a
--- /dev/null
+++ b/tests/cases/debugger_hlsl_basic_vertex_with_legalization.amber
@@ -0,0 +1,128 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET ENGINE_DATA fence_timeout_ms 1000000
+
+VIRTUAL_FILE "vs.hlsl"
+struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+ float4 color : COLOR) {
+ VS_OUTPUT vout;
+ vout.pos = pos;
+ vout.color = color;
+ return vout;
+}
+END
+
+VIRTUAL_FILE "fs.hlsl"
+float4 main(float4 color : COLOR) : SV_TARGET {
+ return color;
+}
+END
+
+SHADER vertex vtex_shader HLSL VIRTUAL_FILE "vs.hlsl"
+SHADER fragment frag_shader HLSL VIRTUAL_FILE "fs.hlsl"
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+# Full frame
+-128 -128
+ 127 127
+-128 127
+-128 -128
+ 127 127
+ 127 -128
+END
+
+BUFFER vert_color DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ FRAMEBUFFER_SIZE 32 32
+
+ ATTACH vtex_shader
+ SHADER_OPTIMIZATION vtex_shader
+ --legalize-hlsl
+ END
+
+ ATTACH frag_shader
+ SHADER_OPTIMIZATION frag_shader
+ --legalize-hlsl
+ END
+
+ VERTEX_DATA position_buf LOCATION 0
+ VERTEX_DATA vert_color LOCATION 1
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+CLEAR pipeline
+
+DEBUG pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+ THREAD VERTEX_INDEX 0
+ EXPECT LOCATION "vs.hlsl" 9 " vout.pos = pos;"
+ EXPECT LOCAL "pos.x" EQ -1.000000
+ EXPECT LOCAL "pos.y" EQ -1.000000
+ EXPECT LOCAL "pos.z" EQ 0.000000
+ EXPECT LOCAL "color.x" EQ 1.000000
+ EXPECT LOCAL "color.y" EQ 0.000000
+ EXPECT LOCAL "color.z" EQ 0.000000
+ STEP_IN
+ EXPECT LOCAL "vout.pos.x" EQ -1.000000
+ EXPECT LOCAL "vout.pos.y" EQ -1.000000
+ EXPECT LOCAL "vout.pos.z" EQ 0.000000
+ EXPECT LOCATION "vs.hlsl" 10 " vout.color = color;"
+ STEP_IN
+ EXPECT LOCAL "vout.color.x" EQ 1.000000
+ EXPECT LOCAL "vout.color.y" EQ 0.000000
+ EXPECT LOCAL "vout.color.z" EQ 0.000000
+ EXPECT LOCATION "vs.hlsl" 12 "}"
+ CONTINUE
+ END
+END
+
+EXPECT framebuffer IDX 0 0 SIZE 32 32 EQ_RGB 255 0 0
diff --git a/tests/cases/debugger_hlsl_function_call.amber b/tests/cases/debugger_hlsl_function_call.amber
new file mode 100644
index 0000000..9504b03
--- /dev/null
+++ b/tests/cases/debugger_hlsl_function_call.amber
@@ -0,0 +1,212 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET ENGINE_DATA fence_timeout_ms 1000000
+
+VIRTUAL_FILE "compute.hlsl"
+
+[[vk::binding(0)]]
+StructuredBuffer<int> data;
+
+int C(int x)
+{
+ int y = x*x;
+ return y;
+}
+
+int B(int x)
+{
+ int y = x + 2;
+ return C(y) * 2;
+}
+
+int A(int x)
+{
+ int y = B(x + 1);
+ int z = B(y) + B(y+1);
+ return x + y + z;
+}
+
+[numthreads(1,1,1)]
+void main() {
+ data[0] = A(data[0]);
+}
+END
+
+SHADER compute shader HLSL VIRTUAL_FILE "compute.hlsl"
+
+BUFFER data DATA_TYPE int32 DATA
+ 10
+END
+
+PIPELINE compute pipeline
+ ATTACH shader
+ SHADER_OPTIMIZATION shader
+ --legalize-hlsl
+ END
+ BIND BUFFER data AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+DEBUG pipeline 1 1 1
+ THREAD GLOBAL_INVOCATION_ID 0 0 0
+ EXPECT CALLSTACK
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 26 " data[0] = A(data[0]);"
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "A" "compute.hlsl" 18
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 18 "{"
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 19 " int y = B(x + 1);"
+ EXPECT LOCAL "x" EQ 10
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "B" "compute.hlsl" 12
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 12 "{"
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "B" "compute.hlsl" 13
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 13 " int y = x + 2;"
+ EXPECT LOCAL "x" EQ 11
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "B" "compute.hlsl" 14
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 14 " return C(y) * 2;"
+ EXPECT LOCAL "x" EQ 11
+ EXPECT LOCAL "y" EQ 13
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "C" "compute.hlsl" 6
+ "B" "compute.hlsl" 14
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 6 "{"
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "C" "compute.hlsl" 7
+ "B" "compute.hlsl" 14
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 7 " int y = x*x;"
+ EXPECT LOCAL "x" EQ 13
+ STEP_IN
+
+ # TODO: Returns are non-steppable. Fix
+ # EXPECT LOCATION "compute.hlsl" 8 " return y;"
+ # EXPECT LOCAL "x" EQ 11 # TODO: "x" should be visible
+ # EXPECT LOCAL "y" EQ 0
+ # STEP_IN
+ # EXPECT CALLSTACK
+ # "B" "compute.hlsl" 14
+ # "A" "compute.hlsl" 19
+ # "main" "compute.hlsl" 26
+ # END
+
+ EXPECT CALLSTACK
+ "B" "compute.hlsl" 14
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 14 " return C(y) * 2;"
+ STEP_IN
+ EXPECT CALLSTACK
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+
+ EXPECT CALLSTACK
+ "A" "compute.hlsl" 19
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 19 " int y = B(x + 1);"
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "A" "compute.hlsl" 20
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 20 " int z = B(y) + B(y+1);"
+ EXPECT LOCAL "y" EQ 338
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "B" "compute.hlsl" 12
+ "A" "compute.hlsl" 20
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 12 "{"
+ STEP_IN
+
+ EXPECT CALLSTACK
+ "B" "compute.hlsl" 13
+ "A" "compute.hlsl" 20
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 13 " int y = x + 2;"
+ STEP_OUT
+
+ EXPECT CALLSTACK
+ "A" "compute.hlsl" 20
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 20 " int z = B(y) + B(y+1);"
+ EXPECT LOCAL "y" EQ 338
+ STEP_OVER
+ STEP_OVER
+
+ EXPECT CALLSTACK
+ "A" "compute.hlsl" 21
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 21 " return x + y + z;"
+ EXPECT LOCAL "y" EQ 338
+ EXPECT LOCAL "z" EQ 463762
+ STEP_OUT
+
+ EXPECT CALLSTACK
+ "main" "compute.hlsl" 26
+ END
+ EXPECT LOCATION "compute.hlsl" 26 " data[0] = A(data[0]);"
+
+ CONTINUE
+ END
+END
+
+EXPECT data IDX 0 EQ 464110
diff --git a/tests/cases/debugger_hlsl_shadowed_vars.amber b/tests/cases/debugger_hlsl_shadowed_vars.amber
new file mode 100644
index 0000000..bf8a790
--- /dev/null
+++ b/tests/cases/debugger_hlsl_shadowed_vars.amber
@@ -0,0 +1,177 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET ENGINE_DATA fence_timeout_ms 1000000
+
+VIRTUAL_FILE "vs.hlsl"
+struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+ float4 color : COLOR) {
+ float a = 1.0;
+ float b = 2.0;
+ float c = 3.0;
+ float x = a + b + c;
+ {
+ float a = 3.0;
+ float b = 4.0;
+ x += a + b + c;
+ {
+ float a = 6.0;
+ x += a + b + c;
+ }
+ x += a + b + c;
+ }
+ x += a + b + c;
+
+ VS_OUTPUT vout;
+ vout.pos = pos;
+ vout.pos.w += x * 0.000001;
+ return vout;
+}
+END
+
+VIRTUAL_FILE "fs.hlsl"
+float4 main() : SV_TARGET {
+ return float4(1.0, 1.0, 1.0, 1.0);
+}
+END
+
+SHADER vertex vtex_shader HLSL VIRTUAL_FILE "vs.hlsl"
+SHADER fragment frag_shader HLSL VIRTUAL_FILE "fs.hlsl"
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+# Full frame
+-128 -128
+ 127 127
+-128 127
+-128 -128
+ 127 127
+ 127 -128
+END
+
+BUFFER vert_color DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+127 127 127 255
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vtex_shader
+ SHADER_OPTIMIZATION vtex_shader
+ --legalize-hlsl
+ END
+
+ ATTACH frag_shader
+ SHADER_OPTIMIZATION frag_shader
+ --legalize-hlsl
+ END
+
+ VERTEX_DATA position_buf LOCATION 0
+ VERTEX_DATA vert_color LOCATION 1
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+CLEAR pipeline
+
+DEBUG pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+ THREAD VERTEX_INDEX 0
+ EXPECT LOCATION "vs.hlsl" 7 " float a = 1.0;"
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 8 " float b = 2.0;"
+ EXPECT LOCAL "a" EQ 1.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 9 " float c = 3.0;"
+ EXPECT LOCAL "a" EQ 1.000000
+ EXPECT LOCAL "b" EQ 2.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 10 " float x = a + b + c;"
+ EXPECT LOCAL "a" EQ 1.000000
+ EXPECT LOCAL "b" EQ 2.000000
+ EXPECT LOCAL "c" EQ 3.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 12 " float a = 3.0;"
+ EXPECT LOCAL "c" EQ 3.000000
+ EXPECT LOCAL "x" EQ 6.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 13 " float b = 4.0;"
+ EXPECT LOCAL "a" EQ 3.000000
+ EXPECT LOCAL "c" EQ 3.000000
+ EXPECT LOCAL "x" EQ 6.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 14 " x += a + b + c;"
+ EXPECT LOCAL "a" EQ 3.000000
+ EXPECT LOCAL "b" EQ 4.000000
+ EXPECT LOCAL "c" EQ 3.000000
+ EXPECT LOCAL "x" EQ 6.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 16 " float a = 6.0;"
+ EXPECT LOCAL "b" EQ 4.000000
+ EXPECT LOCAL "c" EQ 3.000000
+ EXPECT LOCAL "x" EQ 16.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 17 " x += a + b + c;"
+ EXPECT LOCAL "a" EQ 6.000000
+ EXPECT LOCAL "b" EQ 4.000000
+ EXPECT LOCAL "c" EQ 3.000000
+ EXPECT LOCAL "x" EQ 16.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 19 " x += a + b + c;"
+ EXPECT LOCAL "a" EQ 3.000000
+ EXPECT LOCAL "b" EQ 4.000000
+ EXPECT LOCAL "c" EQ 3.000000
+ EXPECT LOCAL "x" EQ 29.000000
+ STEP_IN
+ EXPECT LOCATION "vs.hlsl" 21 " x += a + b + c;"
+ EXPECT LOCAL "a" EQ 1.000000
+ EXPECT LOCAL "b" EQ 2.000000
+ EXPECT LOCAL "c" EQ 3.000000
+ EXPECT LOCAL "x" EQ 39.000000
+ STEP_IN
+ EXPECT LOCAL "x" EQ 45.000000
+ CONTINUE
+ END
+END
+
+EXPECT framebuffer IDX 0 0 SIZE 250 250 EQ_RGB 255 255 255
diff --git a/tests/cases/debugger_spirv_line_stepping.amber b/tests/cases/debugger_spirv_line_stepping.amber
new file mode 100644
index 0000000..1d3e33b
--- /dev/null
+++ b/tests/cases/debugger_spirv_line_stepping.amber
@@ -0,0 +1,125 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SET ENGINE_DATA fence_timeout_ms 1000000
+
+# #version 450
+# layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+# layout(binding = 0, std430) buffer InBuffer
+# {
+# int Data[];
+# } In;
+# layout(binding = 1, std430) buffer OutBuffer
+# {
+# int Data[];
+# } Out;
+# void main()
+# {
+# Out.Data[gl_GlobalInvocationID.x] = In.Data[gl_GlobalInvocationID.x];
+# }
+SHADER compute mah_shader SPIRV-ASM
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+ OpEntryPoint GLCompute %1 "main" %2
+ OpExecutionMode %1 LocalSize 4 1 1
+ OpDecorate %3 ArrayStride 4
+ OpMemberDecorate %4 0 Offset 0
+ OpDecorate %4 BufferBlock
+ OpDecorate %5 DescriptorSet 0
+ OpDecorate %5 Binding 1
+ OpDecorate %2 BuiltIn GlobalInvocationId
+ OpDecorate %6 DescriptorSet 0
+ OpDecorate %6 Binding 0
+ %7 = OpTypeVoid
+ %8 = OpTypeFunction %7
+ %9 = OpTypeInt 32 1
+%10 = OpTypeInt 32 0
+ %3 = OpTypeRuntimeArray %9
+ %4 = OpTypeStruct %3
+%11 = OpTypePointer Uniform %4
+ %5 = OpVariable %11 Uniform
+%12 = OpConstant %9 0
+%13 = OpConstant %10 0
+%14 = OpTypeVector %10 3
+%15 = OpTypePointer Input %14
+ %2 = OpVariable %15 Input
+%16 = OpTypePointer Input %10
+ %6 = OpVariable %11 Uniform
+%17 = OpTypePointer Uniform %9
+ %1 = OpFunction %7 None %8
+%18 = OpLabel
+%19 = OpAccessChain %16 %2 %13
+%20 = OpLoad %10 %19
+%21 = OpAccessChain %17 %6 %12 %20
+%22 = OpLoad %9 %21
+%23 = OpAccessChain %17 %5 %12 %20
+ OpStore %23 %22
+ OpReturn
+ OpFunctionEnd
+END
+
+BUFFER buf_in DATA_TYPE uint32 DATA
+ 20 30 40 50 60
+END
+
+BUFFER buf_out DATA_TYPE uint32 DATA
+ 99 99 99 99 99
+END
+
+PIPELINE compute pipeline
+ ATTACH mah_shader
+
+ BIND BUFFER buf_in AS storage DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER buf_out AS storage DESCRIPTOR_SET 0 BINDING 1
+END
+
+# Only one workgroup. Having only one invocation execute ensures
+# there are no race conditions.
+DEBUG pipeline 1 1 1
+ THREAD GLOBAL_INVOCATION_ID 2 0 0
+ EXPECT CALLSTACK
+ "SPIR-V" "ComputeShader0.spvasm" 20
+ END
+ EXPECT LOCATION "ComputeShader0.spvasm" 20 "%5 = OpVariable %11 Uniform"
+ STEP_IN
+ EXPECT LOCATION "ComputeShader0.spvasm" 25 "%2 = OpVariable %15 Input"
+ STEP_IN
+ EXPECT LOCATION "ComputeShader0.spvasm" 27 "%6 = OpVariable %11 Uniform"
+ STEP_IN
+ EXPECT LOCATION "ComputeShader0.spvasm" 31 "%19 = OpAccessChain %16 %2 %13"
+ STEP_IN
+ EXPECT LOCATION "ComputeShader0.spvasm" 32 "%20 = OpLoad %10 %19"
+ STEP_IN
+ EXPECT LOCAL "%20" EQ 2
+ EXPECT LOCATION "ComputeShader0.spvasm" 33 "%21 = OpAccessChain %17 %6 %12 %20"
+ STEP_IN
+ EXPECT LOCATION "ComputeShader0.spvasm" 34 "%22 = OpLoad %9 %21"
+ STEP_IN
+ EXPECT LOCAL "%22" EQ 40
+ EXPECT LOCATION "ComputeShader0.spvasm" 35 "%23 = OpAccessChain %17 %5 %12 %20"
+ STEP_IN
+ EXPECT LOCATION "ComputeShader0.spvasm" 36 "OpStore %23 %22"
+ STEP_IN
+ EXPECT CALLSTACK
+ "SPIR-V" "ComputeShader0.spvasm" 37
+ END
+ EXPECT LOCATION "ComputeShader0.spvasm" 37 "OpReturn"
+ CONTINUE
+ END
+END
+
+EXPECT buf_in IDX 0 EQ 20 30 40 50 60
+EXPECT buf_out IDX 0 EQ 20 30 40 50 99
+
diff --git a/tests/cases/draw_array_instanced.amber b/tests/cases/draw_array_instanced.amber
new file mode 100644
index 0000000..6a78f2b
--- /dev/null
+++ b/tests/cases/draw_array_instanced.amber
@@ -0,0 +1,66 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader GLSL
+#version 430
+layout(location = 0) in vec2 position;
+layout(location = 0) out vec4 color_out;
+void main()
+{
+ gl_Position = vec4(position.x + float(gl_InstanceIndex) * 0.5, position.y, 0, 1);
+ vec4 colors[4];
+ colors[0] = vec4(1, 0, 0, 1);
+ colors[1] = vec4(0, 1, 0, 1);
+ colors[2] = vec4(0, 0, 1, 1);
+ colors[3] = vec4(0, 1, 1, 1);
+ color_out = colors[gl_InstanceIndex];
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) in vec4 color_in;
+layout(location = 0) out vec4 color_out;
+void main()
+{
+ color_out = color_in;
+}
+END
+
+BUFFER position_buf DATA_TYPE vec2<float> DATA
+-1.0 1.0
+-1.0 -1.0
+-0.5 1.0
+-0.5 -1.0
+-1.0 -1.0
+-0.5 1.0
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vtex_shader
+ ATTACH frag_shader
+
+ VERTEX_DATA position_buf LOCATION 0
+ FRAMEBUFFER_SIZE 40 40
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+RUN pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6 START_INSTANCE 0 INSTANCE_COUNT 4
+EXPECT framebuffer IDX 0 0 SIZE 10 40 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 10 0 SIZE 10 40 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 20 0 SIZE 10 40 EQ_RGBA 0 0 255 255
+EXPECT framebuffer IDX 30 0 SIZE 10 40 EQ_RGBA 0 255 255 255
diff --git a/tests/cases/draw_combined_image_sampler.amber b/tests/cases/draw_combined_image_sampler.amber
new file mode 100644
index 0000000..2b0d750
--- /dev/null
+++ b/tests/cases/draw_combined_image_sampler.amber
@@ -0,0 +1,92 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) sampler2D tex_sampler;
+void main() {
+ color_out = texture(tex_sampler, texcoords_in);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+SAMPLER sampler
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+2.0 0.0
+2.0 2.0
+0.0 2.0
+END
+
+PIPELINE graphics texgen
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+# Generate a texture with a quad at the lower right corner.
+CLEAR_COLOR texgen 0 0 255 255
+CLEAR texgen
+RUN texgen DRAW_RECT POS 128 128 SIZE 128 128
+
+# Draw the texture using a default sampler.
+CLEAR_COLOR pipeline 0 255 0 255
+CLEAR pipeline
+RUN pipeline DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 81 81 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 81 33 SIZE 1 1 EQ_RGBA 0 0 255 255
diff --git a/tests/cases/draw_grid.amber b/tests/cases/draw_grid.amber
new file mode 100644
index 0000000..8d24a11
--- /dev/null
+++ b/tests/cases/draw_grid.amber
@@ -0,0 +1,54 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 1) flat out vec4 frag_color;
+
+void main() {
+ gl_Position = position;
+ frag_color = position;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+layout(location = 1) flat in vec4 frag_color;
+void main() {
+ ivec2 iv = ivec2(frag_color.xy * 256);
+ if ((iv.x & 64) + ((iv.y / 2) & 64) == 64)
+ color_out = vec4(1, 0, 0, 1);
+ else
+ color_out = vec4(0, 1, 1, 1);
+}
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics my_pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+RUN my_pipeline DRAW_GRID POS 0 0 SIZE 250 250 CELLS 8 4
+EXPECT framebuffer IDX 0 0 SIZE 30 60 EQ_RGBA 0 255 255 255
+EXPECT framebuffer IDX 32 64 SIZE 30 60 EQ_RGBA 0 255 255 255
+EXPECT framebuffer IDX 32 0 SIZE 30 60 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 0 64 SIZE 30 60 EQ_RGBA 255 0 0 255
+
diff --git a/tests/cases/draw_grid_multiple_color_attachment.amber b/tests/cases/draw_grid_multiple_color_attachment.amber
new file mode 100644
index 0000000..4707f88
--- /dev/null
+++ b/tests/cases/draw_grid_multiple_color_attachment.amber
@@ -0,0 +1,40 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+layout(location = 1) out vec4 color_out1;
+void main() {
+ color_out = vec4(0.0, 0.499, 0.0, 0.8);
+ color_out1 = vec4(1.0, 1.0, 1.0, 1.0);
+}
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+BUFFER framebuffer1 FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics my_pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ FRAMEBUFFER_SIZE 800 600
+ BIND BUFFER framebuffer AS color LOCATION 0
+ BIND BUFFER framebuffer1 AS color LOCATION 1
+END
+
+RUN my_pipeline DRAW_GRID POS 0 0 SIZE 800 600 CELLS 91 37
+EXPECT framebuffer IDX 0 0 SIZE 800 600 EQ_RGBA 0 127 0 204
+EXPECT framebuffer1 IDX 0 0 SIZE 800 600 EQ_RGBA 255 255 255 255
diff --git a/tests/cases/draw_grid_multiple_pipeline.amber b/tests/cases/draw_grid_multiple_pipeline.amber
new file mode 100644
index 0000000..169b95e
--- /dev/null
+++ b/tests/cases/draw_grid_multiple_pipeline.amber
@@ -0,0 +1,44 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(0.0, 0.5, 0.0, 0.8);
+}
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics my_pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ FRAMEBUFFER_SIZE 800 600
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+DERIVE_PIPELINE pipeline2 FROM my_pipeline
+END
+DERIVE_PIPELINE pipeline3 FROM my_pipeline
+END
+
+RUN my_pipeline DRAW_GRID POS 0 0 SIZE 250 250 CELLS 1 2
+RUN pipeline2 DRAW_GRID POS 250 250 SIZE 250 250 CELLS 3 4
+RUN pipeline3 DRAW_GRID POS 0 250 SIZE 250 250 CELLS 5 6
+
+EXPECT framebuffer IDX 250 250 SIZE 250 250 EQ_RGBA 0 127 0 204 TOLERANCE 0 1 0 1
+EXPECT framebuffer IDX 0 0 SIZE 250 250 EQ_RGBA 0 127 0 204 TOLERANCE 0 1 0 1
+EXPECT framebuffer IDX 0 250 SIZE 250 250 EQ_RGBA 0 127 0 204 TOLERANCE 0 1 0 1
diff --git a/tests/cases/draw_grid_with_buffer.amber b/tests/cases/draw_grid_with_buffer.amber
new file mode 100644
index 0000000..a540744
--- /dev/null
+++ b/tests/cases/draw_grid_with_buffer.amber
@@ -0,0 +1,63 @@
+#!amber
+#
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DEVICE_FEATURE vertexPipelineStoresAndAtomics
+
+SHADER vertex vtex_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 0) readonly buffer block1 {
+ vec4 in_color;
+};
+
+void main() {
+ gl_Position = position;
+ frag_color = in_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+BUFFER data_buf1 DATA_TYPE vec4<float> DATA 1 1 0 1 END
+
+
+BUFFER frame FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline1
+ ATTACH vtex_shader
+ ATTACH frag_shader
+
+ FRAMEBUFFER_SIZE 800 600
+ BIND BUFFER frame AS color LOCATION 0
+ BIND BUFFER data_buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+CLEAR pipeline1
+RUN pipeline1 DRAW_GRID POS 0 0 SIZE 400 300 CELLS 128 128
+
+EXPECT frame IDX 0 0 SIZE 400 300 EQ_RGBA 255 255 0 255
diff --git a/tests/cases/draw_grid_with_two_vertex_data_attached.expect_fail.amber b/tests/cases/draw_grid_with_two_vertex_data_attached.expect_fail.amber
new file mode 100644
index 0000000..56479de
--- /dev/null
+++ b/tests/cases/draw_grid_with_two_vertex_data_attached.expect_fail.amber
@@ -0,0 +1,74 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader GLSL
+#version 430
+
+layout(location = 0) in vec3 position;
+layout(location = 1) in vec4 ver_color;
+layout(location = 0) out vec4 frag_color;
+
+
+void main() {
+ gl_Position = vec4(position, 1.0);
+ frag_color = ver_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+BUFFER position_buf DATA_TYPE vec3<float> DATA
+-1 1 0
+-1 -1 0
+ 1 1 0
+ 1 -1 0
+-1 -1 0
+ 1 1 0
+END
+
+BUFFER vert_color_buf DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vtex_shader
+ ATTACH frag_shader
+
+ VERTEX_DATA position_buf LOCATION 0
+ VERTEX_DATA vert_color_buf LOCATION 1
+
+ FRAMEBUFFER_SIZE 250 250
+ BIND BUFFER framebuffer AS color LOCATION 0
+
+END
+
+RUN pipeline DRAW_GRID POS 100 100 SIZE 100 100 CELLS 10 10
+EXPECT framebuffer IDX 100 100 SIZE 100 100 EQ_RGBA 255 0 0 255
diff --git a/tests/cases/draw_grids.amber b/tests/cases/draw_grids.amber
new file mode 100644
index 0000000..eaf383d
--- /dev/null
+++ b/tests/cases/draw_grids.amber
@@ -0,0 +1,78 @@
+#!amber
+#
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 0) readonly buffer block1 {
+ vec4 in_color;
+};
+
+void main() {
+ gl_Position = position;
+ frag_color = in_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+BUFFER data_buf1 DATA_TYPE vec4<float> DATA 1 0 0 1 END
+BUFFER data_buf2 DATA_TYPE vec4<float> DATA 0 1 0 1 END
+BUFFER data_buf3 DATA_TYPE vec4<float> DATA 0 0 1 1 END
+BUFFER data_buf4 DATA_TYPE vec4<float> DATA .5 0 .5 1 END
+
+BUFFER frame FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline1
+ ATTACH vtex_shader
+ ATTACH frag_shader
+
+ FRAMEBUFFER_SIZE 800 600
+ BIND BUFFER frame AS color LOCATION 0
+ BIND BUFFER data_buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+DERIVE_PIPELINE pipeline2 FROM pipeline1
+ BIND BUFFER data_buf2 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+DERIVE_PIPELINE pipeline3 FROM pipeline1
+ BIND BUFFER data_buf3 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+DERIVE_PIPELINE pipeline4 FROM pipeline1
+ BIND BUFFER data_buf4 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+CLEAR pipeline1
+RUN pipeline1 DRAW_GRID POS 0 0 SIZE 400 300 CELLS 2 3
+RUN pipeline2 DRAW_GRID POS 0 300 SIZE 400 300 CELLS 1 1
+RUN pipeline3 DRAW_GRID POS 400 0 SIZE 400 300 CELLS 100 100
+RUN pipeline4 DRAW_GRID POS 400 300 SIZE 400 300 CELLS 1 71
+
+EXPECT frame IDX 0 0 SIZE 400 300 EQ_RGBA 255 0 0 255
+EXPECT frame IDX 0 300 SIZE 400 300 EQ_RGBA 0 255 0 255
+EXPECT frame IDX 400 0 SIZE 400 300 EQ_RGBA 0 0 255 255
+EXPECT frame IDX 400 300 SIZE 400 300 EQ_RGBA 127 0 127 255 TOLERANCE 1 0 1 0
diff --git a/tests/cases/draw_png_texture.amber b/tests/cases/draw_png_texture.amber
new file mode 100644
index 0000000..b314624
--- /dev/null
+++ b/tests/cases/draw_png_texture.amber
@@ -0,0 +1,66 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) sampler2D tex_sampler;
+void main() {
+ color_out = texture(tex_sampler, texcoords_in);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM FILE PNG texture.png
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+SAMPLER sampler
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+1.0 0.0
+1.0 1.0
+0.0 1.0
+END
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+# Draw the texture using a default sampler.
+CLEAR_COLOR pipeline 0 255 0 255
+CLEAR pipeline
+RUN pipeline DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
diff --git a/tests/cases/draw_polygon_mode.amber b/tests/cases/draw_polygon_mode.amber
new file mode 100644
index 0000000..70ae15b
--- /dev/null
+++ b/tests/cases/draw_polygon_mode.amber
@@ -0,0 +1,90 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DEVICE_FEATURE fillModeNonSolid
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) out vec4 color;
+
+void main()
+{
+ color = vec4(1);
+}
+END
+
+BUFFER position_large DATA_TYPE R8G8_SNORM DATA
+-120 -120
+ 0 120
+ 120 -120
+END
+
+BUFFER position_small DATA_TYPE R8G8_SNORM DATA
+-60 -60
+ 0 60
+ 60 -60
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline_line
+ ATTACH vert_shader
+ ATTACH frag_shader
+
+ VERTEX_DATA position_large LOCATION 0
+ POLYGON_MODE line
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 256 256
+END
+
+PIPELINE graphics pipeline_point
+ ATTACH vert_shader
+ ATTACH frag_shader
+
+ VERTEX_DATA position_small LOCATION 0
+ POLYGON_MODE point
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 256 256
+END
+
+PIPELINE graphics pipeline_line_grid
+ ATTACH vert_shader
+ ATTACH frag_shader
+ POLYGON_MODE line
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 256 256
+END
+
+CLEAR_COLOR pipeline_line 0 0 0 255
+CLEAR pipeline_line
+# Draw a large triangle with lines
+RUN pipeline_line DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 3
+# Draw a rect with points
+RUN pipeline_point DRAW_RECT POS 20 20 SIZE 216 216
+# Draw a small triangle with points
+RUN pipeline_point DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 3
+# Draw a line grid overlapping the small triangle
+RUN pipeline_line_grid DRAW_GRID POS 80 40 SIZE 96 96 CELLS 3 3
+
+# Make sure no solid triangle was drawn by checking the center
+# of the middle grid triangle. That location is covered by all
+# drawn primitives.
+EXPECT framebuffer IDX 134 82 SIZE 5 5 EQ_RGBA 0 0 0 255
diff --git a/tests/cases/draw_rect_and_ortho.vkscript b/tests/cases/draw_rect_and_ortho.vkscript
index 155c02b..9f97264 100644
--- a/tests/cases/draw_rect_and_ortho.vkscript
+++ b/tests/cases/draw_rect_and_ortho.vkscript
@@ -1,3 +1,17 @@
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
[require]
vertexPipelineStoresAndAtomics
diff --git a/tests/cases/draw_rect_multiple_pipeline.amber b/tests/cases/draw_rect_multiple_pipeline.amber
index 3f9cbb0..40efab0 100644
--- a/tests/cases/draw_rect_multiple_pipeline.amber
+++ b/tests/cases/draw_rect_multiple_pipeline.amber
@@ -39,6 +39,6 @@ RUN my_pipeline DRAW_RECT POS 0 0 SIZE 250 250
RUN pipeline2 DRAW_RECT POS 250 250 SIZE 250 250
RUN pipeline3 DRAW_RECT POS 0 250 SIZE 250 250
-EXPECT framebuffer IDX 250 250 SIZE 250 250 EQ_RGBA 0 127 0 204
-EXPECT framebuffer IDX 0 0 SIZE 250 250 EQ_RGBA 0 127 0 204
-EXPECT framebuffer IDX 0 250 SIZE 250 250 EQ_RGBA 0 127 0 204
+EXPECT framebuffer IDX 250 250 SIZE 250 250 EQ_RGBA 0 127 0 204 TOLERANCE 0 1 0 1
+EXPECT framebuffer IDX 0 0 SIZE 250 250 EQ_RGBA 0 127 0 204 TOLERANCE 0 1 0 1
+EXPECT framebuffer IDX 0 250 SIZE 250 250 EQ_RGBA 0 127 0 204 TOLERANCE 0 1 0 1
diff --git a/tests/cases/draw_rectangles.amber b/tests/cases/draw_rectangles.amber
index 2807eb5..21f0260 100644
--- a/tests/cases/draw_rectangles.amber
+++ b/tests/cases/draw_rectangles.amber
@@ -75,4 +75,4 @@ RUN pipeline4 DRAW_RECT POS 400 300 SIZE 400 300
EXPECT frame IDX 0 0 SIZE 400 300 EQ_RGBA 255 0 0 255
EXPECT frame IDX 0 300 SIZE 400 300 EQ_RGBA 0 255 0 255
EXPECT frame IDX 400 0 SIZE 400 300 EQ_RGBA 0 0 255 255
-EXPECT frame IDX 400 300 SIZE 400 300 EQ_RGBA 127 0 127 255
+EXPECT frame IDX 400 300 SIZE 400 300 EQ_RGBA 127 0 127 255 TOLERANCE 1 0 1 0
diff --git a/tests/cases/draw_rectangles_depth_test.amber b/tests/cases/draw_rectangles_depth_test.amber
new file mode 100644
index 0000000..797e99b
--- /dev/null
+++ b/tests/cases/draw_rectangles_depth_test.amber
@@ -0,0 +1,136 @@
+#!amber
+#
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 0) readonly buffer block1 {
+ vec4 in_color;
+ float depth;
+};
+
+void main() {
+ gl_Position = vec4(position.xy, depth, 1.0);
+ frag_color = in_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) sampler2D tex_sampler;
+void main() {
+ float f = texture(tex_sampler, texcoords_in).r;
+ color_out = vec4(f, f, f, 1);
+}
+END
+
+BUFFER data_buf1 DATA_TYPE float DATA 1.0 0.0 0.0 1.0 0.3 END
+BUFFER data_buf2 DATA_TYPE float DATA 0.0 1.0 0.0 1.0 0.5 END
+
+BUFFER position DATA_TYPE vec2<float> DATA
+-1.0 -1.0
+ 1.0 -1.0
+ 1.0 1.0
+-1.0 1.0
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+1.0 0.0
+1.0 1.0
+0.0 1.0
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+BUFFER ddump FORMAT B8G8R8A8_UNORM
+BUFFER depthstencil FORMAT D32_SFLOAT_S8_UINT
+
+SAMPLER sampler
+
+PIPELINE graphics pipeline1
+ ATTACH vert_shader
+ ATTACH frag_shader
+
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+ BIND BUFFER depthstencil AS depth_stencil
+ BIND BUFFER data_buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+
+ DEPTH
+ TEST on
+ WRITE on
+ COMPARE_OP less
+ CLAMP off
+ BOUNDS min 0.0 max 1.0
+ BIAS constant 0.0 clamp 0.0 slope 0.0
+ END
+END
+
+DERIVE_PIPELINE pipeline2 FROM pipeline1
+ BIND BUFFER data_buf2 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+PIPELINE graphics depthdump
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER depthstencil AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER ddump AS color LOCATION 0
+END
+
+CLEAR_DEPTH pipeline1 1.0
+CLEAR_COLOR pipeline1 255 255 255 255
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 0 0 SIZE 200 200
+RUN pipeline2 DRAW_RECT POS 56 56 SIZE 200 200
+RUN depthdump DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 0 0 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 128 128 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 255 255 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT depthstencil IDX 0 EQ 0.3
+EXPECT ddump IDX 0 0 SIZE 1 1 EQ_RGBA 76 76 76 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 255 0 SIZE 1 1 EQ_RGBA 255 255 255 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 0 255 SIZE 1 1 EQ_RGBA 255 255 255 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 255 255 SIZE 1 1 EQ_RGBA 128 128 128 255 TOLERANCE 5% 5% 5% 0
diff --git a/tests/cases/draw_rectangles_depth_test_d24s8.amber b/tests/cases/draw_rectangles_depth_test_d24s8.amber
new file mode 100644
index 0000000..75747a2
--- /dev/null
+++ b/tests/cases/draw_rectangles_depth_test_d24s8.amber
@@ -0,0 +1,135 @@
+#!amber
+#
+# Copyright 2021 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 0) readonly buffer block1 {
+ vec4 in_color;
+ float depth;
+};
+
+void main() {
+ gl_Position = vec4(position.xy, depth, 1.0);
+ frag_color = in_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) sampler2D tex_sampler;
+void main() {
+ float f = texture(tex_sampler, texcoords_in).r;
+ color_out = vec4(f, f, f, 1);
+}
+END
+
+BUFFER data_buf1 DATA_TYPE float DATA 1.0 0.0 0.0 1.0 0.3 END
+BUFFER data_buf2 DATA_TYPE float DATA 0.0 1.0 0.0 1.0 0.5 END
+
+BUFFER position DATA_TYPE vec2<float> DATA
+-1.0 -1.0
+ 1.0 -1.0
+ 1.0 1.0
+-1.0 1.0
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+1.0 0.0
+1.0 1.0
+0.0 1.0
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+BUFFER ddump FORMAT B8G8R8A8_UNORM
+BUFFER depthstencil FORMAT D24_UNORM_S8_UINT
+
+SAMPLER sampler
+
+PIPELINE graphics pipeline1
+ ATTACH vert_shader
+ ATTACH frag_shader
+
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+ BIND BUFFER depthstencil AS depth_stencil
+ BIND BUFFER data_buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+
+ DEPTH
+ TEST on
+ WRITE on
+ COMPARE_OP less
+ CLAMP off
+ BOUNDS min 0.0 max 1.0
+ BIAS constant 0.0 clamp 0.0 slope 0.0
+ END
+END
+
+DERIVE_PIPELINE pipeline2 FROM pipeline1
+ BIND BUFFER data_buf2 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+PIPELINE graphics depthdump
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER depthstencil AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER ddump AS color LOCATION 0
+END
+
+CLEAR_DEPTH pipeline1 1.0
+CLEAR_COLOR pipeline1 255 255 255 255
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 0 0 SIZE 200 200
+RUN pipeline2 DRAW_RECT POS 56 56 SIZE 200 200
+RUN depthdump DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 0 0 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 128 128 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 255 255 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT ddump IDX 0 0 SIZE 1 1 EQ_RGBA 76 76 76 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 255 0 SIZE 1 1 EQ_RGBA 255 255 255 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 0 255 SIZE 1 1 EQ_RGBA 255 255 255 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 255 255 SIZE 1 1 EQ_RGBA 128 128 128 255 TOLERANCE 5% 5% 5% 0
diff --git a/tests/cases/draw_rectangles_depth_test_x8d24.amber b/tests/cases/draw_rectangles_depth_test_x8d24.amber
new file mode 100644
index 0000000..b46bb64
--- /dev/null
+++ b/tests/cases/draw_rectangles_depth_test_x8d24.amber
@@ -0,0 +1,135 @@
+#!amber
+#
+# Copyright 2021 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 0) readonly buffer block1 {
+ vec4 in_color;
+ float depth;
+};
+
+void main() {
+ gl_Position = vec4(position.xy, depth, 1.0);
+ frag_color = in_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) sampler2D tex_sampler;
+void main() {
+ float f = texture(tex_sampler, texcoords_in).r;
+ color_out = vec4(f, f, f, 1);
+}
+END
+
+BUFFER data_buf1 DATA_TYPE float DATA 1.0 0.0 0.0 1.0 0.3 END
+BUFFER data_buf2 DATA_TYPE float DATA 0.0 1.0 0.0 1.0 0.5 END
+
+BUFFER position DATA_TYPE vec2<float> DATA
+-1.0 -1.0
+ 1.0 -1.0
+ 1.0 1.0
+-1.0 1.0
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+1.0 0.0
+1.0 1.0
+0.0 1.0
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+BUFFER ddump FORMAT B8G8R8A8_UNORM
+BUFFER depthstencil FORMAT X8_D24_UNORM_PACK32
+
+SAMPLER sampler
+
+PIPELINE graphics pipeline1
+ ATTACH vert_shader
+ ATTACH frag_shader
+
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+ BIND BUFFER depthstencil AS depth_stencil
+ BIND BUFFER data_buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+
+ DEPTH
+ TEST on
+ WRITE on
+ COMPARE_OP less
+ CLAMP off
+ BOUNDS min 0.0 max 1.0
+ BIAS constant 0.0 clamp 0.0 slope 0.0
+ END
+END
+
+DERIVE_PIPELINE pipeline2 FROM pipeline1
+ BIND BUFFER data_buf2 AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+PIPELINE graphics depthdump
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER depthstencil AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER ddump AS color LOCATION 0
+END
+
+CLEAR_DEPTH pipeline1 1.0
+CLEAR_COLOR pipeline1 255 255 255 255
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 0 0 SIZE 200 200
+RUN pipeline2 DRAW_RECT POS 56 56 SIZE 200 200
+RUN depthdump DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 0 0 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 128 128 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 255 255 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT ddump IDX 0 0 SIZE 1 1 EQ_RGBA 76 76 76 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 255 0 SIZE 1 1 EQ_RGBA 255 255 255 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 0 255 SIZE 1 1 EQ_RGBA 255 255 255 255 TOLERANCE 5% 5% 5% 0
+EXPECT ddump IDX 255 255 SIZE 1 1 EQ_RGBA 128 128 128 255 TOLERANCE 5% 5% 5% 0
diff --git a/tests/cases/draw_rectangles_stencil_test.amber b/tests/cases/draw_rectangles_stencil_test.amber
new file mode 100644
index 0000000..d9790bf
--- /dev/null
+++ b/tests/cases/draw_rectangles_stencil_test.amber
@@ -0,0 +1,149 @@
+#!amber
+#
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 0) readonly buffer block1 {
+ vec4 in_color;
+};
+
+void main() {
+ gl_Position = position;
+ frag_color = in_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+BUFFER data_buf1 DATA_TYPE float DATA 1.0 0.0 0.0 1.0 END
+BUFFER data_buf2 DATA_TYPE float DATA 0.0 1.0 0.0 1.0 END
+BUFFER data_buf3 DATA_TYPE float DATA 0.0 0.0 1.0 1.0 END
+BUFFER data_buf4 DATA_TYPE float DATA 1.0 0.0 1.0 1.0 END
+BUFFER data_buf5 DATA_TYPE float DATA 1.0 1.0 0.0 1.0 END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+BUFFER depthstencil FORMAT D32_SFLOAT_S8_UINT
+
+SAMPLER sampler
+
+PIPELINE graphics pipeline1
+ ATTACH vert_shader
+ ATTACH frag_shader
+
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+ BIND BUFFER depthstencil AS depth_stencil
+ BIND BUFFER data_buf1 AS storage DESCRIPTOR_SET 0 BINDING 0
+
+ STENCIL front_and_back
+ TEST on
+ FAIL_OP replace
+ PASS_OP replace
+ DEPTH_FAIL_OP keep
+ COMPARE_OP always
+ COMPARE_MASK 255
+ WRITE_MASK 255
+ REFERENCE 32
+ END
+END
+
+DERIVE_PIPELINE pipeline2 FROM pipeline1
+ BIND BUFFER data_buf2 AS storage DESCRIPTOR_SET 0 BINDING 0
+
+ STENCIL front_and_back
+ TEST on
+ FAIL_OP increment_and_clamp
+ PASS_OP invert
+ DEPTH_FAIL_OP keep
+ COMPARE_OP equal
+ COMPARE_MASK 255
+ WRITE_MASK 255
+ REFERENCE 32
+ END
+END
+
+DERIVE_PIPELINE pipeline3 FROM pipeline1
+ BIND BUFFER data_buf3 AS storage DESCRIPTOR_SET 0 BINDING 0
+
+ STENCIL front_and_back
+ TEST on
+ FAIL_OP keep
+ PASS_OP keep
+ DEPTH_FAIL_OP keep
+ COMPARE_OP equal
+ COMPARE_MASK 255
+ WRITE_MASK 255
+ REFERENCE 32
+ END
+END
+
+DERIVE_PIPELINE pipeline4 FROM pipeline1
+ BIND BUFFER data_buf4 AS storage DESCRIPTOR_SET 0 BINDING 0
+
+ STENCIL front_and_back
+ TEST on
+ FAIL_OP keep
+ PASS_OP keep
+ DEPTH_FAIL_OP keep
+ COMPARE_OP equal
+ COMPARE_MASK 255
+ WRITE_MASK 255
+ REFERENCE 1
+ END
+END
+
+DERIVE_PIPELINE pipeline5 FROM pipeline1
+ BIND BUFFER data_buf5 AS storage DESCRIPTOR_SET 0 BINDING 0
+
+ STENCIL front_and_back
+ TEST on
+ FAIL_OP keep
+ PASS_OP keep
+ DEPTH_FAIL_OP keep
+ COMPARE_OP equal
+ COMPARE_MASK 255
+ WRITE_MASK 255
+ REFERENCE 223
+ END
+END
+
+CLEAR_STENCIL pipeline1 0
+CLEAR_COLOR pipeline1 255 255 255 255
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 0 0 SIZE 200 200
+RUN pipeline2 DRAW_RECT POS 56 56 SIZE 200 200
+RUN pipeline3 DRAW_RECT POS 0 0 SIZE 256 256
+RUN pipeline4 DRAW_RECT POS 0 0 SIZE 256 256
+RUN pipeline5 DRAW_RECT POS 0 0 SIZE 256 256
+
+EXPECT framebuffer IDX 0 0 SIZE 1 1 EQ_RGBA 0 0 255 255
+EXPECT framebuffer IDX 128 128 SIZE 1 1 EQ_RGBA 255 255 0 255
+EXPECT framebuffer IDX 255 255 SIZE 1 1 EQ_RGBA 255 0 255 255
+EXPECT framebuffer IDX 255 0 SIZE 1 1 EQ_RGBA 255 255 255 255
+EXPECT framebuffer IDX 0 255 SIZE 1 1 EQ_RGBA 255 255 255 255
diff --git a/tests/cases/draw_sampled_image.amber b/tests/cases/draw_sampled_image.amber
new file mode 100644
index 0000000..7e27736
--- /dev/null
+++ b/tests/cases/draw_sampled_image.amber
@@ -0,0 +1,115 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) texture2D tex;
+uniform layout(set=0, binding=1) sampler tex_sampler;
+void main() {
+ color_out = texture(sampler2D(tex, tex_sampler), texcoords_in);
+}
+END
+
+SHADER compute compute_shader GLSL
+#version 430
+layout(local_size_x=16,local_size_y=16) in;
+uniform layout (set=0, binding=0, rgba8) image2D texture;
+void main () {
+ ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
+ vec4 color = imageLoad(texture, uv) + vec4(0, 1.0, 0, 0);
+ imageStore(texture, uv, color);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+SAMPLER sampler
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+2.0 0.0
+2.0 2.0
+0.0 2.0
+END
+
+PIPELINE graphics pipeline1
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline2
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+PIPELINE compute pipeline3
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET 0 BINDING 0
+ FRAMEBUFFER_SIZE 256 256
+END
+
+# Generate a texture with a quad at the lower right corner.
+CLEAR_COLOR pipeline1 0 0 255 255
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 128 128 SIZE 128 128
+
+# Add green color to a 128x128 quad.
+RUN pipeline3 8 8 1
+
+# Draw the texture using a default sampler.
+CLEAR_COLOR pipeline2 0 255 0 255
+CLEAR pipeline2
+RUN pipeline2 DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 33 33 SIZE 1 1 EQ_RGBA 0 255 255 255
+EXPECT framebuffer IDX 81 81 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 81 33 SIZE 1 1 EQ_RGBA 0 0 255 255
diff --git a/tests/cases/draw_storageimage.amber b/tests/cases/draw_storageimage.amber
new file mode 100644
index 0000000..797fdf6
--- /dev/null
+++ b/tests/cases/draw_storageimage.amber
@@ -0,0 +1,80 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0, rgba8) readonly image2D texture;
+void main() {
+ ivec2 uv = ivec2(gl_FragCoord.xy);
+ color_out = imageLoad(texture, ivec2(gl_FragCoord.xy));
+}
+END
+SHADER compute compute_shader GLSL
+#version 430
+layout(local_size_x=10,local_size_y=10) in;
+uniform layout (set=0, binding=0, rgba8) image2D texture;
+
+void main () {
+ ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
+ vec4 color = imageLoad(texture, uv) + vec4(0, 1.0, 0, 0);
+ imageStore(texture, uv, color);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline1
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline2
+ ATTACH vert_shader
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET 0 BINDING 0
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+PIPELINE compute pipeline3
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET 0 BINDING 0
+ FRAMEBUFFER_SIZE 256 256
+END
+
+CLEAR_COLOR pipeline1 0 0 255 255
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 50 50 SIZE 100 100
+RUN pipeline3 5 5 1
+CLEAR_COLOR pipeline2 0 255 0 255
+CLEAR pipeline2
+RUN pipeline2 DRAW_RECT POS 20 20 SIZE 170 170
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 21 21 SIZE 1 1 EQ_RGBA 0 255 255 255
+EXPECT framebuffer IDX 51 51 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 189 189 SIZE 1 1 EQ_RGBA 0 0 255 255
diff --git a/tests/cases/draw_storageimage_multisample.amber b/tests/cases/draw_storageimage_multisample.amber
new file mode 100644
index 0000000..6ebebb5
--- /dev/null
+++ b/tests/cases/draw_storageimage_multisample.amber
@@ -0,0 +1,61 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DEVICE_FEATURE shaderStorageImageMultisample
+
+SHADER compute compute_shader GLSL
+#version 430
+
+layout(local_size_x = 16, local_size_y = 16) in;
+uniform layout(set=0, binding=0, rgba8) image2DMS texture;
+uniform layout(set=0, binding=1, rgba8) image2D sample0;
+uniform layout(set=0, binding=2, rgba8) image2D sample1;
+uniform layout(set=0, binding=3, rgba8) image2D sample2;
+uniform layout(set=0, binding=4, rgba8) image2D sample3;
+void main()
+{
+ ivec2 uv = ivec2(gl_GlobalInvocationID.xy);
+ imageStore(texture, uv, 0, vec4(1, 0, 0, 1));
+ imageStore(texture, uv, 1, vec4(0, 1, 0, 1));
+ imageStore(texture, uv, 2, vec4(0, 0, 1, 1));
+ imageStore(texture, uv, 3, vec4(1, 1, 0, 1));
+ imageStore(sample0, uv, imageLoad(texture, uv, 3));
+ imageStore(sample1, uv, imageLoad(texture, uv, 2));
+ imageStore(sample2, uv, imageLoad(texture, uv, 1));
+ imageStore(sample3, uv, imageLoad(texture, uv, 0));
+}
+END
+
+IMAGE texture FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256 SAMPLES 4
+IMAGE sample0 FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+IMAGE sample1 FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+IMAGE sample2 FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+IMAGE sample3 FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texture AS storage_image DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER sample0 AS storage_image DESCRIPTOR_SET 0 BINDING 1
+ BIND BUFFER sample1 AS storage_image DESCRIPTOR_SET 0 BINDING 2
+ BIND BUFFER sample2 AS storage_image DESCRIPTOR_SET 0 BINDING 3
+ BIND BUFFER sample3 AS storage_image DESCRIPTOR_SET 0 BINDING 4
+END
+
+RUN pipeline 16 16 1
+
+EXPECT sample0 IDX 0 0 SIZE 256 256 EQ_RGBA 255 255 0 255
+EXPECT sample1 IDX 0 0 SIZE 256 256 EQ_RGBA 0 0 255 255
+EXPECT sample2 IDX 0 0 SIZE 256 256 EQ_RGBA 0 255 0 255
+EXPECT sample3 IDX 0 0 SIZE 256 256 EQ_RGBA 255 0 0 255
diff --git a/tests/cases/draw_storagetexelbuffer.amber b/tests/cases/draw_storagetexelbuffer.amber
new file mode 100644
index 0000000..285c08e
--- /dev/null
+++ b/tests/cases/draw_storagetexelbuffer.amber
@@ -0,0 +1,53 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute compute_shader GLSL
+#version 430
+layout(local_size_x=4,local_size_y=1) in;
+uniform layout(set=0, binding=0) samplerBuffer texelsIn;
+uniform layout(set=0, binding=1, rgba32f) imageBuffer texelsOut;
+
+void main ()
+{
+ vec4 color = texelFetch(texelsIn, int(gl_GlobalInvocationID));
+ imageStore(texelsOut, int(gl_GlobalInvocationID), color);
+}
+END
+
+BUFFER texel_buffer_in DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+ 0 255 0 255
+ 0 0 255 255
+ 0 255 255 255
+END
+
+BUFFER texel_buffer_out DATA_TYPE R32G32B32A32_SFLOAT SIZE 4 FILL 0
+
+BUFFER ref DATA_TYPE R32G32B32A32_SFLOAT DATA
+1.0 0.0 0.0 1.0
+0.0 1.0 0.0 1.0
+0.0 0.0 1.0 1.0
+0.0 1.0 1.0 1.0
+END
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+ BIND BUFFER texel_buffer_in AS uniform_texel_buffer DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER texel_buffer_out AS storage_texel_buffer DESCRIPTOR_SET 0 BINDING 1
+END
+
+RUN pipeline 1 1 1
+
+EXPECT texel_buffer_out EQ_BUFFER ref
diff --git a/tests/cases/draw_triangle_list_format.amber b/tests/cases/draw_triangle_list_format.amber
new file mode 100644
index 0000000..894a9fb
--- /dev/null
+++ b/tests/cases/draw_triangle_list_format.amber
@@ -0,0 +1,91 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec4 vert_color;
+layout(location = 0) out vec4 frag_color;
+
+void main() {
+ gl_Position = position;
+ frag_color = vert_color;
+}
+END
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+-128 -128
+ 0 127
+-128 127
+-128 -128
+ 0 127
+ 0 -128
+
+ 0 -128
+ 127 127
+ 0 127
+ 0 -128
+ 127 127
+ 127 -128
+END
+
+BUFFER vert_color DATA_TYPE R8G8B8A8_UNORM DATA
+255 255 255 255
+255 255 255 255
+255 255 255 255
+255 255 255 255
+255 255 255 255
+255 255 255 255
+255 255 255 255
+END
+
+IMAGE framebuffer FORMAT B8G8R8A8_UNORM DIM_2D WIDTH 64 HEIGHT 64
+
+PIPELINE graphics pipeline0
+ ATTACH vtex_shader
+ ATTACH frag_shader
+ VERTEX_DATA position_buf LOCATION 0
+ VERTEX_DATA vert_color LOCATION 1
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 64 64
+END
+
+PIPELINE graphics pipeline1
+ ATTACH vtex_shader
+ ATTACH frag_shader
+ VERTEX_DATA position_buf LOCATION 0 OFFSET 12
+ VERTEX_DATA vert_color LOCATION 1 FORMAT R8_UNORM
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 64 64
+END
+
+CLEAR pipeline0
+
+RUN pipeline0 DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+RUN pipeline1 DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+
+EXPECT framebuffer IDX 0 0 SIZE 32 64 EQ_RGBA 255 255 255 255
+EXPECT framebuffer IDX 32 0 SIZE 32 64 EQ_RGBA 255 0 0 255
diff --git a/tests/cases/draw_triangle_list_offset.amber b/tests/cases/draw_triangle_list_offset.amber
new file mode 100644
index 0000000..e0d3777
--- /dev/null
+++ b/tests/cases/draw_triangle_list_offset.amber
@@ -0,0 +1,147 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec4 vert_color;
+layout(location = 0) out vec4 frag_color;
+
+void main() {
+ gl_Position = position;
+ frag_color = vert_color;
+}
+END
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+-128 -128
+ 0 0
+-128 0
+-128 -128
+ 0 0
+ 0 -128
+
+ 0 -128
+ 127 0
+ 0 0
+ 0 -128
+ 127 0
+ 127 -128
+
+ 0 0
+ 127 127
+ 0 127
+ 0 0
+ 127 127
+ 127 0
+
+-128 0
+ 0 127
+-128 127
+-128 0
+ 0 127
+ 0 0
+END
+
+BUFFER vert_color DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+ 0 0 255 255
+
+ 0 255 255 255
+ 0 255 255 255
+ 0 255 255 255
+ 0 255 255 255
+ 0 255 255 255
+ 0 255 255 255
+END
+
+IMAGE framebuffer FORMAT B8G8R8A8_UNORM DIM_2D WIDTH 64 HEIGHT 64
+
+PIPELINE graphics pipeline0
+ ATTACH vtex_shader
+ ATTACH frag_shader
+ VERTEX_DATA position_buf LOCATION 0
+ VERTEX_DATA vert_color LOCATION 1
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 64 64
+END
+
+PIPELINE graphics pipeline1
+ ATTACH vtex_shader
+ ATTACH frag_shader
+ VERTEX_DATA position_buf LOCATION 0 OFFSET 12
+ VERTEX_DATA vert_color LOCATION 1 OFFSET 24
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 64 64
+END
+
+PIPELINE graphics pipeline2
+ ATTACH vtex_shader
+ ATTACH frag_shader
+ VERTEX_DATA position_buf LOCATION 0 OFFSET 24
+ VERTEX_DATA vert_color LOCATION 1 OFFSET 48
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 64 64
+END
+
+PIPELINE graphics pipeline3
+ ATTACH vtex_shader
+ ATTACH frag_shader
+ VERTEX_DATA position_buf LOCATION 0 OFFSET 36
+ VERTEX_DATA vert_color LOCATION 1 OFFSET 72
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 64 64
+END
+
+CLEAR pipeline0
+
+RUN pipeline0 DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+RUN pipeline1 DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+RUN pipeline2 DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+RUN pipeline3 DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+
+EXPECT framebuffer IDX 0 0 SIZE 32 32 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 32 0 SIZE 32 32 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 32 32 SIZE 32 32 EQ_RGBA 0 0 255 255
+EXPECT framebuffer IDX 0 32 SIZE 32 32 EQ_RGBA 0 255 255 255
diff --git a/tests/cases/draw_triangle_list_stride.amber b/tests/cases/draw_triangle_list_stride.amber
new file mode 100644
index 0000000..49c1d46
--- /dev/null
+++ b/tests/cases/draw_triangle_list_stride.amber
@@ -0,0 +1,106 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec4 vert_color;
+layout(location = 0) out vec4 frag_color;
+
+void main() {
+ gl_Position = position;
+ frag_color = vert_color;
+}
+END
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main() {
+ final_color = frag_color;
+}
+END
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+-128 -128
+ 0 127
+-128 127
+-128 -128
+ 0 127
+ 0 -128
+
+ 0 -128
+ 127 127
+ 0 127
+ 0 -128
+ 127 127
+ 127 -128
+END
+
+BUFFER vert_color DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+ 0 255 0 255
+255 0 0 255
+ 0 0 0 255
+255 0 0 255
+ 0 255 0 255
+255 0 0 255
+ 0 0 0 255
+255 0 0 255
+ 0 255 0 255
+255 0 0 255
+ 0 0 0 255
+255 0 0 255
+ 0 255 0 255
+255 0 0 255
+ 0 0 0 255
+255 0 0 255
+ 0 255 0 255
+255 0 0 255
+ 0 0 0 255
+255 0 0 255
+ 0 255 0 255
+END
+
+IMAGE framebuffer FORMAT B8G8R8A8_UNORM DIM_2D WIDTH 64 HEIGHT 64
+
+PIPELINE graphics pipeline0
+ ATTACH vtex_shader
+ ATTACH frag_shader
+ VERTEX_DATA position_buf LOCATION 0
+ VERTEX_DATA vert_color LOCATION 1 STRIDE 8
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 64 64
+END
+
+PIPELINE graphics pipeline1
+ ATTACH vtex_shader
+ ATTACH frag_shader
+ VERTEX_DATA position_buf LOCATION 0 OFFSET 12
+ VERTEX_DATA vert_color LOCATION 1 OFFSET 4 STRIDE 16
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 64 64
+END
+
+CLEAR pipeline0
+
+RUN pipeline0 DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+RUN pipeline1 DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+
+EXPECT framebuffer IDX 0 0 SIZE 32 64 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 32 0 SIZE 32 64 EQ_RGBA 0 255 0 255
diff --git a/tests/cases/draw_uniformtexelbuffer.amber b/tests/cases/draw_uniformtexelbuffer.amber
new file mode 100644
index 0000000..4ece208
--- /dev/null
+++ b/tests/cases/draw_uniformtexelbuffer.amber
@@ -0,0 +1,56 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) samplerBuffer texels;
+
+void main()
+{
+ color_out = texelFetch(texels, int(gl_FragCoord.x) % 4);
+}
+END
+
+BUFFER texel_buffer DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+ 0 255 0 255
+ 0 0 255 255
+ 0 255 255 255
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texel_buffer AS uniform_texel_buffer DESCRIPTOR_SET 0 BINDING 0
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+CLEAR_COLOR pipeline 0 0 255 255
+CLEAR pipeline
+RUN pipeline DRAW_RECT POS 50 50 SIZE 100 100
+
+EXPECT framebuffer IDX 50 50 SIZE 1 100 EQ_RGBA 0 0 255 255
+EXPECT framebuffer IDX 51 50 SIZE 1 100 EQ_RGBA 0 255 255 255
+EXPECT framebuffer IDX 52 50 SIZE 1 100 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 53 50 SIZE 1 100 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 54 50 SIZE 1 100 EQ_RGBA 0 0 255 255
+EXPECT framebuffer IDX 55 50 SIZE 1 100 EQ_RGBA 0 255 255 255
+EXPECT framebuffer IDX 56 50 SIZE 1 100 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 57 50 SIZE 1 100 EQ_RGBA 0 255 0 255
diff --git a/tests/cases/float16.amber b/tests/cases/float16.amber
new file mode 100644
index 0000000..88baf13
--- /dev/null
+++ b/tests/cases/float16.amber
@@ -0,0 +1,50 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+INSTANCE_EXTENSION VK_KHR_get_physical_device_properties2
+DEVICE_EXTENSION VK_KHR_shader_float16_int8
+DEVICE_FEATURE Float16Int8Features.shaderFloat16
+
+SHADER compute f16 GLSL
+#version 450
+#extension GL_AMD_gpu_shader_half_float : enable
+
+layout(set=0, binding=0) buffer Buf {
+ float16_t h[3];
+} data;
+
+void main() {
+ int idx = int(gl_GlobalInvocationID.x);
+ data.h[idx] = data.h[idx] * 2.0hf;
+}
+END
+
+BUFFER buf DATA_TYPE float16 DATA
+-0.0
+2.4
+-2.4
+END
+
+PIPELINE compute pipeline
+ ATTACH f16
+
+ BIND BUFFER buf AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+RUN pipeline 3 1 1
+
+EXPECT buf IDX 0 TOLERANCE 0.1 EQ 0.0
+EXPECT buf IDX 2 TOLERANCE 0.1 EQ 4.8
+EXPECT buf IDX 4 TOLERANCE 0.1 EQ -4.8
diff --git a/tests/cases/glsl_read_and_write_image3d_rgba32i.amber b/tests/cases/glsl_read_and_write_image3d_rgba32i.amber
new file mode 100644
index 0000000..75e8d8b
--- /dev/null
+++ b/tests/cases/glsl_read_and_write_image3d_rgba32i.amber
@@ -0,0 +1,76 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute write GLSL
+#version 450
+layout(set=0, binding=0, rgba32i) uniform iimage3D im3d;
+void main() {
+ uvec3 gid = gl_GlobalInvocationID;
+ ivec3 coord = ivec3(gid.x, gid.y, gid.z);
+ ivec4 data = ivec4(gid.x + 1, gid.y + 1, gid.z + 1, 0);
+ imageStore(im3d, coord, data);
+}
+END
+
+SHADER compute read GLSL
+#version 450
+
+layout(set=0, binding=0) uniform itexture3D im3d;
+layout(set=0, binding=1) uniform sampler s;
+layout(set=0, binding=2) buffer A { ivec4 x[]; } data;
+
+void main() {
+ uvec3 gid = gl_GlobalInvocationID;
+ ivec3 coord = ivec3(gid.x, gid.y, gid.z);
+ uint linear = 4 * gid.z + 2 * gid.y + gid.x;
+ data.x[linear] = texture(isampler3D(im3d, s), coord);
+}
+END
+
+BUFFER out_buf DATA_TYPE vec4<int32> SIZE 8 FILL 15
+IMAGE im3d DATA_TYPE vec4<int32> DIM_3D \
+ WIDTH 2 HEIGHT 2 DEPTH 2 FILL 0
+SAMPLER sampler \
+ ADDRESS_MODE_U clamp_to_edge \
+ ADDRESS_MODE_V clamp_to_edge \
+ ADDRESS_MODE_W clamp_to_edge \
+ MIN_FILTER nearest \
+ MAG_FILTER nearest \
+ MIN_LOD 0.0 \
+ MAX_LOD 0.0
+
+PIPELINE compute write_pipe
+ ATTACH write
+ BIND BUFFER im3d AS storage_image DESCRIPTOR_SET 0 BINDING 0
+END
+
+PIPELINE compute read_pipe
+ ATTACH read
+ BIND BUFFER im3d AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1
+ BIND BUFFER out_buf AS storage DESCRIPTOR_SET 0 BINDING 2
+END
+
+RUN write_pipe 2 2 2
+RUN read_pipe 2 2 2
+
+EXPECT out_buf IDX 0 EQ 1 1 1 0
+EXPECT out_buf IDX 16 EQ 2 1 1 0
+EXPECT out_buf IDX 32 EQ 1 2 1 0
+EXPECT out_buf IDX 48 EQ 2 2 1 0
+EXPECT out_buf IDX 64 EQ 1 1 2 0
+EXPECT out_buf IDX 80 EQ 2 1 2 0
+EXPECT out_buf IDX 96 EQ 1 2 2 0
+EXPECT out_buf IDX 112 EQ 2 2 2 0
diff --git a/tests/cases/graphics_descriptor_array_combined_image_sampler.amber b/tests/cases/graphics_descriptor_array_combined_image_sampler.amber
new file mode 100644
index 0000000..d7fe3c0
--- /dev/null
+++ b/tests/cases/graphics_descriptor_array_combined_image_sampler.amber
@@ -0,0 +1,95 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_texgen GLSL
+#version 430
+layout(location = 0) out vec4 color_out0;
+layout(location = 1) out vec4 color_out1;
+void main() {
+ color_out0 = vec4(1.0, 0.0, 0.0, 1.0);
+ color_out1 = vec4(0.0, 1.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) sampler2D tex_sampler[2];
+void main() {
+ color_out = texture(tex_sampler[0], texcoords_in);
+ color_out = color_out + texture(tex_sampler[1], texcoords_in);
+}
+END
+
+IMAGE texture0 FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+IMAGE texture1 FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+IMAGE framebuffer FORMAT B8G8R8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+SAMPLER sampler
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+2.0 0.0
+2.0 2.0
+0.0 2.0
+END
+
+PIPELINE graphics texgen
+ ATTACH vert_shader
+ ATTACH frag_shader_texgen
+ BIND BUFFER texture0 AS color LOCATION 0
+ BIND BUFFER texture1 AS color LOCATION 1
+END
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER_ARRAY texture0 texture1 AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+# Generate a texture with a quad at the lower right corner.
+CLEAR_COLOR texgen 0 0 255 255
+CLEAR texgen
+RUN texgen DRAW_RECT POS 128 128 SIZE 128 128
+
+# Draw the texture using a default sampler.
+CLEAR_COLOR pipeline 0 255 0 255
+CLEAR pipeline
+RUN pipeline DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 81 81 SIZE 1 1 EQ_RGBA 255 255 0 255
+EXPECT framebuffer IDX 81 33 SIZE 1 1 EQ_RGBA 0 0 255 255
diff --git a/tests/cases/graphics_descriptor_array_sampled_image.amber b/tests/cases/graphics_descriptor_array_sampled_image.amber
new file mode 100644
index 0000000..497f6da
--- /dev/null
+++ b/tests/cases/graphics_descriptor_array_sampled_image.amber
@@ -0,0 +1,97 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_texgen GLSL
+#version 430
+layout(location = 0) out vec4 color_out0;
+layout(location = 1) out vec4 color_out1;
+void main() {
+ color_out0 = vec4(1.0, 0.0, 0.0, 1.0);
+ color_out1 = vec4(0.0, 1.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) texture2D tex[2];
+uniform layout(set=0, binding=1) sampler tex_sampler;
+void main() {
+ color_out = texture(sampler2D(tex[0], tex_sampler), texcoords_in);
+ color_out += texture(sampler2D(tex[1], tex_sampler), texcoords_in);
+}
+END
+
+IMAGE texture0 FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+IMAGE texture1 FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+IMAGE framebuffer FORMAT B8G8R8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+SAMPLER sampler
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+2.0 0.0
+2.0 2.0
+0.0 2.0
+END
+
+PIPELINE graphics pipeline_texgen
+ ATTACH vert_shader
+ ATTACH frag_shader_texgen
+ BIND BUFFER texture0 AS color LOCATION 0
+ BIND BUFFER texture1 AS color LOCATION 1
+END
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER_ARRAY texture0 texture1 AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+# Generate a texture with a quad at the lower right corner.
+CLEAR_COLOR pipeline_texgen 0 0 255 255
+CLEAR pipeline_texgen
+RUN pipeline_texgen DRAW_RECT POS 128 128 SIZE 128 128
+
+# Draw the texture using a default sampler.
+CLEAR_COLOR pipeline 0 255 0 255
+CLEAR pipeline
+RUN pipeline DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 81 81 SIZE 1 1 EQ_RGBA 255 255 0 255
+EXPECT framebuffer IDX 81 33 SIZE 1 1 EQ_RGBA 0 0 255 255
diff --git a/tests/cases/graphics_descriptor_array_sampler.amber b/tests/cases/graphics_descriptor_array_sampler.amber
new file mode 100644
index 0000000..8c6df32
--- /dev/null
+++ b/tests/cases/graphics_descriptor_array_sampler.amber
@@ -0,0 +1,57 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) texture2D tex;
+uniform layout(set=0, binding=1) sampler tex_sampler[2];
+void main() {
+ // Sample outside texture to get border color.
+ vec4 black = texture(sampler2D(tex, tex_sampler[0]), vec2(2.0));
+ vec4 white = texture(sampler2D(tex, tex_sampler[1]), vec2(2.0));
+ color_out = vec4(white.x, black.y, white.z, black.w);
+}
+END
+
+IMAGE texture FORMAT R8G8B8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+IMAGE framebuffer FORMAT B8G8R8A8_UNORM DIM_2D WIDTH 256 HEIGHT 256
+SAMPLER sampler0 ADDRESS_MODE_U clamp_to_border BORDER_COLOR float_opaque_black
+SAMPLER sampler1 ADDRESS_MODE_U clamp_to_border BORDER_COLOR float_opaque_white
+
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+
+PIPELINE graphics pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER_ARRAY sampler0 sampler1 DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+CLEAR_COLOR pipeline 0 0 0 255
+CLEAR pipeline
+RUN pipeline DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 0 0 255
+EXPECT framebuffer IDX 128 128 SIZE 1 1 EQ_RGBA 255 0 255 255
diff --git a/tests/cases/graphics_dynamic_buffers.amber b/tests/cases/graphics_dynamic_buffers.amber
new file mode 100644
index 0000000..ff48d74
--- /dev/null
+++ b/tests/cases/graphics_dynamic_buffers.amber
@@ -0,0 +1,135 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader_uniform GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 0) readonly uniform block0
+{
+ vec4 in_color;
+};
+
+void main()
+{
+ gl_Position = position;
+ frag_color = in_color;
+}
+END
+
+SHADER vertex vtex_shader_storage GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 1) readonly buffer block0
+{
+ vec4 in_color;
+};
+
+void main()
+{
+ gl_Position = position;
+ frag_color = in_color;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main()
+{
+ final_color = frag_color;
+}
+END
+
+# The Vulkan spec lists the maximum value of minStorageBufferOffsetAlignment
+# (i.e. the maximum possible alignment requirement) as 256 bytes.
+# Meaningful data is placed at this offset.
+BUFFER buf DATA_TYPE vec4<float> DATA
+1.0 0.0 0.0 1.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 1.0 0.0 1.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 0.0 0.0
+0.0 0.0 1.0 1.0
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline0
+ ATTACH vtex_shader_uniform
+ ATTACH frag_shader
+
+ # Using the maximum possible required offset alignment of 256 bytes to support all implementations.
+ BIND BUFFER buf AS uniform_dynamic DESCRIPTOR_SET 0 BINDING 0 OFFSET 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+
+ FRAMEBUFFER_SIZE 256 256
+END
+
+PIPELINE graphics pipeline1
+ ATTACH vtex_shader_storage
+ ATTACH frag_shader
+
+ # Using the maximum possible required offset alignment of 256 bytes to support all implementations.
+ BIND BUFFER buf AS storage_dynamic DESCRIPTOR_SET 0 BINDING 1 OFFSET 512
+ BIND BUFFER framebuffer AS color LOCATION 0
+
+ FRAMEBUFFER_SIZE 256 256
+END
+
+CLEAR_COLOR pipeline0 0 0 0 255
+CLEAR pipeline0
+RUN pipeline0 DRAW_RECT POS 0 0 SIZE 128 128
+RUN pipeline1 DRAW_RECT POS 128 128 SIZE 128 128
+
+EXPECT framebuffer IDX 0 0 SIZE 128 128 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 128 0 SIZE 128 128 EQ_RGBA 0 0 0 255
+EXPECT framebuffer IDX 128 128 SIZE 128 128 EQ_RGBA 0 0 255 255
+EXPECT framebuffer IDX 0 128 SIZE 128 128 EQ_RGBA 0 0 0 255
diff --git a/tests/cases/graphics_push_constants.amber b/tests/cases/graphics_push_constants.amber
index 9e025f6..22cfef9 100644
--- a/tests/cases/graphics_push_constants.amber
+++ b/tests/cases/graphics_push_constants.amber
@@ -55,5 +55,5 @@ END
CLEAR pipeline
RUN pipeline DRAW_RECT POS 0 0 SIZE 250 250
-EXPECT framebuffer IDX 0 0 SIZE 250 125 EQ_RGBA 127 127 127 255
+EXPECT framebuffer IDX 0 0 SIZE 250 125 EQ_RGBA 127 127 127 255 TOLERANCE 1 1 1 0
EXPECT framebuffer IDX 0 125 SIZE 250 125 EQ_RGBA 0 0 0 0
diff --git a/tests/cases/graphics_uniform_buffer.vkscript b/tests/cases/graphics_uniform_buffer.vkscript
new file mode 100644
index 0000000..00a47d4
--- /dev/null
+++ b/tests/cases/graphics_uniform_buffer.vkscript
@@ -0,0 +1,35 @@
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+[vertex shader passthrough]
+[fragment shader]
+#version 430
+
+layout(set = 0, binding = 0) uniform Uniform {
+ float uniform_value;
+};
+
+layout(location = 0) out vec4 outColor;
+
+void main() {
+ outColor = vec4(uniform_value, 0.0, 0.0, uniform_value);
+}
+
+[test]
+uniform ubo 0:0 float 0 1.0
+
+clear
+draw rect -1 -1 2 2
+
+probe rect rgba (0, 0, 250, 250) (1.0 0.0 0.0 1.0)
diff --git a/tests/cases/image_data.amber b/tests/cases/image_data.amber
new file mode 100644
index 0000000..3d1f79f
--- /dev/null
+++ b/tests/cases/image_data.amber
@@ -0,0 +1,92 @@
+#!amber
+# Copyright 2021 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute compute_shader GLSL
+#version 460
+
+layout(local_size_x = 4, local_size_y = 4, local_size_z = 1) in;
+
+layout(binding = 0, r32f) uniform readonly image3D inImg;
+layout(binding = 1, r32f) uniform image3D outImg;
+
+void main() {
+ // Get current pixel
+ const int current_x = int(gl_GlobalInvocationID.x);
+ const int current_y = int(gl_GlobalInvocationID.y);
+
+ for (int idx = 0; idx < 4; ++idx) {
+ vec4 result = imageLoad(inImg, ivec3(current_x, current_y, 3 - idx));
+ imageStore(outImg, ivec3(current_x, current_y, idx), result);
+ }
+}
+END
+
+IMAGE outputImage DATA_TYPE float DIM_3D WIDTH 4 HEIGHT 4 DEPTH 4 FILL 0.0
+
+IMAGE inputImage DATA_TYPE float DIM_3D WIDTH 4 HEIGHT 4 DEPTH 4 DATA
+ 0.110 0.111 0.112 0.113
+ 0.120 0.121 0.122 0.123
+ 0.130 0.131 0.132 0.133
+ 0.140 0.141 0.142 0.143
+
+ 0.210 0.211 0.212 0.213
+ 0.220 0.221 0.222 0.223
+ 0.230 0.231 0.232 0.233
+ 0.240 0.241 0.242 0.243
+
+ 0.310 0.311 0.312 0.313
+ 0.320 0.321 0.322 0.323
+ 0.330 0.331 0.332 0.333
+ 0.340 0.341 0.342 0.343
+
+ 0.410 0.411 0.412 0.413
+ 0.420 0.421 0.422 0.423
+ 0.430 0.431 0.432 0.433
+ 0.440 0.441 0.442 0.443
+END
+
+IMAGE expectedImage DATA_TYPE float DIM_3D WIDTH 4 HEIGHT 4 DEPTH 4 DATA
+ 0.410 0.411 0.412 0.413
+ 0.420 0.421 0.422 0.423
+ 0.430 0.431 0.432 0.433
+ 0.440 0.441 0.442 0.443
+
+ 0.310 0.311 0.312 0.313
+ 0.320 0.321 0.322 0.323
+ 0.330 0.331 0.332 0.333
+ 0.340 0.341 0.342 0.343
+
+ 0.210 0.211 0.212 0.213
+ 0.220 0.221 0.222 0.223
+ 0.230 0.231 0.232 0.233
+ 0.240 0.241 0.242 0.243
+
+ 0.110 0.111 0.112 0.113
+ 0.120 0.121 0.122 0.123
+ 0.130 0.131 0.132 0.133
+ 0.140 0.141 0.142 0.143
+END
+
+PIPELINE compute pipeline
+ ATTACH compute_shader
+
+ BIND BUFFER inputImage AS storage_image DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER outputImage AS storage_image DESCRIPTOR_SET 0 BINDING 1
+END
+
+RUN pipeline 1 1 1
+
+EXPECT outputImage EQ_BUFFER expectedImage
+
diff --git a/tests/cases/int8.amber b/tests/cases/int8.amber
new file mode 100644
index 0000000..31d4403
--- /dev/null
+++ b/tests/cases/int8.amber
@@ -0,0 +1,53 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+INSTANCE_EXTENSION VK_KHR_get_physical_device_properties2
+DEVICE_EXTENSION VK_KHR_shader_float16_int8
+DEVICE_EXTENSION VK_KHR_storage_buffer_storage_class
+DEVICE_EXTENSION VK_KHR_8bit_storage
+DEVICE_FEATURE Float16Int8Features.shaderInt8
+DEVICE_FEATURE Storage8BitFeatures.uniformAndStorageBuffer8BitAccess
+DEVICE_FEATURE Storage8BitFeatures.storagePushConstant8
+
+SHADER compute comp_shader GLSL
+#version 450
+#extension GL_EXT_shader_explicit_arithmetic_types_int8 : require
+
+layout(set=0, binding=0) buffer Buf {
+ int8_t value;
+} data;
+
+layout(push_constant) uniform PushConstantsBlock {
+ int8_t factor;
+} pushConstants;
+
+void main() {
+ data.value = data.value * pushConstants.factor;
+}
+END
+
+BUFFER buf DATA_TYPE int8 DATA 63 END
+BUFFER pushc DATA_TYPE int8 DATA 2 END
+
+PIPELINE compute pipeline
+ ATTACH comp_shader
+
+ BIND BUFFER buf AS storage DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER pushc AS push_constant
+END
+
+RUN pipeline 1 1 1
+
+EXPECT buf IDX 0 EQ 126
diff --git a/tests/cases/magfilter_linear.amber b/tests/cases/magfilter_linear.amber
new file mode 100644
index 0000000..cab8d6a
--- /dev/null
+++ b/tests/cases/magfilter_linear.amber
@@ -0,0 +1,96 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) texture2D tex;
+uniform layout(set=0, binding=1) sampler tex_sampler;
+void main() {
+ color_out = texture(sampler2D(tex, tex_sampler), texcoords_in);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+SAMPLER sampler MAG_FILTER linear
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+2.0 0.0
+2.0 2.0
+0.0 2.0
+END
+
+PIPELINE graphics pipeline1
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 2 2
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline2
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+# Generate a 2x2 texture with a one pixel sized chessboard pattern.
+CLEAR_COLOR pipeline1 0 0 255 255
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 0 0 SIZE 1 1
+RUN pipeline1 DRAW_RECT POS 1 1 SIZE 1 1
+
+# Draw a textured quad with linear magnification.
+CLEAR_COLOR pipeline2 0 255 0 255
+CLEAR pipeline2
+RUN pipeline2 DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 255 0 255
+# Sample a pixel between two upsampled texels. The result should be roughly
+# a mean value of the original colors.
+EXPECT framebuffer IDX 80 80 SIZE 1 1 EQ_RGBA 128 0 128 255 TOLERANCE 3 0 3 0
diff --git a/tests/cases/magfilter_nearest.amber b/tests/cases/magfilter_nearest.amber
new file mode 100644
index 0000000..05cff7a
--- /dev/null
+++ b/tests/cases/magfilter_nearest.amber
@@ -0,0 +1,97 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) texture2D tex;
+uniform layout(set=0, binding=1) sampler tex_sampler;
+void main() {
+ color_out = texture(sampler2D(tex, tex_sampler), texcoords_in);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+SAMPLER sampler MAG_FILTER nearest
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+0.0 0.0
+2.0 0.0
+2.0 2.0
+0.0 2.0
+END
+
+PIPELINE graphics pipeline1
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 2 2
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline2
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+# Generate a 2x2 texture with a one pixel sized chessboard pattern.
+CLEAR_COLOR pipeline1 0 0 255 255
+CLEAR pipeline1
+RUN pipeline1 DRAW_RECT POS 0 0 SIZE 1 1
+RUN pipeline1 DRAW_RECT POS 1 1 SIZE 1 1
+
+# Draw a textured quad with nearest magnification.
+CLEAR_COLOR pipeline2 0 255 0 255
+CLEAR pipeline2
+RUN pipeline2 DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 255 0 255
+# With nearest magnification filter the chessboard patter should
+# remain intact. Sample inside two different colored rectangles.
+EXPECT framebuffer IDX 81 81 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 129 81 SIZE 1 1 EQ_RGBA 0 0 255 255
diff --git a/tests/cases/matrices_uniform_draw.amber b/tests/cases/matrices_uniform_draw.amber
index 05466e9..85b0a93 100644
--- a/tests/cases/matrices_uniform_draw.amber
+++ b/tests/cases/matrices_uniform_draw.amber
@@ -1,4 +1,19 @@
#!amber
+
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
# AmberScript generated by GraphicsFuzz
SHADER vertex gfz_vert PASSTHROUGH
@@ -895,13 +910,13 @@ CLEAR gfz_pipeline
RUN gfz_pipeline DRAW_RECT POS 0 0 SIZE 256 256
EXPECT framebuffer IDX 0 0 SIZE 85 85 EQ_RGBA 0 0 0 255
-EXPECT framebuffer IDX 85 0 SIZE 85 85 EQ_RGBA 96 96 96 255
-EXPECT framebuffer IDX 171 0 SIZE 85 85 EQ_RGBA 127 127 127 255
+EXPECT framebuffer IDX 85 0 SIZE 85 85 EQ_RGBA 96 96 96 255 TOLERANCE 1 1 1 0
+EXPECT framebuffer IDX 171 0 SIZE 85 85 EQ_RGBA 127 127 127 255 TOLERANCE 1 1 1 0
-EXPECT framebuffer IDX 0 85 SIZE 85 85 EQ_RGBA 96 96 96 255
-EXPECT framebuffer IDX 85 85 SIZE 85 85 EQ_RGBA 143 143 143 255
-EXPECT framebuffer IDX 171 85 SIZE 85 85 EQ_RGBA 191 191 191 255
+EXPECT framebuffer IDX 0 85 SIZE 85 85 EQ_RGBA 96 96 96 255 TOLERANCE 1 1 1 0
+EXPECT framebuffer IDX 85 85 SIZE 85 85 EQ_RGBA 143 143 143 255 TOLERANCE 1 1 1 0
+EXPECT framebuffer IDX 171 85 SIZE 85 85 EQ_RGBA 191 191 191 255 TOLERANCE 1 1 1 0
-EXPECT framebuffer IDX 0 171 SIZE 85 85 EQ_RGBA 127 127 127 255
-EXPECT framebuffer IDX 85 171 SIZE 85 85 EQ_RGBA 191 191 191 255
+EXPECT framebuffer IDX 0 171 SIZE 85 85 EQ_RGBA 127 127 127 255 TOLERANCE 1 1 1 0
+EXPECT framebuffer IDX 85 171 SIZE 85 85 EQ_RGBA 191 191 191 255 TOLERANCE 1 1 1 0
EXPECT framebuffer IDX 171 171 SIZE 85 85 EQ_RGBA 255 255 255 255
diff --git a/tests/cases/matrix_initialization.amber b/tests/cases/matrix_initialization.amber
index 65066a2..e77d554 100644
--- a/tests/cases/matrix_initialization.amber
+++ b/tests/cases/matrix_initialization.amber
@@ -1,4 +1,19 @@
#!amber
+
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
# AmberScript generated by GraphicsFuzz
SHADER vertex gfz_vert PASSTHROUGH
diff --git a/tests/cases/minfilter.expect_fail.amber b/tests/cases/minfilter.expect_fail.amber
new file mode 100644
index 0000000..7e8854c
--- /dev/null
+++ b/tests/cases/minfilter.expect_fail.amber
@@ -0,0 +1,126 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) texture2D tex;
+uniform layout(set=0, binding=1) sampler tex_sampler;
+void main() {
+ color_out = texture(sampler2D(tex, tex_sampler), texcoords_in);
+}
+END
+
+BUFFER texture_small FORMAT B8G8R8A8_UNORM
+BUFFER texture FORMAT B8G8R8A8_UNORM
+BUFFER framebuffer_linear FORMAT B8G8R8A8_UNORM
+BUFFER framebuffer_nearest FORMAT B8G8R8A8_UNORM
+SAMPLER sampler_linear MIN_FILTER linear MAG_FILTER nearest
+SAMPLER sampler_nearest MIN_FILTER nearest MAG_FILTER nearest
+BUFFER position DATA_TYPE vec2<float> DATA
+-1.0 -1.0
+ 1.0 -1.0
+ 1.0 1.0
+-1.0 1.0
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+ 0.0 0.0
+16.0 0.0
+16.0 16.0
+ 0.0 16.0
+END
+
+PIPELINE graphics pipeline_red
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 2 2
+ BIND BUFFER texture_small AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline_tex_repeat
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture_small AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler_nearest DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline_linear
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler_linear DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer_linear AS color LOCATION 0
+END
+
+PIPELINE graphics pipeline_nearest
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS sampled_image DESCRIPTOR_SET 0 BINDING 0
+ BIND SAMPLER sampler_nearest DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer_nearest AS color LOCATION 0
+END
+
+# Generate 2x2 pattern.
+CLEAR_COLOR pipeline_red 0 0 255 255
+CLEAR pipeline_red
+RUN pipeline_red DRAW_RECT POS 0 0 SIZE 1 1
+RUN pipeline_red DRAW_RECT POS 1 1 SIZE 1 1
+
+# Make the pattern repeat into 256x256 texture
+RUN pipeline_tex_repeat DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+# Draw the repeated texture into a quad and again repeat 16 times
+# to make it smaller. Do this with both nearest and linear filter mode.
+CLEAR_COLOR pipeline_linear 0 255 0 255
+CLEAR pipeline_linear
+RUN pipeline_linear DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+CLEAR_COLOR pipeline_nearest 0 255 0 255
+CLEAR pipeline_nearest
+RUN pipeline_nearest DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+# Linear and nearest filtering produce different results so this should fail.
+EXPECT framebuffer_nearest EQ_BUFFER framebuffer_linear
diff --git a/tests/cases/multiple_samplers.amber b/tests/cases/multiple_samplers.amber
new file mode 100644
index 0000000..dd37e8e
--- /dev/null
+++ b/tests/cases/multiple_samplers.amber
@@ -0,0 +1,126 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+
+SHADER fragment frag_shader_red GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec2 texcoords_in;
+layout(location = 0) out vec2 texcoords_out;
+void main() {
+ gl_Position = position;
+ texcoords_out = texcoords_in;
+}
+END
+
+SHADER fragment frag_shader_tex GLSL
+#version 430
+layout(location = 0) in vec2 texcoords_in;
+layout(location = 0) out vec4 color_out;
+uniform layout(set=0, binding=0) sampler2D tex_sampler_white;
+uniform layout(set=0, binding=1) sampler2D tex_sampler_black;
+void main() {
+ // Use a sampler with a white border color for the right
+ // side of the color buffer and a sampler with a black
+ // border color for the left side.
+ if (gl_FragCoord.x > 128.0)
+ color_out = texture(tex_sampler_white, texcoords_in);
+ else
+ color_out = texture(tex_sampler_black, texcoords_in);
+}
+END
+
+BUFFER texture FORMAT R8G8B8A8_UNORM
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+# A sampler that generates white when texture coordinates
+# go out of range [0..1].
+SAMPLER sampler_white \
+ ADDRESS_MODE_U clamp_to_border \
+ ADDRESS_MODE_V clamp_to_border \
+ BORDER_COLOR float_opaque_white
+
+# A sampler that generates black when texture coordinates
+# go out of range [0..1].
+SAMPLER sampler_black \
+ ADDRESS_MODE_U clamp_to_border \
+ ADDRESS_MODE_V clamp_to_border \
+ BORDER_COLOR float_opaque_black
+
+BUFFER position DATA_TYPE vec2<float> DATA
+-0.75 -0.75
+ 0.75 -0.75
+ 0.75 0.75
+-0.75 0.75
+END
+BUFFER texcoords DATA_TYPE vec2<float> DATA
+-0.5 -0.5
+ 1.5 -0.5
+ 1.5 1.5
+-0.5 1.5
+END
+
+# A pipeline for generating a texture. Renders pure red.
+PIPELINE graphics texgen
+ ATTACH vert_shader
+ ATTACH frag_shader_red
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+# A pipeline for drawing a textured quad using two samplers.
+PIPELINE graphics pipeline
+ ATTACH vert_shader_tex
+ ATTACH frag_shader_tex
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler_white DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler_black DESCRIPTOR_SET 0 BINDING 1
+ VERTEX_DATA position LOCATION 0
+ VERTEX_DATA texcoords LOCATION 1
+ FRAMEBUFFER_SIZE 256 256
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+# Generate a texture with a blue background and red quad at the lower right corner.
+CLEAR_COLOR texgen 0 0 255 255
+CLEAR texgen
+RUN texgen DRAW_RECT POS 128 128 SIZE 128 128
+
+# Clear to green and draw the generated texture in the center of the screen leaving
+# the background color to the edges. Use texture coordinates that go past the [0..1]
+# range to show border colors. The left side of the texture is using a sampler with
+# a black border, and the right side uses a sampler with a white border.
+CLEAR_COLOR pipeline 0 255 0 255
+CLEAR pipeline
+RUN pipeline DRAW_ARRAY AS TRIANGLE_FAN START_IDX 0 COUNT 4
+
+# Check for the green background.
+EXPECT framebuffer IDX 1 1 SIZE 1 1 EQ_RGBA 0 255 0 255
+# Check for the black border color.
+EXPECT framebuffer IDX 55 55 SIZE 1 1 EQ_RGBA 0 0 0 255
+# Check for the blue part of the texture.
+EXPECT framebuffer IDX 105 105 SIZE 1 1 EQ_RGBA 0 0 255 255
+# Check for the red part of the texture.
+EXPECT framebuffer IDX 150 150 SIZE 1 1 EQ_RGBA 255 0 0 255
+# Check for the white border color.
+EXPECT framebuffer IDX 200 200 SIZE 1 1 EQ_RGBA 255 255 255 255
diff --git a/tests/cases/multiple_ssbo_update_with_graphics_pipeline.vkscript b/tests/cases/multiple_ssbo_update_with_graphics_pipeline.vkscript
index 173a52e..c175d05 100644
--- a/tests/cases/multiple_ssbo_update_with_graphics_pipeline.vkscript
+++ b/tests/cases/multiple_ssbo_update_with_graphics_pipeline.vkscript
@@ -85,6 +85,12 @@ void main() {
[test]
clear
+# Allocate enough space for the uniform buffers including 16-byte alignment.
+ssbo 1:0 96
+ssbo 1:2 96
+ssbo 2:1 96
+ssbo 2:3 96
+
ssbo 0 subdata float 0 0.1 0.2 0.3 0.4
ssbo 0 subdata float 16 0.9 0.8 0.7 0.6
diff --git a/tests/cases/multiple_ssbo_with_sparse_descriptor_set_in_compute_pipeline_less_than_4.vkscript b/tests/cases/multiple_ssbo_with_sparse_descriptor_set_in_compute_pipeline_less_than_4.vkscript
index 5ce111b..b5c14e6 100644
--- a/tests/cases/multiple_ssbo_with_sparse_descriptor_set_in_compute_pipeline_less_than_4.vkscript
+++ b/tests/cases/multiple_ssbo_with_sparse_descriptor_set_in_compute_pipeline_less_than_4.vkscript
@@ -45,10 +45,33 @@ void main() {
}
[test]
-ssbo 1:0 subdata float 0 0.1 0.2 0.3 0.4
-ssbo 1:2 subdata float 0 0.1 0.2 0.3 0.4
-ssbo 3:1 subdata float 0 0.1 0.2 0.3 0.4
-ssbo 0:3 subdata float 0 0.1 0.2 0.3 0.4
+ssbo 1:0 subdata float 0 0.1 0.2 0.3 0.4 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0
+
+ssbo 1:2 subdata float 0 0.1 0.2 0.3 0.4 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0
+
+ssbo 3:1 subdata float 0 0.1 0.2 0.3 0.4 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0
+
+ssbo 0:3 subdata float 0 0.1 0.2 0.3 0.4 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0 0.0 \
+ 0.0 0.0 0.0
compute 4 1 1
diff --git a/tests/cases/opencl_generated_push_constants.amber b/tests/cases/opencl_generated_push_constants.amber
new file mode 100644
index 0000000..e479f5c
--- /dev/null
+++ b/tests/cases/opencl_generated_push_constants.amber
@@ -0,0 +1,79 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute work_dim_shader OPENCL-C
+kernel void foo(global int* out) {
+ *out = get_work_dim();
+}
+END
+
+SHADER compute global_offset_shader OPENCL-C
+kernel void foo(global int* out) {
+ out[0] = get_global_offset(0);
+ out[1] = get_global_offset(1);
+ out[2] = get_global_offset(2);
+}
+END
+
+SHADER compute all_constants_shader OPENCL-C
+kernel void foo(global int* out) {
+ out[0] = get_global_offset(0);
+ out[1] = get_global_offset(1);
+ out[2] = get_global_offset(2);
+ out[3] = get_work_dim();
+}
+END
+
+BUFFER work_dim_buf DATA_TYPE uint32 SIZE 1 FILL -1
+BUFFER global_offset_buf DATA_TYPE uint32 SIZE 3 FILL -1
+BUFFER all_constants_buf DATA_TYPE uint32 SIZE 4 FILL -1
+
+PIPELINE compute work_dim_pipeline
+ ATTACH work_dim_shader ENTRY_POINT foo
+ COMPILE_OPTIONS work_dim_shader
+ -work-dim
+ END
+ BIND BUFFER work_dim_buf AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+PIPELINE compute global_offset_pipeline
+ ATTACH global_offset_shader ENTRY_POINT foo
+ COMPILE_OPTIONS global_offset_shader
+ -global-offset
+ END
+ BIND BUFFER global_offset_buf AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+PIPELINE compute all_constants_pipeline
+ ATTACH all_constants_shader ENTRY_POINT foo
+ COMPILE_OPTIONS all_constants_shader
+ -work-dim -global-offset
+ END
+ BIND BUFFER all_constants_buf AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+RUN work_dim_pipeline 1 1 1
+EXPECT work_dim_buf IDX 0 EQ 3
+
+RUN global_offset_pipeline 1 1 1
+EXPECT global_offset_buf IDX 0 EQ 0
+EXPECT global_offset_buf IDX 4 EQ 0
+EXPECT global_offset_buf IDX 8 EQ 0
+
+RUN all_constants_pipeline 1 1 1
+EXPECT all_constants_buf IDX 0 EQ 0
+EXPECT all_constants_buf IDX 4 EQ 0
+EXPECT all_constants_buf IDX 8 EQ 0
+EXPECT all_constants_buf IDX 12 EQ 3
diff --git a/tests/cases/opencl_read_and_write_image3d_rgba32i.amber b/tests/cases/opencl_read_and_write_image3d_rgba32i.amber
new file mode 100644
index 0000000..9c84a58
--- /dev/null
+++ b/tests/cases/opencl_read_and_write_image3d_rgba32i.amber
@@ -0,0 +1,74 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute write OPENCL-C
+#pragma OPENCL EXTENSION cl_khr_3d_image_writes : enable
+kernel void write_foo(write_only image3d_t im) {
+ int4 dim = get_image_dim(im);
+ int gid_x = get_global_id(0);
+ int gid_y = get_global_id(1);
+ int gid_z = get_global_id(2);
+ int4 coord = (int4)(gid_x, gid_y, gid_z, 0);
+ int4 data = (int4)(gid_x + 1, gid_y + 1, gid_z + 1, 0);
+ write_imagei(im, coord, data);
+}
+END
+
+SHADER compute read OPENCL-C
+kernel void read_foo(read_only image3d_t im, sampler_t sampler, global int4* out) {
+ int gid_x = get_global_id(0);
+ int gid_y = get_global_id(1);
+ int gid_z = get_global_id(2);
+ float4 coord = (float4)(gid_x, gid_y, gid_z, 0);
+ int linear = 4 * gid_z + 2 * gid_y + gid_x;
+ out[linear] = read_imagei(im, sampler, coord);
+}
+END
+
+BUFFER out_buf DATA_TYPE vec4<int32> SIZE 8 FILL 15
+IMAGE im3d DATA_TYPE vec4<int32> DIM_3D \
+ WIDTH 2 HEIGHT 2 DEPTH 2 FILL 0
+SAMPLER sampler \
+ ADDRESS_MODE_U clamp_to_edge \
+ ADDRESS_MODE_V clamp_to_edge \
+ ADDRESS_MODE_W clamp_to_edge \
+ MIN_FILTER nearest \
+ MAG_FILTER nearest \
+ MIN_LOD 0.0 \
+ MAX_LOD 0.0
+
+PIPELINE compute write_pipe
+ ATTACH write ENTRY_POINT write_foo
+ BIND BUFFER im3d KERNEL ARG_NAME im
+END
+
+PIPELINE compute read_pipe
+ ATTACH read ENTRY_POINT read_foo
+ BIND BUFFER im3d KERNEL ARG_NAME im
+ BIND SAMPLER sampler KERNEL ARG_NAME sampler
+ BIND BUFFER out_buf KERNEL ARG_NAME out
+END
+
+RUN write_pipe 2 2 2
+RUN read_pipe 2 2 2
+
+EXPECT out_buf IDX 0 EQ 1 1 1 0
+EXPECT out_buf IDX 16 EQ 2 1 1 0
+EXPECT out_buf IDX 32 EQ 1 2 1 0
+EXPECT out_buf IDX 48 EQ 2 2 1 0
+EXPECT out_buf IDX 64 EQ 1 1 2 0
+EXPECT out_buf IDX 80 EQ 2 1 2 0
+EXPECT out_buf IDX 96 EQ 1 2 2 0
+EXPECT out_buf IDX 112 EQ 2 2 2 0
diff --git a/tests/cases/opencl_read_image.amber b/tests/cases/opencl_read_image.amber
new file mode 100644
index 0000000..a25a298
--- /dev/null
+++ b/tests/cases/opencl_read_image.amber
@@ -0,0 +1,68 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vs PASSTHROUGH
+
+SHADER fragment fs GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER compute read_imagef OPENCL-C
+kernel void foo(read_only image2d_t image, sampler_t sampler, global float4* out) {
+ int gid_x = get_global_id(0);
+ int gid_y = get_global_id(1);
+ int linear = 2 * gid_y + gid_x;
+ float2 coord = (float2)(gid_x, gid_y);
+ out[linear] = read_imagef(image, sampler, coord);
+}
+END
+
+IMAGE texture DATA_TYPE vec4<float> DIM_2D WIDTH 2 HEIGHT 2 FILL 0.0
+BUFFER out_buf DATA_TYPE vec4<float> DATA
+2.0 2.0 2.0 2.0
+2.0 2.0 2.0 2.0
+2.0 2.0 2.0 2.0
+2.0 2.0 2.0 2.0
+END
+SAMPLER sampler
+
+PIPELINE compute read_pipe
+ ATTACH read_imagef ENTRY_POINT foo
+ BIND BUFFER out_buf KERNEL ARG_NAME out
+ BIND BUFFER texture KERNEL ARG_NAME image
+ BIND SAMPLER sampler KERNEL ARG_NAME sampler
+END
+
+PIPELINE graphics fill_red
+ ATTACH vs
+ ATTACH fs
+ FRAMEBUFFER_SIZE 2 2
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+CLEAR_COLOR fill_red 0 0 3 3
+CLEAR fill_red
+RUN fill_red DRAW_RECT POS 0 0 SIZE 2 2
+
+RUN read_pipe 2 2 1
+
+EXPECT out_buf IDX 0 EQ 1.0 0.0 0.0 1.0
+EXPECT out_buf IDX 16 EQ 1.0 0.0 0.0 1.0
+EXPECT out_buf IDX 32 EQ 1.0 0.0 0.0 1.0
+EXPECT out_buf IDX 48 EQ 1.0 0.0 0.0 1.0
diff --git a/tests/cases/opencl_read_image_literal_sampler.amber b/tests/cases/opencl_read_image_literal_sampler.amber
new file mode 100644
index 0000000..4c72cfe
--- /dev/null
+++ b/tests/cases/opencl_read_image_literal_sampler.amber
@@ -0,0 +1,69 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vs PASSTHROUGH
+
+SHADER fragment fs GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+void main() {
+ color_out = vec4(1.0, 0.0, 0.0, 1.0);
+}
+END
+
+SHADER compute read_imagef OPENCL-C
+const sampler_t sampler = CLK_ADDRESS_CLAMP_TO_EDGE | CLK_NORMALIZED_COORDS_FALSE | CLK_FILTER_NEAREST;
+kernel void foo(read_only image2d_t image, global float4* out) {
+ int gid_x = get_global_id(0);
+ int gid_y = get_global_id(1);
+ int linear = 2 * gid_y + gid_x;
+ float2 coord = (float2)(gid_x, gid_y);
+ out[linear] = read_imagef(image, sampler, coord);
+}
+END
+
+IMAGE texture DATA_TYPE vec4<float> DIM_2D WIDTH 2 HEIGHT 2 FILL 0.0
+BUFFER out_buf DATA_TYPE vec4<float> DATA
+2.0 2.0 2.0 2.0
+2.0 2.0 2.0 2.0
+2.0 2.0 2.0 2.0
+2.0 2.0 2.0 2.0
+END
+SAMPLER sampler
+
+PIPELINE compute read_pipe
+ ATTACH read_imagef ENTRY_POINT foo
+ BIND BUFFER out_buf KERNEL ARG_NAME out
+ BIND BUFFER texture KERNEL ARG_NAME image
+END
+
+PIPELINE graphics fill_red
+ ATTACH vs
+ ATTACH fs
+ FRAMEBUFFER_SIZE 2 2
+ BIND BUFFER texture AS color LOCATION 0
+END
+
+CLEAR_COLOR fill_red 0 0 3 3
+CLEAR fill_red
+RUN fill_red DRAW_RECT POS 0 0 SIZE 2 2
+
+RUN read_pipe 2 2 1
+
+EXPECT out_buf IDX 0 EQ 1.0 0.0 0.0 1.0
+EXPECT out_buf IDX 16 EQ 1.0 0.0 0.0 1.0
+EXPECT out_buf IDX 32 EQ 1.0 0.0 0.0 1.0
+EXPECT out_buf IDX 48 EQ 1.0 0.0 0.0 1.0
+
diff --git a/tests/cases/opencl_set_arg.amber b/tests/cases/opencl_set_arg.amber
index eb3358e..b73e8be 100644
--- a/tests/cases/opencl_set_arg.amber
+++ b/tests/cases/opencl_set_arg.amber
@@ -24,6 +24,7 @@ BUFFER out_buf1 DATA_TYPE uint32 DATA 0 END
BUFFER out_buf2 DATA_TYPE uint32 DATA 0 END
BUFFER out_buf3 DATA_TYPE uint32 DATA 0 END
BUFFER out_buf4 DATA_TYPE uint32 DATA 0 END
+BUFFER out_buf5 DATA_TYPE uint32 DATA 0 END
PIPELINE compute p1
ATTACH my_shader ENTRY_POINT line
@@ -56,12 +57,23 @@ DERIVE_PIPELINE p4 FROM p1
END
END
+DERIVE_PIPELINE p5 FROM p1
+ BIND BUFFER out_buf5 KERNEL ARG_NAME out
+ SET KERNEL ARG_NAME slope AS int32 3
+ COMPILE_OPTIONS my_shader
+ -cluster-pod-kernel-args
+ -pod-pushconstant
+ END
+END
+
RUN p1 1 1 1
RUN p2 1 1 1
RUN p3 1 1 1
RUN p4 1 1 1
+RUN p5 1 1 1
EXPECT out_buf1 IDX 0 EQ 7
EXPECT out_buf2 IDX 0 EQ 7
EXPECT out_buf3 IDX 0 EQ 7
EXPECT out_buf4 IDX 0 EQ 10
+EXPECT out_buf5 IDX 0 EQ 10
diff --git a/tests/cases/opencl_write_image.amber b/tests/cases/opencl_write_image.amber
new file mode 100644
index 0000000..28ada5a
--- /dev/null
+++ b/tests/cases/opencl_write_image.amber
@@ -0,0 +1,45 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER compute write_imagef OPENCL-C
+kernel void foo(write_only image2d_t image, global float4* data) {
+ int gid_x = get_global_id(0);
+ int gid_y = get_global_id(1);
+ int linear = 2 * gid_y + gid_x;
+ int2 coord = (int2)(gid_x, gid_y);
+ write_imagef(image, coord, data[linear]);
+}
+END
+
+IMAGE texture DATA_TYPE vec4<float> DIM_2D WIDTH 2 HEIGHT 2 FILL 0.0
+BUFFER data DATA_TYPE vec4<float> DATA
+1.0 2.0 3.0 4.0
+2.0 3.0 4.0 1.0
+3.0 4.0 1.0 2.0
+4.0 1.0 2.0 3.0
+END
+
+PIPELINE compute read_pipe
+ ATTACH write_imagef ENTRY_POINT foo
+ BIND BUFFER data KERNEL ARG_NAME data
+ BIND BUFFER texture KERNEL ARG_NAME image
+END
+
+RUN read_pipe 2 2 1
+
+EXPECT texture IDX 0 EQ 1.0 2.0 3.0 4.0
+EXPECT texture IDX 16 EQ 2.0 3.0 4.0 1.0
+EXPECT texture IDX 32 EQ 3.0 4.0 1.0 2.0
+EXPECT texture IDX 48 EQ 4.0 1.0 2.0 3.0
diff --git a/tests/cases/position_to_ssbo.amber b/tests/cases/position_to_ssbo.vkscript
index 39f7aae..39f7aae 100644
--- a/tests/cases/position_to_ssbo.amber
+++ b/tests/cases/position_to_ssbo.vkscript
diff --git a/tests/cases/relative_includes_hlsl.amber b/tests/cases/relative_includes_hlsl.amber
new file mode 100644
index 0000000..dd91eea
--- /dev/null
+++ b/tests/cases/relative_includes_hlsl.amber
@@ -0,0 +1,56 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+VIRTUAL_FILE "relative.hlsl"
+#error "Wrong include picked!"
+END
+
+VIRTUAL_FILE "subdir/relative.hlsl"
+// Correct include!
+struct VS_OUTPUT {
+ float4 pos : SV_POSITION;
+ float4 color : COLOR;
+};
+
+VS_OUTPUT main(float4 pos : POSITION,
+ float4 color : COLOR) {
+ VS_OUTPUT vout;
+ vout.pos = pos;
+ vout.color = color;
+ return vout;
+}
+END
+
+VIRTUAL_FILE "subdir/include.hlsl"
+#include "relative.hlsl"
+END
+
+VIRTUAL_FILE "main.hlsl"
+#include "subdir/include.hlsl"
+END
+
+SHADER vertex vtex_shader HLSL VIRTUAL_FILE main.hlsl
+
+SHADER fragment frag_shader HLSL
+float4 main(float4 color : COLOR) : SV_TARGET {
+ return color;
+}
+END
+
+PIPELINE graphics pipeline
+ ATTACH vtex_shader
+ ATTACH frag_shader
+END
diff --git a/tests/cases/runtime_array_std140.amber b/tests/cases/runtime_array_std140.amber
new file mode 100644
index 0000000..009a700
--- /dev/null
+++ b/tests/cases/runtime_array_std140.amber
@@ -0,0 +1,42 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+layout(std140, binding = 0) readonly buffer Data {
+ float d[];
+} data;
+
+void main() {
+ color_out = vec4(data.d[0]/255, data.d[1]/255, data.d[2]/255, data.d[3]/255);
+}
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+BUFFER data DATA_TYPE float[] STD140 DATA 1.0 64.0 128.0 220.0 END
+
+PIPELINE graphics my_pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+ BIND BUFFER data AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+RUN my_pipeline DRAW_RECT POS 0 0 SIZE 250 250
+EXPECT framebuffer IDX 0 0 SIZE 250 250 EQ_RGBA 1 64 128 220
diff --git a/tests/cases/runtime_array_std430.amber b/tests/cases/runtime_array_std430.amber
new file mode 100644
index 0000000..a30658f
--- /dev/null
+++ b/tests/cases/runtime_array_std430.amber
@@ -0,0 +1,42 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader PASSTHROUGH
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) out vec4 color_out;
+layout(std430, binding = 0) readonly buffer Data {
+ float d[];
+} data;
+
+void main() {
+ color_out = vec4(data.d[0]/255, data.d[1]/255, data.d[2]/255, data.d[3]/255);
+}
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+BUFFER data DATA_TYPE float[] STD430 DATA 1.0 64.0 128.0 220.0 END
+
+PIPELINE graphics my_pipeline
+ ATTACH vert_shader
+ ATTACH frag_shader
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+ BIND BUFFER data AS storage DESCRIPTOR_SET 0 BINDING 0
+END
+
+RUN my_pipeline DRAW_RECT POS 0 0 SIZE 250 250
+EXPECT framebuffer IDX 0 0 SIZE 250 250 EQ_RGBA 1 64 128 220
diff --git a/tests/cases/storage16.amber b/tests/cases/storage16.amber
new file mode 100644
index 0000000..5cf8113
--- /dev/null
+++ b/tests/cases/storage16.amber
@@ -0,0 +1,52 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+INSTANCE_EXTENSION VK_KHR_get_physical_device_properties2
+DEVICE_EXTENSION VK_KHR_storage_buffer_storage_class
+DEVICE_EXTENSION VK_KHR_16bit_storage
+DEVICE_FEATURE shaderInt16
+DEVICE_FEATURE Storage16BitFeatures.uniformAndStorageBuffer16BitAccess
+DEVICE_FEATURE Storage16BitFeatures.storagePushConstant16
+
+SHADER compute comp_shader GLSL
+#version 450
+#extension GL_EXT_shader_explicit_arithmetic_types_int16 : require
+
+layout(set=0, binding=0) buffer Buf {
+ int16_t value;
+} data;
+
+layout(push_constant) uniform PushConstantsBlock {
+ int16_t factor;
+} pushConstants;
+
+void main() {
+ data.value = data.value * pushConstants.factor;
+}
+END
+
+BUFFER buf DATA_TYPE int16 DATA 16383 END
+BUFFER pushc DATA_TYPE int16 DATA 2 END
+
+PIPELINE compute pipeline
+ ATTACH comp_shader
+
+ BIND BUFFER buf AS storage DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER pushc AS push_constant
+END
+
+RUN pipeline 1 1 1
+
+EXPECT buf IDX 0 EQ 32766
diff --git a/tests/cases/texture.png b/tests/cases/texture.png
new file mode 100644
index 0000000..465bf9a
--- /dev/null
+++ b/tests/cases/texture.png
Binary files differ
diff --git a/tests/cases/texture_base_mip_level.amber b/tests/cases/texture_base_mip_level.amber
new file mode 100644
index 0000000..5b426b9
--- /dev/null
+++ b/tests/cases/texture_base_mip_level.amber
@@ -0,0 +1,81 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader_mipclear PASSTHROUGH
+
+SHADER vertex vert_shader_tex GLSL
+#version 430
+
+layout(location = 0) in vec3 position_in;
+layout(location = 0) out vec4 color_out;
+layout(set = 0, binding = 0) uniform highp sampler2D tex;
+
+void main() {
+ gl_Position = vec4(position_in, 1.0);
+ color_out = vec4(texture(tex, vec2(0.5)));
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 color_in;
+layout(location = 0) out vec4 color_out;
+
+void main() {
+ color_out = color_in;
+}
+END
+
+BUFFER texture FORMAT B8G8R8A8_UNORM MIP_LEVELS 2
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+SAMPLER sampler MAX_LOD 2.0
+
+PIPELINE graphics mipclear_pipeline0
+ ATTACH vert_shader_mipclear
+ ATTACH frag_shader
+ BIND BUFFER texture AS color LOCATION 0 BASE_MIP_LEVEL 0
+ FRAMEBUFFER_SIZE 512 512
+END
+
+PIPELINE graphics mipclear_pipeline1
+ ATTACH vert_shader_mipclear
+ ATTACH frag_shader
+ BIND BUFFER texture AS color LOCATION 0 BASE_MIP_LEVEL 1
+ FRAMEBUFFER_SIZE 256 256
+END
+
+PIPELINE graphics tex_pipeline
+ ATTACH vert_shader_tex
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0 BASE_MIP_LEVEL 1
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 512 512
+END
+
+CLEAR_COLOR mipclear_pipeline0 255 0 0 255
+CLEAR mipclear_pipeline0
+CLEAR_COLOR mipclear_pipeline1 0 255 0 255
+CLEAR mipclear_pipeline1
+
+CLEAR_COLOR tex_pipeline 0 0 0 255
+CLEAR tex_pipeline
+RUN tex_pipeline DRAW_RECT POS 0 0 SIZE 512 512
+
+# Check corners of the frame buffer: each should have a color from mip level 1
+EXPECT framebuffer IDX 0 511 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 511 0 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 511 511 SIZE 1 1 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 0 0 SIZE 1 1 EQ_RGBA 0 255 0 255
diff --git a/tests/cases/texture_lod.amber b/tests/cases/texture_lod.amber
new file mode 100644
index 0000000..ee6a787
--- /dev/null
+++ b/tests/cases/texture_lod.amber
@@ -0,0 +1,101 @@
+#!amber
+# Copyright 2019 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vert_shader_mipclear PASSTHROUGH
+
+SHADER vertex vert_shader_lod GLSL
+#version 430
+
+layout(location = 0) in vec3 position_in;
+layout(location = 0) out vec4 color_out;
+layout(set = 0, binding = 0) uniform highp sampler2D tex;
+
+void main() {
+ gl_Position = vec4(position_in, 1.0);
+ // Pick a color from the center of a mipmap. Each corner point gets its own mip level.
+ color_out = vec4(textureLod(tex, vec2(0.5), float(gl_VertexIndex % 4)));
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 color_in;
+layout(location = 0) out vec4 color_out;
+
+void main() {
+ color_out = color_in;
+}
+END
+
+BUFFER texture FORMAT B8G8R8A8_UNORM MIP_LEVELS 4
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+SAMPLER sampler MAX_LOD 4.0
+
+PIPELINE graphics mipclear_pipeline0
+ ATTACH vert_shader_mipclear
+ ATTACH frag_shader
+ BIND BUFFER texture AS color LOCATION 0 BASE_MIP_LEVEL 0
+ FRAMEBUFFER_SIZE 512 512
+END
+
+PIPELINE graphics mipclear_pipeline1
+ ATTACH vert_shader_mipclear
+ ATTACH frag_shader
+ BIND BUFFER texture AS color LOCATION 0 BASE_MIP_LEVEL 1
+ FRAMEBUFFER_SIZE 256 256
+END
+
+PIPELINE graphics mipclear_pipeline2
+ ATTACH vert_shader_mipclear
+ ATTACH frag_shader
+ BIND BUFFER texture AS color LOCATION 0 BASE_MIP_LEVEL 2
+ FRAMEBUFFER_SIZE 128 128
+END
+
+PIPELINE graphics mipclear_pipeline3
+ ATTACH vert_shader_mipclear
+ ATTACH frag_shader
+ BIND BUFFER texture AS color LOCATION 0 BASE_MIP_LEVEL 3
+ FRAMEBUFFER_SIZE 64 64
+END
+
+PIPELINE graphics lod_pipeline
+ ATTACH vert_shader_lod
+ ATTACH frag_shader
+ BIND BUFFER texture AS combined_image_sampler SAMPLER sampler DESCRIPTOR_SET 0 BINDING 0
+ BIND BUFFER framebuffer AS color LOCATION 0
+ FRAMEBUFFER_SIZE 512 512
+END
+
+# Clear all mip levels to different color.
+CLEAR_COLOR mipclear_pipeline0 255 0 0 255
+CLEAR mipclear_pipeline0
+CLEAR_COLOR mipclear_pipeline1 0 255 0 255
+CLEAR mipclear_pipeline1
+CLEAR_COLOR mipclear_pipeline2 0 0 255 255
+CLEAR mipclear_pipeline2
+CLEAR_COLOR mipclear_pipeline3 255 255 0 255
+CLEAR mipclear_pipeline3
+
+CLEAR_COLOR lod_pipeline 0 0 0 255
+CLEAR lod_pipeline
+RUN lod_pipeline DRAW_RECT POS 0 0 SIZE 512 512
+
+# Check corners of the frame buffer: each should have a color from a different mip level.
+EXPECT framebuffer IDX 0 511 SIZE 1 1 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 511 0 SIZE 1 1 EQ_RGBA 255 255 0 255
+EXPECT framebuffer IDX 511 511 SIZE 1 1 EQ_RGBA 0 0 255 255
+EXPECT framebuffer IDX 0 0 SIZE 1 1 EQ_RGBA 0 255 0 255
diff --git a/tests/cases/vec4data.bin b/tests/cases/vec4data.bin
new file mode 100644
index 0000000..1c60b14
--- /dev/null
+++ b/tests/cases/vec4data.bin
Binary files differ
diff --git a/tests/cases/vec4data.txt b/tests/cases/vec4data.txt
new file mode 100644
index 0000000..ceb620d
--- /dev/null
+++ b/tests/cases/vec4data.txt
@@ -0,0 +1 @@
+1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0
diff --git a/tests/cases/vertex_data_two_locations.amber b/tests/cases/vertex_data_two_locations.amber
new file mode 100644
index 0000000..a9a4491
--- /dev/null
+++ b/tests/cases/vertex_data_two_locations.amber
@@ -0,0 +1,84 @@
+#!amber
+# Copyright 2021 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 position;
+layout(location = 1) in vec4 vert_color0;
+layout(location = 2) in vec4 vert_color1;
+layout(location = 0) out vec4 frag_color;
+
+void main()
+{
+ gl_Position = position;
+ frag_color = vert_color0 + vert_color1;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+
+layout(location = 0) in vec4 frag_color;
+layout(location = 0) out vec4 final_color;
+
+void main()
+{
+ final_color = frag_color;
+}
+END
+
+BUFFER position_buf DATA_TYPE R8G8_SNORM DATA
+-128 -128
+ 127 127
+-128 127
+-128 -128
+ 127 127
+ 127 -128
+END
+
+BUFFER vert_color DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+255 0 0 255
+
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+ 0 255 0 255
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vtex_shader
+ ATTACH frag_shader
+
+ VERTEX_DATA position_buf LOCATION 0
+ VERTEX_DATA vert_color LOCATION 1
+ VERTEX_DATA vert_color LOCATION 2 OFFSET 24
+
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+CLEAR pipeline
+
+RUN pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6
+EXPECT framebuffer IDX 0 0 SIZE 250 250 EQ_RGBA 255 255 0 255
diff --git a/tests/cases/vertex_rate_instance.amber b/tests/cases/vertex_rate_instance.amber
new file mode 100644
index 0000000..edee1a6
--- /dev/null
+++ b/tests/cases/vertex_rate_instance.amber
@@ -0,0 +1,70 @@
+#!amber
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+SHADER vertex vtex_shader GLSL
+#version 430
+layout(location = 0) in vec2 position;
+layout(location = 1) in vec4 color_in;
+layout(location = 0) out vec4 color_out;
+void main()
+{
+ gl_Position = vec4(position.x + float(gl_InstanceIndex) * 0.5, position.y, 0, 1);
+ color_out = color_in;
+}
+END
+
+SHADER fragment frag_shader GLSL
+#version 430
+layout(location = 0) in vec4 color_in;
+layout(location = 0) out vec4 color_out;
+void main()
+{
+ color_out = color_in;
+}
+END
+
+BUFFER position_buf DATA_TYPE vec2<float> DATA
+-1.0 1.0
+-1.0 -1.0
+-0.5 1.0
+-0.5 -1.0
+-1.0 -1.0
+-0.5 1.0
+END
+
+BUFFER color_buf DATA_TYPE R8G8B8A8_UNORM DATA
+255 0 0 255
+ 0 255 0 255
+ 0 0 255 255
+ 0 255 255 255
+END
+
+BUFFER framebuffer FORMAT B8G8R8A8_UNORM
+
+PIPELINE graphics pipeline
+ ATTACH vtex_shader
+ ATTACH frag_shader
+
+ VERTEX_DATA position_buf LOCATION 0 RATE vertex
+ VERTEX_DATA color_buf LOCATION 1 RATE instance
+ FRAMEBUFFER_SIZE 40 40
+ BIND BUFFER framebuffer AS color LOCATION 0
+END
+
+RUN pipeline DRAW_ARRAY AS TRIANGLE_LIST START_IDX 0 COUNT 6 START_INSTANCE 0 INSTANCE_COUNT 4
+EXPECT framebuffer IDX 0 0 SIZE 10 40 EQ_RGBA 255 0 0 255
+EXPECT framebuffer IDX 10 0 SIZE 10 40 EQ_RGBA 0 255 0 255
+EXPECT framebuffer IDX 20 0 SIZE 10 40 EQ_RGBA 0 0 255 255
+EXPECT framebuffer IDX 30 0 SIZE 10 40 EQ_RGBA 0 255 255 255
diff --git a/tests/run_tests.py b/tests/run_tests.py
index 580706c..1fb7eb6 100755
--- a/tests/run_tests.py
+++ b/tests/run_tests.py
@@ -41,30 +41,86 @@ SUPPRESSIONS = {
# https://github.com/KhronosGroup/MoltenVK/issues/527
"multiple_ssbo_update_with_graphics_pipeline.vkscript",
"multiple_ubo_update_with_graphics_pipeline.vkscript",
- # DXC not currently building on bot
- "draw_triangle_list_hlsl.amber",
- # CLSPV not built by default
- "opencl_bind_buffer.amber",
- "opencl_c_copy.amber",
- "opencl_set_arg.amber"
],
"Linux": [
- # DXC not currently building on bot
- "draw_triangle_list_hlsl.amber",
- # CLSPV not built by default
- "opencl_bind_buffer.amber",
- "opencl_c_copy.amber",
- "opencl_set_arg.amber"
],
"Win": [
- # DXC not currently building on bot
- "draw_triangle_list_hlsl.amber",
- # CLSPV not built by default
- "opencl_bind_buffer.amber",
- "opencl_c_copy.amber",
- "opencl_set_arg.amber"
- ]
- }
+ ]
+}
+
+DEBUGGER_CASES = [
+ "debugger_hlsl_basic_compute.amber",
+ "debugger_hlsl_basic_fragment.amber",
+ "debugger_hlsl_basic_vertex.amber",
+ "debugger_hlsl_shadowed_vars.amber",
+ "debugger_spirv_line_stepping.amber",
+ "debugger_hlsl_basic_fragment_with_legalization.amber",
+ "debugger_hlsl_basic_vertex_with_legalization.amber",
+ "debugger_hlsl_function_call.amber",
+ "debugger_hlsl_shadowed_vars.amber",
+]
+
+SUPPRESSIONS_SWIFTSHADER = [
+ # Incorrect rendering: github.com/google/amber/issues/727
+ "draw_array_instanced.vkscript",
+ # Exceeds device limit maxComputeWorkGroupInvocations
+ "draw_sampled_image.amber",
+ # No geometry shader support
+ "draw_triangle_list_using_geom_shader.vkscript",
+ # No tessellation shader support
+ "draw_triangle_list_using_tessellation.vkscript",
+ # Vertex buffer format not supported
+ "draw_triangle_list_in_r8g8b8a8_srgb_color_frame.vkscript",
+ "draw_triangle_list_in_r32g32b32a32_sfloat_color_frame.vkscript",
+ "draw_triangle_list_in_r16g16b16a16_uint_color_frame.vkscript",
+ # Color attachment format is not supported
+ "draw_triangle_list_in_r16g16b16a16_snorm_color_frame.vkscript",
+ "draw_triangle_list_in_r8g8b8a8_snorm_color_frame.vkscript",
+ # No supporting device for Float16Int8Features
+ "float16.amber",
+ "int8.amber",
+ # No supporting device for the required 16-bit storage features
+ "storage16.amber",
+ # Exceeded maxBoundDescriptorSets limit of physical device
+ "multiple_ssbo_with_sparse_descriptor_set_in_compute_pipeline.vkscript",
+ # shaderStorageImageWriteWithoutFormat but is not enabled on the device
+ "opencl_read_and_write_image3d_rgba32i.amber",
+ "opencl_write_image.amber",
+ "glsl_read_and_write_image3d_rgba32i.amber",
+ # shaderStorageImageMultisample feature not supported
+ "draw_storageimage_multisample.amber",
+ # Fails on Ubuntu bot
+ "debugger_hlsl_basic_vertex_with_legalization.amber",
+ "debugger_hlsl_function_call.amber",
+ "debugger_hlsl_shadowed_vars.amber",
+ # Unsupported depth/stencil formats
+ "draw_rectangles_depth_test_d24s8.amber",
+ "draw_rectangles_depth_test_x8d24.amber",
+]
+
+OPENCL_CASES = [
+ "opencl_bind_buffer.amber",
+ "opencl_c_copy.amber",
+ "opencl_generated_push_constants.amber",
+ "opencl_read_and_write_image3d_rgba32i.amber",
+ "opencl_read_image.amber",
+ "opencl_read_image_literal_sampler.amber",
+ "opencl_set_arg.amber",
+ "opencl_write_image.amber",
+ ]
+
+DXC_CASES = [
+ "draw_triangle_list_hlsl.amber",
+ "relative_includes_hlsl.amber",
+ "debugger_hlsl_basic_compute.amber",
+ "debugger_hlsl_basic_fragment.amber",
+ "debugger_hlsl_basic_vertex.amber",
+ "debugger_hlsl_shadowed_vars.amber",
+ "debugger_hlsl_basic_fragment_with_legalization.amber",
+ "debugger_hlsl_basic_vertex_with_legalization.amber",
+ "debugger_hlsl_function_call.amber",
+ "debugger_hlsl_shadowed_vars.amber",
+]
SUPPRESSIONS_DAWN = [
# Dawn does not support push constants
@@ -122,13 +178,26 @@ SUPPRESSIONS_DAWN = [
"multiple_ssbo_update_with_graphics_pipeline.vkscript",
# Currently not working, under investigation
"draw_triangle_list_with_depth.vkscript",
+ # draw_grid not implemented for dawn yet
+ "draw_grid.amber",
+ "draw_grid_multiple_color_attachment.amber",
+ "draw_grid_multiple_pipeline.amber",
+ "draw_grids.amber",
+ "draw_grid.vkscript",
+ "draw_grid_with_buffer.amber",
+ "draw_grid_with_two_vertex_data_attached.expect_fail.amber",
]
class TestCase:
- def __init__(self, input_path, parse_only, use_dawn):
+ def __init__(self, input_path, parse_only, use_dawn, use_opencl, use_dxc,
+ use_swiftshader, test_debugger):
self.input_path = input_path
self.parse_only = parse_only
self.use_dawn = use_dawn
+ self.use_opencl = use_opencl
+ self.use_dxc = use_dxc
+ self.use_swiftshader = use_swiftshader
+ self.test_debugger = test_debugger
self.results = {}
@@ -138,10 +207,31 @@ class TestCase:
def IsSuppressed(self):
system = platform.system()
+
+ base = os.path.basename(self.input_path)
+ is_dawn_suppressed = base in SUPPRESSIONS_DAWN
+ if self.use_dawn and is_dawn_suppressed:
+ return True
+
+ is_swiftshader_suppressed = base in SUPPRESSIONS_SWIFTSHADER
+ if self.use_swiftshader and is_swiftshader_suppressed:
+ return True
+
+ is_opencl_test = base in OPENCL_CASES
+ if not self.use_opencl and is_opencl_test:
+ return True
+
+ is_dxc_test = base in DXC_CASES
+ if not self.use_dxc and is_dxc_test:
+ return True
+
+ is_debugger_test = base in DEBUGGER_CASES
+ if not self.test_debugger and is_debugger_test:
+ return True
+
if system in SUPPRESSIONS.keys():
- is_system_suppressed = os.path.basename(self.input_path) in SUPPRESSIONS[system]
- is_dawn_suppressed = os.path.basename(self.input_path) in SUPPRESSIONS_DAWN
- return is_system_suppressed | (self.use_dawn & is_dawn_suppressed)
+ is_system_suppressed = base in SUPPRESSIONS[system]
+ return is_system_suppressed
return False
@@ -160,7 +250,18 @@ class TestCase:
class TestRunner:
def RunTest(self, tc):
- print "Testing %s" % tc.GetInputPath()
+ print("Testing {}".format(tc.GetInputPath()))
+
+ # Amber and SwiftShader both use the VK_DEBUGGER_PORT environment variable
+ # for specifying the Debug Adapter Protocol port number.
+ # This needs to be set before creating the Vulkan device.
+ # We remove this key from the enviroment if the test is not a debugger test
+ # so that full SPIR-V optimizations are preserved.
+ is_debugger_test = os.path.basename(tc.GetInputPath()) in DEBUGGER_CASES
+ if is_debugger_test:
+ os.environ["VK_DEBUGGER_PORT"] = "19020"
+ elif "VK_DEBUGGER_PORT" in os.environ:
+ del os.environ["VK_DEBUGGER_PORT"]
cmd = [self.options.test_prog_path, '-q']
if tc.IsParseOnly():
@@ -171,14 +272,14 @@ class TestRunner:
try:
err = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
- if err != "" and not tc.IsExpectedFail() and not tc.IsSuppressed():
- sys.stdout.write(err)
+ if len(err) != 0 and not tc.IsExpectedFail() and not tc.IsSuppressed():
+ sys.stdout.write(err.decode('utf-8'))
return False
except Exception as e:
- print e.output
if not tc.IsExpectedFail() and not tc.IsSuppressed():
- print e
+ print("{}".format("".join(map(chr, bytearray(e.output)))))
+ print(e)
return False
return True
@@ -201,23 +302,23 @@ class TestRunner:
if len(self.failures) > 0:
self.failures.sort()
- print '\nSummary of Failures:'
+ print('\nSummary of Failures:')
for failure in self.failures:
- print failure
+ print(failure)
if len(self.suppressed) > 0:
self.suppressed.sort()
- print '\nSummary of Suppressions:'
+ print('\nSummary of Suppressions:')
for suppression in self.suppressed:
- print suppression
+ print(suppression)
- print
- print 'Test cases executed: %d' % len(self.test_cases)
- print ' Successes: %d' % (len(self.test_cases) - len(self.suppressed) - len(self.failures))
- print ' Failures: %d' % len(self.failures)
- print ' Suppressed: %d' % len(self.suppressed)
- print
+ print('')
+ print('Test cases executed: {}'.format(len(self.test_cases)))
+ print(' Successes: {}'.format((len(self.test_cases) - len(self.suppressed) - len(self.failures))))
+ print(' Failures: {}'.format(len(self.failures)))
+ print(' Suppressed: {}'.format(len(self.suppressed)))
+ print('')
def Run(self):
@@ -239,32 +340,47 @@ class TestRunner:
parser.add_option('--use-dawn',
action="store_true", default=False,
help='Use dawn as the backend; Default is Vulkan')
+ parser.add_option('--use-opencl',
+ action="store_true", default=False,
+ help='Enable OpenCL tests')
+ parser.add_option('--use-dxc',
+ action="store_true", default=False,
+ help='Enable DXC tests')
+ parser.add_option('--use-swiftshader',
+ action="store_true", default=False,
+ help='Tells test runner swiftshader is the device')
+ parser.add_option('--test-debugger',
+ action="store_true", default=False,
+ help='Include debugger tests')
self.options, self.args = parser.parse_args()
if self.options.test_prog_path == None:
test_prog = os.path.abspath(os.path.join(self.options.build_dir, 'amber'))
if not os.path.isfile(test_prog):
- print "Cannot find test program %s" % test_prog
+ print("Cannot find test program {}".format(test_prog))
return 1
self.options.test_prog_path = test_prog
if not os.path.isfile(self.options.test_prog_path):
- print "--test-prog-path must point to an executable"
+ print("--test-prog-path must point to an executable")
return 1
- input_file_re = re.compile('^.+[.][amber|vkscript]')
+ input_file_re = re.compile('^.+[\.](amber|vkscript)')
self.test_cases = []
if self.args:
for filename in self.args:
input_path = os.path.join(self.options.test_dir, filename)
if not os.path.isfile(input_path):
- print "Cannot find test file '%s'" % filename
+ print("Cannot find test file '{}'".format(filename))
return 1
- self.test_cases.append(TestCase(input_path, self.options.parse_only, self.options.use_dawn))
+ self.test_cases.append(TestCase(input_path, self.options.parse_only,
+ self.options.use_dawn, self.options.use_opencl,
+ self.options.use_dxc, self.options.use_swiftshader,
+ self.options.test_debugger))
else:
for file_dir, _, filename_list in os.walk(self.options.test_dir):
@@ -273,7 +389,10 @@ class TestRunner:
input_path = os.path.join(file_dir, input_filename)
if os.path.isfile(input_path):
self.test_cases.append(
- TestCase(input_path, self.options.parse_only, self.options.use_dawn))
+ TestCase(input_path, self.options.parse_only,
+ self.options.use_dawn, self.options.use_opencl,
+ self.options.use_dxc, self.options.use_swiftshader,
+ self.options.test_debugger))
self.failures = []
self.suppressed = []
diff --git a/third_party/CMakeLists.txt b/third_party/CMakeLists.txt
index 356acb1..4319556 100644
--- a/third_party/CMakeLists.txt
+++ b/third_party/CMakeLists.txt
@@ -38,6 +38,8 @@ if (${AMBER_ENABLE_SHADERC})
set(SHADERC_THIRD_PARTY_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR} CACHE STRING "")
set(SHADERC_SKIP_TESTS ON CACHE BOOL ON)
+ set(SHADERC_ENABLE_WERROR_COMPILE OFF CACHE BOOL "disable werror")
+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/shaderc)
endif()
@@ -65,14 +67,26 @@ if (${AMBER_USE_LOCAL_VULKAN})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/vulkan-validationlayers)
endif()
+if (${AMBER_ENABLE_VK_DEBUGGING})
+ set(CPPDAP_JSON_DIR ${CMAKE_CURRENT_SOURCE_DIR}/json)
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/cppdap)
+endif()
+
if (${AMBER_ENABLE_SWIFTSHADER})
- set(BUILD_EGL FALSE CACHE BOOL FALSE)
- set(BUILD_GLESv2 FALSE CACHE BOOL FALSE)
- set(BUILD_GLES_CM FALSE CACHE BOOL FALSE)
- set(BUILD_VULKAN TRUE CACHE BOOL TRUE)
- set(BUILD_SAMPLES FALSE CACHE BOOL FALSE)
- set(BUILD_TESTS FALSE CACHE BOOL FALSE)
- set(WARNINGS_AS_ERRORS FALSE CACHE BOOL FALSE)
+ set(SWIFTSHADER_BUILD_EGL FALSE)
+ set(SWIFTSHADER_BUILD_GLESv2 FALSE)
+ set(SWIFTSHADER_BUILD_GLES_CM FALSE)
+ set(SWIFTSHADER_BUILD_VULKAN TRUE)
+ set(SWIFTSHADER_BUILD_SAMPLES FALSE)
+ set(SWIFTSHADER_BUILD_TESTS FALSE)
+ set(SWIFTSHADER_WARNINGS_AS_ERRORS FALSE)
+ set(SWIFTSHADER_LOGGING_LEVEL "Error")
+
+ if (${AMBER_ENABLE_VK_DEBUGGING})
+ set(SWIFTSHADER_ENABLE_VULKAN_DEBUGGER TRUE)
+ set(SWIFTSHADER_BUILD_CPPDAP FALSE) # Already built above
+ endif (${AMBER_ENABLE_VK_DEBUGGING})
+
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/swiftshader)
endif()
@@ -131,14 +145,21 @@ if (${AMBER_ENABLE_DXC})
endif()
if (${AMBER_ENABLE_CLSPV})
- set(CLSPV_LLVM_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/clspv-llvm/llvm" CACHE STRING "")
- set(CLSPV_CLANG_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/clspv-llvm/clang" CACHE STRING "")
+ if (NOT DEFINED AMBER_CLSPV_DIR)
+ set(AMBER_CLSPV_DIR "${CMAKE_CURRENT_SOURCE_DIR}/clspv")
+ endif()
+ if (NOT DEFINED AMBER_CLSPV_LLVM_DIR)
+ set(AMBER_CLSPV_LLVM_DIR "${CMAKE_CURRENT_SOURCE_DIR}/clspv-llvm")
+ endif()
+
+ set(CLSPV_LLVM_SOURCE_DIR "${AMBER_CLSPV_LLVM_DIR}/llvm" CACHE STRING "")
+ set(CLSPV_CLANG_SOURCE_DIR "${AMBER_CLSPV_LLVM_DIR}/clang" CACHE STRING "")
set(SPIRV_HEADERS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/spirv-headers" CACHE STRING "")
set(SPIRV_TOOLS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/spirv-tools" CACHE STRING "")
set(LINKER_SUPPORTS_COLOR_DIAGNOSTICS 0 CACHE INTERNAL 0 FORCE)
set(LLVM_TEMPORARILY_ALLOW_OLD_TOOLCHAIN ON CACHE STRING "")
- set(LLVM_BUILD_STATIC ON CACHE BOOL "")
+ set(LLVM_BUILD_STATIC OFF CACHE BOOL "")
set(LLVM_INCLUDE_BENCHMARKS OFF CACHE BOOL "")
set(LLVM_INCLUDE_DOCS OFF CACHE BOOL "")
set(LLVM_INCLUDE_EXAMPLES OFF CACHE BOOL "")
@@ -146,5 +167,5 @@ if (${AMBER_ENABLE_CLSPV})
set(LLVM_INCLUDE_TESTS OFF CACHE BOOL "")
set(LLVM_INCLUDE_UTILS OFF CACHE BOOL "")
- add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/clspv)
+ add_subdirectory(${AMBER_CLSPV_DIR} ${CMAKE_CURRENT_BINARY_DIR}/clspv)
endif()
diff --git a/tools/amber-syntax.vim b/tools/amber-syntax.vim
index dfc77f9..4da5204 100644
--- a/tools/amber-syntax.vim
+++ b/tools/amber-syntax.vim
@@ -37,8 +37,28 @@ syn keyword amberBlockCmd FORMAT FRAMEBUFFER_SIZE LOCATION BIND SAMPLER
syn keyword amberBlockCmd VERTEX_DATA INDEX_DATA INDEXED IMAGE_ATTACHMENT
syn keyword amberBlockCmd DEPTH_STENCIL_ATTACHMENT DEVICE_FEATURE TOLERANCE
syn keyword amberBlockCmd REPEAT COPY DERIVE_PIPELINE FROM STD140 STD430
+syn keyword amberBlockCmd DEVICE_EXTENSION IMAGE INSTANCE_EXTENSION SET
+syn keyword amberBlockCmd STRUCT VIRTUAL_FILE CLEAR_DEPTH CLEAR_STENCIL
+syn keyword amberBlockCmd DEBUG TARGET_ENV SHADER_OPTIMIZATION COMPILE_OPTIONS
+syn keyword amberBlockCmd POLYGON_MODE DEPTH STENCIL SUBGROUP SPECIALIZE
+syn keyword amberBlockCmd FULLY_POPULATED VARYING_SIZE REQUIRED_SIZE
+syn keyword amberBlockCmd MIN MAX BUFFER_ARRAY SAMPLER_ARRAY KERNEL OFFSET
+syn keyword amberBlockCmd BASE_MIP_LEVEL ARG_NUMBER RATE TEST CLAMP
+syn keyword amberBlockCmd WRITE COMPARE_OP BOUNDS BIAS FAIL_OP PASS_OP
+syn keyword amberBlockCmd DEPTH_FAIL_OP COMPARE_MASK WRITE_MASK REFERENCE
+syn keyword amberBlockCmd STRIDE ARRAY_STRIDE MATRIX_STRIDE MIP_LEVELS
+syn keyword amberBlockCmd FILE DIM_1D DIM_2D DIM_3D WIDTH HEIGHT SAMPLES
+syn keyword amberBlockCmd TEXT BINARY PNG DRAW_GRID CELLS INSTANCE_COUNT
+syn keyword amberBlockCmd START_INSTANCE THREAD GLOBAL_INVOCATION_ID
+syn keyword amberBlockCmd VERTEX_INDEX FRAGMENT_WINDOW_SPACE_POSITION
+syn keyword amberBlockCmd CALLSTACK STEP_IN STEP_OUT STEP_OVER CONTINUE TO
+syn keyword amberBlockCmd ENGINE_DATA MAG_FILTER MIN_FILTER ADDRESS_MODE_U
+syn keyword amberBlockCmd ADDRESS_MODE_V ADDRESS_MODE_W BORDER_COLOR
+syn keyword amberBlockCmd MIN_LOD MAX_LOD NORMALIZED_COORDS UNNORMALIZED_COORDS
+syn keyword amberBlockCmd ARG_NAME
syn keyword amberComparator EQ NE LT LE GT GE EQ_RGB EQ_RGBA EQ_BUFFER RMSE_BUFFER
+syn keyword amberComparator EQ_HISTOGRAM_EMD_BUFFER
syn keyword amberKeyword compute vertex geometry fragment graphics
syn keyword amberKeyword tessellation_evaulation tessellation_control multi
@@ -51,6 +71,28 @@ syn keyword amberTopology triangle_list_with_adjacench triangle_strip
syn keyword amberTopology triangle_strip_with_adjacency triangle_fan patch_list
syn keyword amberBufferType uniform storage push_constant color depth_stencil
+syn keyword amberBufferType uniform_dynamic storage_dynamic combined_image_sampler
+syn keyword amberBufferType storage_image sampled_image uniform_texel_buffer
+syn keyword amberBufferType storage_texel_buffer
+
+syn keyword amberAddressMode repeat mirrored_repeat clamp_to_edge clamp_to_border
+syn keyword amberAddressMode mirror_clamp_to_edge
+
+syn keyword amberCompareOp never less equal less_or_equal greater not_equal
+syn keyword amberCompareOp greater_or_equal always
+
+syn keyword amberStencilOp keep zero replace increment_and_clamp decrement_and_clamp
+syn keyword amberStencilOp invert increment_and_wrap decrement_and_wrap
+
+syn keyword amberBorderColor float_transparent_black int_transparent_black
+syn keyword amberBorderColor float_opaque_black int_opaque_black float_opaque_white
+syn keyword amberBorderColor int_opaque_white
+
+syn keyword amberFace front back front_and_back
+
+syn keyword amberFilter nearest linear
+
+syn keyword amberPolygonMode fill line point
let b:current_syntax = "amber"
hi def link amberTodo Todo
@@ -65,3 +107,10 @@ hi def link amberFormat Type
hi def link amberComparator Keyword
hi def link amberTopology Type
hi def link amberBufferType Type
+hi def link amberAddressMode Type
+hi def link amberCompareOp Type
+hi def link amberStencilOp Type
+hi def link amberBorderColor Type
+hi def link amberFace Type
+hi def link amberFilter Type
+hi def link amberPolygonMode Type
diff --git a/tools/amber.sublime-syntax b/tools/amber.sublime-syntax
index a21ab92..3141153 100644
--- a/tools/amber.sublime-syntax
+++ b/tools/amber.sublime-syntax
@@ -21,7 +21,7 @@ contexts:
scope: keyword.control.amber
- match: '\b(PASSTHROUGH|PIPELINE|ATTACH|ENTRY_POINT|DESCRIPTOR_SET|INC_BY)\b'
scope: keyword.control.amber
- - match: '\b(BINDING|IDX|TO|RUN|DRAW_RECT|POS|DRAW_ARRAY|IN|AS|START_IDX)\b'
+ - match: '\b(BINDING|IDX|TO|RUN|DRAW_RECT|POS|DRAW_GRID|CELLS|DRAW_ARRAY|IN|AS|START_IDX)\b'
scope: keyword.control.amber
- match: '\b(COUNT|CLEAR_COLOR|CLEAR|EXPECT|TYPE|FRAMEBUFFER|SHADER_OPTIMIZATION)\b'
scope: keyword.control.amber
diff --git a/tools/check_code_format.sh b/tools/check_code_format.sh
index e4818f9..72a8398 100755
--- a/tools/check_code_format.sh
+++ b/tools/check_code_format.sh
@@ -18,14 +18,14 @@
#
# This script assumes to be invoked at the project root directory.
-FILES_TO_CHECK=$(git diff --name-only master | grep -E ".*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$")
+FILES_TO_CHECK=$(git diff --name-only main | grep -E ".*\.(cpp|cc|c\+\+|cxx|c|h|hpp)$")
if [ -z "${FILES_TO_CHECK}" ]; then
echo "No source code to check for formatting."
exit 0
fi
-FORMAT_DIFF=$(git diff -U0 master -- ${FILES_TO_CHECK} | python ./tools/clang-format-diff.py -p1 -style=file)
+FORMAT_DIFF=$(git diff -U0 main -- ${FILES_TO_CHECK} | python ./tools/clang-format-diff.py -p1 -style=file)
if [ -z "${FORMAT_DIFF}" ]; then
echo "All source code in PR properly formatted."
diff --git a/tools/check_language.py b/tools/check_language.py
new file mode 100755
index 0000000..b7ca528
--- /dev/null
+++ b/tools/check_language.py
@@ -0,0 +1,179 @@
+#!/usr/bin/env python
+
+# Copyright 2020 The Amber Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Script to check files for inclusive language. The script will scan all files
+and flag non-inclusive terminology which is identified.
+
+Usage, run the script from a folder and the script will scan down through that
+folder.
+"""
+
+import fnmatch
+import os
+import re
+import sys
+
+REGEXES = [
+ r"(?i)black[-_]?list",
+ r"(?i)white[-_]?list",
+ r"(?i)gr[ea]y[-_]?list",
+ r"(?i)(first class citizen)",
+ r"(?i)black[-_]?hat",
+ r"(?i)white[-_]?hat",
+ r"(?i)gr[ea]y[-_]?hat",
+ r"(?i)master",
+ r"(?i)slave",
+ r"(?i)\bhim\b",
+ r"(?i)\bhis\b",
+ r"(?i)\bshe\b",
+ r"(?i)\bher\b",
+ r"(?i)\bhers\b",
+ r"(?i)\bman\b",
+ r"(?i)\bwoman\b",
+ r"(?i)\she\s",
+ r"(?i)\she$",
+ r"(?i)^he\s",
+ r"(?i)^he$",
+ r"(?i)\she['|\u2019]d\s",
+ r"(?i)\she['|\u2019]d$",
+ r"(?i)^he['|\u2019]d\s",
+ r"(?i)^he['|\u2019]d$",
+ r"(?i)\she['|\u2019]s\s",
+ r"(?i)\she['|\u2019]s$",
+ r"(?i)^he['|\u2019]s\s",
+ r"(?i)^he['|\u2019]s$",
+ r"(?i)\she['|\u2019]ll\s",
+ r"(?i)\she['|\u2019]ll$",
+ r"(?i)^he['|\u2019]ll\s",
+ r"(?i)^he['|\u2019]ll$",
+ r"(?i)grandfather",
+ r"(?i)\bmitm\b",
+ r"(?i)\bcrazy\b",
+ r"(?i)\binsane\b",
+ r"(?i)\bblind\sto\b",
+ r"(?i)\bflying\sblind\b",
+ r"(?i)\bblind\seye\b",
+ r"(?i)\bcripple\b",
+ r"(?i)\bcrippled\b",
+ r"(?i)\bdumb\b",
+ r"(?i)\bdummy\b",
+ r"(?i)\bparanoid\b",
+ r"(?i)\bsane\b",
+ r"(?i)\bsanity\b",
+ r"(?i)red[-_]?line",
+]
+
+SUPPRESSIONS = [
+ r"(?i)MS_SLAVE",
+ r"(?i)man[ -_]?page",
+]
+
+
+REGEX_LIST = []
+for reg in REGEXES:
+ REGEX_LIST.append(re.compile(reg))
+
+SUPPRESSION_LIST = []
+for supp in SUPPRESSIONS:
+ SUPPRESSION_LIST.append(re.compile(supp))
+
+def find(top, filename_glob, skip_glob_list):
+ """Returns files in the tree rooted at top matching filename_glob but not
+ in directories matching skip_glob_list."""
+
+ file_list = []
+ for path, dirs, files in os.walk(top):
+ for glob in skip_glob_list:
+ for match in fnmatch.filter(dirs, glob):
+ dirs.remove(match)
+ for filename in fnmatch.filter(files, filename_glob):
+ if filename == os.path.basename(__file__):
+ continue
+ file_list.append(os.path.join(path, filename))
+ return file_list
+
+
+def filtered_descendants(glob):
+ """Returns glob-matching filenames under the current directory, but skips
+ some irrelevant paths."""
+ return find('.', glob, ['third_party', 'external', 'build*', 'out*',
+ 'CompilerIdCXX', '.git'])
+
+def check_match(filename, contents):
+ """Check if contents contains any matching entries"""
+ ret = False
+ for reg in REGEX_LIST:
+ match = reg.search(contents)
+ if match:
+ suppressed = False
+ for supp in SUPPRESSION_LIST:
+ idx = match.start()
+ supp_match = supp.match(contents[idx:])
+ if supp_match:
+ suppressed = True
+
+ # This is a hack to handle the MS_ prefix that is needed
+ # to check for. Find a better way if we get more suppressions
+ # which modify the prefix of the string
+ if idx >= 3:
+ supp_match = supp.match(contents[idx - 3:])
+ if supp_match:
+ suppressed = True
+
+ if not suppressed:
+ # No matching suppression.
+ print("{}: found non-inclusive language: {}".format(
+ filename, match.group(0)))
+ ret = True
+
+ return ret
+
+
+def alert_if_lang_matches(glob):
+ """Prints names of all files matching non-inclusive language.
+
+ Finds all glob-matching files under the current directory and checks if they
+ contain the language pattern. Prints the names of all the files that
+ match.
+
+ Returns the total number of file names printed.
+ """
+ verbose = False
+ printed_count = 0
+ for file in filtered_descendants(glob):
+ has_match = False
+ try:
+ with open(file, 'r', encoding='utf8') as contents:
+ if check_match(file, contents.read()):
+ printed_count += 1
+ except:
+ if verbose:
+ print("skipping {}".format(file))
+
+ return printed_count
+
+
+def main():
+ globs = ['*']
+ count = 0
+ for glob in globs:
+ count += alert_if_lang_matches(glob)
+
+ sys.exit(count > 0)
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/check_language_test.py b/tools/check_language_test.py
new file mode 100755
index 0000000..8f20791
--- /dev/null
+++ b/tools/check_language_test.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+# Copyright 2020 The Amber Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unit tests for check_language.py."""
+
+import os
+import sys
+import unittest
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+import check_language
+
+class TestCheckLanguage(unittest.TestCase):
+ def testMatches(self):
+ tests = ["blacklist", "black-list", "black_list", "whitelist",
+ "white-list", "white_list", "greylist", "grey-list", "grey_list",
+ "graylist", "gray-list", "gray_list", "first class citizen",
+ "blackhat", "black-hat", "black_hat", "whitehat", "white-hat",
+ "white_hat", "greyhat", "grey-hat", "grey_hat", "grayhat",
+ "gray-hat", "gray_hat", "master", "slave", "him", "his", "she",
+ "her", "hers", "man", "woman", "he", "he'd", "he's", "he'll",
+ "he\u2019d", "he\u2019s", "he\u2019ll",
+ "grandfather", "mitm", "crazy", "insane", "blind to",
+ "flying blind", "blind eye", "cripple", "crippled", "dumb",
+ "dummy", "paranoid", "sane", "sanity", "redline", "red-line",
+ "red_line"]
+
+ for word in tests:
+ self.assertTrue(
+ check_language.check_match("", "this is a " + word + " attempt"), word)
+
+
+ def testSuppression(self):
+ self.assertFalse(check_language.check_match("", "in the man-pages"))
+ self.assertFalse(check_language.check_match("", "the MS_SLAVE test"))
+
+
+ def testMatchStartofFileWhenRequireSpace(self):
+ self.assertTrue(check_language.check_match("", "he said"))
+
+
+ def testMatchOverNewline(self):
+ self.assertTrue(check_language.check_match("", "flying\nblind"))
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/format b/tools/format
index 867f99d..5c2a87a 100755
--- a/tools/format
+++ b/tools/format
@@ -1,5 +1,19 @@
#!/bin/bash
+# Copyright 2020 The Amber Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 src -name "*.h" -exec clang-format -i {} \;
find src -name "*.cc" -exec clang-format -i {} \;
find samples -name "*.h" -exec clang-format -i {} \;
diff --git a/tools/git-sync-deps b/tools/git-sync-deps
index 4da1217..3090588 100755
--- a/tools/git-sync-deps
+++ b/tools/git-sync-deps
@@ -1,37 +1,27 @@
-#!/usr/bin/env python
-# Copyright 2014 Google Inc.
+#!/usr/bin/env python3
+# Copyright 2019 Google LLC
#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * 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.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
+# http://www.apache.org/licenses/LICENSE-2.0
#
-# 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
-# OWNER 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.
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
"""Parse a DEPS file and git checkout all of the dependencies.
Args:
An optional list of deps_os values.
+ --with-swiftshader Checkout Swiftshader dependencies
+ --with-clspv Checkout clspv dependencies
+ --with-dxc Checkout dxc dependencies
+
Environment Variables:
GIT_EXECUTABLE: path to "git" binary; if unset, will look for one of
['git', 'git.exe', 'git.bat'] in your default path.
@@ -51,13 +41,16 @@ Git Config:
git config --unset sync-deps.disable
"""
-
-from builtins import bytes
import os
+import re
import subprocess
import sys
import threading
+from builtins import bytes
+with_clspv = False
+with_dxc = False
+with_swiftshader = False
def git_executable():
"""Find the git executable.
@@ -150,6 +143,9 @@ def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
Raises an exception if any calls to git fail.
"""
+ if verbose:
+ status(directory, checkoutable)
+
if not os.path.isdir(directory):
subprocess.check_call(
[git, 'clone', '--quiet', repo, directory])
@@ -171,8 +167,6 @@ def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
if 0 == subprocess.call([git, 'checkout', '--quiet', checkoutable],
cwd=directory, stderr=devnull):
# if this succeeds, skip slow `git fetch`.
- if verbose:
- status(directory, checkoutable) # Success.
return
# If the repo has changed, always force use of the correct repo.
@@ -184,13 +178,15 @@ def git_checkout_to_directory(git, repo, checkoutable, directory, verbose):
subprocess.check_call([git, 'checkout', '--quiet', checkoutable], cwd=directory)
- if verbose:
- status(directory, checkoutable) # Success.
-
def parse_file_to_dict(path):
dictionary = {}
- exec(compile(open(path).read(), path, 'exec'), dictionary)
+ contents = open(path).read()
+ # Need to convert Var() to vars[], so that the DEPS is actually Python. Var()
+ # comes from Autoroller using gclient which has a slightly different DEPS
+ # format.
+ contents = re.sub(r"Var\((.*?)\)", r"vars[\1]", contents)
+ exec(contents, dictionary)
return dictionary
@@ -228,10 +224,22 @@ def git_sync_deps(deps_file_path, command_line_os_requests, verbose):
list_of_arg_lists = []
for directory in sorted(dependencies):
if '@' in dependencies[directory]:
- repo, checkoutable = dependencies[directory].split('@', 1)
+ repo, checkoutable = dependencies[directory].rsplit('@', 1)
else:
raise Exception("please specify commit or tag")
+ if not with_clspv and directory == 'third_party/clspv':
+ continue
+
+ if not with_clspv and directory == 'third_party/clspv-llvm':
+ continue
+
+ if not with_dxc and directory == 'third_party/dxc':
+ continue
+
+ if not with_swiftshader and directory == 'third_party/swiftshader':
+ continue
+
relative_directory = os.path.join(deps_file_directory, directory)
list_of_arg_lists.append(
@@ -258,6 +266,9 @@ def multithread(function, list_of_arg_lists):
def main(argv):
+ global with_clspv
+ global with_dxc
+ global with_swiftshader
deps_file_path = os.environ.get('GIT_SYNC_DEPS_PATH', DEFAULT_DEPS_PATH)
verbose = not bool(os.environ.get('GIT_SYNC_DEPS_QUIET', False))
@@ -265,6 +276,15 @@ def main(argv):
usage(deps_file_path)
return 1
+ if '--with-clspv' in argv:
+ with_clspv = True
+
+ if '--with-dxc' in argv:
+ with_dxc = True
+
+ if '--with-swiftshader' in argv:
+ with_swiftshader = True
+
git_sync_deps(deps_file_path, argv, verbose)
# subprocess.check_call(
# [sys.executable,
diff --git a/tools/roll-all b/tools/roll-all
new file mode 100755
index 0000000..97f00c3
--- /dev/null
+++ b/tools/roll-all
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# Copyright 2020 The Amber Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Defined to use origin/master instead of origin/main
+clspv=1
+clspv_llvm=1
+cpplint=1
+dxc=1
+glslang=1
+googletest=1
+json=1
+lodepng=1
+spirv_headers=1
+spirv_tools=1
+swiftshader=1
+vulkan_headers=1
+vulkan_loader=1
+vulkan_validationlayers=1
+
+
+# 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)
+
+for i in $(find third_party -maxdepth 1 -mindepth 1 -type d); do
+ name=`echo "${i%%/}" | cut -f2 -d'/' | tr '-' '_'`
+
+ if [ "x${name}" == "xcpplint" ]; then
+ continue
+ fi
+
+ trunk="origin/main"
+ if [ ! -z ${!name} ]; then
+ trunk="origin/master"
+ fi
+
+ roll-dep --ignore-dirty-tree --roll-to="${trunk}" "${i%%/}";
+done
+
+git rebase --interactive "${old_head}"
diff --git a/tools/update_vk_wrappers.py b/tools/update_vk_wrappers.py
index 9acfc8e..0fc9f87 100755
--- a/tools/update_vk_wrappers.py
+++ b/tools/update_vk_wrappers.py
@@ -186,36 +186,40 @@ def main():
outdir = sys.argv[1]
srcdir = sys.argv[2]
- vkfile = os.path.join(srcdir, 'third_party', 'vulkan-headers', 'registry', 'vk.xml')
- incfile = os.path.join(srcdir, 'src', 'vulkan', 'vk-funcs.inc')
-
- data = read_inc(incfile)
-
- wrapper_content = ''
- header_content = ''
- if os.path.isfile(vkfile):
- vk_data = read_vk(vkfile)
- wrapper_content = gen_wrappers(data, vk_data)
- header_content = gen_headers(data, vk_data)
- else:
- wrapper_content = gen_direct(data)
- header_content = gen_direct_headers(data)
-
- outfile = os.path.join(outdir, 'vk-wrappers.inc')
- if os.path.isfile(outfile):
- with open(outfile, 'r') as f:
- if wrapper_content == f.read():
- return
- with open(outfile, 'w') as f:
- f.write(wrapper_content)
-
- hdrfile = os.path.join(outdir, 'vk-wrappers.h')
- if os.path.isfile(hdrfile):
- with open(hdrfile, 'r') as f:
- if header_content == f.read():
- return
- with open(hdrfile, 'w') as f:
- f.write(header_content)
+ vulkan_versions = ("1-0", "1-1")
+
+ for vulkan_version in vulkan_versions:
+
+ vkfile = os.path.join(srcdir, 'third_party', 'vulkan-headers', 'registry', 'vk.xml')
+ incfile = os.path.join(srcdir, 'src', 'vulkan', 'vk-funcs-%s.inc' % vulkan_version)
+
+ data = read_inc(incfile)
+
+ wrapper_content = ''
+ header_content = ''
+ if os.path.isfile(vkfile):
+ vk_data = read_vk(vkfile)
+ wrapper_content = gen_wrappers(data, vk_data)
+ header_content = gen_headers(data, vk_data)
+ else:
+ wrapper_content = gen_direct(data)
+ header_content = gen_direct_headers(data)
+
+ outfile = os.path.join(outdir, 'vk-wrappers-%s.inc' % vulkan_version)
+ if os.path.isfile(outfile):
+ with open(outfile, 'r') as f:
+ if wrapper_content == f.read():
+ return
+ with open(outfile, 'w') as f:
+ f.write(wrapper_content)
+
+ hdrfile = os.path.join(outdir, 'vk-wrappers-%s.h' % vulkan_version)
+ if os.path.isfile(hdrfile):
+ with open(hdrfile, 'r') as f:
+ if header_content == f.read():
+ return
+ with open(hdrfile, 'w') as f:
+ f.write(header_content)
if __name__ == '__main__':